cdk-nextjs 0.3.11 → 0.4.1

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.
Files changed (106) hide show
  1. package/.jsii +1248 -748
  2. package/API.md +1033 -531
  3. package/README.md +1 -2
  4. package/assets/lambdas/assets-deployment/assets-deployment.lambda/assets-deployment.Dockerfile +12 -8
  5. package/assets/lambdas/assets-deployment/assets-deployment.lambda/index.js +67 -56
  6. package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +246 -0
  7. package/docs/breaking-changes.md +9 -0
  8. package/docs/notes.md +170 -20
  9. package/lib/constants.d.ts +32 -0
  10. package/lib/constants.js +38 -0
  11. package/lib/generated-structs/OptionalCustomResourceProps.d.ts +104 -0
  12. package/lib/generated-structs/OptionalCustomResourceProps.js +3 -0
  13. package/lib/generated-structs/OptionalDockerImageAssetProps.d.ts +21 -0
  14. package/lib/generated-structs/OptionalDockerImageAssetProps.js +1 -1
  15. package/lib/generated-structs/OptionalNextjsAssetsDeploymentProps.d.ts +4 -3
  16. package/lib/generated-structs/OptionalNextjsAssetsDeploymentProps.js +1 -1
  17. package/lib/generated-structs/OptionalNextjsBuildProps.d.ts +1 -1
  18. package/lib/generated-structs/OptionalNextjsBuildProps.js +1 -1
  19. package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +3 -3
  20. package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
  21. package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +42 -0
  22. package/lib/generated-structs/OptionalNextjsPostDeployProps.js +3 -0
  23. package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +41 -0
  24. package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +4 -0
  25. package/lib/index.d.ts +6 -5
  26. package/lib/index.js +6 -8
  27. package/lib/lambdas/assets-deployment/assets-deployment.lambda.js +5 -23
  28. package/lib/lambdas/assets-deployment/fs-to-fs.js +18 -3
  29. package/lib/lambdas/assets-deployment/fs-to-s3.d.ts +8 -2
  30. package/lib/lambdas/assets-deployment/fs-to-s3.js +14 -8
  31. package/lib/lambdas/post-deploy/create-invalidation.d.ts +2 -0
  32. package/lib/lambdas/post-deploy/create-invalidation.js +19 -0
  33. package/lib/lambdas/post-deploy/post-deploy-function.d.ts +13 -0
  34. package/lib/lambdas/post-deploy/post-deploy-function.js +22 -0
  35. package/lib/lambdas/post-deploy/post-deploy.lambda.d.ts +9 -0
  36. package/lib/lambdas/post-deploy/post-deploy.lambda.js +56 -0
  37. package/lib/lambdas/post-deploy/prune-fs.d.ts +10 -0
  38. package/lib/lambdas/post-deploy/prune-fs.js +33 -0
  39. package/lib/lambdas/post-deploy/prune-s3.d.ts +15 -0
  40. package/lib/lambdas/post-deploy/prune-s3.js +101 -0
  41. package/lib/lambdas/utils.js +35 -0
  42. package/lib/nextjs-assets-deployment.d.ts +15 -51
  43. package/lib/nextjs-assets-deployment.js +27 -25
  44. package/lib/nextjs-build/builder.Dockerfile +12 -8
  45. package/lib/nextjs-build/cdk-nextjs-cache-handler.cjs +63 -0
  46. package/lib/nextjs-build/cdk-nextjs-cache-handler.d.ts +21 -0
  47. package/lib/nextjs-build/cdk-nextjs-cache-handler.js +49 -0
  48. package/lib/nextjs-build/global-containers.Dockerfile +29 -19
  49. package/lib/nextjs-build/global-functions.Dockerfile +30 -24
  50. package/lib/nextjs-build/nextjs-build.d.ts +51 -26
  51. package/lib/nextjs-build/nextjs-build.js +108 -88
  52. package/lib/nextjs-build/regional-containers.Dockerfile +30 -20
  53. package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +0 -1
  54. package/lib/nextjs-compute/nextjs-compute-base-props.js +1 -1
  55. package/lib/nextjs-compute/nextjs-containers.d.ts +2 -1
  56. package/lib/nextjs-compute/nextjs-containers.js +7 -5
  57. package/lib/nextjs-compute/nextjs-functions.d.ts +1 -0
  58. package/lib/nextjs-compute/nextjs-functions.js +8 -11
  59. package/lib/nextjs-distribution.d.ts +1 -1
  60. package/lib/nextjs-distribution.js +4 -4
  61. package/lib/nextjs-file-system.js +1 -1
  62. package/lib/nextjs-post-deploy.d.ts +101 -0
  63. package/lib/nextjs-post-deploy.js +78 -0
  64. package/lib/nextjs-static-assets.js +1 -1
  65. package/lib/nextjs-vpc.d.ts +1 -1
  66. package/lib/nextjs-vpc.js +4 -4
  67. package/lib/root-constructs/nextjs-base-overrides.d.ts +4 -0
  68. package/lib/root-constructs/nextjs-base-overrides.js +1 -1
  69. package/lib/root-constructs/nextjs-base-props.d.ts +15 -7
  70. package/lib/root-constructs/nextjs-base-props.js +1 -1
  71. package/lib/root-constructs/nextjs-global-containers.d.ts +8 -8
  72. package/lib/root-constructs/nextjs-global-containers.js +25 -15
  73. package/lib/root-constructs/nextjs-global-functions.d.ts +8 -11
  74. package/lib/root-constructs/nextjs-global-functions.js +25 -25
  75. package/lib/root-constructs/nextjs-regional-containers.d.ts +8 -0
  76. package/lib/root-constructs/nextjs-regional-containers.js +26 -9
  77. package/lib/utils/get-architecture.d.ts +2 -0
  78. package/lib/utils/get-architecture.js +8 -0
  79. package/lib/utils/handle-deprecated-properties.d.ts +7 -0
  80. package/lib/utils/handle-deprecated-properties.js +14 -0
  81. package/package.json +17 -19
  82. package/assets/lambdas/revalidate/revalidate.lambda/index.js +0 -67
  83. package/lib/common.d.ts +0 -25
  84. package/lib/common.js +0 -30
  85. package/lib/generated-structs/OptionalNextjsInvalidationProps.d.ts +0 -11
  86. package/lib/generated-structs/OptionalNextjsInvalidationProps.js +0 -3
  87. package/lib/lambdas/assets-deployment/prune-s3.d.ts +0 -15
  88. package/lib/lambdas/assets-deployment/prune-s3.js +0 -42
  89. package/lib/lambdas/assets-deployment/s3.d.ts +0 -2
  90. package/lib/lambdas/assets-deployment/s3.js +0 -7
  91. package/lib/lambdas/assets-deployment/utils.js +0 -35
  92. package/lib/lambdas/revalidate/revalidate-function.d.ts +0 -13
  93. package/lib/lambdas/revalidate/revalidate-function.js +0 -22
  94. package/lib/lambdas/revalidate/revalidate.lambda.d.ts +0 -2
  95. package/lib/lambdas/revalidate/revalidate.lambda.js +0 -53
  96. package/lib/nextjs-build/add-cache-handler.d.ts +0 -1
  97. package/lib/nextjs-build/add-cache-handler.js +0 -23
  98. package/lib/nextjs-build/add-cache-handler.mjs +0 -18
  99. package/lib/nextjs-build/cache-handler.cjs +0 -21878
  100. package/lib/nextjs-build/cache-handler.d.ts +0 -6
  101. package/lib/nextjs-build/cache-handler.js +0 -26
  102. package/lib/nextjs-invalidation.d.ts +0 -19
  103. package/lib/nextjs-invalidation.js +0 -52
  104. package/lib/nextjs-revalidation.d.ts +0 -30
  105. package/lib/nextjs-revalidation.js +0 -65
  106. /package/lib/lambdas/{assets-deployment/utils.d.ts → utils.d.ts} +0 -0
