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,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /* eslint-disable import/no-extraneous-dependencies */
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_os_1 = require("node:os");
7
+ const node_path_1 = require("node:path");
8
+ const esbuild_1 = require("esbuild");
9
+ // `adapter.mts` is ESM that calls `import.meta.resolve`, which ts-jest's
10
+ // CommonJS cannot run, so it is bundled the way `pnpm bundle` does and driven
11
+ // from a real `node`. Its `cdk-nextjs/…` specifiers resolve against a stand-in
12
+ // package carrying the real `exports`, so the test needs no prior bundle.
13
+ const repoRoot = (0, node_path_1.join)(__dirname, "..", "..");
14
+ let dir;
15
+ let pkgDir;
16
+ beforeAll(() => {
17
+ // Real, since `import.meta.resolve` returns real paths (macOS's tmpdir is a link).
18
+ dir = (0, node_fs_1.realpathSync)((0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "cdk-nextjs-adapter-")));
19
+ pkgDir = (0, node_path_1.join)(dir, "node_modules", "cdk-nextjs");
20
+ const { exports } = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(repoRoot, "package.json"), "utf-8"));
21
+ (0, node_fs_1.mkdirSync)(pkgDir, { recursive: true });
22
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(pkgDir, "package.json"), JSON.stringify({ name: "cdk-nextjs", exports }));
23
+ for (const target of Object.values(exports)) {
24
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)((0, node_path_1.join)(pkgDir, target.import)), { recursive: true });
25
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(pkgDir, target.import), "");
26
+ }
27
+ (0, node_fs_1.symlinkSync)((0, node_path_1.join)(repoRoot, "node_modules", "next"), (0, node_path_1.join)(dir, "node_modules", "next"));
28
+ (0, esbuild_1.buildSync)({
29
+ entryPoints: [(0, node_path_1.join)(repoRoot, "src/adapter/adapter.mts")],
30
+ bundle: true,
31
+ platform: "node",
32
+ target: "node24",
33
+ format: "esm",
34
+ external: ["next"],
35
+ outfile: (0, node_path_1.join)(dir, "adapter.mjs"),
36
+ banner: {
37
+ js: "import { createRequire as __cdkNextjsCreateRequire } from 'node:module'; const require = __cdkNextjsCreateRequire(import.meta.url);",
38
+ },
39
+ logLevel: "silent",
40
+ });
41
+ });
42
+ afterAll(() => {
43
+ (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
44
+ });
45
+ /** `adapter.modifyConfig(config, { phase, nextVersion })`, run by `node`. */
46
+ function modifyConfig(config, phase, nextVersion = "16.3.0") {
47
+ const script = `import adapter from ${JSON.stringify((0, node_path_1.join)(dir, "adapter.mjs"))};` +
48
+ `const out = await adapter.modifyConfig(${JSON.stringify(config)}, ` +
49
+ `${JSON.stringify({ phase, nextVersion })});` +
50
+ `process.stdout.write(JSON.stringify(out));`;
51
+ return JSON.parse((0, node_child_process_1.execFileSync)(process.execPath, ["--input-type=module", "-e", script], {
52
+ cwd: dir,
53
+ encoding: "utf-8",
54
+ stdio: ["ignore", "pipe", "pipe"],
55
+ }));
56
+ }
57
+ const handler = (path) => (0, node_path_1.join)(pkgDir, "lib/adapter", path);
58
+ const ours = () => ({
59
+ default: handler("use-cache-default-handler.mjs"),
60
+ remote: handler("use-cache-remote-handler.mjs"),
61
+ });
62
+ describe("modifyConfig", () => {
63
+ it("wires cdk-nextjs's cache handlers into the build", () => {
64
+ const out = modifyConfig({ images: {} }, "phase-production-build");
65
+ expect(out.cacheHandler).toBe(handler("cache-handler.mjs"));
66
+ expect(out.cacheHandlers).toEqual(ours());
67
+ expect(out.images.customCacheHandler).toBe(true);
68
+ });
69
+ it("keeps the handlers the app configured, and adds the missing ones", () => {
70
+ const out = modifyConfig({
71
+ cacheHandler: "/app/my-handler.js",
72
+ cacheHandlers: { remote: "/app/remote.js", custom: "/app/custom.js" },
73
+ images: { customCacheHandler: "/app/images.js" },
74
+ }, "phase-production-build");
75
+ expect(out.cacheHandler).toBe("/app/my-handler.js");
76
+ expect(out.cacheHandlers).toEqual({
77
+ default: ours().default,
78
+ remote: "/app/remote.js",
79
+ custom: "/app/custom.js",
80
+ });
81
+ expect(out.images.customCacheHandler).toBe("/app/images.js");
82
+ });
83
+ // `next start` registers `use cache` handlers from this config, once.
84
+ it("registers the same `use cache` handlers for `next start`", () => {
85
+ const out = modifyConfig({ cacheHandlers: { default: "/app/default.js" } }, "phase-production-server");
86
+ expect(out.cacheHandlers).toEqual({
87
+ default: "/app/default.js",
88
+ remote: ours().remote,
89
+ });
90
+ expect(out.cacheHandler).toBeUndefined();
91
+ });
92
+ it("leaves other phases alone", () => {
93
+ expect(modifyConfig({ images: {} }, "phase-development-server")).toEqual({
94
+ images: {},
95
+ });
96
+ });
97
+ });
98
+ describe("assertSupportedNextVersion", () => {
99
+ it.each(["16.2.9", "15.5.0"])("rejects Next.js %s at build time", (version) => {
100
+ expect(() => modifyConfig({ images: {} }, "phase-production-build", version)).toThrow(`Next.js ${version} is not supported`);
101
+ });
102
+ it.each(["16.3.0", "17.0.0-canary.1"])("accepts Next.js %s", (version) => {
103
+ expect(() => modifyConfig({ images: {} }, "phase-production-build", version)).not.toThrow();
104
+ });
105
+ });
106
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRhcHRlci50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FkYXB0ZXIvYWRhcHRlci50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsc0RBQXNEO0FBQ3RELDJEQUFrRDtBQUNsRCxxQ0FRaUI7QUFDakIscUNBQWlDO0FBQ2pDLHlDQUEwQztBQUMxQyxxQ0FBb0M7QUFFcEMseUVBQXlFO0FBQ3pFLDhFQUE4RTtBQUM5RSwrRUFBK0U7QUFDL0UsMEVBQTBFO0FBQzFFLE1BQU0sUUFBUSxHQUFHLElBQUEsZ0JBQUksRUFBQyxTQUFTLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQzdDLElBQUksR0FBVyxDQUFDO0FBQ2hCLElBQUksTUFBYyxDQUFDO0FBRW5CLFNBQVMsQ0FBQyxHQUFHLEVBQUU7SUFDYixtRkFBbUY7SUFDbkYsR0FBRyxHQUFHLElBQUEsc0JBQVksRUFBQyxJQUFBLHFCQUFXLEVBQUMsSUFBQSxnQkFBSSxFQUFDLElBQUEsZ0JBQU0sR0FBRSxFQUFFLHFCQUFxQixDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZFLE1BQU0sR0FBRyxJQUFBLGdCQUFJLEVBQUMsR0FBRyxFQUFFLGNBQWMsRUFBRSxZQUFZLENBQUMsQ0FBQztJQUNqRCxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FDNUIsSUFBQSxzQkFBWSxFQUFDLElBQUEsZ0JBQUksRUFBQyxRQUFRLEVBQUUsY0FBYyxDQUFDLEVBQUUsT0FBTyxDQUFDLENBQ3RELENBQUM7SUFDRixJQUFBLG1CQUFTLEVBQUMsTUFBTSxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdkMsSUFBQSx1QkFBYSxFQUNYLElBQUEsZ0JBQUksRUFBQyxNQUFNLEVBQUUsY0FBYyxDQUFDLEVBQzVCLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFB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QUNWLFlBQVksQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsRUFBRSx3QkFBd0IsRUFBRSxPQUFPLENBQUMsQ0FDaEUsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDbEIsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgZXhlY0ZpbGVTeW5jIH0gZnJvbSBcIm5vZGU6Y2hpbGRfcHJvY2Vzc1wiO1xuaW1wb3J0IHtcbiAgbWtkaXJTeW5jLFxuICBta2R0ZW1wU3luYyxcbiAgcmVhZEZpbGVTeW5jLFxuICByZWFscGF0aFN5bmMsXG4gIHJtU3luYyxcbiAgc3ltbGlua1N5bmMsXG4gIHdyaXRlRmlsZVN5bmMsXG59IGZyb20gXCJub2RlOmZzXCI7XG5pbXBvcnQgeyB0bXBkaXIgfSBmcm9tIFwibm9kZTpvc1wiO1xuaW1wb3J0IHsgZGlybmFtZSwgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IGJ1aWxkU3luYyB9IGZyb20gXCJlc2J1aWxkXCI7XG5cbi8vIGBhZGFwdGVyLm10c2AgaXMgRVNNIHRoYXQgY2FsbHMgYGltcG9ydC5tZXRhLnJlc29sdmVgLCB3aGljaCB0cy1qZXN0J3Ncbi8vIENvbW1vbkpTIGNhbm5vdCBydW4sIHNvIGl0IGlzIGJ1bmRsZWQgdGhlIHdheSBgcG5wbSBidW5kbGVgIGRvZXMgYW5kIGRyaXZlblxuLy8gZnJvbSBhIHJlYWwgYG5vZGVgLiBJdHMgYGNkay1uZXh0anMv4oCmYCBzcGVjaWZpZXJzIHJlc29sdmUgYWdhaW5zdCBhIHN0YW5kLWluXG4vLyBwYWNrYWdlIGNhcnJ5aW5nIHRoZSByZWFsIGBleHBvcnRzYCwgc28gdGhlIHRlc3QgbmVlZHMgbm8gcHJpb3IgYnVuZGxlLlxuY29uc3QgcmVwb1Jvb3QgPSBqb2luKF9fZGlybmFtZSwgXCIuLlwiLCBcIi4uXCIpO1xubGV0IGRpcjogc3RyaW5nO1xubGV0IHBrZ0Rpcjogc3RyaW5nO1xuXG5iZWZvcmVBbGwoKCkgPT4ge1xuICAvLyBSZWFsLCBzaW5jZSBgaW1wb3J0Lm1ldGEucmVzb2x2ZWAgcmV0dXJucyByZWFsIHBhdGhzIChtYWNPUydzIHRtcGRpciBpcyBhIGxpbmspLlxuICBkaXIgPSByZWFscGF0aFN5bmMobWtkdGVtcFN5bmMoam9pbih0bXBkaXIoKSwgXCJjZGstbmV4dGpzLWFkYXB0ZXItXCIpKSk7XG4gIHBrZ0RpciA9IGpvaW4oZGlyLCBcIm5vZGVfbW9kdWxlc1wiLCBcImNkay1uZXh0anNcIik7XG4gIGNvbnN0IHsgZXhwb3J0cyB9ID0gSlNPTi5wYXJzZShcbiAgICByZWFkRmlsZVN5bmMoam9pbihyZXBvUm9vdCwgXCJwYWNrYWdlLmpzb25cIiksIFwidXRmLThcIiksXG4gICk7XG4gIG1rZGlyU3luYyhwa2dEaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICB3cml0ZUZpbGVTeW5jKFxuICAgIGpvaW4ocGtnRGlyLCBcInBhY2thZ2UuanNvblwiKSxcbiAgICBKU09OLnN0cmluZ2lmeSh7IG5hbWU6IFwiY2RrLW5leHRqc1wiLCBleHBvcnRzIH0pLFxuICApO1xuICBmb3IgKGNvbnN0IHRhcmdldCBvZiBPYmplY3QudmFsdWVzKGV4cG9ydHMpIGFzIHsgaW1wb3J0OiBzdHJpbmcgfVtdKSB7XG4gICAgbWtkaXJTeW5jKGRpcm5hbWUoam9pbihwa2dEaXIsIHRhcmdldC5pbXBvcnQpKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgd3JpdGVGaWxlU3luYyhqb2luKHBrZ0RpciwgdGFyZ2V0LmltcG9ydCksIFwiXCIpO1xuICB9XG4gIHN5bWxpbmtTeW5jKFxuICAgIGpvaW4ocmVwb1Jvb3QsIFwibm9kZV9tb2R1bGVzXCIsIFwibmV4dFwiKSxcbiAgICBqb2luKGRpciwgXCJub2RlX21vZHVsZXNcIiwgXCJuZXh0XCIpLFxuICApO1xuICBidWlsZFN5bmMoe1xuICAgIGVudHJ5UG9pbnRzOiBbam9pbihyZXBvUm9vdCwgXCJzcmMvYWRhcHRlci9hZGFwdGVyLm10c1wiKV0sXG4gICAgYnVuZGxlOiB0cnVlLFxuICAgIHBsYXRmb3JtOiBcIm5vZGVcIixcbiAgICB0YXJnZXQ6IFwibm9kZTI0XCIsXG4gICAgZm9ybWF0OiBcImVzbVwiLFxuICAgIGV4dGVybmFsOiBbXCJuZXh0XCJdLFxuICAgIG91dGZpbGU6IGpvaW4oZGlyLCBcImFkYXB0ZXIubWpzXCIpLFxuICAgIGJhbm5lcjoge1xuICAgICAganM6IFwiaW1wb3J0IHsgY3JlYXRlUmVxdWlyZSBhcyBfX2Nka05leHRqc0NyZWF0ZVJlcXVpcmUgfSBmcm9tICdub2RlOm1vZHVsZSc7IGNvbnN0IHJlcXVpcmUgPSBfX2Nka05leHRqc0NyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKTtcIixcbiAgICB9LFxuICAgIGxvZ0xldmVsOiBcInNpbGVudFwiLFxuICB9KTtcbn0pO1xuXG5hZnRlckFsbCgoKSA9PiB7XG4gIHJtU3luYyhkaXIsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbn0pO1xuXG4vKiogYGFkYXB0ZXIubW9kaWZ5Q29uZmlnKGNvbmZpZywgeyBwaGFzZSwgbmV4dFZlcnNpb24gfSlgLCBydW4gYnkgYG5vZGVgLiAqL1xuZnVuY3Rpb24gbW9kaWZ5Q29uZmlnKFxuICBjb25maWc6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICBwaGFzZTogc3RyaW5nLFxuICBuZXh0VmVyc2lvbiA9IFwiMTYuMy4wXCIsXG4pOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgY29uc3Qgc2NyaXB0ID1cbiAgICBgaW1wb3J0IGFkYXB0ZXIgZnJvbSAke0pTT04uc3RyaW5naWZ5KGpvaW4oZGlyLCBcImFkYXB0ZXIubWpzXCIpKX07YCArXG4gICAgYGNvbnN0IG91dCA9IGF3YWl0IGFkYXB0ZXIubW9kaWZ5Q29uZmlnKCR7SlNPTi5zdHJpbmdpZnkoY29uZmlnKX0sIGAgK1xuICAgIGAke0pTT04uc3RyaW5naWZ5KHsgcGhhc2UsIG5leHRWZXJzaW9uIH0pfSk7YCArXG4gICAgYHByb2Nlc3Muc3Rkb3V0LndyaXRlKEpTT04uc3RyaW5naWZ5KG91dCkpO2A7XG4gIHJldHVybiBKU09OLnBhcnNlKFxuICAgIGV4ZWNGaWxlU3luYyhwcm9jZXNzLmV4ZWNQYXRoLCBbXCItLWlucHV0LXR5cGU9bW9kdWxlXCIsIFwiLWVcIiwgc2NyaXB0XSwge1xuICAgICAgY3dkOiBkaXIsXG4gICAgICBlbmNvZGluZzogXCJ1dGYtOFwiLFxuICAgICAgc3RkaW86IFtcImlnbm9yZVwiLCBcInBpcGVcIiwgXCJwaXBlXCJdLFxuICAgIH0pLFxuICApO1xufVxuXG5jb25zdCBoYW5kbGVyID0gKHBhdGg6IHN0cmluZykgPT4gam9pbihwa2dEaXIsIFwibGliL2FkYXB0ZXJcIiwgcGF0aCk7XG5jb25zdCBvdXJzID0gKCkgPT4gKHtcbiAgZGVmYXVsdDogaGFuZGxlcihcInVzZS1jYWNoZS1kZWZhdWx0LWhhbmRsZXIubWpzXCIpLFxuICByZW1vdGU6IGhhbmRsZXIoXCJ1c2UtY2FjaGUtcmVtb3RlLWhhbmRsZXIubWpzXCIpLFxufSk7XG5cbmRlc2NyaWJlKFwibW9kaWZ5Q29uZmlnXCIsICgpID0+IHtcbiAgaXQoXCJ3aXJlcyBjZGstbmV4dGpzJ3MgY2FjaGUgaGFuZGxlcnMgaW50byB0aGUgYnVpbGRcIiwgKCkgPT4ge1xuICAgIGNvbnN0IG91dCA9IG1vZGlmeUNvbmZpZyh7IGltYWdlczoge30gfSwgXCJwaGFzZS1wcm9kdWN0aW9uLWJ1aWxkXCIpO1xuICAgIGV4cGVjdChvdXQuY2FjaGVIYW5kbGVyKS50b0JlKGhhbmRsZXIoXCJjYWNoZS1oYW5kbGVyLm1qc1wiKSk7XG4gICAgZXhwZWN0KG91dC5jYWNoZUhhbmRsZXJzKS50b0VxdWFsKG91cnMoKSk7XG4gICAgZXhwZWN0KG91dC5pbWFnZXMuY3VzdG9tQ2FjaGVIYW5kbGVyKS50b0JlKHRydWUpO1xuICB9KTtcblxuICBpdChcImtlZXBzIHRoZSBoYW5kbGVycyB0aGUgYXBwIGNvbmZpZ3VyZWQsIGFuZCBhZGRzIHRoZSBtaXNzaW5nIG9uZXNcIiwgKCkgPT4ge1xuICAgIGNvbnN0IG91dCA9IG1vZGlmeUNvbmZpZyhcbiAgICAgIHtcbiAgICAgICAgY2FjaGVIYW5kbGVyOiBcIi9hcHAvbXktaGFuZGxlci5qc1wiLFxuICAgICAgICBjYWNoZUhhbmRsZXJzOiB7IHJlbW90ZTogXCIvYXBwL3JlbW90ZS5qc1wiLCBjdXN0b206IFwiL2FwcC9jdXN0b20uanNcIiB9LFxuICAgICAgICBpbWFnZXM6IHsgY3VzdG9tQ2FjaGVIYW5kbGVyOiBcIi9hcHAvaW1hZ2VzLmpzXCIgfSxcbiAgICAgIH0sXG4gICAgICBcInBoYXNlLXByb2R1Y3Rpb24tYnVpbGRcIixcbiAgICApO1xuICAgIGV4cGVjdChvdXQuY2FjaGVIYW5kbGVyKS50b0JlKFwiL2FwcC9teS1oYW5kbGVyLmpzXCIpO1xuICAgIGV4cGVjdChvdXQuY2FjaGVIYW5kbGVycykudG9FcXVhbCh7XG4gICAgICBkZWZhdWx0OiBvdXJzKCkuZGVmYXVsdCxcbiAgICAgIHJlbW90ZTogXCIvYXBwL3JlbW90ZS5qc1wiLFxuICAgICAgY3VzdG9tOiBcIi9hcHAvY3VzdG9tLmpzXCIsXG4gICAgfSk7XG4gICAgZXhwZWN0KG91dC5pbWFnZXMuY3VzdG9tQ2FjaGVIYW5kbGVyKS50b0JlKFwiL2FwcC9pbWFnZXMuanNcIik7XG4gIH0pO1xuXG4gIC8vIGBuZXh0IHN0YXJ0YCByZWdpc3RlcnMgYHVzZSBjYWNoZWAgaGFuZGxlcnMgZnJvbSB0aGlzIGNvbmZpZywgb25jZS5cbiAgaXQoXCJyZWdpc3RlcnMgdGhlIHNhbWUgYHVzZSBjYWNoZWAgaGFuZGxlcnMgZm9yIGBuZXh0IHN0YXJ0YFwiLCAoKSA9PiB7XG4gICAgY29uc3Qgb3V0ID0gbW9kaWZ5Q29uZmlnKFxuICAgICAgeyBjYWNoZUhhbmRsZXJzOiB7IGRlZmF1bHQ6IFwiL2FwcC9kZWZhdWx0LmpzXCIgfSB9LFxuICAgICAgXCJwaGFzZS1wcm9kdWN0aW9uLXNlcnZlclwiLFxuICAgICk7XG4gICAgZXhwZWN0KG91dC5jYWNoZUhhbmRsZXJzKS50b0VxdWFsKHtcbiAgICAgIGRlZmF1bHQ6IFwiL2FwcC9kZWZhdWx0LmpzXCIsXG4gICAgICByZW1vdGU6IG91cnMoKS5yZW1vdGUsXG4gICAgfSk7XG4gICAgZXhwZWN0KG91dC5jYWNoZUhhbmRsZXIpLnRvQmVVbmRlZmluZWQoKTtcbiAgfSk7XG5cbiAgaXQoXCJsZWF2ZXMgb3RoZXIgcGhhc2VzIGFsb25lXCIsICgpID0+IHtcbiAgICBleHBlY3QobW9kaWZ5Q29uZmlnKHsgaW1hZ2VzOiB7fSB9LCBcInBoYXNlLWRldmVsb3BtZW50LXNlcnZlclwiKSkudG9FcXVhbCh7XG4gICAgICBpbWFnZXM6IHt9LFxuICAgIH0pO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZShcImFzc2VydFN1cHBvcnRlZE5leHRWZXJzaW9uXCIsICgpID0+IHtcbiAgaXQuZWFjaChbXCIxNi4yLjlcIiwgXCIxNS41LjBcIl0pKFxuICAgIFwicmVqZWN0cyBOZXh0LmpzICVzIGF0IGJ1aWxkIHRpbWVcIixcbiAgICAodmVyc2lvbikgPT4ge1xuICAgICAgZXhwZWN0KCgpID0+XG4gICAgICAgIG1vZGlmeUNvbmZpZyh7IGltYWdlczoge30gfSwgXCJwaGFzZS1wcm9kdWN0aW9uLWJ1aWxkXCIsIHZlcnNpb24pLFxuICAgICAgKS50b1Rocm93KGBOZXh0LmpzICR7dmVyc2lvbn0gaXMgbm90IHN1cHBvcnRlZGApO1xuICAgIH0sXG4gICk7XG5cbiAgaXQuZWFjaChbXCIxNi4zLjBcIiwgXCIxNy4wLjAtY2FuYXJ5LjFcIl0pKFwiYWNjZXB0cyBOZXh0LmpzICVzXCIsICh2ZXJzaW9uKSA9PiB7XG4gICAgZXhwZWN0KCgpID0+XG4gICAgICBtb2RpZnlDb25maWcoeyBpbWFnZXM6IHt9IH0sIFwicGhhc2UtcHJvZHVjdGlvbi1idWlsZFwiLCB2ZXJzaW9uKSxcbiAgICApLm5vdC50b1Rocm93KCk7XG4gIH0pO1xufSk7XG4iXX0=
@@ -0,0 +1,489 @@
1
+ import { DynamoDBClient, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
2
+ import { S3Client } from "@aws-sdk/client-s3";
3
+ /**
4
+ * Where the cache lives, as the constructs pass it to the compute through the
5
+ * environment (`NextjsFunctions`, `NextjsContainers`). Empty strings for what is
6
+ * not configured, which each handler reads as "that layer is disabled".
7
+ */
8
+ export interface AwsCacheConfig {
9
+ /** `CDK_NEXTJS_CACHE_BUCKET_NAME`. */
10
+ bucketName: string;
11
+ /** `CDK_NEXTJS_REVALIDATION_TABLE_NAME`. */
12
+ tableName: string;
13
+ /** `AWS_REGION`, `us-east-1` when unset. */
14
+ region: string;
15
+ /** `CDK_NEXTJS_BUILD_ID`: every S3 key and every marker row is scoped to it. */
16
+ buildId: string;
17
+ }
18
+ /** The configuration from the environment. */
19
+ export declare function resolveAwsCacheConfig(): AwsCacheConfig;
20
+ /**
21
+ * `{buildId}/{cacheKey}.json`, the incremental cache's key convention.
22
+ *
23
+ * The root path becomes `index`, and a leading slash is dropped so there is no
24
+ * empty folder under the build prefix. Everything this returns ends in `.json`,
25
+ * which is what keeps it disjoint from {@link useCacheS3Key}.
26
+ */
27
+ export declare function buildS3Key(buildId: string, cacheKey: string): string;
28
+ /**
29
+ * The folder under the build prefix that `'use cache'` entries are stored in.
30
+ * @see useCacheS3Key
31
+ */
32
+ export declare const USE_CACHE_KEY_PREFIX = "_use-cache";
33
+ /**
34
+ * `{buildId}/_use-cache/{hash}.entry`, where a `cacheHandlers` entry is stored.
35
+ *
36
+ * Hashed because a `'use cache'` key is the serialized arguments of the call and
37
+ * has no length limit, where an S3 key stops at 1024 bytes. The `.entry` suffix
38
+ * rather than `.json` is what makes a collision with the incremental cache
39
+ * impossible rather than unlikely: every key {@link buildS3Key} writes ends in
40
+ * `.json`, including one for a route that happens to be named `/_use-cache/…`.
41
+ * Still under the build prefix, so post-deploy pruning drops them with the build.
42
+ */
43
+ export declare function useCacheS3Key(buildId: string, keyHash: string): string;
44
+ /**
45
+ * The `durations` Next.js 16 passes a tag revalidation for a profile:
46
+ * `revalidateTag("posts", "max")` arrives as `{ expire: <the profile's expire> }`.
47
+ * Absent for `updateTag` and a bare `revalidateTag`, which expire immediately.
48
+ */
49
+ export interface RevalidateDurations {
50
+ expire?: number;
51
+ }
52
+ /**
53
+ * A tag's marker row, as timestamps in milliseconds. Written by
54
+ * {@link markerUpdate}; each field is absent until a revalidation of that shape
55
+ * has happened.
56
+ */
57
+ export interface TagMarker {
58
+ /** Expired outright: `updateTag`, or `revalidateTag` with no profile. */
59
+ revalidatedAt?: number;
60
+ /** Stale from here: `revalidateTag(tag, profile)`. */
61
+ staleAt?: number;
62
+ /** When the profile's `expire` runs out, which may be in the future. */
63
+ expiredAt?: number;
64
+ }
65
+ /**
66
+ * What the tag markers say about an entry: untouched, served stale while a
67
+ * background render replaces it, or expired and re-rendered before answering.
68
+ */
69
+ export type RevalidationState = "fresh" | "stale" | "expired";
70
+ /**
71
+ * What `marker` means for an entry created at `createdAt`, at `now`.
72
+ *
73
+ * `expiredAt` in the future is a profile's `expire` that has not come yet,
74
+ * compared the way Next.js's `areTagsExpired` does.
75
+ */
76
+ export declare function markerState(marker: TagMarker, createdAt: number, now: number): RevalidationState;
77
+ /**
78
+ * The clock every tag marker is stamped with, and every entry timestamp it is
79
+ * compared against: the one Next.js stamps `CacheEntry.timestamp` with
80
+ * (`performance.timeOrigin + performance.now()`, in `use-cache-wrapper.js` and
81
+ * the default handler), rather than `Date.now()`. Both handlers write the same
82
+ * marker rows, so both have to use it: two clocks on one row let the last writer
83
+ * decide which clock the others compare against, and they drift apart for as
84
+ * long as a Lambda sandbox or Fargate task lives.
85
+ */
86
+ export declare function markerClock(): number;
87
+ /**
88
+ * The marker fields a tag revalidated at `now` sets.
89
+ *
90
+ * Without `durations` - `updateTag`, or `revalidateTag` with no profile - the
91
+ * tag's entries expire immediately (`revalidatedAt`). With them the entries go
92
+ * stale now and expire after the profile's `expire`, the same pair of
93
+ * timestamps Next.js's `FileSystemCache.revalidateTag` records. A profile with
94
+ * no `expire` sets only `staleAt`, and whatever `expiredAt` the tag had stays.
95
+ */
96
+ export declare function markerFor(now: number, durations: RevalidateDurations | undefined): TagMarker;
97
+ /**
98
+ * `existing` with the fields `update` sets, as a {@link markerUpdate} leaves
99
+ * the row: a field `update` does not set keeps its value.
100
+ */
101
+ export declare function applyMarker(existing: TagMarker | undefined, update: TagMarker): TagMarker;
102
+ /** The marker-row update that sets {@link markerFor}'s fields. */
103
+ export declare function markerUpdate(now: number, durations: RevalidateDurations | undefined): Pick<ConstructorParameters<typeof UpdateItemCommand>[0], "UpdateExpression" | "ExpressionAttributeValues">;
104
+ /** DynamoDB's cap on keys in one `BatchGetItem`. */
105
+ export declare const BATCH_GET_MAX_KEYS = 100;
106
+ /**
107
+ * How often, at most, an instance catches up on revalidations other instances
108
+ * ran, unless `CDK_NEXTJS_TAG_REFRESH_MS` says otherwise: the staleness window
109
+ * for a `revalidateTag` run elsewhere. Every cache hit checks its tags'
110
+ * markers, and every marker of a deployment shares one partition key, so
111
+ * reading them per request capped a route's cache hits at the partition's read
112
+ * throughput (~1,900 req/s on NextjsRegionalFunctions). Once a second, from the
113
+ * revalidation log, the reads scale with instances instead of requests or tags.
114
+ */
115
+ export declare const DEFAULT_TAG_REFRESH_MS = 1000;
116
+ /**
117
+ * The bare-tag marker rows of the revalidation table (`pk = buildId`,
118
+ * `sk = tag`), which record when a tag was last revalidated and how.
119
+ *
120
+ * The table also holds the incremental cache's `tag#s3Key` mapping rows, which
121
+ * are its own business and stay in `S3CacheHandler`: only the markers mean the
122
+ * same thing to every handler. What an instance keeps of them between reads is
123
+ * {@link TrackedTagMarkers}.
124
+ */
125
+ export declare class TagMarkerTable {
126
+ private readonly client;
127
+ private readonly tableName;
128
+ private readonly buildId;
129
+ constructor(client: DynamoDBClient, tableName: string, buildId: string);
130
+ /**
131
+ * Record `tag`'s revalidation at `now`. See {@link markerUpdate}.
132
+ *
133
+ * Returns the row as this write left it, so the instance that wrote it can
134
+ * track it rather than read it back: reads are eventually consistent, and a
135
+ * read-back racing replication could return the marker from before the write.
136
+ */
137
+ write(tag: string, now: number, durations: RevalidateDurations | undefined): Promise<TagMarker | undefined>;
138
+ /**
139
+ * The marker rows for `tags` that exist, by tag, in one `BatchGetItem` per
140
+ * {@link BATCH_GET_MAX_KEYS} tags rather than a `GetItem` each: a page
141
+ * carries its whole implicit `_N_T_/…` chain plus the app's own tags, a
142
+ * page with a few fetches checks each of them too, and every one is on the
143
+ * request path. A tag with no row is absent from the result.
144
+ */
145
+ read(tags: string[]): Promise<Map<string, TagMarker>>;
146
+ }
147
+ /**
148
+ * How long a revalidation log row is kept (`ttl`, epoch seconds). DynamoDB never
149
+ * deletes an item before its TTL, only some time after, so a reader that last
150
+ * queried less than this long ago (less a margin for writers' clocks) is sure
151
+ * to find every row it has not seen yet.
152
+ */
153
+ export declare const REVALIDATION_LOG_TTL_MS: number;
154
+ /** The most `Query` pages one {@link RevalidationLog.query} reads. */
155
+ export declare const REVALIDATION_LOG_MAX_PAGES = 5;
156
+ /** A revalidation read back from the log. */
157
+ export interface RevalidationLogRow {
158
+ /** The row's sort key: unique per revalidation of a tag. */
159
+ sk: string;
160
+ /** When it was written, on the writer's `Date.now()`. */
161
+ at: number;
162
+ tag: string;
163
+ /** The marker fields the revalidation set, as {@link markerFor} returns them. */
164
+ marker: TagMarker;
165
+ }
166
+ /**
167
+ * The revalidation log: one row per tag revalidation (`pk = <buildId>#log`,
168
+ * `sk = <Date.now(), zero-padded>#<tag>`), which expires after
169
+ * {@link REVALIDATION_LOG_TTL_MS}.
170
+ *
171
+ * The marker rows stay the source of truth. The log exists so an instance
172
+ * can ask "what was revalidated since I last looked?" in one `Query`, instead
173
+ * of re-reading every tag it tracks to find out that almost none changed. Every
174
+ * marker row of a deployment shares one partition, and those re-reads from a
175
+ * handful of busy instances were enough to throttle it.
176
+ *
177
+ * The sort key's timestamp is the writer's wall clock (`Date.now()`), not the
178
+ * performance clock ({@link markerClock}) the marker values are stamped with by both
179
+ * handlers: it only orders rows for readers' cursors, which are on their own
180
+ * wall clocks, and is never compared with an entry's timestamp.
181
+ */
182
+ export declare class RevalidationLog {
183
+ private readonly client;
184
+ private readonly tableName;
185
+ private readonly pk;
186
+ constructor(client: DynamoDBClient, tableName: string, buildId: string);
187
+ /**
188
+ * Record that `tag` was revalidated at `at` (`Date.now()`), setting `marker`.
189
+ *
190
+ * Two revalidations of `tag` in the same millisecond would share a sort key,
191
+ * and the second would overwrite the first. So the row is only written where
192
+ * its key is free, and on a collision it moves to the next millisecond, up
193
+ * to {@link LOG_PUT_ATTEMPTS} times.
194
+ */
195
+ put(tag: string, at: number, marker: TagMarker): Promise<void>;
196
+ /**
197
+ * The rows written at or after `since` (`Date.now()`), oldest first.
198
+ * `truncated` when there were more than {@link REVALIDATION_LOG_MAX_PAGES}
199
+ * pages of them, and `rows` is only the start.
200
+ *
201
+ * Eventually consistent: a row written a moment ago may not show yet, which
202
+ * is what the reader's lookback is for.
203
+ */
204
+ query(since: number): Promise<{
205
+ rows: RevalidationLogRow[];
206
+ truncated: boolean;
207
+ }>;
208
+ }
209
+ /**
210
+ * How far behind the last query's start the next one starts reading the log:
211
+ * the room for the `Query` being eventually consistent and for writers' clocks
212
+ * running behind the reader's. Rows inside it are read again, and dropped as
213
+ * already applied.
214
+ */
215
+ export declare const REVALIDATION_LOG_LOOKBACK_MS = 5000;
216
+ /**
217
+ * The longest {@link TrackedTagMarkers} trusts the log alone for a tag before
218
+ * reading its marker row again. The log is the fast path, but the marker rows
219
+ * are the source of truth: a log row whose write failed would otherwise be
220
+ * missed for as long as the tag stays tracked.
221
+ *
222
+ * Rolling, not all at once: each refresh re-reads at most
223
+ * {@link BATCH_GET_MAX_KEYS} of the tags due, oldest first. Re-reading them all
224
+ * together cost ~500 RCU at 1000 tags - a fleet started by one load test did it
225
+ * in the same minute, 12,800 RCU, and throttled - and would be ~5,000 RCU at
226
+ * {@link DEFAULT_MAX_TRACKED_TAGS}, past what the one partition serves in a
227
+ * second. Spread out, 10,000 tracked tags cost ~8 RCU a second per instance.
228
+ *
229
+ * Each tag is due at a random 75-100% of this after it was last read, so tags
230
+ * first read together - an instance warming up - do not all come due together
231
+ * again every interval: the load test that measured the rolling re-read saw
232
+ * its warm-up's reads return as a 30-36 RCU/s bump ten minutes later.
233
+ */
234
+ export declare const DEFAULT_TAG_RESYNC_MS: number;
235
+ /**
236
+ * How long since the last successful log query the log can still be trusted to
237
+ * hold every row not read yet: its TTL less a margin for writers' clocks. Past
238
+ * it - a Lambda frozen between invocations, a run of failed queries - the
239
+ * tracked markers are forgotten, to be read again as they are needed, and the
240
+ * log is followed from there.
241
+ */
242
+ export declare const MAX_REVALIDATION_LOG_GAP_MS: number;
243
+ /**
244
+ * The most tags one instance tracks. Past it the least recently used is
245
+ * forgotten, which costs a read the next time it is needed: an untracked tag
246
+ * is read before it is trusted.
247
+ *
248
+ * A tracked tag costs memory (~2 MB at this bound) and its share of the rolling
249
+ * re-read, not a read every refresh, so the bound is set by how many tags an
250
+ * instance really uses. Every path is a tag of its own (`_N_T_/<path>`), and at
251
+ * 1000 an app of 1000 tagged pages kept evicting what it was about to need:
252
+ * half its requests re-read a marker.
253
+ */
254
+ export declare const DEFAULT_MAX_TRACKED_TAGS = 10000;
255
+ /**
256
+ * The revalidation table's marker rows and its log, which are only ever used
257
+ * together, or neither for tags local to this process.
258
+ */
259
+ export type TagTable = {
260
+ markers: TagMarkerTable;
261
+ log: RevalidationLog;
262
+ } | {
263
+ markers?: undefined;
264
+ log?: undefined;
265
+ };
266
+ /** How {@link TrackedTagMarkers} is built. */
267
+ export type TrackedTagMarkersOptions = TagTable & {
268
+ /**
269
+ * How often, at most, {@link TrackedTagMarkers.refresh} reads anything.
270
+ * @default DEFAULT_TAG_REFRESH_MS
271
+ */
272
+ refreshIntervalMs?: number;
273
+ maxTrackedTags?: number;
274
+ /** @see DEFAULT_TAG_RESYNC_MS */
275
+ resyncIntervalMs?: number;
276
+ /** The wall clock the log's cursor and the intervals are kept on. */
277
+ clock?: () => number;
278
+ /** `Math.random`, for when each tag's re-read comes due. */
279
+ random?: () => number;
280
+ debug?: (message: string) => void;
281
+ };
282
+ /**
283
+ * One instance's copy of the marker rows of the tags it has needed, kept
284
+ * current through the {@link RevalidationLog}: the process's one tag manifest
285
+ * (`sharedTagManifest`), for the `'use cache'` handlers and the incremental
286
+ * cache alike.
287
+ *
288
+ * Next.js's built-in `'use cache'` handler keeps tags in a module-level map
289
+ * (`tags-manifest.external.js`) that only its own `updateTags` writes, and its
290
+ * `refreshTags` is a no-op, so a `revalidateTag` on one instance never reaches
291
+ * the others: they keep serving the revalidated entry, including inside pages
292
+ * that are being re-rendered *because* of that revalidation. The
293
+ * `cacheHandlers` interface's answer is `refreshTags`, "called before starting
294
+ * a new request … to refresh the local tags manifest", and this is that
295
+ * manifest, backed by the same marker rows the incremental cache's
296
+ * `revalidateTag` writes (`pk = buildId`, `sk = tag`).
297
+ *
298
+ * The cost is bounded per instance, not per request:
299
+ * - {@link ensure} reads a tag's marker the first time it is needed and not
300
+ * again until it is evicted, so a tag costs one read per instance, not one
301
+ * per request.
302
+ * - {@link refresh}, at most once per `refreshIntervalMs`, sends one `Query`
303
+ * for the log rows written since the last one, and applies those of tags
304
+ * this instance tracks. Concurrent callers share it. Alongside, it re-reads
305
+ * the markers of up to {@link BATCH_GET_MAX_KEYS} tags not read for
306
+ * {@link DEFAULT_TAG_RESYNC_MS}, in one `BatchGetItem`.
307
+ * A revalidation on another instance is therefore seen within
308
+ * `refreshIntervalMs` (plus the query itself). The instance that ran it applies
309
+ * it itself, at once, with {@link set}.
310
+ */
311
+ export declare class TrackedTagMarkers {
312
+ private readonly markers;
313
+ private readonly log;
314
+ private readonly refreshIntervalMs;
315
+ private readonly maxTrackedTags;
316
+ private readonly resyncIntervalMs;
317
+ private readonly clock;
318
+ private readonly random;
319
+ private readonly debug;
320
+ /** By tag, least recently used first. */
321
+ private readonly tags;
322
+ /**
323
+ * When each tracked tag's marker is due to be read again: a random 75-100%
324
+ * of `resyncIntervalMs` after it was last known from the table - read,
325
+ * written here, or new. In the order they became known, least recent first.
326
+ */
327
+ private readonly dueAt;
328
+ private readonly reading;
329
+ /**
330
+ * Log rows for tags whose first read is in flight, merged by tag: the read
331
+ * may have been answered before the revalidation, so they are applied to its
332
+ * result rather than dropped.
333
+ */
334
+ private readonly pendingRows;
335
+ /** Tracked tags whose read failed, for the next refresh to read again. */
336
+ private readonly unread;
337
+ private lastRefresh;
338
+ private refreshing;
339
+ /**
340
+ * Where the next log query starts (`clock()`): the last successful one's
341
+ * start, less {@link REVALIDATION_LOG_LOOKBACK_MS}.
342
+ */
343
+ private cursor;
344
+ /** When the last successful log query started, or the log was first read. */
345
+ private lastLogRead;
346
+ /** Log rows at or after the cursor already applied, by sort key. */
347
+ private readonly applied;
348
+ /** {@link update}s of the current turn, by their arguments. */
349
+ private readonly updating;
350
+ constructor(options: TrackedTagMarkersOptions);
351
+ /** `tag`'s marker as this instance knows it, if it tracks `tag`. */
352
+ get(tag: string): TagMarker | undefined;
353
+ /**
354
+ * Track `tag` with `marker`: what this instance just wrote, which it knows
355
+ * without reading it back.
356
+ */
357
+ set(tag: string, marker: TagMarker): void;
358
+ /**
359
+ * Note a use of `tags`: those of an entry this instance just stored.
360
+ *
361
+ * With the table, a tag not tracked yet is left untracked, to be read by
362
+ * {@link ensure} the first time an entry needs it. Nothing revalidated before
363
+ * the new entry was created applies to *it*, but tracking is per tag, not per
364
+ * entry: tracking the tag with an empty marker would also vouch for every
365
+ * older entry that carries it - one another instance stored in S3, or one
366
+ * this instance kept in memory across a {@link forget} - and a revalidation
367
+ * from before the log's cursor would go unseen until the rolling re-read.
368
+ *
369
+ * Without the table every revalidation is this process's own, applied with
370
+ * {@link set}, so a tag is tracked at once.
371
+ */
372
+ track(tags: readonly string[]): void;
373
+ /**
374
+ * Track `tags` as they are known now, without reading them, with an empty
375
+ * marker for any not tracked yet.
376
+ */
377
+ private trackUnread;
378
+ /**
379
+ * Read the markers of whichever of `tags` this instance does not track yet.
380
+ * Needed before judging an entry this instance did not create, since its
381
+ * tags may have been revalidated at any time before.
382
+ */
383
+ ensure(tags: readonly string[]): Promise<void>;
384
+ /**
385
+ * Catch up on revalidations other instances ran, at most once per
386
+ * `refreshIntervalMs`. See {@link TrackedTagMarkers} for the cost.
387
+ */
388
+ refresh(): Promise<void>;
389
+ /**
390
+ * Record a revalidation of `tags`: here at once, and in the revalidation
391
+ * table for every other instance - the marker row plus a log row per tag.
392
+ *
393
+ * Next.js calls `revalidateTag` on the incremental cache and `updateTags` on
394
+ * every distinct `cacheHandlers` entry, all in one synchronous loop
395
+ * (`revalidation-utils`), and all of them share this manifest. So an
396
+ * identical call in the same turn joins the first instead of writing the
397
+ * rows again. One in a later turn is a revalidation of its own, even while
398
+ * the first one's writes are in flight, and gets its own, later, marker.
399
+ * Never rejects: resolves `false` when a row failed to write (the error is
400
+ * logged), so other instances may not see it.
401
+ */
402
+ update(tags: readonly string[], durations: RevalidateDurations | undefined): Promise<boolean>;
403
+ /**
404
+ * What the tracked markers say about an entry carrying `tags`, created at
405
+ * `createdAt`: the most severe answer of any of them.
406
+ */
407
+ state(tags: readonly string[], createdAt: number): RevalidationState;
408
+ /**
409
+ * `getExpiration`'s answer: the latest time any of `tags` expired, `0` if
410
+ * none has. Only expirations already past count - a profile's future
411
+ * `expire` is not an expiration yet - and a stale mark does not, matching
412
+ * Next.js's default handler, which reads only `expired` for implicit tags.
413
+ */
414
+ expiration(tags: readonly string[]): number;
415
+ private write;
416
+ /**
417
+ * Put `tag`'s log row, stamped on the wall clock (the marker values are on
418
+ * Next.js's: see {@link RevalidationLog}).
419
+ *
420
+ * Readers only look {@link REVALIDATION_LOG_LOOKBACK_MS} behind their last
421
+ * query, so a row that shows up much later than its timestamp - a slow
422
+ * connection, retries under throttling - is behind every cursor and never
423
+ * read. A slow put is therefore made again under a fresh timestamp, which
424
+ * readers will see; one slow twice is reported as a failure.
425
+ */
426
+ private putLogRow;
427
+ /**
428
+ * One refresh started at `at`: the log rows since the cursor, and the next
429
+ * slice of the rolling re-read.
430
+ *
431
+ * A failed query changes nothing but the log: the tags stay tracked as they
432
+ * were, and the next refresh asks again from the same cursor.
433
+ */
434
+ private sync;
435
+ /**
436
+ * The tags to read on this refresh, at most one `BatchGetItem`'s worth:
437
+ * those whose read failed, then those whose re-read has come due, in the
438
+ * order they were last read.
439
+ */
440
+ private due;
441
+ /**
442
+ * Forget every tracked marker, and follow the log from `at`: what is needed
443
+ * next is read from its marker as it is needed, spread over the requests
444
+ * that need it, rather than every tracked tag re-read at once.
445
+ */
446
+ private forget;
447
+ /**
448
+ * Move the cursor forward to `cursor`, forgetting applied rows before it:
449
+ * no query returns them again.
450
+ */
451
+ private advance;
452
+ /**
453
+ * Read `tags` from the table, and report whether that worked. A read that
454
+ * fails leaves them tracked as they were, and the next refresh reads them
455
+ * again: the same "assume the entry is valid" the incremental cache has
456
+ * always answered with, rather than a read on every request while DynamoDB
457
+ * is unavailable.
458
+ */
459
+ private readInto;
460
+ private takePendingRows;
461
+ /** Record that `tag`'s marker was known from the table at `at`. */
462
+ private known;
463
+ private remember;
464
+ private touch;
465
+ }
466
+ /**
467
+ * A marker as read from the table or the log, keeping whatever this instance
468
+ * already knows that the read does not show yet: reads are eventually
469
+ * consistent, and a read straight after this instance's own revalidation could
470
+ * otherwise take it back.
471
+ */
472
+ export declare function mergeMarkers(local: TagMarker | undefined, remote: TagMarker | undefined): TagMarker;
473
+ /**
474
+ * The cache bucket, as the handlers use it: whole objects read and written as
475
+ * UTF-8 text under the keys above.
476
+ */
477
+ export declare class CacheBucket {
478
+ private readonly client;
479
+ private readonly bucketName;
480
+ constructor(client: S3Client, bucketName: string);
481
+ /**
482
+ * The body of the object at `key`, or `undefined` when there is none (`NoSuchKey`, or an
483
+ * empty response body). Any other error is thrown: the handlers disagree on
484
+ * whether one is worth logging.
485
+ */
486
+ get(key: string): Promise<string | undefined>;
487
+ /** Write `body` as a JSON object at `key`. */
488
+ putJson(key: string, body: string): Promise<void>;
489
+ }