sst 2.36.5 → 2.36.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -158,7 +158,7 @@ export interface ApiGatewayV1ApiProps<Authorizers extends Record<string, ApiGate
158
158
  */
159
159
  id?: string;
160
160
  /**
161
- * Override the internally created rest api
161
+ * Override the internally created REST API.
162
162
  *
163
163
  * @example
164
164
  * ```js
@@ -195,9 +195,12 @@ export interface ApiGatewayV1ApiProps<Authorizers extends Record<string, ApiGate
195
195
  * ```
196
196
  *
197
197
  * API Gateway REST API is structured in a tree structure:
198
- * - Each path part is a separate API Gateway resource object.
199
- * - And a path part is a child resource of the preceding part.
200
- * So the part path /notes, is a child resource of the root resource /. And /notes/{noteId} is a child resource of /notes. If /notes has been created in the imported API, you have to import it before creating the /notes/{noteId} child route.
198
+ *
199
+ * - Each path part is a separate API Gateway resource object
200
+ * - And a path part is a child resource of the preceding part
201
+ *
202
+ * So the part path `/notes`, is a child resource of the root resource `/`. And `/notes/{noteId}` is a child resource of `/notes`.
203
+ * If `/notes` has been created in the imported API, you have to import it before creating the `/notes/{noteId}` child route.
201
204
  */
