cdk-nextjs 0.6.1 → 0.7.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.
Files changed (266) hide show
  1. package/.jsii +2467 -2714
  2. package/.jsii.tabl.json +1 -1
  3. package/API.md +1725 -1909
  4. package/CLAUDE.md +2 -1
  5. package/README.md +429 -130
  6. package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +152 -170
  7. package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
  8. package/docs/adapter-runtime.md +185 -0
  9. package/docs/breaking-changes.md +687 -4
  10. package/docs/caching-guide.md +156 -112
  11. package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
  12. package/docs/development-guide.md +1 -1
  13. package/docs/harness-coverage.md +598 -0
  14. package/docs/init-cache-deployment.md +32 -1
  15. package/docs/next-build-output-guide.md +30 -20
  16. package/examples/README.md +5 -1
  17. package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
  18. package/lib/adapter/__fixtures__/app-playground.json +4917 -0
  19. package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
  20. package/lib/adapter/adapter.mjs +1548 -185
  21. package/lib/adapter/adapter.test.js +106 -0
  22. package/lib/adapter/aws-cache-store.d.ts +489 -0
  23. package/lib/adapter/aws-cache-store.js +1004 -0
  24. package/lib/adapter/aws-cache-store.test.d.ts +1 -0
  25. package/lib/adapter/aws-cache-store.test.js +361 -0
  26. package/lib/adapter/build-outputs.d.ts +80 -0
  27. package/lib/adapter/build-outputs.js +852 -0
  28. package/lib/adapter/build-outputs.test.d.ts +1 -0
  29. package/lib/adapter/build-outputs.test.js +861 -0
  30. package/lib/adapter/cache-handler.d.ts +9 -2
  31. package/lib/adapter/cache-handler.js +72 -96
  32. package/lib/adapter/cache-handler.mjs +18956 -17774
  33. package/lib/adapter/cache-handler.test.js +263 -31
  34. package/lib/adapter/cache-kinds.d.ts +32 -0
  35. package/lib/adapter/cache-kinds.js +35 -0
  36. package/lib/adapter/cache-kinds.test.d.ts +1 -0
  37. package/lib/adapter/cache-kinds.test.js +69 -0
  38. package/lib/adapter/cache-utils.d.ts +90 -0
  39. package/lib/adapter/cache-utils.js +199 -4
  40. package/lib/adapter/cache-utils.test.d.ts +1 -0
  41. package/lib/adapter/cache-utils.test.js +179 -0
  42. package/lib/adapter/function-groups.d.ts +215 -0
  43. package/lib/adapter/function-groups.js +1099 -0
  44. package/lib/adapter/function-groups.test.d.ts +1 -0
  45. package/lib/adapter/function-groups.test.js +743 -0
  46. package/lib/adapter/init-cache.d.ts +6 -0
  47. package/lib/adapter/init-cache.js +245 -0
  48. package/lib/adapter/init-cache.test.d.ts +1 -0
  49. package/lib/adapter/init-cache.test.js +137 -0
  50. package/lib/adapter/local-file-cache-handler.d.ts +23 -0
  51. package/lib/adapter/local-file-cache-handler.js +52 -1
  52. package/lib/adapter/memory-cache-handler.d.ts +26 -17
  53. package/lib/adapter/memory-cache-handler.js +32 -50
  54. package/lib/adapter/memory-cache-handler.test.js +105 -85
  55. package/lib/adapter/s3-cache-handler.d.ts +179 -40
  56. package/lib/adapter/s3-cache-handler.js +807 -277
  57. package/lib/adapter/s3-cache-handler.test.js +1186 -31
  58. package/lib/adapter/use-cache-common.d.ts +90 -0
  59. package/lib/adapter/use-cache-common.js +250 -0
  60. package/lib/adapter/use-cache-common.test.d.ts +1 -0
  61. package/lib/adapter/use-cache-common.test.js +646 -0
  62. package/lib/adapter/use-cache-default-handler.d.ts +24 -0
  63. package/lib/adapter/use-cache-default-handler.js +90 -0
  64. package/lib/adapter/use-cache-default-handler.mjs +38645 -0
  65. package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
  66. package/lib/adapter/use-cache-handlers.test.js +349 -0
  67. package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
  68. package/lib/adapter/use-cache-remote-handler.js +186 -0
  69. package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
  70. package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
  71. package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
  72. package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
  73. package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
  74. package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
  75. package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
  76. package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
  77. package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
  78. package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +13 -0
  79. package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
  80. package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +11 -2
  81. package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
  82. package/lib/index.d.ts +6 -7
  83. package/lib/index.js +2 -4
  84. package/lib/lambdas/post-deploy/post-deploy.lambda.js +3 -2
  85. package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
  86. package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
  87. package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
  88. package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
  89. package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
  90. package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
  91. package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
  92. package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
  93. package/lib/lambdas/post-deploy/prune-s3.d.ts +19 -3
  94. package/lib/lambdas/post-deploy/prune-s3.js +54 -15
  95. package/lib/lambdas/post-deploy/prune-s3.test.d.ts +1 -0
  96. package/lib/lambdas/post-deploy/prune-s3.test.js +255 -0
  97. package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
  98. package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
  99. package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
  100. package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
  101. package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
  102. package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
  103. package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
  104. package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
  105. package/lib/nextjs-api.d.ts +107 -18
  106. package/lib/nextjs-api.js +349 -39
  107. package/lib/nextjs-api.test.js +582 -1
  108. package/lib/nextjs-build/global-containers.Dockerfile +15 -10
  109. package/lib/nextjs-build/nextjs-build.d.ts +339 -63
  110. package/lib/nextjs-build/nextjs-build.js +684 -327
  111. package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
  112. package/lib/nextjs-build/nextjs-build.test.js +412 -0
  113. package/lib/nextjs-build/patch-fetch.js +1 -1
  114. package/lib/nextjs-build/patch-fetch.test.js +166 -11
  115. package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
  116. package/lib/nextjs-cache.d.ts +82 -4
  117. package/lib/nextjs-cache.js +186 -10
  118. package/lib/nextjs-cache.test.js +176 -1
  119. package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
  120. package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
  121. package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
  122. package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
  123. package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
  124. package/lib/nextjs-compute/nextjs-containers.js +57 -20
  125. package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
  126. package/lib/nextjs-compute/nextjs-functions.js +113 -67
  127. package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
  128. package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
  129. package/lib/nextjs-distribution.d.ts +194 -23
  130. package/lib/nextjs-distribution.js +535 -62
  131. package/lib/nextjs-distribution.test.d.ts +1 -0
  132. package/lib/nextjs-distribution.test.js +697 -0
  133. package/lib/nextjs-post-deploy.d.ts +23 -3
  134. package/lib/nextjs-post-deploy.js +14 -10
  135. package/lib/nextjs-post-deploy.test.d.ts +1 -0
  136. package/lib/nextjs-post-deploy.test.js +34 -0
  137. package/lib/nextjs-static-assets.d.ts +23 -4
  138. package/lib/nextjs-static-assets.js +29 -6
  139. package/lib/nextjs-static-assets.test.js +20 -1
  140. package/lib/root-constructs/nextjs-base-construct.d.ts +105 -45
  141. package/lib/root-constructs/nextjs-base-construct.js +144 -34
  142. package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
  143. package/lib/root-constructs/nextjs-global-containers.js +20 -70
  144. package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
  145. package/lib/root-constructs/nextjs-global-functions.js +52 -69
  146. package/lib/root-constructs/nextjs-regional-containers.d.ts +25 -5
  147. package/lib/root-constructs/nextjs-regional-containers.js +17 -15
  148. package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
  149. package/lib/root-constructs/nextjs-regional-functions.js +8 -25
  150. package/lib/runtime/api-gateway-path.d.ts +34 -0
  151. package/lib/runtime/api-gateway-path.js +71 -0
  152. package/lib/runtime/api-gateway-path.test.d.ts +1 -0
  153. package/lib/runtime/api-gateway-path.test.js +65 -0
  154. package/lib/runtime/case-redirect.d.ts +22 -0
  155. package/lib/runtime/case-redirect.js +63 -0
  156. package/lib/runtime/case-redirect.test.d.ts +1 -0
  157. package/lib/runtime/case-redirect.test.js +32 -0
  158. package/lib/runtime/core.d.ts +234 -0
  159. package/lib/runtime/core.js +1213 -0
  160. package/lib/runtime/core.test.d.ts +1 -0
  161. package/lib/runtime/core.test.js +1678 -0
  162. package/lib/runtime/deployment-root.d.ts +13 -0
  163. package/lib/runtime/deployment-root.js +29 -0
  164. package/lib/runtime/dispatch.d.ts +331 -0
  165. package/lib/runtime/dispatch.js +751 -0
  166. package/lib/runtime/dispatch.test.d.ts +1 -0
  167. package/lib/runtime/dispatch.test.js +853 -0
  168. package/lib/runtime/entrypoints.d.ts +57 -0
  169. package/lib/runtime/entrypoints.js +115 -0
  170. package/lib/runtime/http/abort-signal.d.ts +6 -0
  171. package/lib/runtime/http/abort-signal.js +19 -0
  172. package/lib/runtime/http/abort-signal.test.d.ts +1 -0
  173. package/lib/runtime/http/abort-signal.test.js +49 -0
  174. package/lib/runtime/http/node-server.d.ts +38 -0
  175. package/lib/runtime/http/node-server.js +99 -0
  176. package/lib/runtime/http/node-server.test.d.ts +1 -0
  177. package/lib/runtime/http/node-server.test.js +96 -0
  178. package/lib/runtime/http/node-sink.d.ts +16 -0
  179. package/lib/runtime/http/node-sink.js +48 -0
  180. package/lib/runtime/http/node-sink.test.d.ts +1 -0
  181. package/lib/runtime/http/node-sink.test.js +66 -0
  182. package/lib/runtime/http/request.d.ts +62 -0
  183. package/lib/runtime/http/request.js +134 -0
  184. package/lib/runtime/http/request.test.d.ts +1 -0
  185. package/lib/runtime/http/request.test.js +103 -0
  186. package/lib/runtime/http/response.d.ts +77 -0
  187. package/lib/runtime/http/response.js +229 -0
  188. package/lib/runtime/http/response.test.d.ts +1 -0
  189. package/lib/runtime/http/response.test.js +178 -0
  190. package/lib/runtime/http/sink.d.ts +73 -0
  191. package/lib/runtime/http/sink.js +188 -0
  192. package/lib/runtime/http/sink.test.d.ts +1 -0
  193. package/lib/runtime/http/sink.test.js +257 -0
  194. package/lib/runtime/image-utils.d.ts +78 -0
  195. package/lib/runtime/image-utils.js +129 -0
  196. package/lib/runtime/image-utils.test.d.ts +1 -0
  197. package/lib/runtime/image-utils.test.js +237 -0
  198. package/lib/runtime/image.d.ts +76 -0
  199. package/lib/runtime/image.js +296 -0
  200. package/lib/runtime/image.test.d.ts +1 -0
  201. package/lib/runtime/image.test.js +405 -0
  202. package/lib/runtime/lambda-event.d.ts +4 -0
  203. package/lib/runtime/lambda-event.js +113 -0
  204. package/lib/runtime/lambda-event.test.d.ts +1 -0
  205. package/lib/runtime/lambda-event.test.js +119 -0
  206. package/lib/runtime/lambda.mjs +41595 -0
  207. package/lib/runtime/load-module.d.ts +8 -0
  208. package/lib/runtime/load-module.js +35 -0
  209. package/lib/runtime/manifest.d.ts +215 -0
  210. package/lib/runtime/manifest.js +118 -0
  211. package/lib/runtime/middleware.d.ts +43 -0
  212. package/lib/runtime/middleware.js +113 -0
  213. package/lib/runtime/middleware.test.d.ts +1 -0
  214. package/lib/runtime/middleware.test.js +259 -0
  215. package/lib/runtime/next-modules.d.ts +55 -0
  216. package/lib/runtime/next-modules.js +124 -0
  217. package/lib/runtime/next-modules.test.d.ts +1 -0
  218. package/lib/runtime/next-modules.test.js +34 -0
  219. package/lib/runtime/public-files.d.ts +32 -0
  220. package/lib/runtime/public-files.js +117 -0
  221. package/lib/runtime/public-files.test.d.ts +1 -0
  222. package/lib/runtime/public-files.test.js +97 -0
  223. package/lib/runtime/server.mjs +41588 -0
  224. package/lib/runtime/static-files.d.ts +50 -0
  225. package/lib/runtime/static-files.js +266 -0
  226. package/lib/runtime/static-files.test.d.ts +1 -0
  227. package/lib/runtime/static-files.test.js +118 -0
  228. package/lib/runtime/util.d.ts +28 -0
  229. package/lib/runtime/util.js +79 -0
  230. package/lib/utils/base-path.d.ts +123 -11
  231. package/lib/utils/base-path.js +276 -39
  232. package/lib/utils/base-path.test.js +275 -50
  233. package/lib/utils/get-architecture.d.ts +7 -8
  234. package/lib/utils/get-architecture.js +12 -13
  235. package/lib/utils/zip-directory.d.ts +14 -0
  236. package/lib/utils/zip-directory.js +121 -0
  237. package/lib/utils/zip-directory.test.d.ts +1 -0
  238. package/lib/utils/zip-directory.test.js +99 -0
  239. package/package.json +22 -3
  240. package/scripts/capture-adapter-fixture.mjs +325 -0
  241. package/scripts/e2e-deploy.sh +398 -0
  242. package/scripts/e2e-harness/README.md +707 -0
  243. package/scripts/e2e-harness/app.js +370 -0
  244. package/scripts/e2e-harness/common.sh +249 -0
  245. package/scripts/e2e-harness/screen.mjs +302 -0
  246. package/scripts/e2e-harness/stage-proxy.mjs +145 -0
  247. package/scripts/e2e-harness/type-filters.mjs +47 -0
  248. package/scripts/e2e-logs.sh +75 -0
  249. package/scripts/e2e-offline.sh +91 -0
  250. package/scripts/e2e-sweep.sh +370 -0
  251. package/scripts/e2e-warm.sh +114 -0
  252. package/scripts/zero-config-build.mjs +418 -0
  253. package/docs/1k-concurrent-users-executions.png +0 -0
  254. package/docs/plans/adapter-runtime-release.md +0 -362
  255. package/docs/plans/phase-0-image-function-opt-in.md +0 -295
  256. package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
  257. package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
  258. package/lib/image-optimization/handler-utils.d.ts +0 -34
  259. package/lib/image-optimization/handler-utils.js +0 -83
  260. package/lib/image-optimization/handler-utils.test.js +0 -178
  261. package/lib/nextjs-build/functions.Dockerfile +0 -31
  262. package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
  263. package/lib/nextjs-compute/nextjs-image-function.js +0 -59
  264. package/lib/utils/experimental-flags.d.ts +0 -24
  265. package/lib/utils/experimental-flags.js +0 -30
  266. /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
