sst 2.31.0 → 2.32.0

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.
package/constructs/App.js CHANGED
@@ -264,28 +264,28 @@ export class App extends CDKApp {
264
264
  const functions = useFunctions();
265
265
  const sourcemaps = functions.sourcemaps.forStack(child.stackName);
266
266
  if (sourcemaps.length) {
267
- const policy = new Policy(child, "FunctionSourcemapUploaderPolicy", {
267
+ const policy = new Policy(child, "SourcemapUploaderPolicy", {
268
268
  statements: [
269
269
  new PolicyStatement({
270
270
  effect: Effect.ALLOW,
271
271
  actions: ["s3:GetObject", "s3:PutObject"],
272
272
  resources: [
273
- sourcemaps[0].bucket.bucketArn + "/*",
273
+ sourcemaps[0].srcBucket.bucketArn + "/*",
274
274
  `arn:${child.partition}:s3:::${bootstrap.bucket}/*`,
275
275
  ],
276
276
  }),
277
277
  ],
278
278
  });
279
279
  child.customResourceHandler.role?.attachInlinePolicy(policy);
280
- const resource = new CustomResource(child, "FunctionSourcemapUploader", {
280
+ const resource = new CustomResource(child, "SourcemapUploader", {
281
281
  serviceToken: child.customResourceHandler.functionArn,
282
- resourceType: "Custom::FunctionSourcemapUploader",
282
+ resourceType: "Custom::SourcemapUploader",
283
283
  properties: {
284
284
  app: this.name,
285
285
  stage: this.stage,
286
- bootstrap: bootstrap.bucket,
287
- bucket: sourcemaps[0].bucket.bucketName,
288
- functions: sourcemaps.map((s) => [s.func.functionArn, s.key]),
286
+ tarBucket: bootstrap.bucket,
287
+ srcBucket: sourcemaps[0].srcBucket.bucketName,
288
+ sourcemaps: sourcemaps.map((s) => [s.tarKey, s.srcKey]),
289
289
  },
290
290
  });
291
291
  resource.node.addDependency(policy);
@@ -94,15 +94,6 @@ export interface DistributionProps {
94
94
  * ```
95
95
  */
96
96
  customDomain?: string | DistributionDomainProps;
97
- /**
98
- * The SSR function is deployed to Lambda in a single region. Alternatively, you can enable this option to deploy to Lambda@Edge.
99
- * @default false
100
- */
101
- /**
102
- * While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
103
- * @default false
104
- */
105
- waitForInvalidation?: boolean;
106
97
  scopeOverride?: IConstruct;
107
98
  cdk: {
108
99
  distribution: CdkDistributionProps | IDistribution;
@@ -132,7 +123,11 @@ export declare class Distribution extends Construct {
132
123
  hostedZone: IHostedZone | undefined;
133
124
  certificate: ICertificate | undefined;
134
125
  };
135
- createInvalidation(buildId?: string, paths?: string[]): CustomResource;
126
+ createInvalidation(props?: {
127
+ version?: string;
128
+ paths?: string[];
129
+ wait?: boolean;
130
+ }): CustomResource;
136
131
  private validateCloudFrontDistributionSettings;
137
132
  private validateCustomDomainSettings;
138
133
  private lookupHostedZone;
@@ -70,7 +70,8 @@ export class Distribution extends Construct {
70
70
  certificate: this.certificate,
71
71
  };
72
72
  }
73
- createInvalidation(buildId, paths) {
73
+ createInvalidation(props) {
74
+ const { version, paths, wait } = props ?? {};
74
75
  const stack = Stack.of(this);
75
76
  const policy = new Policy(this.scope, "CloudFrontInvalidatorPolicy", {
76
77
  statements: [
@@ -91,10 +92,11 @@ export class Distribution extends Construct {
91
92
  serviceToken: stack.customResourceHandler.functionArn,
92
93
  resourceType: "Custom::CloudFrontInvalidator",
93
94
  properties: {
94
- buildId: buildId || Date.now().toString(),
95
+ version: version ||
96
+ Date.now().toString(16) + Math.random().toString(16).slice(2),
95
97
  distributionId: this.distribution.distributionId,
96
- paths: paths ?? ["/*"],
97
- waitForInvalidation: this.props.waitForInvalidation,
98
+ paths: [...new Set(paths ?? ["/*"])],
99
+ wait: wait ?? false,
98
100
  },
99
101
  });
100
102
  resource.node.addDependency(policy);
@@ -693,14 +693,14 @@ export declare class Function extends CDKFunction implements SSTConstruct {
693
693
  export declare const useFunctions: () => {
694
694
  sourcemaps: {
695
695
  add(stack: string, source: {
696
- bucket: IBucket;
697
- key: string;
698
- func: Function;
696
+ srcBucket: IBucket;
697
+ srcKey: string;
698
+ tarKey: string;
699
699
  }): void;
700
700
  forStack(stack: string): {
701
- bucket: IBucket;
702
- key: string;
703
- func: Function;
701
+ srcBucket: IBucket;
702
+ srcKey: string;
703
+ tarKey: string;
704
704
  }[];
705
705
  };
706
706
  fromID(id: string): FunctionProps | undefined;
@@ -257,9 +257,9 @@ export class Function extends CDKFunction {
257
257
  });
258
258
  await fs.rm(result.sourcemap);
259
259
  useFunctions().sourcemaps.add(stack.stackName, {
260
- bucket: asset.bucket,
261
- key: asset.s3ObjectKey,
262
- func: this,
260
+ srcBucket: asset.bucket,
261
+ srcKey: asset.s3ObjectKey,
262
+ tarKey: this.functionArn,
263
263
  });
264
264
  }
265
265
  // Update code
@@ -123,7 +123,11 @@ type NextjsSiteNormalizedProps = NextjsSiteProps & SsrSiteNormalizedProps;
123
123
  */
124
124
  export declare class NextjsSite extends SsrSite {
125
125
  props: NextjsSiteNormalizedProps;
126
- private routes?;
126
+ private _routes?;
127
+ private routesManifest?;
128
+ private appPathRoutesManifest?;
129
+ private appPathsManifest?;
130
+ private pagesManifest?;
127
131
  constructor(scope: Construct, id: string, props?: NextjsSiteProps);
128
132
  static buildDefaultServerCachePolicyProps(): CachePolicyProps;
129
133
  protected plan(bucket: Bucket): {
@@ -335,10 +339,18 @@ export declare class NextjsSite extends SsrSite {
335
339
  };
336
340
  };
337
341
  private wrapServerFunction;
342
+ private removeSourcemaps;
338
343
  private useRoutes;
344
+ private useRoutesManifest;
345
+ private useAppPathRoutesManifest;
346
+ private useAppPathsManifest;
347
+ private usePagesManifest;
339
348
  private getBuildId;
349
+ private getSourcemapForAppRoute;
350
+ private getSourcemapForPagesRoute;
340
351
  private isPerRouteLoggingEnabled;
341
352
  private disableDefaultLogging;
353
+ private uploadSourcemaps;
342
354
  private static buildCloudWatchRouteName;
343
355
  private static buildCloudWatchRouteHash;
344
356
  static _test: {
@@ -1,6 +1,8 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import zlib from "zlib";
3
4
  import crypto from "crypto";
5
+ import { globSync } from "glob";
4
6
  import { Duration as CdkDuration, RemovalPolicy, CustomResource, } from "aws-cdk-lib/core";
5
7
  import { Code, Runtime, Function as CdkFunction, Architecture, LayerVersion, } from "aws-cdk-lib/aws-lambda";
6
8
  import { AttributeType, Billing, TableV2 as Table, } from "aws-cdk-lib/aws-dynamodb";
@@ -13,6 +15,9 @@ import { toCdkSize } from "./util/size.js";
13
15
  import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam";
14
16
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
15
17
  import { VisibleError } from "../error.js";
18
+ import { Asset } from "aws-cdk-lib/aws-s3-assets";
19
+ import { useFunctions } from "./Function.js";
20
+ import { useDeferredTasks } from "./deferred_task.js";
16
21
  const LAYER_VERSION = "2";
17
22
  const DEFAULT_OPEN_NEXT_VERSION = "2.2.4";
18
23
  const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
@@ -34,7 +39,11 @@ const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
34
39
  * ```
35
40
  */
36
41
  export class NextjsSite extends SsrSite {
37
- routes;
42
+ _routes;
43
+ routesManifest;
44
+ appPathRoutesManifest;
45
+ appPathsManifest;
46
+ pagesManifest;
38
47
  constructor(scope, id, props) {
39
48
  const streaming = props?.experimental?.streaming ?? false;
40
49
  const disableDynamoDBCache = props?.experimental?.disableDynamoDBCache ?? false;
@@ -57,6 +66,7 @@ export class NextjsSite extends SsrSite {
57
66
  });
58
67
  if (this.isPerRouteLoggingEnabled()) {
59
68
  this.disableDefaultLogging();
69
+ this.uploadSourcemaps();
60
70
  }
61
71
  if (!disableIncrementalCache) {
62
72
  this.createRevalidationQueue();
@@ -80,6 +90,7 @@ export class NextjsSite extends SsrSite {
80
90
  CACHE_BUCKET_REGION: Stack.of(this).region,
81
91
  },
82
92
  });
93
+ this.removeSourcemaps();
83
94
  return this.validatePlan({
84
95
  cloudFrontFunctions: {
85
96
  serverCfFunction: {
@@ -312,7 +323,10 @@ export class NextjsSite extends SsrSite {
312
323
  const injections = [];
313
324
  if (this.isPerRouteLoggingEnabled()) {
314
325
  injections.push(`
315
- const routeData = ${JSON.stringify(this.useRoutes())}.find(({ regex }) => event.rawPath.match(new RegExp(regex)));
326
+ const routeData = ${JSON.stringify(this.useRoutes().map(({ regex, logGroupPath }) => ({
327
+ regex,
328
+ logGroupPath,
329
+ })))}.find(({ regex }) => event.rawPath.match(new RegExp(regex)));
316
330
  if (routeData) {
317
331
  console.log("::sst::" + JSON.stringify({
318
332
  action:"log.split",
@@ -349,53 +363,178 @@ export class NextjsSite extends SsrSite {
349
363
  handler: `${wrapperName}.handler`,
350
364
  };
351
365
  }
366
+ removeSourcemaps() {
367
+ const { path: sitePath } = this.props;
368
+ const files = globSync("**/*.js.map", {
369
+ cwd: path.join(sitePath, ".open-next", "server-function"),
370
+ nodir: true,
371
+ dot: true,
372
+ });
373
+ for (const file of files) {
374
+ fs.rmSync(path.join(sitePath, ".open-next", "server-function", file));
375
+ }
376
+ }
352
377
  useRoutes() {
353
- if (this.routes)
354
- return this.routes;
355
- const id = this.node.id;
378
+ if (this._routes)
379
+ return this._routes;
380
+ const routesManifest = this.useRoutesManifest();
381
+ this._routes = [
382
+ ...[...routesManifest.dynamicRoutes, ...routesManifest.staticRoutes]
383
+ .map(({ page, regex }) => {
384
+ const cwRoute = NextjsSite.buildCloudWatchRouteName(page);
385
+ const cwHash = NextjsSite.buildCloudWatchRouteHash(page);
386
+ const sourcemapPath = this.getSourcemapForAppRoute(page) ||
387
+ this.getSourcemapForPagesRoute(page);
388
+ return {
389
+ route: page,
390
+ regex,
391
+ logGroupPath: `/${cwHash}${cwRoute}`,
392
+ sourcemapPath: sourcemapPath,
393
+ sourcemapKey: cwHash,
394
+ };
395
+ })
396
+ .sort((a, b) => a.route.localeCompare(b.route)),
397
+ ...(routesManifest.dataRoutes || [])
398
+ .map(({ page, dataRouteRegex }) => {
399
+ const routeDisplayName = page.endsWith("/")
400
+ ? `/_next/data/BUILD_ID${page}index.json`
401
+ : `/_next/data/BUILD_ID${page}.json`;
402
+ const cwRoute = NextjsSite.buildCloudWatchRouteName(routeDisplayName);
403
+ const cwHash = NextjsSite.buildCloudWatchRouteHash(page);
404
+ return {
405
+ route: routeDisplayName,
406
+ regex: dataRouteRegex,
407
+ logGroupPath: `/${cwHash}${cwRoute}`,
408
+ };
409
+ })
410
+ .sort((a, b) => a.route.localeCompare(b.route)),
411
+ ];
412
+ return this._routes;
413
+ }
414
+ useRoutesManifest() {
415
+ if (this.routesManifest)
416
+ return this.routesManifest;
356
417
  const { path: sitePath } = this.props;
418
+ const id = this.node.id;
357
419
  try {
358
- const content = JSON.parse(fs
420
+ const content = fs
359
421
  .readFileSync(path.join(sitePath, ".next/routes-manifest.json"))
360
- .toString());
361
- this.routes = [
362
- ...[...content.dynamicRoutes, ...content.staticRoutes]
363
- .map(({ page, regex }) => {
364
- const cwRoute = NextjsSite.buildCloudWatchRouteName(page);
365
- const cwHash = NextjsSite.buildCloudWatchRouteHash(page);
366
- return {
367
- route: page,
368
- regex,
369
- logGroupPath: `/${cwHash}${cwRoute}`,
370
- };
371
- })
372
- .sort((a, b) => a.route.localeCompare(b.route)),
373
- ...(content.dataRoutes || [])
374
- .map(({ page, dataRouteRegex }) => {
375
- const routeDisplayName = page.endsWith("/")
376
- ? `/_next/data/BUILD_ID${page}index.json`
377
- : `/_next/data/BUILD_ID${page}.json`;
378
- const cwRoute = NextjsSite.buildCloudWatchRouteName(routeDisplayName);
379
- const cwHash = NextjsSite.buildCloudWatchRouteHash(`data:${page}`);
380
- return {
381
- route: routeDisplayName,
382
- regex: dataRouteRegex,
383
- logGroupPath: `/${cwHash}${cwRoute}`,
384
- };
385
- })
386
- .sort((a, b) => a.route.localeCompare(b.route)),
387
- ];
388
- return this.routes;
422
+ .toString();
423
+ this.routesManifest = JSON.parse(content);
424
+ return this.routesManifest;
389
425
  }
390
426
  catch (e) {
391
427
  console.error(e);
392
428
  throw new VisibleError(`Failed to read routes data from ".next/routes-manifest.json" for the "${id}" site.`);
393
429
  }
394
430
  }
431
+ useAppPathRoutesManifest() {
432
+ if (this.appPathRoutesManifest)
433
+ return this.appPathRoutesManifest;
434
+ const { path: sitePath } = this.props;
435
+ try {
436
+ const content = fs
437
+ .readFileSync(path.join(sitePath, ".next/app-path-routes-manifest.json"))
438
+ .toString();
439
+ this.appPathRoutesManifest = JSON.parse(content);
440
+ return this.appPathRoutesManifest;
441
+ }
442
+ catch (e) {
443
+ return {};
444
+ }
445
+ }
446
+ useAppPathsManifest() {
447
+ if (this.appPathsManifest)
448
+ return this.appPathsManifest;
449
+ const { path: sitePath } = this.props;
450
+ try {
451
+ const content = fs
452
+ .readFileSync(path.join(sitePath, ".next/server/app-paths-manifest.json"))
453
+ .toString();
454
+ this.appPathsManifest = JSON.parse(content);
455
+ return this.appPathsManifest;
456
+ }
457
+ catch (e) {
458
+ return {};
459
+ }
460
+ }
461
+ usePagesManifest() {
462
+ if (this.pagesManifest)
463
+ return this.pagesManifest;
464
+ const { path: sitePath } = this.props;
465
+ try {
466
+ const content = fs
467
+ .readFileSync(path.join(sitePath, ".next/server/pages-manifest.json"))
468
+ .toString();
469
+ this.pagesManifest = JSON.parse(content);
470
+ return this.pagesManifest;
471
+ }
472
+ catch (e) {
473
+ return {};
474
+ }
475
+ }
395
476
  getBuildId() {
396
477
  const { path: sitePath } = this.props;
397
478
  return fs.readFileSync(path.join(sitePath, ".next/BUILD_ID")).toString();
398
479
  }
480
+ getSourcemapForAppRoute(page) {
481
+ const { path: sitePath } = this.props;
482
+ // Step 1: look up in "appPathRoutesManifest" to find the key with
483
+ // value equal to the page
484
+ // {
485
+ // "/_not-found": "/_not-found",
486
+ // "/about/page": "/about",
487
+ // "/about/profile/page": "/about/profile",
488
+ // "/page": "/",
489
+ // "/favicon.ico/route": "/favicon.ico"
490
+ // }
491
+ const appPathRoutesManifest = this.useAppPathRoutesManifest();
492
+ const appPathRoute = Object.keys(appPathRoutesManifest).find((key) => appPathRoutesManifest[key] === page);
493
+ if (!appPathRoute)
494
+ return;
495
+ // Step 2: look up in "appPathsManifest" to find the file with key equal
496
+ // to the page
497
+ // {
498
+ // "/_not-found": "app/_not-found.js",
499
+ // "/about/page": "app/about/page.js",
500
+ // "/about/profile/page": "app/about/profile/page.js",
501
+ // "/page": "app/page.js",
502
+ // "/favicon.ico/route": "app/favicon.ico/route.js"
503
+ // }
504
+ const appPathsManifest = this.useAppPathsManifest();
505
+ const filePath = appPathsManifest[appPathRoute];
506
+ if (!filePath)
507
+ return;
508
+ // Step 3: check the .map file exists
509
+ const sourcemapPath = path.join(sitePath, ".next", "server", `${filePath}.map`);
510
+ if (!fs.existsSync(sourcemapPath))
511
+ return;
512
+ return sourcemapPath;
513
+ }
514
+ getSourcemapForPagesRoute(page) {
515
+ const { path: sitePath } = this.props;
516
+ // Step 1: look up in "pathsManifest" to find the file with key equal
517
+ // to the page
518
+ // {
519
+ // "/_app": "pages/_app.js",
520
+ // "/_error": "pages/_error.js",
521
+ // "/404": "pages/404.html",
522
+ // "/api/hello": "pages/api/hello.js",
523
+ // "/api/auth/[...nextauth]": "pages/api/auth/[...nextauth].js",
524
+ // "/api/next-auth-restricted": "pages/api/next-auth-restricted.js",
525
+ // "/": "pages/index.js",
526
+ // "/ssr": "pages/ssr.js"
527
+ // }
528
+ const pagesManifest = this.usePagesManifest();
529
+ const filePath = pagesManifest[page];
530
+ if (!filePath)
531
+ return;
532
+ // Step 2: check the .map file exists
533
+ const sourcemapPath = path.join(sitePath, ".next", "server", `${filePath}.map`);
534
+ if (!fs.existsSync(sourcemapPath))
535
+ return;
536
+ return sourcemapPath;
537
+ }
399
538
  isPerRouteLoggingEnabled() {
400
539
  return (!this.doNotDeploy &&
401
540
  !this.props.edge &&
@@ -424,6 +563,28 @@ export class NextjsSite extends SsrSite {
424
563
  });
425
564
  server.role?.attachInlinePolicy(policy);
426
565
  }
566
+ uploadSourcemaps() {
567
+ const stack = Stack.of(this);
568
+ const server = this.serverFunction;
569
+ this.useRoutes().forEach(({ sourcemapPath, sourcemapKey }) => {
570
+ if (!sourcemapPath || !sourcemapKey)
571
+ return;
572
+ useDeferredTasks().add(async () => {
573
+ // zip sourcemap
574
+ const zipPath = `${sourcemapPath}.gz.zip`;
575
+ const data = await fs.promises.readFile(sourcemapPath);
576
+ await fs.promises.writeFile(zipPath, zlib.gzipSync(data));
577
+ const asset = new Asset(this, `Sourcemap-${sourcemapKey}`, {
578
+ path: zipPath,
579
+ });
580
+ useFunctions().sourcemaps.add(stack.stackName, {
581
+ srcBucket: asset.bucket,
582
+ srcKey: asset.s3ObjectKey,
583
+ tarKey: path.join(server.functionArn, sourcemapKey),
584
+ });
585
+ });
586
+ });
587
+ }
427
588
  static buildCloudWatchRouteName(route) {
428
589
  return route.replace(/[^a-zA-Z0-9_\-/.#]/g, "");
429
590
  }
@@ -375,7 +375,6 @@ type ServiceNormalizedProps = ServiceProps & {
375
375
  memory: Exclude<ServiceProps["memory"], undefined>;
376
376
  port: Exclude<ServiceProps["port"], undefined>;
377
377
  logRetention: Exclude<ServiceProps["logRetention"], undefined>;
378
- waitForInvalidation: Exclude<ServiceProps["waitForInvalidation"], undefined>;
379
378
  };
380
379
  /**
381
380
  * The `Service` construct is a higher level CDK construct that makes it easy to create modern web apps with Server Side Rendering capabilities.
@@ -159,7 +159,6 @@ export class Service extends Construct {
159
159
  memory: props?.memory || "0.5 GB",
160
160
  port: props?.port || 3000,
161
161
  logRetention: props?.logRetention || "infinite",
162
- waitForInvalidation: false,
163
162
  ...props,
164
163
  };
165
164
  this.doNotDeploy =
@@ -214,21 +214,6 @@ export interface SsrSiteProps {
214
214
  * ```
215
215
  */
216
216
  textEncoding?: "utf-8" | "iso-8859-1" | "windows-1252" | "ascii" | "none";
217
- /**
218
- * The strategy to use for invalidating the CDN cache. By default, the CDN cache will invalidate on changes any cached file, but this could become slow on very large projects.
219
- * - "never" - No invalidation will be performed.
220
- * - "all" - All files will be invalidated when any file changes. (Default, requires checking file content which will increase deployment time)
221
- * - "versioned" - Only versioned files will be invalidated when versioned files change.
222
- * - "always" - All files are invalidated on every deployment.
223
- * @default all
224
- * @example
225
- * ```js
226
- * assets: {
227
- * cdnInvalidationStrategy: "versioned"
228
- * }
229
- * ```
230
- */
231
- cdnInvalidationStrategy?: "never" | "all" | "versioned" | "always";
232
217
  /**
233
218
  * The TTL for versioned files (ex: `main-1234.css`) in the CDN and browser cache. Ignored when `versionedFilesCacheHeader` is specified.
234
219
  * @default 1 year
@@ -294,9 +279,45 @@ export interface SsrSiteProps {
294
279
  */
295
280
  _uploadConcurrency?: number;
296
281
  };
282
+ invalidation?: {
283
+ /**
284
+ * While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
285
+ * @default false
286
+ * @example
287
+ * ```js
288
+ * invalidation: {
289
+ * wait: true,
290
+ * }
291
+ * ```
292
+ */
293
+ wait?: boolean;
294
+ /**
295
+ * The paths to invalidate. There are three built-in options:
296
+ * - "none" - No invalidation will be performed.
297
+ * - "all" - All files will be invalidated when any file changes.
298
+ * - "versioned" - Only versioned files will be invalidated when versioned files change.
299
+ * Alternatively you can pass in an array of paths to invalidate.
300
+ * @default "all"
301
+ * @example
302
+ * Disable invalidation:
303
+ * ```js
304
+ * invalidation: {
305
+ * paths: "none",
306
+ * }
307
+ * ```
308
+ * Invalidate "index.html" and all files under the "products" route:
309
+ * ```js
310
+ * invalidation: {
311
+ * paths: ["/index.html", "/products/*"],
312
+ * }
313
+ * ```
314
+ */
315
+ paths?: "none" | "all" | "versioned" | string[];
316
+ };
297
317
  /**
298
318
  * While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
299
319
  * @default false
320
+ * @deprecated Use `invalidation.wait` instead.
300
321
  */
301
322
  waitForInvalidation?: boolean;
302
323
  cdk?: {
@@ -343,13 +364,16 @@ export interface SsrSiteProps {
343
364
  server?: Pick<CdkFunctionProps, "layers" | "vpc" | "vpcSubnets" | "securityGroups" | "allowAllOutbound" | "allowPublicSubnet" | "architecture" | "logRetention"> & Pick<FunctionProps, "copyFiles">;
344
365
  };
345
366
  }
367
+ type SsrSiteInvalidationNormalizedProps = Exclude<SsrSiteProps["invalidation"], undefined>;
346
368
  export type SsrSiteNormalizedProps = SsrSiteProps & {
347
369
  path: Exclude<SsrSiteProps["path"], undefined>;
348
370
  typesPath: Exclude<SsrSiteProps["typesPath"], undefined>;
349
371
  runtime: Exclude<SsrSiteProps["runtime"], undefined>;
350
372
  timeout: Exclude<SsrSiteProps["timeout"], undefined>;
351
373
  memorySize: Exclude<SsrSiteProps["memorySize"], undefined>;
352
- waitForInvalidation: Exclude<SsrSiteProps["waitForInvalidation"], undefined>;
374
+ invalidation: Exclude<SsrSiteProps["invalidation"], undefined> & {
375
+ paths: Exclude<SsrSiteInvalidationNormalizedProps["paths"], undefined>;
376
+ };
353
377
  };
354
378
  /**
355
379
  * The `SsrSite` construct is a higher level CDK construct that makes it easy to create modern web apps with Server Side Rendering capabilities.