202
205
  importedPaths?: {
203
206
  [path: string]: string;
@@ -30,6 +30,7 @@ export declare class AstroSite extends SsrSite {
30
30
  type: "function";
31
31
  constructId: string;
32
32
  function: import("./SsrFunction.js").SsrFunctionProps;
33
+ injections?: string[] | undefined;
33
34
  streaming?: boolean | undefined;
34
35
  } | {
35
36
  type: "image-optimization-function";
@@ -1,11 +1,8 @@
1
1
  import { Construct } from "constructs";
2
- import { Duration as CdkDuration } from "aws-cdk-lib/core";
3
2
  import { Runtime, FunctionProps, Architecture } from "aws-cdk-lib/aws-lambda";
4
3
  import { SsrSite, SsrSiteNormalizedProps, SsrSiteProps } from "./SsrSite.js";
5
4
  import { Size } from "./util/size.js";
6
5
  import { Bucket } from "aws-cdk-lib/aws-s3";
7
- import { PolicyStatement } from "aws-cdk-lib/aws-iam";
8
- import { RetentionDays } from "aws-cdk-lib/aws-logs";
9
6
  import { CachePolicyProps } from "aws-cdk-lib/aws-cloudfront";
10
7
  export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
11
8
  /**
@@ -148,78 +145,15 @@ export declare class NextjsSite extends SsrSite {
148
145
  edgeServer: {
149
146
  constructId: string;
150
147
  function: {
151
- layers: import("aws-cdk-lib/aws-lambda").ILayerVersion[] | undefined;
148
+ description: string;
149
+ bundle: string;
152
150
  handler: string;
153
- bundle?: string | undefined;
154
- runtime?: "nodejs16.x" | "nodejs18.x" | "nodejs20.x" | undefined;
155
- timeout?: number | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | undefined;
156
- memorySize?: number | `${number} MB` | `${number} GB` | undefined;
157
- permissions?: import("./index.js").Permissions | undefined;
158
- environment?: Record<string, string> | undefined;
159
- bind?: import("./Construct.js").SSTConstruct[] | undefined;
160
- nodejs?: import("./Function.js").NodeJSProps | undefined;
161
- copyFiles?: import("./Function.js").FunctionCopyFilesProps[] | undefined;
162
- logRetention?: RetentionDays | undefined;
163
- functionName?: string | undefined;
164
- tracing?: import("aws-cdk-lib/aws-lambda").Tracing | undefined;
165
- architecture?: Architecture | undefined;
166
- description?: string | undefined;
167
- ephemeralStorageSize?: import("aws-cdk-lib/core").Size | undefined;
168
- initialPolicy?: PolicyStatement[] | undefined;
169
- role?: import("aws-cdk-lib/aws-iam").IRole | undefined;
170
- vpc?: import("aws-cdk-lib/aws-ec2").IVpc | undefined;
171
- vpcSubnets?: import("aws-cdk-lib/aws-ec2").SubnetSelection | undefined;
172
- securityGroups?: import("aws-cdk-lib/aws-ec2").ISecurityGroup[] | undefined;
173
- allowAllOutbound?: boolean | undefined;
174
- deadLetterQueueEnabled?: boolean | undefined;
175
- deadLetterQueue?: import("aws-cdk-lib/aws-sqs").IQueue | undefined;
176
- deadLetterTopic?: import("aws-cdk-lib/aws-sns").ITopic | undefined;
177
- snapStart?: import("aws-cdk-lib/aws-lambda").SnapStartConf | undefined;
178
- profiling?: boolean | undefined;
179
- profilingGroup?: import("aws-cdk-lib/aws-codeguruprofiler").IProfilingGroup | undefined;
180
- insightsVersion?: import("aws-cdk-lib/aws-lambda").LambdaInsightsVersion | undefined;
181
- adotInstrumentation?: import("aws-cdk-lib/aws-lambda").AdotInstrumentationConfig | undefined;
182
- paramsAndSecrets?: import("aws-cdk-lib/aws-lambda").ParamsAndSecretsLayerVersion | undefined;
183
- reservedConcurrentExecutions?: number | undefined;
184
- events?: import("aws-cdk-lib/aws-lambda").IEventSource[] | undefined;
185
- logRetentionRole?: import("aws-cdk-lib/aws-iam").IRole | undefined;
186
- logRetentionRetryOptions?: import("aws-cdk-lib/aws-lambda").LogRetentionRetryOptions | undefined;
187
- currentVersionOptions?: import("aws-cdk-lib/aws-lambda").VersionOptions | undefined;
188
- filesystem?: import("aws-cdk-lib/aws-lambda").FileSystem | undefined;
189
- allowPublicSubnet?: boolean | undefined;
190
- environmentEncryption?: import("aws-cdk-lib/aws-kms").IKey | undefined;
191
- codeSigningConfig?: import("aws-cdk-lib/aws-lambda").ICodeSigningConfig | undefined;
192
- runtimeManagementMode?: import("aws-cdk-lib/aws-lambda").RuntimeManagementMode | undefined;
193
- logGroup?: import("aws-cdk-lib/aws-logs").ILogGroup | undefined;
194
- logFormat?: string | undefined;
195
- applicationLogLevel?: string | undefined;
196
- systemLogLevel?: string | undefined;
197
- onFailure?: import("aws-cdk-lib/aws-lambda").IDestination | undefined;
198
- onSuccess?: import("aws-cdk-lib/aws-lambda").IDestination | undefined;
199
- maxEventAge?: CdkDuration | undefined;
200
- retryAttempts?: number | undefined;
201
- } | {
151
+ environment: {
152
+ CACHE_BUCKET_NAME: string;
153
+ CACHE_BUCKET_KEY_PREFIX: string;
154
+ CACHE_BUCKET_REGION: string;
155
+ };
202
156
  layers: import("aws-cdk-lib/aws-lambda").ILayerVersion[] | undefined;
203
- handler: string;
204
- bundle?: string | undefined;
205
- runtime?: "nodejs16.x" | "nodejs18.x" | "nodejs20.x" | undefined;
206
- timeout?: number | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | undefined;
207
- memorySize?: number | `${number} MB` | `${number} GB` | undefined;
208
- permissions?: import("./index.js").Permissions | undefined;
209
- /**
210
- * How the logs are stored in CloudWatch
211
- * - "combined" - Logs from all routes are stored in the same log group.
212
- * - "per-route" - Logs from each route are stored in a separate log group.
213
- * @default "per-route"
214
- * @example
215
- * ```js
216
- * logging: "combined",
217
- * ```
218
- */
219
- environment?: Record<string, string> | undefined;
220
- bind?: import("./Construct.js").SSTConstruct[] | undefined;
221
- nodejs?: import("./Function.js").NodeJSProps | undefined;
222
- scopeOverride?: import("constructs").IConstruct | undefined;
223
157
  };
224
158
  };
225
159
  } | undefined;