@@ -0,0 +1,185 @@
1
+ # The adapter runtime
2
+
3
+ How cdk-nextjs serves a Next.js app on all four deployment types: what runs at
4
+ build time, what runs per request, where it knowingly differs from `next start`,
5
+ and what each deployment type cannot do. The reasoning behind individual
6
+ decisions lives in the code comments next to them; this is the map.
7
+
8
+ Written against Next.js 16.3.5. `@next/routing` is exact-pinned to the `next`
9
+ version and bumped with it.
10
+
11
+ ## Build time: `onBuildComplete`
12
+
13
+ `src/adapter/adapter.mts` is the Next.js [deployment adapter][adapters]. Its
14
+ `onBuildComplete` hook calls `buildAdapterManifest` in
15
+ `src/adapter/build-outputs.ts`, which produces two things inside
16
+ `<distDir>/cdk-nextjs-adapter/`:
17
+
18
+ - **The staging tree** (`app/`, or `groups/<name>/` with `functionGroups`): the
19
+ union of every shipped output's traced `assets`, keyed by repo-root-relative
20
+ path. It replaces `output: "standalone"`, which cdk-nextjs no longer sets. This
21
+ directory is the _deployment root_: the Lambda zip and the container image's
22
+ `WORKDIR`. pnpm's symlinks are staged as symlinks, so `NextjsFunctions` zips
23
+ the root itself (`src/utils/zip-directory.ts`) rather than leaving it to
24
+ cdk-assets, which would dereference them.
25
+ - **`manifest.json`** (`src/runtime/manifest.ts`): the only contract between
26
+ build, synth and runtime. It holds `ctx.routing` verbatim, the entrypoint for
27
+ every route template, the build's static files, middleware, and the config
28
+ fields the runtime needs. Every path in it is a repo-root-relative POSIX key,
29
+ never an absolute path.
30
+
31
+ The same hook seeds the init cache for the S3 + DynamoDB cache handler, and it
32
+ refuses any output whose runtime is not `nodejs` (`assertNodeRuntimes`).
33
+
34
+ What is deliberately _not_ in the staging tree: `<distDir>/static` and
35
+ `public/`. On the Lambda types, CloudFront or API Gateway answers them from S3,
36
+ and `public/` alone can exceed the 250 MB unzipped Lambda limit.
37
+ `NextjsGlobalContainers` leaves them out for the same reason CloudFront makes
38
+ them unnecessary; only `NextjsRegionalContainers`, with no CDN in front of it,
39
+ copies both into its image (`src/nextjs-build/regional-containers.Dockerfile`).
40
+
41
+ ## Request time
42
+
43
+ ```
44
+ shell ──► NextjsRuntime.handle ──► Dispatcher.dispatch ──► one of:
45
+ lambda.mts (src/runtime/core.ts) (dispatch.ts) entrypoint
46
+ server.mts @next/routing static file
47
+ resolveRoutes /_next/image
48
+ + middleware runner redirect / external rewrite
49
+ middleware's own response
50
+ 404 / 500 ladder
51
+ ```
52
+
53
+ - **Shells.** `lambda.mts` (Lambda response streaming; Function URL events on
54
+ `NextjsGlobalFunctions`, API Gateway REST streaming events on
55
+ `NextjsRegionalFunctions`) and `server.mts` (`node:http`, both Containers
56
+ types). Each only translates its input into a `RuntimeRequest` and its output
57
+ into a `ResponseSink`. Both run the same core, so the container e2e suite
58
+ exercises the code Lambda runs.
59
+ - **Core** (`core.ts`). It synthesizes `IncomingMessage`/`ServerResponse` shims
60
+ (`http/`), dispatches, and acts on the result. It gzips streamed responses
61
+ itself, because neither API Gateway in STREAM mode nor CloudFront compresses a
62
+ response without `Content-Length`. It awaits `waitUntil` work before
63
+ returning, since Lambda freezes on return.
64
+ - **Dispatch** (`dispatch.ts`). Route matching is `@next/routing`'s
65
+ `resolveRoutes`, the library `next start`'s router was refactored to expose.
66
+ Dispatch adds what it gets wrong or leaves out, each with a comment:
67
+ `trailingSlash` variants, the i18n root, `nxtP` param repair, redirect-location
68
+ normalization. The routing table (pathnames plus every spelling of every
69
+ static file) is built once per cold start and cached.
70
+ - **Middleware** (`middleware.ts`) runs through `resolveRoutes`'s
71
+ `invokeMiddleware` callback. Request bodies are teed so middleware and the
72
+ entrypoint both read them.
73
+ - **Entrypoints** (`entrypoints.ts`) are required lazily, on first use, which is
74
+ most of the cold-start win over `server.js`.
75
+ - **Static files** (`static-files.ts`) go through Next.js's own `serveStatic`
76
+ (`send`), called as `next start` calls it. `public/` is listed off disk at
77
+ cold start (`public-files.ts`) rather than from the manifest, so files a
78
+ `postbuild` writes are included. On the Lambda types the directory is not in
79
+ the package: the list comes from `public-files.json`, written at synth, and
80
+ the files are streamed from the static-assets bucket.
81
+ - **Image optimization** (`image.ts`) runs inside the runtime, after middleware.
82
+ It reads local source images from the static-assets bucket, except on
83
+ `NextjsRegionalContainers`, which has them on disk. There
84
+ is no separate image Lambda any more, because middleware never ran for it.
85
+ - **Cache** (`src/adapter/cache-handler.ts`): memory in front of S3 + DynamoDB
86
+ at runtime, local files at build time. It also creates the CloudFront
87
+ invalidations for on-demand revalidation on the Global types.
88
+ - **`'use cache'` handlers** (`src/adapter/use-cache-{default,remote}-handler.ts`,
89
+ registered as `cacheHandlers`): `default` keeps entries in memory, `remote`
90
+ in S3 with memory in front, and both read tag revalidations from the same
91
+ DynamoDB marker rows as the cache handler (`src/adapter/aws-cache-store.ts`
92
+ is the plumbing all three share). Past a tag's first read, all three learn
93
+ about revalidations from the revalidation log (`RevalidationLog`, `pk =
94
+ <buildId>#log`) through `TrackedTagMarkers`: one `Query` per refresh interval
95
+ instead of re-reading every tracked marker. The cache handler and the
96
+ `'use cache'` handlers share one manifest, so an instance sends one.
97
+
98
+ ### Invariants worth not breaking
99
+
100
+ - **`process.cwd()` is the staged project dir.** Next.js inlines
101
+ `relative(buildCwd, projectDir)` into every entrypoint and resolves it against
102
+ the runtime cwd. `loadRuntime` chdirs and asserts this. `build-outputs.ts`
103
+ asserts the build ran from the project dir, so the inlined value is `""`.
104
+ - **`minimalMode` is never set.** The non-minimal path keeps the incremental
105
+ cache inside the entrypoint, which the cache handler depends on.
106
+ - **PPR is origin-only**, as in `next start`. The CDN-shell variant needs
107
+ CloudFront to splice an edge response with an origin stream, which it cannot
108
+ do.
109
+ - **One Dispatcher serves every request**, concurrently on the container shell,
110
+ so per-request state (the middleware invoker, with its `waitUntil` and
111
+ `signal`) travels in the `DispatchRequest`, never on the Dispatcher.
112
+
113
+ ### Why `functionGroups` exists
114
+
115
+ It's the largest feature here, and it's deliberate. One heavy dependency (a
116
+ PDF renderer, a headless browser) can push a zip Lambda past 250 MB, and it
117
+ makes every route pay its cold start. Splitting that route into its own
118
+ function fixes both and gives it its own memory and timeout, as Vercel and
119
+ OpenNext do. A container-image Lambda fixes only the size, and makes every
120
+ cold start slower, so it isn't a replacement.
121
+
122
+ Keep the cost in one place: the adapter decides the group edge (CloudFront
123
+ behaviors, API Gateway resources) once, checks it reaches every file, and
124
+ writes it to the manifest. The constructs read that list rather than
125
+ recomputing it.
126
+
127
+ ## Where it differs from `next start`, knowingly
128
+
129
+ | Behavior | `next start` | cdk-nextjs | Why |
130
+ | ------------------------------------------------------------ | -------------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
131
+ | Edge runtime, legacy edge `middleware.ts` | supported | build fails | Deprecated upstream. Node-runtime `proxy.ts` is supported. |
132
+ | `dynamicParams = false` | concrete prerenders served, others 404 | every param 404s | Next.js gates the route behind preview cookies and expects the platform to serve prerenders off the CDN. Supporting it is a design change. |
133
+ | Rewrite destination with a repeated query key (`?a=1&a=2`) | array | last value only | `@next/routing` bug ([vercel/next.js#99155](https://github.com/vercel/next.js/issues/99155)). |
134
+ | Static file requested with POST/PUT/DELETE | 405 rendered through `/_error` | 405, plain text | Same status and `Allow`; not worth an error-page render. |
135
+ | Response compression | gzip or brotli | gzip only, in the runtime | Brotli's streaming throughput is poor at default quality. |
136
+ | `public/` under a locale prefix | default locales only | default locales only | Same. Listed because it surprises people: `/fr/x.txt` 404s unless `fr` is a domain's default. |
137
+ | `X-Forwarded-Host` | ignored | ignored, except on `NextjsGlobalFunctions` | Same as `next start`. The Global Functions URL is reachable only through CloudFront, which overwrites the header with the viewer's host, and must see its own domain as `Host`. Trusted only on a request Lambda verified the SigV4 of. |
138
+ | `If-Range` on a `public/` file reached by a rewrite | range honored if the validator matches | whole file (200), except on `NextjsRegionalContainers` | Elsewhere the file is streamed from S3, which has no `If-Range`; sending the whole file is what HTTP allows. `Range` itself is honored. |
139
+ | Grouped route requested in the wrong case (`/API/reports/1`) | served | 308 to the route's spelling (`/api/reports/1`) | Edge path matching is case-sensitive, so the request reaches a function that doesn't carry the route. Only with `functionGroups`. |
140
+ | Missing `/_next/static/*` on the Global types | 404, body `Not Found` | 404, S3's XML body | S3 answers the Origin Access Control request itself; the body would need a Lambda@Edge rewrite. |
141
+ | `resume-data-cache` cases | pass | 2 cases fail | A multi-instance cache can't give the per-process guarantee the fixture asserts. |
142
+ | `config.maxDuration`, `preferredRegion` | honored on Vercel | dropped, with a warning | A Lambda timeout is per function, not per route; region is a stack-level choice. |
143
+
144
+ The compatibility harness keeps the full record, with a verdict per excluded
145
+ case: [`harness-coverage.md`](./harness-coverage.md).
146
+
147
+ ## Per-type limitations
148
+
149
+ | | Global Functions | Global Containers | Regional Functions | Regional Containers |
150
+ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------ | ---------------------- |
151
+ | Front door | CloudFront → Function URL (IAM, SigV4) | CloudFront → ALB (VPC origin) | API Gateway REST → Lambda | ALB |
152
+ | Static assets | S3 via CloudFront | S3 via CloudFront | S3 via API Gateway | from disk in the image |
153
+ | 75-behavior budget: top-level `public/` entries plus `functionGroups` patterns | yes | yes | no | no |
154
+ | Client `Authorization` header | used by SigV4; send credentials under another name | passes | passes | passes |
155
+ | POST/PUT body from a non-browser client | needs `x-amz-content-sha256` | fine | fine | fine |
156
+ | HEAD `content-length` | always `0` (Function URL) | declared | declared | declared |
157
+ | `public/` names | no CloudFront-spellable character → no behavior, warning; the server function streams it from S3 | same | outside `[a-zA-Z0-9:._-$]` at top level → no resource, warning; the server function streams it from S3 | any name |
158
+ | `functionGroups` | yes | no | yes | no |
159
+ | Stage prefix | — | — | put back for a `basePath` that starts with it | — |
160
+
161
+ Each of these is in the README's Limitations too, or in `breaking-changes.md`
162
+ for anything that changed from an earlier release.
163
+
164
+ ## Debugging
165
+
166
+ - **Compare with `next start` on the same build.** `scripts/e2e-offline.sh
167
+ <next.js fixture>` builds one fixture through the adapter and serves it twice,
168
+ through our container shell and through `next start`. Most "why does the
169
+ deployment disagree" questions come down to diffing the two responses. For an
170
+ app in `examples/`, run `lib/runtime/server.mjs` from its
171
+ `.next/cdk-nextjs-adapter/app`, with `.next/static` and `public` copied in as
172
+ the regional containers Dockerfile does.
173
+ - **Tell the CDN from the origin.** Behind CloudFront, `x-cache` says whether the
174
+ edge answered, and every other header is the one the _cached copy_ was stored
175
+ with. An `x-nextjs-cache: REVALIDATED` on a `Hit from cloudfront` is an old
176
+ origin response, not a fresh render. The dynamic cache policy caches only what
177
+ sends `Cache-Control` (`s-maxage`); everything else goes to the origin.
178
+ - **Dispatch without AWS.** `src/runtime/dispatch.test.ts` runs the real
179
+ Dispatcher against manifests built from captured `onBuildComplete` contexts.
180
+ Add a case there before you deploy. Refresh the captures with
181
+ `scripts/capture-adapter-fixture.mjs` after a `next` upgrade.
182
+ - **Logs.** Unhandled errors are logged by the core before the error page is
183
+ rendered. The cache handler logs under `DEBUG=cdk-nextjs:*`.
184
+
185
+ [adapters]: https://nextjs.org/docs/app/api-reference/adapters