Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-lambda): add streaming support to main preset #2786

Draft
wants to merge 10 commits into
base: v2
Choose a base branch
from
6 changes: 4 additions & 2 deletions src/presets/_types.gen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Auto-generated using gen-presets script

import type { PresetOptions as AwsAmplifyOptions } from "./aws-amplify/preset";
import type { PresetOptions as AwsLambdaOptions } from "./aws-lambda/preset";
import type { PresetOptions as AzureOptions } from "./azure/preset";
import type { PresetOptions as CloudflareOptions } from "./cloudflare/preset";
import type { PresetOptions as FirebaseOptions } from "./firebase/preset";
Expand All @@ -9,13 +10,14 @@ import type { PresetOptions as VercelOptions } from "./vercel/preset";

export interface PresetOptions {
awsAmplify: AwsAmplifyOptions;
awsLambda: AwsLambdaOptions;
azure: AzureOptions;
cloudflare: CloudflareOptions;
firebase: FirebaseOptions;
netlify: NetlifyOptions;
vercel: VercelOptions;
}

export type PresetName = "alwaysdata" | "aws-amplify" | "aws-lambda" | "aws-lambda-streaming" | "azure" | "azure-functions" | "azure-swa" | "base-worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-module" | "cloudflare-pages" | "cloudflare-pages-static" | "cloudflare-worker" | "deno" | "deno-deploy" | "deno-server" | "digital-ocean" | "edgio" | "firebase" | "flight-control" | "genezio" | "github-pages" | "gitlab-pages" | "heroku" | "iis" | "iis-handler" | "iis-node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlify-edge" | "netlify-legacy" | "netlify-static" | "nitro-dev" | "nitro-prerender" | "node" | "node-cluster" | "node-listener" | "node-server" | "platform-sh" | "render-com" | "service-worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercel-static" | "winterjs" | "zeabur" | "zeabur-static" | "zerops" | "zerops-static";
export type PresetName = "alwaysdata" | "aws-amplify" | "aws-lambda" | "azure" | "azure-functions" | "azure-swa" | "base-worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-module" | "cloudflare-pages" | "cloudflare-pages-static" | "cloudflare-worker" | "deno" | "deno-deploy" | "deno-server" | "digital-ocean" | "edgio" | "firebase" | "flight-control" | "genezio" | "github-pages" | "gitlab-pages" | "heroku" | "iis" | "iis-handler" | "iis-node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlify-edge" | "netlify-legacy" | "netlify-static" | "nitro-dev" | "nitro-prerender" | "node" | "node-cluster" | "node-listener" | "node-server" | "platform-sh" | "render-com" | "service-worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercel-static" | "winterjs" | "zeabur" | "zeabur-static" | "zerops" | "zerops-static";

export type PresetNameInput = "alwaysdata" | "aws-amplify" | "awsAmplify" | "aws_amplify" | "aws-lambda" | "awsLambda" | "aws_lambda" | "aws-lambda-streaming" | "awsLambdaStreaming" | "aws_lambda_streaming" | "azure" | "azure-functions" | "azureFunctions" | "azure_functions" | "azure-swa" | "azureSwa" | "azure_swa" | "base-worker" | "baseWorker" | "base_worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-module" | "cloudflareModule" | "cloudflare_module" | "cloudflare-pages" | "cloudflarePages" | "cloudflare_pages" | "cloudflare-pages-static" | "cloudflarePagesStatic" | "cloudflare_pages_static" | "cloudflare-worker" | "cloudflareWorker" | "cloudflare_worker" | "deno" | "deno-deploy" | "denoDeploy" | "deno_deploy" | "deno-server" | "denoServer" | "deno_server" | "digital-ocean" | "digitalOcean" | "digital_ocean" | "edgio" | "firebase" | "flight-control" | "flightControl" | "flight_control" | "genezio" | "github-pages" | "githubPages" | "github_pages" | "gitlab-pages" | "gitlabPages" | "gitlab_pages" | "heroku" | "iis" | "iis-handler" | "iisHandler" | "iis_handler" | "iis-node" | "iisNode" | "iis_node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlifyBuilder" | "netlify_builder" | "netlify-edge" | "netlifyEdge" | "netlify_edge" | "netlify-legacy" | "netlifyLegacy" | "netlify_legacy" | "netlify-static" | "netlifyStatic" | "netlify_static" | "nitro-dev" | "nitroDev" | "nitro_dev" | "nitro-prerender" | "nitroPrerender" | "nitro_prerender" | "node" | "node-cluster" | "nodeCluster" | "node_cluster" | "node-listener" | "nodeListener" | "node_listener" | "node-server" | "nodeServer" | "node_server" | "platform-sh" | "platformSh" | "platform_sh" | "render-com" | "renderCom" | "render_com" | "service-worker" | "serviceWorker" | "service_worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercelEdge" | "vercel_edge" | "vercel-static" | "vercelStatic" | "vercel_static" | "winterjs" | "zeabur" | "zeabur-static" | "zeaburStatic" | "zeabur_static" | "zerops" | "zerops-static" | "zeropsStatic" | "zerops_static" | (string & {});
export type PresetNameInput = "alwaysdata" | "aws-amplify" | "awsAmplify" | "aws_amplify" | "aws-lambda" | "awsLambda" | "aws_lambda" | "azure" | "azure-functions" | "azureFunctions" | "azure_functions" | "azure-swa" | "azureSwa" | "azure_swa" | "base-worker" | "baseWorker" | "base_worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-module" | "cloudflareModule" | "cloudflare_module" | "cloudflare-pages" | "cloudflarePages" | "cloudflare_pages" | "cloudflare-pages-static" | "cloudflarePagesStatic" | "cloudflare_pages_static" | "cloudflare-worker" | "cloudflareWorker" | "cloudflare_worker" | "deno" | "deno-deploy" | "denoDeploy" | "deno_deploy" | "deno-server" | "denoServer" | "deno_server" | "digital-ocean" | "digitalOcean" | "digital_ocean" | "edgio" | "firebase" | "flight-control" | "flightControl" | "flight_control" | "genezio" | "github-pages" | "githubPages" | "github_pages" | "gitlab-pages" | "gitlabPages" | "gitlab_pages" | "heroku" | "iis" | "iis-handler" | "iisHandler" | "iis_handler" | "iis-node" | "iisNode" | "iis_node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlifyBuilder" | "netlify_builder" | "netlify-edge" | "netlifyEdge" | "netlify_edge" | "netlify-legacy" | "netlifyLegacy" | "netlify_legacy" | "netlify-static" | "netlifyStatic" | "netlify_static" | "nitro-dev" | "nitroDev" | "nitro_dev" | "nitro-prerender" | "nitroPrerender" | "nitro_prerender" | "node" | "node-cluster" | "nodeCluster" | "node_cluster" | "node-listener" | "nodeListener" | "node_listener" | "node-server" | "nodeServer" | "node_server" | "platform-sh" | "platformSh" | "platform_sh" | "render-com" | "renderCom" | "render_com" | "service-worker" | "serviceWorker" | "service_worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercelEdge" | "vercel_edge" | "vercel-static" | "vercelStatic" | "vercel_static" | "winterjs" | "zeabur" | "zeabur-static" | "zeaburStatic" | "zeabur_static" | "zerops" | "zerops-static" | "zeropsStatic" | "zerops_static" | (string & {});
24 changes: 12 additions & 12 deletions src/presets/aws-lambda/preset.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { defineNitroPreset } from "nitropack/kit";
export type { AwsLambdaOptions as PresetOptions } from "./types";