@@ -259,80 +193,18 @@ export declare class NextjsSite extends SsrSite {
259
193
  type: "function";
260
194
  constructId: string;
261
195
  function: {
262
- layers: import("aws-cdk-lib/aws-lambda").ILayerVersion[] | undefined;
196
+ description: string;
197
+ bundle: string;
263
198
  handler: string;
264
- bundle?: string | undefined;
265
- runtime?: "nodejs16.x" | "nodejs18.x" | "nodejs20.x" | undefined;
266
- timeout?: number | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | undefined;
267
- memorySize?: number | `${number} MB` | `${number} GB` | undefined;
268
- permissions?: import("./index.js").Permissions | undefined;
269
- environment?: Record<string, string> | undefined;
270
- bind?: import("./Construct.js").SSTConstruct[] | undefined;
271
- nodejs?: import("./Function.js").NodeJSProps | undefined;
272
- copyFiles?: import("./Function.js").FunctionCopyFilesProps[] | undefined;
273
- logRetention?: RetentionDays | undefined;
274
- functionName?: string | undefined;
275
- tracing?: import("aws-cdk-lib/aws-lambda").Tracing | undefined;
276
- architecture?: Architecture | undefined;
277
- description?: string | undefined;
278
- ephemeralStorageSize?: import("aws-cdk-lib/core").Size | undefined;
279
- initialPolicy?: PolicyStatement[] | undefined;
280
- role?: import("aws-cdk-lib/aws-iam").IRole | undefined;
281
- vpc?: import("aws-cdk-lib/aws-ec2").IVpc | undefined;
282
- vpcSubnets?: import("aws-cdk-lib/aws-ec2").SubnetSelection | undefined;
283
- securityGroups?: import("aws-cdk-lib/aws-ec2").ISecurityGroup[] | undefined;
284
- allowAllOutbound?: boolean | undefined;
285
- deadLetterQueueEnabled?: boolean | undefined;
286
- deadLetterQueue?: import("aws-cdk-lib/aws-sqs").IQueue | undefined;
287
- deadLetterTopic?: import("aws-cdk-lib/aws-sns").ITopic | undefined;
288
- snapStart?: import("aws-cdk-lib/aws-lambda").SnapStartConf | undefined;
289
- profiling?: boolean | undefined;
290
- profilingGroup?: import("aws-cdk-lib/aws-codeguruprofiler").IProfilingGroup | undefined;
291
- insightsVersion?: import("aws-cdk-lib/aws-lambda").LambdaInsightsVersion | undefined;
292
- adotInstrumentation?: import("aws-cdk-lib/aws-lambda").AdotInstrumentationConfig | undefined;
293
- paramsAndSecrets?: import("aws-cdk-lib/aws-lambda").ParamsAndSecretsLayerVersion | undefined;
294
- reservedConcurrentExecutions?: number | undefined;
295
- events?: import("aws-cdk-lib/aws-lambda").IEventSource[] | undefined;
296
- logRetentionRole?: import("aws-cdk-lib/aws-iam").IRole | undefined;
297
- logRetentionRetryOptions?: import("aws-cdk-lib/aws-lambda").LogRetentionRetryOptions | undefined;
298
- currentVersionOptions?: import("aws-cdk-lib/aws-lambda").VersionOptions | undefined;
299
- filesystem?: import("aws-cdk-lib/aws-lambda").FileSystem | undefined;
300
- allowPublicSubnet?: boolean | undefined;
301
- environmentEncryption?: import("aws-cdk-lib/aws-kms").IKey | undefined;
302
- codeSigningConfig?: import("aws-cdk-lib/aws-lambda").ICodeSigningConfig | undefined;
303
- runtimeManagementMode?: import("aws-cdk-lib/aws-lambda").RuntimeManagementMode | undefined;
304
- logGroup?: import("aws-cdk-lib/aws-logs").ILogGroup | undefined;
305
- logFormat?: string | undefined;
306
- applicationLogLevel?: string | undefined;
307
- systemLogLevel?: string | undefined;
308
- onFailure?: import("aws-cdk-lib/aws-lambda").IDestination | undefined;
309
- onSuccess?: import("aws-cdk-lib/aws-lambda").IDestination | undefined;
310
- maxEventAge?: CdkDuration | undefined;
311
- retryAttempts?: number | undefined;
312
- } | {
199
+ environment: {
200
+ CACHE_BUCKET_NAME: string;
201
+ CACHE_BUCKET_KEY_PREFIX: string;
202
+ CACHE_BUCKET_REGION: string;
203
+ };
313
204
  layers: import("aws-cdk-lib/aws-lambda").ILayerVersion[] | undefined;
314
- handler: string;
315
- bundle?: string | undefined;
316
- runtime?: "nodejs16.x" | "nodejs18.x" | "nodejs20.x" | undefined;
317
- timeout?: number | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | undefined;
318
- memorySize?: number | `${number} MB` | `${number} GB` | undefined;
319
- permissions?: import("./index.js").Permissions | undefined;
320
- /**
321
- * How the logs are stored in CloudWatch
322
- * - "combined" - Logs from all routes are stored in the same log group.
323
- * - "per-route" - Logs from each route are stored in a separate log group.
324
- * @default "per-route"
325
- * @example
326
- * ```js
327
- * logging: "combined",
328
- * ```
329
- */
330
- environment?: Record<string, string> | undefined;
331
- bind?: import("./Construct.js").SSTConstruct[] | undefined;
332
- nodejs?: import("./Function.js").NodeJSProps | undefined;
333
- scopeOverride?: import("constructs").IConstruct | undefined;
334
205
  };
335
206
  streaming: boolean | undefined;
207
+ injections: string[];
336
208
  } | undefined;