package/README.md CHANGED
@@ -24,14 +24,13 @@ Deploy [Next.js](https://nextjs.org/) apps on [AWS](https://aws.amazon.com/) wit
24
24
 
25
25
  ## Features
26
26
 
27
- - Supports all features of Next.js App and Pages Router for [Node.js Runtime](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#nodejs-runtime)
27
+ - Supports all features of Next.js App and Pages Router for [Node.js Runtime](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#nodejs-runtime). One exception is `NextjsGlobalFunctions` does not currently support [ISR](https://nextjs.org/docs/app/guides/incremental-static-regeneration) - waiting for [Official Next.js Deployment Adapters API](https://github.com/vercel/next.js/discussions/77740).
28
28
  - Choose your AWS architecture for Next.js with the supported constructs: `NextjsGlobalFunctions`, `NextjsGlobalContainers`, `NextjsRegionalContainers`.
29
29
  - Global Content Delivery Network (CDN) built with [Amazon CloudFront](https://aws.amazon.com/cloudfront/) to deliver content with low latency and high transfer speeds.
30
30
  - Serverless functions powered by [AWS Lambda](https://aws.amazon.com/lambda/) or serverless containers powered by [AWS Fargate](https://aws.amazon.com/fargate/).
31
31
  - Static assets (JS, CSS, public folder) are stored and served from [Amazon Simple Storage Service (S3)](https://aws.amazon.com/s3/) for global constructs to decrease latency and reduce compute costs.
32
32
  - [Optimized images](https://nextjs.org/docs/pages/building-your-application/optimizing/images), [data cache](https://nextjs.org/docs/app/building-your-application/caching#data-cache), and [full route cache](https://nextjs.org/docs/app/building-your-application/caching#full-route-cache) are shared across compute with [Amazon Elastic File System (EFS)](https://aws.amazon.com/efs/).
33
33
  - Customize every construct via `overrides`.
34
- - WIP: When using AWS Lambda for compute, async revalidation is supported with [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/).
35
34
  - AWS security and operational best practices are utilized, guided by [cdk-nag](https://github.com/cdklabs/cdk-nag).
36
35
  - First class support for [monorepos](https://monorepo.tools/).
37
36
  - [AWS GovCloud (US)](https://aws.amazon.com/govcloud-us) compatible (with `NextjsRegionalContainers`).
@@ -1,15 +1,19 @@
1
1
  #checkov:skip=CKV_DOCKER_2: healthcheck not needed for Custom Resource
2
2
  #checkov:skip=CKV_DOCKER_3: lambda container creates user by default
3
3
  #checkov:skip=CKV_DOCKER_7: latest tag is ok to use for local builder container
4
- ARG BUILDER_IMAGE_TAG
5
- FROM $BUILDER_IMAGE_TAG as builder
6
- FROM public.ecr.aws/lambda/nodejs:22 as runner
4
+ ARG BUILDER_IMAGE_ALIAS=cdk-nextjs/builder:latest
5
+ FROM $BUILDER_IMAGE_ALIAS AS builder
6
+ FROM public.ecr.aws/lambda/nodejs:22 AS runner
7
+ # do not set WORKDIR b/c it's configured by public.ecr.aws/lambda/nodejs:22 to be /var/task for lambda to run
7
8
 
8
- ARG RELATIVE_PATH_TO_WORKSPACE
9
- COPY --from=builder --chown=nextjs:nodejs /app/$RELATIVE_PATH_TO_WORKSPACE/public /app/public
10
- # see src/nextjs-assets-deployment.ts's #createCustomResource method to see what
11
- # we copy from within this resulting image in /app/.next to S3 and EFS
12
- COPY --from=builder --chown=nextjs:nodejs /app/$RELATIVE_PATH_TO_WORKSPACE/.next /app/.next
9
+ ARG RELATIVE_PATH_TO_PACKAGE
10
+ ARG PUBLIC_PATH
11
+ COPY --from=builder --chown=nextjs:nodejs /app/$RELATIVE_PATH_TO_PACKAGE/$PUBLIC_PATH /app/$PUBLIC_PATH
12
+ # 1. see src/nextjs-assets-deployment.ts's #createCustomResource method to see what
13
+ # we copy from this resulting image to S3 and EFS
14
+ # 2. we don't copy from /app/$RELATIVE_PATH_TO_PACKAGE/.next/standalone like in
15
+ # compute images because we need .next/cache folder which doesn't exist within standalone
16
+ COPY --from=builder --chown=nextjs:nodejs /app/$RELATIVE_PATH_TO_PACKAGE/.next /app/.next
13
17
  # create fetch-cache if not created by next build
14
18
  RUN mkdir -p /app/.next/cache/fetch-cache
15
19
  # copy bundled custom resource handler
@@ -8568,7 +8568,7 @@ var require_mime_types = __commonJS({
8568
8568
  "node_modules/.pnpm/mime-types@2.1.35/node_modules/mime-types/index.js"(exports2) {
8569
8569
  "use strict";
8570
8570
  var db = require_mime_db();
8571
- var extname = require("path").extname;
8571
+ var extname2 = require("path").extname;
8572
8572
  var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
8573
8573
  var TEXT_TYPE_REGEXP = /^text\//i;
8574
8574
  exports2.charset = charset;
@@ -8622,7 +8622,7 @@ var require_mime_types = __commonJS({
8622
8622
  if (!path || typeof path !== "string") {
8623
8623
  return false;
8624
8624
  }
8625
- var extension2 = extname("x." + path).toLowerCase().substr(1);
8625
+ var extension2 = extname2("x." + path).toLowerCase().substr(1);
8626
8626
  if (!extension2) {
8627
8627
  return false;
8628
8628
  }
@@ -8663,25 +8663,60 @@ var import_node_fs4 = require("node:fs");
8663
8663
 
8664
8664
  // src/lambdas/assets-deployment/fs-to-fs.ts
8665
8665
  var import_node_fs = require("node:fs");
8666
+ var import_node_path = require("node:path");
8667
+
8668
+ // src/lambdas/utils.ts
8669
+ function cfnResponse(props) {
8670
+ const body = JSON.stringify({
8671
+ Status: props.responseStatus,
8672
+ Reason: "See the details in CloudWatch Log Stream: " + props.context.logStreamName,
8673
+ PhysicalResourceId: props.physicalResourceId || props.context.logStreamName,
8674
+ StackId: props.event.StackId,
8675
+ RequestId: props.event.RequestId,
8676
+ LogicalResourceId: props.event.LogicalResourceId,
8677
+ Data: props.responseData
8678
+ });
8679
+ return fetch(props.event.ResponseURL, {
8680
+ method: "PUT",
8681
+ body,
8682
+ headers: { "content-type": "", "content-length": body.length.toString() }
8683
+ });
8684
+ }
8685
+ function debug(value) {
8686
+ if (process.env.DEBUG) console.log(JSON.stringify(value, null, 2));
8687
+ }
8688
+
8689
+ // src/lambdas/assets-deployment/fs-to-fs.ts
8666
8690
  function fsToFs(props) {
8667
- const { sourcePath, destinationPath } = props;
8668
- (0, import_node_fs.cpSync)(sourcePath, destinationPath, { recursive: true });
8691
+ const { sourcePath, destinationPath, includeExtensions } = props;
8692
+ debug(`Copying ${sourcePath} to ${destinationPath} recursively`);
8693
+ (0, import_node_fs.cpSync)(sourcePath, destinationPath, {
8694
+ recursive: true,
8695
+ filter: (_src, dest) => {
8696
+ const isDirectory = (0, import_node_path.extname)(dest) === "";
8697
+ if (isDirectory) {
8698
+ return true;
8699
+ }
8700
+ return includeExtensions ? includeExtensions.includes((0, import_node_path.extname)(dest)) : true;
8701
+ }
8702
+ });
8669
8703
  }
8670
8704
 
8671
8705
  // src/lambdas/assets-deployment/fs-to-s3.ts
8672
8706
  var import_node_fs3 = require("node:fs");
8673
- var import_node_path2 = require("node:path");
8707
+ var import_node_path3 = require("node:path");
8708
+ var import_client_s3 = require("@aws-sdk/client-s3");
8674
8709
  var import_lib_storage = require("@aws-sdk/lib-storage");
8675
8710
  var mime = __toESM(require_mime_types());
8676
8711
 
8677
8712
  // src/lambdas/assets-deployment/common.ts
8678
8713
  var import_node_fs2 = require("node:fs");
8679
- var import_node_path = require("node:path");
8714
+ var import_node_path2 = require("node:path");
8680
8715
  function listFilePaths(dirPath) {
8681
8716
  const filePaths = [];
8682
8717
  const directory = (0, import_node_fs2.readdirSync)(dirPath, { withFileTypes: true });
8683
8718
  for (const d of directory) {
8684
- const filePath = (0, import_node_path.resolve)(dirPath, d.name);
8719
+ const filePath = (0, import_node_path2.resolve)(dirPath, d.name);
8685
8720
  if (d.isDirectory()) {
8686
8721
  filePaths.push(...listFilePaths(filePath));
8687
8722
  } else {
@@ -8696,34 +8731,23 @@ async function* chunkArray(array, chunkSize) {
8696
8731
  }
8697
8732
  }
8698
8733
 
8699
- // src/lambdas/assets-deployment/s3.ts
8700
- var import_client_s3 = require("@aws-sdk/client-s3");
8701
- var s3 = new import_client_s3.S3Client({});
8702
-
8703
- // src/lambdas/assets-deployment/utils.ts
8704
- function cfnResponse(props) {
8705
- const body = JSON.stringify({
8706
- Status: props.responseStatus,
8707
- Reason: "See the details in CloudWatch Log Stream: " + props.context.logStreamName,
8708
- PhysicalResourceId: props.physicalResourceId || props.context.logStreamName,
8709
- StackId: props.event.StackId,
8710
- RequestId: props.event.RequestId,
8711
- LogicalResourceId: props.event.LogicalResourceId,
8712
- Data: props.responseData
8713
- });
8714
- return fetch(props.event.ResponseURL, {
8715
- method: "PUT",
8716
- body,
8717
- headers: { "content-type": "", "content-length": body.length.toString() }
8718
- });
8719
- }
8720
- function debug(value) {
8721
- if (process.env.DEBUG) console.log(JSON.stringify(value, null, 2));
8722
- }
8734
+ // src/constants.ts
8735
+ var CACHE_PATH = ".next/cache";
8736
+ var DATA_CACHE_PATH = `${CACHE_PATH}/fetch-cache`;
8737
+ var IMAGE_CACHE_PATH = `${CACHE_PATH}/images`;
8738
+ var SERVER_DIST_PATH = ".next/server";
8739
+ var FULL_ROUTE_CACHE_PATH = `${SERVER_DIST_PATH}/app`;
8723
8740
 
8724
8741
  // src/lambdas/assets-deployment/fs-to-s3.ts
8725
- async function fsToS3(props, nextjsType) {
8726
- const { destinationBucketName, destinationKeyPrefix, sourcePath } = props;
8742
+ var s3 = new import_client_s3.S3Client({});
8743
+ async function fsToS3(props) {
8744
+ const {
8745
+ buildId,
8746
+ destinationBucketName,
8747
+ destinationKeyPrefix,
8748
+ nextjsType,
8749
+ sourcePath
8750
+ } = props;
8727
8751
  const sourceFilePaths = listFilePaths(sourcePath);
8728
8752
  for await (const filePathChunk of chunkArray(sourceFilePaths, 100)) {
8729
8753
  const putObjectInputs = filePathChunk.map(
@@ -8738,7 +8762,7 @@ async function fsToS3(props, nextjsType) {
8738
8762
  if (path.includes(".next/static/chunks/main-app-") && nextjsType === "GLOBAL_FUNCTIONS" /* GLOBAL_FUNCTIONS */) {
8739
8763
  const mainAppFileContent = (0, import_node_fs3.readFileSync)(path);
8740
8764
  const patchFetchContent = (0, import_node_fs3.readFileSync)(
8741
- (0, import_node_path2.join)(__dirname, "patch-fetch.js")
8765
+ (0, import_node_path3.join)(__dirname, "patch-fetch.js")
8742
8766
  ).toString().replace('"use strict";', "");
8743
8767
  body = patchFetchContent + "\n" + mainAppFileContent;
8744
8768
  } else {
@@ -8748,7 +8772,10 @@ async function fsToS3(props, nextjsType) {
8748
8772
  Body: body,
8749
8773
  Bucket: destinationBucketName,
8750
8774
  ContentType: contentType,
8751
- Key: key
8775
+ Key: key,
8776
+ Metadata: {
8777
+ "next-build-id": buildId
8778
+ }
8752
8779
  };
8753
8780
  }
8754
8781
  );
@@ -8768,36 +8795,26 @@ async function fsToS3(props, nextjsType) {
8768
8795
  function createS3Key({ keyPrefix, path, basePath }) {
8769
8796
  const objectKeyParts = [];
8770
8797
  if (keyPrefix) objectKeyParts.push(keyPrefix);
8771
- objectKeyParts.push((0, import_node_path2.relative)(basePath, path));
8772
- return (0, import_node_path2.join)(...objectKeyParts);
8773
- }
8774
-
8775
- // src/lambdas/assets-deployment/prune-s3.ts
8776
- var import_client_s32 = require("@aws-sdk/client-s3");
8777
- async function pruneS3(props) {
8778
- props;
8798
+ objectKeyParts.push((0, import_node_path3.relative)(basePath, path));
8799
+ return (0, import_node_path3.join)(...objectKeyParts);
8779
8800
  }
8780
8801
 
8781
8802
  // src/lambdas/assets-deployment/assets-deployment.lambda.ts
8782
8803
  var handler = async (event, context) => {
8783
8804
  debug({ event });
8784
8805
  let responseStatus = "FAILED" /* Failed */;
8785
- let previewModeId = "";
8786
8806
  try {
8787
8807
  const props = event.ResourceProperties;
8788
8808
  if (event.RequestType === "Create" || event.RequestType === "Update") {
8789
- const { actions, nextjsType } = props;
8809
+ const { actions, buildId, nextjsType } = props;
8790
8810
  for (const action of actions) {
8791
8811
  if (action.type === "fs-to-fs") {
8792
8812
  fsToFs(action);
8793
8813
  } else if (action.type === "fs-to-s3") {
8794
- await fsToS3(action, nextjsType);
8795
- } else if (action.type === "prune-s3") {
8796
- await pruneS3(action);
8814
+ await fsToS3({ ...action, nextjsType, buildId });
8797
8815
  }
8798
8816
  }
8799
8817
  initImageCache(props.imageCachePath);
8800
- previewModeId = getPreviewModeId(props.prerenderManifestPath);
8801
8818
  responseStatus = "SUCCESS" /* Success */;
8802
8819
  } else {
8803
8820
  responseStatus = "SUCCESS" /* Success */;
@@ -8809,7 +8826,7 @@ var handler = async (event, context) => {
8809
8826
  event,
8810
8827
  context,
8811
8828
  responseStatus,
8812
- responseData: { previewModeId }
8829
+ responseData: {}
8813
8830
  });
8814
8831
  }
8815
8832
  };
@@ -8818,12 +8835,6 @@ function initImageCache(imageCachePath) {
8818
8835
  (0, import_node_fs4.mkdirSync)(imageCachePath);
8819
8836
  }
8820
8837
  }
8821
- function getPreviewModeId(prerenderManifestPath) {
8822
- const prerenderManifest = JSON.parse(
8823
- (0, import_node_fs4.readFileSync)(prerenderManifestPath, { encoding: "utf-8" })
8824
- );
8825
- return prerenderManifest.preview.previewModeId;
8826
- }
8827
8838
  // Annotate the CommonJS export names for ESM import in node:
8828
8839
  0 && (module.exports = {
8829
8840
  handler
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lambdas/post-deploy/post-deploy.lambda.ts
21
+ var post_deploy_lambda_exports = {};
22
+ __export(post_deploy_lambda_exports, {
23
+ handler: () => handler
24
+ });
25
+ module.exports = __toCommonJS(post_deploy_lambda_exports);
26
+
27
+ // src/lambdas/post-deploy/create-invalidation.ts
28
+ var import_client_cloudfront = require("@aws-sdk/client-cloudfront");
29
+ var cfClient = new import_client_cloudfront.CloudFrontClient();
30
+ async function createInvalidation(input) {
31
+ return cfClient.send(
32
+ new import_client_cloudfront.CreateInvalidationCommand({
33
+ DistributionId: input.distributionId,
34
+ InvalidationBatch: {
35
+ CallerReference: input.invalidationBatch.callerReference,
36
+ Paths: {
37
+ Quantity: input.invalidationBatch.paths.quantity,
38
+ Items: input.invalidationBatch.paths.items
39
+ }
40
+ }
41
+ })
42
+ );
43
+ }
44
+
45
+ // src/lambdas/post-deploy/prune-fs.ts
46
+ var import_node_fs = require("node:fs");
47
+ var import_node_path = require("node:path");
48
+
49
+ // src/lambdas/utils.ts
50
+ function cfnResponse(props) {
51
+ const body = JSON.stringify({
52
+ Status: props.responseStatus,
53
+ Reason: "See the details in CloudWatch Log Stream: " + props.context.logStreamName,
54
+ PhysicalResourceId: props.physicalResourceId || props.context.logStreamName,
55
+ StackId: props.event.StackId,
56
+ RequestId: props.event.RequestId,
57
+ LogicalResourceId: props.event.LogicalResourceId,
58
+ Data: props.responseData
59
+ });
60
+ return fetch(props.event.ResponseURL, {
61
+ method: "PUT",
62
+ body,
63
+ headers: { "content-type": "", "content-length": body.length.toString() }
64
+ });
65
+ }
66
+ function debug(value) {
67
+ if (process.env.DEBUG) console.log(JSON.stringify(value, null, 2));
68
+ }
69
+
70
+ // src/lambdas/post-deploy/prune-fs.ts
71
+ function pruneFs(props) {
72
+ const { mountPath, currentBuildId } = props;
73
+ if (!(0, import_node_fs.existsSync)(mountPath)) {
74
+ debug(`Mount path ${mountPath} does not exist, nothing to prune`);
75
+ return;
76
+ }
77
+ const entries = (0, import_node_fs.readdirSync)(mountPath);
78
+ const directoriesToDelete = entries.filter((entry) => {
79
+ const entryPath = (0, import_node_path.join)(mountPath, entry);
80
+ return (0, import_node_fs.statSync)(entryPath).isDirectory() && entry !== currentBuildId;
81
+ });
82
+ for (const dir of directoriesToDelete) {
83
+ const dirPath = (0, import_node_path.join)(mountPath, dir);
84
+ debug(`Pruning directory: ${dirPath}`);
85
+ (0, import_node_fs.rmSync)(dirPath, { recursive: true, force: true });
86
+ }
87
+ debug(
88
+ `Pruned ${directoriesToDelete.length} directories, kept ${currentBuildId}`
89
+ );
90
+ }
91
+
92
+ // src/lambdas/post-deploy/prune-s3.ts
93
+ var import_client_s3 = require("@aws-sdk/client-s3");
94
+ var s3Client = new import_client_s3.S3Client();
95
+ var MAX_CONCURRENT_OPERATIONS = 50;
96
+ async function pruneS3(props) {
97
+ const { bucketName, currentBuildId, msTtl } = props;
98
+ const cutoffDate = new Date(Date.now() - msTtl);
99
+ const objectsToDelete = [];
100
+ let continuationToken = void 0;
101
+ let listObjectsCount = 0;
102
+ do {
103
+ const listObjectsV2Input = {
104
+ Bucket: bucketName,
105
+ ContinuationToken: continuationToken
106
+ };
107
+ const listResponse = await s3Client.send(
108
+ new import_client_s3.ListObjectsV2Command(listObjectsV2Input)
109
+ );
110
+ if (!listResponse.Contents || listResponse.Contents.length === 0) {
111
+ break;
112
+ }
113
+ const oldObjects = listResponse.Contents.filter((obj) => {
114
+ const lastModified = obj.LastModified || /* @__PURE__ */ new Date();
115
+ return obj.Key && lastModified < cutoffDate;
116
+ });
117
+ debug(
118
+ `Checking old objects metadata to determine pruning: ${oldObjects.map((o) => o.Key)}`
119
+ );
120
+ const checkResults = await processBatch(
121
+ oldObjects,
122
+ MAX_CONCURRENT_OPERATIONS,
123
+ async (object) => {
124
+ if (!object.Key) return null;
125
+ try {
126
+ const headResponse = await s3Client.send(
127
+ new import_client_s3.HeadObjectCommand({
128
+ Bucket: bucketName,
129
+ Key: object.Key
130
+ })
131
+ );
132
+ const objectBuildId = headResponse.Metadata?.["next-build-id"];
133
+ if (objectBuildId !== currentBuildId) {
134
+ return { Key: object.Key };
135
+ }
136
+ } catch (error) {
137
+ console.error(`Error checking object ${object.Key}:`, error);
138
+ }
139
+ return null;
140
+ }
141
+ );
142
+ objectsToDelete.push(
143
+ ...checkResults.filter(Boolean)
144
+ );
145
+ if (listResponse.NextContinuationToken) {
146
+ continuationToken = listResponse.NextContinuationToken;
147
+ }
148
+ listObjectsCount++;
149
+ } while (continuationToken && listObjectsCount <= 100);
150
+ if (objectsToDelete.length > 0) {
151
+ const deleteBatches = [];
152
+ for (let i = 0; i < objectsToDelete.length; i += 1e3) {
153
+ const batch = objectsToDelete.slice(i, i + 1e3);
154
+ deleteBatches.push(batch);
155
+ }
156
+ await processBatch(
157
+ deleteBatches,
158
+ 5,
159
+ // Process up to 5 delete batches in parallel
160
+ async (batch) => {
161
+ try {
162
+ debug(
163
+ `Deleting objects: ${batch.map((b) => b.Key)} from ${bucketName}`
164
+ );
165
+ await s3Client.send(
166
+ new import_client_s3.DeleteObjectsCommand({
167
+ Bucket: bucketName,
168
+ Delete: { Objects: batch }
169
+ })
170
+ );
171
+ debug(`Deleted ${batch.length} objects from ${bucketName}`);
172
+ } catch (error) {
173
+ console.error("Error deleting objects:", error);
174
+ }
175
+ }
176
+ );
177
+ }
178
+ debug(
179
+ `Pruning complete. Deleted ${objectsToDelete.length} objects from ${bucketName}`
180
+ );
181
+ }
182
+ async function processBatch(items, batchSize, processFn) {
183
+ const results = [];
184
+ for (let i = 0; i < items.length; i += batchSize) {
185
+ const batch = items.slice(i, i + batchSize);
186
+ const batchResults = await Promise.all(batch.map(processFn));
187
+ results.push(...batchResults);
188
+ }
189
+ return results;
190
+ }
191
+
192
+ // src/constants.ts
193
+ var MOUNT_PATH = "/mnt/cdk-nextjs";
194
+ var CACHE_PATH = ".next/cache";
195
+ var DATA_CACHE_PATH = `${CACHE_PATH}/fetch-cache`;
196
+ var IMAGE_CACHE_PATH = `${CACHE_PATH}/images`;
197
+ var SERVER_DIST_PATH = ".next/server";
198
+ var FULL_ROUTE_CACHE_PATH = `${SERVER_DIST_PATH}/app`;
199
+
200
+ // src/lambdas/post-deploy/post-deploy.lambda.ts
201
+ var handler = async (event, context) => {
202
+ debug({ event });
203
+ let responseStatus = "FAILED" /* Failed */;
204
+ try {
205
+ const props = event.ResourceProperties;
206
+ if (event.RequestType === "Create" || event.RequestType === "Update") {
207
+ const {
208
+ buildId,
209
+ createInvalidationCommandInput,
210
+ msTtl,
211
+ staticAssetsBucketName
212
+ } = props;
213
+ const promises = [];
214
+ if (createInvalidationCommandInput) {
215
+ promises.push(createInvalidation(createInvalidationCommandInput));
216
+ }
217
+ pruneFs({ currentBuildId: buildId, mountPath: MOUNT_PATH });
218
+ if (staticAssetsBucketName) {
219
+ promises.push(
220
+ pruneS3({
221
+ bucketName: staticAssetsBucketName,
222
+ currentBuildId: buildId,
223
+ msTtl: parseInt(msTtl)
224
+ })
225
+ );
226
+ }
227
+ await Promise.all(promises);
228
+ responseStatus = "SUCCESS" /* Success */;
229
+ } else {
230
+ responseStatus = "SUCCESS" /* Success */;
231
+ }
232
+ } catch (err) {
233
+ console.error(err);
234
+ } finally {
235
+ await cfnResponse({
236
+ event,
237
+ context,
238
+ responseStatus,
239
+ responseData: {}
240
+ });
241
+ }
242
+ };
243
+ // Annotate the CommonJS export names for ESM import in node:
244
+ 0 && (module.exports = {
245
+ handler
246
+ });
@@ -1,5 +1,14 @@
1
1
  # Breaking Changes
2
2
 
3
+ ## 0.4.0
4
+
5
+ - Change `Nextjs{...}Props.relativePathToWorkspace` -> `Nextjs{...}Props.relativePathToPackage` because workspace references entire monorepo not a package in the monorepo.
6
+ - [NextjsGlobalFunctions] Remove `NextjsRevalidation`. `NextjsRevalidation` was an initial effor to support ISR. This wasn't working as intended. With [RFC: Deployment Adapters API](https://github.com/vercel/next.js/discussions/77740) specifically addressing the pain point, "No signal for when background work like revalidating is complete", cdk-nextjs will support ISR once the `waitFor` callback is officially supported by Next.js instead of using internal workaround. This is inline with the design principles of cdk-nextjs. Removing this construct simplifies cdk-nextjs and makes it more maintainable long term.
7
+ - [NextjsGlobal...] Refactor `NextjsInvalidation` to `NextjsPostDeploy` which now does not use `AwsCustomResource` but is `CustomResource` which calls `CreateInvalidation` API within, in addition to pruning EFS old BUILD_ID folder and old S3 static assets
8
+ - Relevant but not user facing change is that EFS Mounts are now segmented by BUILD_ID and S3 objects are now tagged with "next-build-id" metadata which are a step towards Blue/Green deployments.
9
+ - Change BUILDER_IMAGE_TAG to BUILDER_IMAGE_ALIAS in `NextjsBuild` and Dockerfiles. This change is mostly internal and will only affect you if you used overrides. Reason for change was to resolve Docker warning, `InvalidDefaultArgInFrom`, and to improve semantics. A tag in Dockerfile reference is what comes after the :. Alias is repo:tag. Now repository (cdk-nextjs/builder) and tag (CDK's `node.addr.slice(30)`) so we can properly default to `cdk-nextjs/builder:latest` in Dockerfiles.
10
+ - Remove `overrides.{nextjs...}.nextjsBuildProps.customDockerfilePath` in favor of `buildContext` + `overrides.{nextjs...}.nextjsBuildProps.file`. Simplies from 2 ways to 1. Default builder image Dockerfile name is now "builder.Dockerfile" too.
11
+
3
12
  ## 0.3.0
4
13
 
5
14
  - Change `NextjsDistributionProps.loadBalancer` type from `ILoadBalancerV2` to `ApplicationLoadBalancer`