sst 2.30.4 → 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);
@@ -1,20 +1,9 @@
1
1
  import { ErrorResponse, DistributionProps, BehaviorOptions, IOrigin } from "aws-cdk-lib/aws-cloudfront";
2
- export interface BaseSiteFileOptionsFilter {
3
- include?: string;
4
- exclude?: string;
5
- }
6
2
  export interface BaseSiteFileOptions {
7
- filters: BaseSiteFileOptionsFilter[];
8
- cacheControl?: string;
9
- contentType?: string;
10
- contentEncoding?: string;
11
- }
12
- export interface BaseSiteFileOptionsDeprecated {
13
- include?: string | string[];
14
- exclude?: string | string[];
3
+ files: string | string[];
4
+ ignore?: string | string[];
15
5
  cacheControl?: string;
16
6
  contentType?: string;
17
- contentEncoding?: string;
18
7
  }
19
8
  export interface BaseSiteEnvironmentOutputsInfo {
20
9
  path: string;
@@ -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
@@ -27,7 +27,7 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
27
27
  * logging: "per-route",
28
28
  * ```
29
29
  */
30
- _logging?: "combined" | "per-route";
30
+ logging?: "combined" | "per-route";
31
31
  imageOptimization?: {
32
32
  /**
33
33
  * The amount of memory in MB allocated for image optimization function.
@@ -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,7 +15,10 @@ 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";
16
- const LAYER_VERSION = "9";
18
+ import { Asset } from "aws-cdk-lib/aws-s3-assets";
19
+ import { useFunctions } from "./Function.js";
20
+ import { useDeferredTasks } from "./deferred_task.js";
21
+ const LAYER_VERSION = "2";
17
22
  const DEFAULT_OPEN_NEXT_VERSION = "2.2.4";
18
23
  const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
19
24
  "accept",
@@ -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: {
@@ -305,14 +316,17 @@ export class NextjsSite extends SsrSite {
305
316
  };
306
317
  }
307
318
  wrapServerFunction(config) {
308
- const { path: sitePath, experimental } = this.props;
319
+ const { path: sitePath, experimental, cdk } = this.props;
309
320
  const stack = Stack.of(this);
310
321
  const wrapperName = "nextjssite-index";
311
322
  const serverPath = path.join(sitePath, ".open-next", "server-function");
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",
@@ -341,65 +355,194 @@ export class NextjsSite extends SsrSite {
341
355
  ...config,
342
356
  layers: this.isPerRouteLoggingEnabled()
343
357
  ? [
344
- LayerVersion.fromLayerVersionArn(this, "SSTExtension", `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension:${LAYER_VERSION}`),
358
+ LayerVersion.fromLayerVersionArn(this, "SSTExtension", cdk?.server?.architecture?.name === Architecture.X86_64.name
359
+ ? `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-amd64:${LAYER_VERSION}`
360
+ : `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-arm64:${LAYER_VERSION}`),
345
361
  ]
346
362
  : undefined,
347
363
  handler: `${wrapperName}.handler`,
348
364
  };
349
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
+ }
350
377
  useRoutes() {
351
- if (this.routes)
352
- return this.routes;
353
- 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;
354
417
  const { path: sitePath } = this.props;
418
+ const id = this.node.id;
355
419
  try {
356
- const content = JSON.parse(fs
420
+ const content = fs
357
421
  .readFileSync(path.join(sitePath, ".next/routes-manifest.json"))
358
- .toString());
359
- this.routes = [
360
- ...[...content.dynamicRoutes, ...content.staticRoutes]
361
- .map(({ page, regex }) => {
362
- const cwRoute = NextjsSite.buildCloudWatchRouteName(page);
363
- const cwHash = NextjsSite.buildCloudWatchRouteHash(page);
364
- return {
365
- route: page,
366
- regex,
367
- logGroupPath: `/${cwHash}${cwRoute}`,
368
- };
369
- })
370
- .sort((a, b) => a.route.localeCompare(b.route)),
371
- ...(content.dataRoutes || [])
372
- .map(({ page, dataRouteRegex }) => {
373
- const routeDisplayName = page.endsWith("/")
374
- ? `/_next/data/BUILD_ID${page}index.json`
375
- : `/_next/data/BUILD_ID${page}.json`;
376
- const cwRoute = NextjsSite.buildCloudWatchRouteName(routeDisplayName);
377
- const cwHash = NextjsSite.buildCloudWatchRouteHash(`data:${page}`);
378
- return {
379
- route: routeDisplayName,
380
- regex: dataRouteRegex,
381
- logGroupPath: `/${cwHash}${cwRoute}`,
382
- };
383
- })
384
- .sort((a, b) => a.route.localeCompare(b.route)),
385
- ];
386
- return this.routes;
422
+ .toString();
423
+ this.routesManifest = JSON.parse(content);
424
+ return this.routesManifest;
387
425
  }
388
426
  catch (e) {
389
427
  console.error(e);
390
428
  throw new VisibleError(`Failed to read routes data from ".next/routes-manifest.json" for the "${id}" site.`);
391
429
  }
392
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
+ }
393
476
  getBuildId() {
394
477
  const { path: sitePath } = this.props;
395
478
  return fs.readFileSync(path.join(sitePath, ".next/BUILD_ID")).toString();
396
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
+ }
397
538
  isPerRouteLoggingEnabled() {
398
539
  return (!this.doNotDeploy &&
399
540
  !this.props.edge &&
400
- this.props._logging === "per-route");
541
+ this.props.logging === "per-route");
401
542
  }
402
543
  disableDefaultLogging() {
544
+ // Note: keep default logs enabled
545
+ return;
403
546
  const stack = Stack.of(this);
404
547
  const server = this.serverFunction;
405
548
  const policy = new Policy(this, "DisableLoggingPolicy", {
@@ -420,6 +563,28 @@ export class NextjsSite extends SsrSite {
420
563
  });
421
564
  server.role?.attachInlinePolicy(policy);
422
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
+ }
423
588
  static buildCloudWatchRouteName(route) {
424
589
  return route.replace(/[^a-zA-Z0-9_\-/.#]/g, "");
425
590
  }
package/constructs/RDS.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import glob from "glob";
2
+ import { globSync } from "glob";
3
3
  import fs from "fs";
4
4
  import url from "url";
5
5
  import * as crypto from "crypto";
@@ -347,7 +347,7 @@ export class RDS extends Construct {
347
347
  }
348
348
  generateMigrationsHash(migrations) {
349
349
  // Get all files inside the migrations folder
350
- const files = glob.sync("**", {
350
+ const files = globSync("**", {
351
351
  dot: true,
352
352
  nodir: true,
353
353
  follow: true,
@@ -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 =