337
209
  };
338
210
  edge: boolean;
@@ -374,7 +246,6 @@ export declare class NextjsSite extends SsrSite {
374
246
  secrets: string[];
375
247
  };
376
248
  };
377
- private wrapServerFunction;
378
249
  private removeSourcemaps;
379
250
  private useRoutes;
380
251
  private useRoutesManifest;
@@ -382,6 +253,7 @@ export declare class NextjsSite extends SsrSite {
382
253
  private useAppPathsManifest;
383
254
  private usePagesManifest;
384
255
  private usePrerenderManifest;
256
+ private useServerFunctionPerRouteLoggingInjection;
385
257
  private getBuildId;
386
258
  private getSourcemapForAppRoute;
387
259
  private getSourcemapForPagesRoute;
@@ -89,8 +89,9 @@ export class NextjsSite extends SsrSite {
89
89
  return super.buildDefaultServerCachePolicyProps(DEFAULT_CACHE_POLICY_ALLOWED_HEADERS);
90
90
  }
91
91
  plan(bucket) {
92
- const { path: sitePath, edge, experimental, imageOptimization, } = this.props;
93
- const serverConfig = this.wrapServerFunction({
92
+ const { path: sitePath, edge, experimental, imageOptimization, cdk, } = this.props;
93
+ const stack = Stack.of(this);
94
+ const serverConfig = {
94
95
  description: "Next.js server",
95
96
  bundle: path.join(sitePath, ".open-next", "server-function"),
96
97
  handler: "index.handler",
@@ -99,7 +100,14 @@ export class NextjsSite extends SsrSite {
99
100
  CACHE_BUCKET_KEY_PREFIX: "_cache",
100
101
  CACHE_BUCKET_REGION: Stack.of(this).region,
101
102
  },
102
- });
103
+ layers: this.isPerRouteLoggingEnabled()
104
+ ? [
105
+ LayerVersion.fromLayerVersionArn(this, "SSTExtension", cdk?.server?.architecture?.name === Architecture.X86_64.name
106
+ ? `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-amd64:${LAYER_VERSION}`
107
+ : `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-arm64:${LAYER_VERSION}`),
108
+ ]
109
+ : undefined,
110
+ };
103
111
  this.removeSourcemaps();
104
112
  return this.validatePlan({
105
113
  edge: edge ?? false,
@@ -126,6 +134,9 @@ export class NextjsSite extends SsrSite {
126
134
  constructId: "ServerFunction",
127
135
  function: serverConfig,
128
136
  streaming: experimental?.streaming,
137
+ injections: this.isPerRouteLoggingEnabled()
138
+ ? [this.useServerFunctionPerRouteLoggingInjection()]
139
+ : [],
129
140
  },
130
141
  }),
131
142
  imageOptimizer: {
@@ -331,56 +342,6 @@ export class NextjsSite extends SsrSite {
331
342
  },
332
343
  };
333
344
  }
334
- wrapServerFunction(config) {
335
- const { path: sitePath, experimental, cdk } = this.props;
336
- const stack = Stack.of(this);
337
- const wrapperName = "nextjssite-index";
338
- const serverPath = path.join(sitePath, ".open-next", "server-function");
339
- const injections = [];
340
- if (this.isPerRouteLoggingEnabled()) {
341
- injections.push(`
342
- if (event.rawPath) {
343
- const routeData = ${JSON.stringify(this.useRoutes().map(({ regex, logGroupPath }) => ({
344
- regex,
345
- logGroupPath,
346
- })))}.find(({ regex }) => event.rawPath.match(new RegExp(regex)));
347
- if (routeData) {
348
- console.log("::sst::" + JSON.stringify({
349
- action:"log.split",
350
- properties: {
351
- logGroupName:"/sst/lambda/" + context.functionName + routeData.logGroupPath,
352
- },
353
- }));
354
- }
355
- }`);
356
- }
357
- fs.writeFileSync(path.join(serverPath, `${wrapperName}.mjs`), experimental?.streaming
358
- ? [
359
- `export const handler = awslambda.streamifyResponse(async (event, context) => {`,
360
- ...injections,
361
- ` const { handler: rawHandler} = await import("./index.mjs");`,
362
- ` return rawHandler(event, context);`,
363
- `});`,
364
- ].join("\n")
365
- : [
366
- `export const handler = async (event, context) => {`,
367
- ...injections,
368
- ` const { handler: rawHandler} = await import("./index.mjs");`,
369
- ` return rawHandler(event, context);`,
370
- `};`,
371
- ].join("\n"));
372
- return {
373
- ...config,
374
- layers: this.isPerRouteLoggingEnabled()
375
- ? [
376
- LayerVersion.fromLayerVersionArn(this, "SSTExtension", cdk?.server?.architecture?.name === Architecture.X86_64.name
377
- ? `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-amd64:${LAYER_VERSION}`
378
- : `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-arm64:${LAYER_VERSION}`),
379
- ]
380
- : undefined,
381
- handler: `${wrapperName}.handler`,
382
- };
383
- }
384
345
  removeSourcemaps() {
385
346
  const { path: sitePath } = this.props;
386
347
  const files = globSync("**/*.js.map", {
@@ -506,6 +467,23 @@ export class NextjsSite extends SsrSite {
506
467
  Logger.debug("Failed to load prerender-manifest.json", e);
507
468
  }
508
469
  }
470
+ useServerFunctionPerRouteLoggingInjection() {
471
+ return `
472
+ if (event.rawPath) {
473
+ const routeData = ${JSON.stringify(this.useRoutes().map(({ regex, logGroupPath }) => ({
474
+ regex,
475
+ logGroupPath,
476
+ })))}.find(({ regex }) => event.rawPath.match(new RegExp(regex)));
477
+ if (routeData) {
478
+ console.log("::sst::" + JSON.stringify({
479
+ action:"log.split",
480
+ properties: {
481
+ logGroupName:"/sst/lambda/" + context.functionName + routeData.logGroupPath,
482
+ },
483
+ }));
484
+ }
485
+ }`;
486
+ }
509
487
  getBuildId() {
510
488
  const { path: sitePath } = this.props;
511
489
  return fs.readFileSync(path.join(sitePath, ".next/BUILD_ID")).toString();
@@ -19,6 +19,8 @@ export interface SsrFunctionProps extends Omit<FunctionOptions, "memorySize" | "
19
19
  nodejs?: NodeJSProps;
20
20
  copyFiles?: FunctionCopyFilesProps[];
21
21
  logRetention?: RetentionDays;
22
+ streaming?: boolean;
23
+ injections?: string[];
22
24
  }
23
25
  export declare class SsrFunction extends Construct implements SSTConstruct {
24
26
  readonly id: string;
@@ -43,6 +45,7 @@ export declare class SsrFunction extends Construct implements SSTConstruct {
43
45
  private bind;
44
46
  private buildAssetFromHandler;
45
47
  private buildAssetFromBundle;
48
+ private writeWrapperFile;
46
49
  private updateCodeReplacer;
47
50
  private updateFunction;
48
51
  /** @internal */
@@ -38,6 +38,8 @@ export class SsrFunction extends Construct {
38
38
  this.props = {
39
39
  timeout: 10,
40
40
  memorySize: 1024,
41
+ streaming: false,
42
+ injections: [],
41
43
  ...props,
42
44
  environment: props.environment || {},
43
45
  permissions: props.permissions || [],
@@ -53,15 +55,15 @@ export class SsrFunction extends Construct {
53
55
  this.assetReplacer = assetReplacer;
54
56
  this.assetReplacerPolicy = assetReplacerPolicy;
55
57
  useDeferredTasks().add(async () => {
56
- const { bundle } = props;
57
- const code = bundle
58
+ const { bundle } = this.props;
59
+ const { code, handler } = bundle
58
60
  ? await this.buildAssetFromBundle(bundle)
59
61
  : await this.buildAssetFromHandler();
60
62
  const codeConfig = code.bind(this.function);
61
63
  const assetBucket = codeConfig.s3Location?.bucketName;
62
64
  const assetKey = codeConfig.s3Location?.objectKey;
63
65
  this.updateCodeReplacer(assetBucket, assetKey);
64
- this.updateFunction(code, assetBucket, assetKey);
66
+ this.updateFunction(code, assetBucket, assetKey, handler);
65
67
  });
66
68
  const app = this.node.root;
67
69
  app.registerTypes(this);
@@ -171,11 +173,12 @@ export class SsrFunction extends Construct {
171
173
  });
172
174
  }
173
175
  async buildAssetFromHandler() {
176
+ const { handler, runtime, nodejs, copyFiles, injections } = this.props;
174
177
  useFunctions().add(this.node.addr, {
175
- handler: this.props.handler,
176
- runtime: this.props.runtime,
177
- nodejs: this.props.nodejs,
178
- copyFiles: this.props.copyFiles,
178
+ handler,
179
+ runtime,
180
+ nodejs,
181
+ copyFiles,
179
182
  });
180
183
  // build function
181
184
  const result = await useRuntimeHandlers().build(this.node.addr, "deploy");
@@ -185,6 +188,7 @@ export class SsrFunction extends Construct {
185
188
  `There was a problem bundling the SSR function for the "${this.node.id}" Site.`,
186
189
  ...result.errors,
187
190
  ].join("\n"));
191
+ const newHandler = await this.writeWrapperFile(result.out, result.handler);
188
192
  // upload sourcemap
189
193
  const stack = Stack.of(this);
190
194
  if (result.sourcemap) {
@@ -200,9 +204,11 @@ export class SsrFunction extends Construct {
200
204
  });
201
205
  }
202
206
  this.missingSourcemap = !result.sourcemap;
203
- return AssetCode.fromAsset(result.out);
207
+ return { code: AssetCode.fromAsset(result.out), handler: newHandler };
204
208
  }
205
209
  async buildAssetFromBundle(bundle) {
210
+ const { handler } = this.props;
211
+ const newHandler = await this.writeWrapperFile(bundle, handler);
206
212
  // Note: cannot point the bundle to the `.open-next/server-function`
207
213
  // b/c the folder contains node_modules. And pnpm node_modules
208
214
  // contains symlinks. CDK cannot zip symlinks correctly.
@@ -214,7 +220,35 @@ export class SsrFunction extends Construct {
214
220
  if (result.status !== 0) {
215
221
  throw new Error(`There was a problem generating the assets package.`);
216
222
  }
217
- return AssetCode.fromAsset(path.join(outputPath, "server-function.zip"));
223
+ return {
224
+ code: AssetCode.fromAsset(path.join(outputPath, "server-function.zip")),
225
+ handler: newHandler,
226
+ };
227
+ }
228
+ async writeWrapperFile(bundle, handler) {
229
+ const { streaming, injections } = this.props;
230
+ if (injections.length === 0)
231
+ return handler;
232
+ const { dir: handlerDir, name: oldHandlerName, ext: oldHandlerExt, } = path.posix.parse(handler);
233
+ const oldHandlerFunction = oldHandlerExt.replace(/^\./, "");
234
+ const newHandlerName = "server-index";
235
+ const newHandlerFunction = "handler";
236
+ await fs.writeFile(path.join(bundle, handlerDir, `${newHandlerName}.mjs`), streaming
237
+ ? [
238
+ `export const ${newHandlerFunction} = awslambda.streamifyResponse(async (event, context) => {`,
239
+ ...injections,
240
+ ` const { ${oldHandlerFunction}: rawHandler} = await import("./${oldHandlerName}.mjs");`,
241
+ ` return rawHandler(event, context);`,
242
+ `});`,
243
+ ].join("\n")
244
+ : [
245
+ `export const ${newHandlerFunction} = async (event, context) => {`,
246
+ ...injections,
247
+ ` const { ${oldHandlerFunction}: rawHandler} = await import("./${oldHandlerName}.mjs");`,
248
+ ` return rawHandler(event, context);`,
249
+ `};`,
250
+ ].join("\n"));
251
+ return path.posix.join(handlerDir, `${newHandlerName}.${newHandlerFunction}`);
218
252
  }
219
253
  updateCodeReplacer(assetBucket, assetKey) {
220
254
  const stack = Stack.of(this);
@@ -225,8 +259,9 @@ export class SsrFunction extends Construct {
225
259
  const cfnPolicy = this.assetReplacerPolicy.node.defaultChild;
226
260
  cfnPolicy.addPropertyOverride("PolicyDocument.Statement.0.Resource", `arn:${stack.partition}:s3:::${assetBucket}/*`);
227
261
  }
228
- updateFunction(code, assetBucket, assetKey) {
262
+ updateFunction(code, assetBucket, assetKey, handler) {
229
263
  const cfnFunction = this.function.node.defaultChild;
264
+ cfnFunction.handler = handler;
230
265
  cfnFunction.code = {
231
266
  s3Bucket: assetBucket,
232
267
  s3Key: assetKey,
@@ -26,6 +26,7 @@ type FunctionOriginConfig = {
26
26
  type: "function";
27
27
  constructId: string;
28
28
  function: SsrFunctionProps;
29
+ injections?: string[];
29
30
  streaming?: boolean;
30
31
  };
31
32
  type ImageOptimizationFunctionOriginConfig = {
@@ -443,6 +443,11 @@ function handler(event) {
443
443
  ...props.function.environment,
444
444
  },
445
445
  ...cdk?.server,
446
+ streaming: props.streaming,
447
+ injections: [
448
+ ...(warm ? [useServerFunctionWarmingInjection()] : []),
449
+ ...(props.injections || []),
450
+ ],
446
451
  });
447
452
  ssrFunctions.push(fn);
448
453
  bucket.grantReadWrite(fn?.role);
@@ -632,6 +637,16 @@ function handler(event) {
632
637
  OriginRequestPolicy.fromOriginRequestPolicyId(self, "ServerOriginRequestPolicy", "b689b0a8-53d0-40ab-baf2-68738e2966ac");
633
638
  return singletonOriginRequestPolicy;
634
639
  }
640
+ function useServerFunctionWarmingInjection() {
641
+ return `
642
+ if (event.type === "warmer") {
643
+ return new Promise((resolve) => {
644
+ setTimeout(() => {
645
+ resolve({ serverId: "server-" + Math.random().toString(36).slice(2, 8) });
646
+ }, event.delay);
647
+ });
648
+ }`;
649
+ }
635
650
  function getS3FileOptions(copy) {
636
651
  const fileOptions = [];
637
652
  const nonVersionedFilesTTL = typeof assets?.nonVersionedFilesTTL === "number"
@@ -9,10 +9,11 @@ export function createLoader(batchFn) {
9
9
  const batch = current;
10
10
  if (!batch)
11
11
  return;
12
- const result = await batchFn(batch.keys);
13
- for (let i = 0; i < result.length; i++) {
14
- batch.promises[i](result[i]);
15
- }
12
+ batchFn(batch.keys).then((result) => {
13
+ for (let i = 0; i < result.length; i++) {
14
+ batch.promises[i](result[i]);
15
+ }
16
+ });
16
17
  current = undefined;
17
18
  }
18
19
  function getBatch() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "sideEffects": false,
3
3
  "name": "sst",
4
- "version": "2.36.5",
4
+ "version": "2.36.7",
5
5
  "bin": {
6
6
  "sst": "cli/sst.js"
7
7
  },
@@ -120,7 +120,7 @@
120
120
  "@types/ws": "^8.5.3",
121
121
  "@types/yargs": "^17.0.13",
122
122
  "archiver": "^5.3.1",
123
- "astro-sst": "2.36.5",
123
+ "astro-sst": "2.36.7",
124
124
  "async": "^3.2.4",
125
125
  "tsx": "^3.12.1",
126
126
  "typescript": "^5.2.2",