const awsLambda = defineNitroPreset(
{
entry: "./runtime/aws-lambda",
awsLambda: {
streaming: false,
},
hooks: {
"rollup:before": (nitro, rollupConfig) => {
if (nitro.options.awsLambda.streaming) {
(rollupConfig.input as string) += "-streaming";
}
},
},
},
{
name: "aws-lambda" as const,
url: import.meta.url,
}
);

const awsLambdaStreaming = defineNitroPreset(
{
extends: "aws-lambda",
entry: "./runtime/aws-lambda-streaming",
},
{
name: "aws-lambda-streaming" as const,
url: import.meta.url,
}
);

export default [awsLambda, awsLambdaStreaming] as const;
export default [awsLambda] as const;
68 changes: 37 additions & 31 deletions src/presets/aws-lambda/runtime/aws-lambda-streaming.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Readable } from "node:stream";
import type {
APIGatewayProxyEventV2,
APIGatewayProxyStructuredResultV2,
Expand All @@ -11,31 +12,6 @@ import {
} from "nitropack/runtime/internal";
import { withQuery } from "ufo";

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace awslambda {
// https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html
function streamifyResponse(
handler: (
event: APIGatewayProxyEventV2,
responseStream: NodeJS.WritableStream,
context: Context
) => Promise<void>
): any;

// eslint-disable-next-line @typescript-eslint/no-namespace
namespace HttpResponseStream {
function from(
stream: NodeJS.WritableStream,
metadata: {
statusCode: APIGatewayProxyStructuredResultV2["statusCode"];
headers: APIGatewayProxyStructuredResultV2["headers"];
}
): NodeJS.WritableStream;
}
}
}

const nitroApp = useNitroApp();

export const handler = awslambda.streamifyResponse(
Expand Down Expand Up @@ -72,25 +48,55 @@ export const handler = awslambda.streamifyResponse(
},
};
if (r.body) {
const reader = r.body as ReadableStream;
const writer = awslambda.HttpResponseStream.from(
responseStream,
httpResponseMetadata
);
await streamToNodeStream(reader.getReader(), responseStream);
if (!(r.body as ReadableStream).getReader) {
writer.write(r.body as any /* TODO */);
writer.end();
return;
}
const reader = (r.body as ReadableStream).getReader();
await streamToNodeStream(reader, responseStream);
writer.end();
}
}
);

const streamToNodeStream = async (
reader: ReadableStreamDefaultReader<Uint8Array>,
async function streamToNodeStream(
reader: Readable | ReadableStreamDefaultReader,
writer: NodeJS.WritableStream
) => {
) {
let readResult = await reader.read();
while (!readResult.done) {
writer.write(readResult.value);
readResult = await reader.read();
}
writer.end();
};
}

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace awslambda {
// https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html
function streamifyResponse(
handler: (
event: APIGatewayProxyEventV2,
responseStream: NodeJS.WritableStream,
context: Context
) => Promise<void>
): any;

// eslint-disable-next-line @typescript-eslint/no-namespace
namespace HttpResponseStream {
function from(
stream: NodeJS.WritableStream,
metadata: {
statusCode: APIGatewayProxyStructuredResultV2["statusCode"];
headers: APIGatewayProxyStructuredResultV2["headers"];
}
): NodeJS.WritableStream;
}
}
}
3 changes: 3 additions & 0 deletions src/presets/aws-lambda/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface AwsLambdaOptions {
streaming?: boolean;
}