cdk-nextjs 0.7.5 → 0.7.7

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 (69) hide show
  1. package/.jsii +38 -38
  2. package/.jsii.tabl.json +1 -1
  3. package/README.md +12 -3
  4. package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +31 -13
  5. package/docs/adapter-runtime.md +1 -1
  6. package/docs/caching-guide.md +11 -1
  7. package/docs/harness-coverage.md +11 -9
  8. package/lib/adapter/adapter.mjs +220 -149
  9. package/lib/adapter/aws-cache-store.d.ts +242 -7
  10. package/lib/adapter/aws-cache-store.js +520 -54
  11. package/lib/adapter/aws-cache-store.test.js +710 -3
  12. package/lib/adapter/build-outputs.js +23 -1
  13. package/lib/adapter/build-outputs.test.js +18 -1
  14. package/lib/adapter/cache-handler.d.ts +4 -3
  15. package/lib/adapter/cache-handler.js +1 -1
  16. package/lib/adapter/cache-handler.mjs +552 -91
  17. package/lib/adapter/cache-handler.test.js +31 -1
  18. package/lib/adapter/cache-utils.d.ts +68 -2
  19. package/lib/adapter/cache-utils.js +108 -2
  20. package/lib/adapter/cache-utils.test.js +45 -1
  21. package/lib/adapter/init-cache.d.ts +1 -1
  22. package/lib/adapter/init-cache.js +60 -37
  23. package/lib/adapter/init-cache.test.js +55 -2
  24. package/lib/adapter/local-file-cache-handler.d.ts +2 -2
  25. package/lib/adapter/local-file-cache-handler.js +4 -5
  26. package/lib/adapter/s3-cache-handler.d.ts +23 -7
  27. package/lib/adapter/s3-cache-handler.js +63 -27
  28. package/lib/adapter/s3-cache-handler.test.js +168 -1
  29. package/lib/adapter/use-cache-common.d.ts +53 -1
  30. package/lib/adapter/use-cache-common.js +121 -14
  31. package/lib/adapter/use-cache-common.test.js +138 -1
  32. package/lib/adapter/use-cache-default-handler.js +36 -6
  33. package/lib/adapter/use-cache-default-handler.mjs +557 -51
  34. package/lib/adapter/use-cache-handlers.test.js +247 -7
  35. package/lib/adapter/use-cache-remote-handler.js +10 -9
  36. package/lib/adapter/use-cache-remote-handler.mjs +597 -101
  37. package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +2 -1
  38. package/lib/lambdas/post-deploy/prune-revalidation-table.js +35 -13
  39. package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +19 -1
  40. package/lib/nextjs-api.js +1 -1
  41. package/lib/nextjs-build/nextjs-build.d.ts +0 -7
  42. package/lib/nextjs-build/nextjs-build.js +4 -11
  43. package/lib/nextjs-build/nextjs-build.test.js +4 -3
  44. package/lib/nextjs-cache.js +1 -1
  45. package/lib/nextjs-compute/nextjs-containers.js +1 -1
  46. package/lib/nextjs-compute/nextjs-functions.js +1 -1
  47. package/lib/nextjs-distribution.js +1 -1
  48. package/lib/nextjs-post-deploy.js +1 -1
  49. package/lib/nextjs-static-assets.js +1 -1
  50. package/lib/root-constructs/nextjs-base-construct.js +1 -1
  51. package/lib/root-constructs/nextjs-global-containers.js +1 -1
  52. package/lib/root-constructs/nextjs-global-functions.js +1 -1
  53. package/lib/root-constructs/nextjs-regional-containers.js +1 -1
  54. package/lib/root-constructs/nextjs-regional-functions.js +1 -1
  55. package/lib/runtime/core.js +18 -4
  56. package/lib/runtime/core.test.js +17 -1
  57. package/lib/runtime/image.d.ts +19 -0
  58. package/lib/runtime/image.js +28 -2
  59. package/lib/runtime/image.test.js +43 -1
  60. package/lib/runtime/lambda.mjs +30 -6
  61. package/lib/runtime/server.mjs +30 -6
  62. package/lib/runtime/tag-manifest.d.ts +15 -0
  63. package/lib/runtime/tag-manifest.js +24 -0
  64. package/lib/runtime/tag-manifest.test.d.ts +1 -0
  65. package/lib/runtime/tag-manifest.test.js +21 -0
  66. package/lib/utils/deployment-build-id.d.ts +11 -0
  67. package/lib/utils/deployment-build-id.js +15 -0
  68. package/package.json +3 -3
  69. package/scripts/e2e-harness/README.md +1 -1
@@ -808,11 +808,167 @@ import {
808
808
  writeFile
809
809
  } from "node:fs/promises";
810
810
  import { createRequire } from "node:module";
811
- import { dirname, join, relative, resolve, sep } from "node:path";
811
+ import { dirname, join, relative, resolve, sep as sep2 } from "node:path";
812
+
813
+ // src/adapter/cache-utils.ts
814
+ import { createHash } from "node:crypto";
815
+ import { sep } from "node:path";
812
816
 
813
817
  // src/constants.ts
814
818
  var LOG_PREFIX = "[cdk-nextjs]";
815
819
 
820
+ // src/utils/base-path.ts
821
+ function normalizeBasePath(basePath) {
822
+ return (basePath || "").replace(/^\/+/, "").replace(/\/+$/, "");
823
+ }
824
+ function hasPathPrefix(path, prefix) {
825
+ return !!prefix && (path === prefix || path.startsWith(`${prefix}/`));
826
+ }
827
+ function basePathPrefix(basePath) {
828
+ const bare = normalizeBasePath(basePath);
829
+ return bare ? `/${bare}` : "";
830
+ }
831
+
832
+ // src/adapter/cache-utils.ts
833
+ function preprocessValue(value) {
834
+ if (value == null) {
835
+ return value;
836
+ }
837
+ if (Buffer.isBuffer(value)) {
838
+ return {
839
+ __type: "Buffer",
840
+ base64: value.toString("base64")
841
+ };
842
+ }
843
+ if (value instanceof Map) {
844
+ const entries = {};
845
+ for (const [key, val] of value.entries()) {
846
+ entries[key] = preprocessValue(val);
847
+ }
848
+ return {
849
+ __type: "Map",
850
+ data: entries
851
+ };
852
+ }
853
+ if (Array.isArray(value)) {
854
+ return value.map((item) => preprocessValue(item));
855
+ }
856
+ if (typeof value === "object") {
857
+ const processed = {};
858
+ for (const [key, val] of Object.entries(value)) {
859
+ processed[key] = preprocessValue(val);
860
+ }
861
+ return processed;
862
+ }
863
+ return value;
864
+ }
865
+ function serializeCacheValue(value) {
866
+ const processed = preprocessValue(value);
867
+ return JSON.stringify(processed);
868
+ }
869
+ function prerenderPathToCacheKey(pathname, basePath) {
870
+ const route = (hasPathPrefix(pathname, basePath) ? pathname.slice(basePath.length) : pathname).replace(/^\/+/, "");
871
+ return route === "" ? "index" : route;
872
+ }
873
+ function cacheKeyFileName(cacheKey) {
874
+ let cleanCacheKey = cacheKey;
875
+ if (cacheKey === "/" || cacheKey === "") {
876
+ cleanCacheKey = "index";
877
+ } else if (cacheKey.startsWith("/")) {
878
+ cleanCacheKey = cacheKey.slice(1);
879
+ }
880
+ return `${cleanCacheKey}.json`;
881
+ }
882
+ function sha256Hex(value) {
883
+ return createHash("sha256").update(value).digest("hex");
884
+ }
885
+ var MAX_S3_KEY_BYTES = 1024;
886
+ var MAX_BUILD_ID_BYTES = 127;
887
+ var LONG_KEY_PREFIX = "_long-key";
888
+ function cacheObjectName(cacheKey) {
889
+ const name = cacheKeyFileName(cacheKey);
890
+ if (Buffer.byteLength(name) <= MAX_S3_KEY_BYTES - (MAX_BUILD_ID_BYTES + 1)) {
891
+ return name;
892
+ }
893
+ return `${LONG_KEY_PREFIX}/${sha256Hex(name)}.long`;
894
+ }
895
+ function routeCacheKeyFromFilePath(filePath) {
896
+ const path = filePath?.split(sep).join("/");
897
+ const at = path?.indexOf(ROUTE_CACHE_DIR) ?? -1;
898
+ if (at < 0) {
899
+ return void 0;
900
+ }
901
+ const key = path.slice(at + "/server/".length).replace(/\.[^./]+$/, "");
902
+ return ROUTE_CACHE_KEY_PREFIX.test(key) ? key : void 0;
903
+ }
904
+ var ROUTE_CACHE_DIR = "/server/route-cache/";
905
+ var ROUTE_CACHE_KEY_PREFIX = /^route-cache\/[A-Z_]+\/[0-9a-f]{64}\/\$(?=\/)/;
906
+ var INDEX_SUFFIX = "/index";
907
+ var PAGES_DATA_PATHNAME = /^(.*)\/_next\/data\/[^/]+\/(.+)\.json$/;
908
+ function groupPrerenders(prerenders) {
909
+ const htmlPathnames = new Set(
910
+ prerenders.map((p) => p.pathname).filter(
911
+ (p) => !p.endsWith(".rsc") && !p.includes(".segments/") && !PAGES_DATA_PATHNAME.test(p)
912
+ )
913
+ );
914
+ const groups = /* @__PURE__ */ new Map();
915
+ const variantsFor = (base) => {
916
+ let variants = groups.get(base);
917
+ if (!variants) {
918
+ variants = { segments: [] };
919
+ groups.set(base, variants);
920
+ }
921
+ return variants;
922
+ };
923
+ const routeOf = (base) => {
924
+ if (base.endsWith(INDEX_SUFFIX) && !htmlPathnames.has(base)) {
925
+ const parent = base.slice(0, -INDEX_SUFFIX.length) || "/";
926
+ if (htmlPathnames.has(parent)) {
927
+ return parent;
928
+ }
929
+ }
930
+ return base;
931
+ };
932
+ for (const prerender of prerenders) {
933
+ const { pathname } = prerender;
934
+ const dataRoute = PAGES_DATA_PATHNAME.exec(pathname);
935
+ if (dataRoute) {
936
+ variantsFor(routeOf(`${dataRoute[1]}/${dataRoute[2]}`)).data = prerender;
937
+ } else if (pathname.includes(".segments/")) {
938
+ variantsFor(routeOf(pathname.split(".segments/")[0])).segments.push(
939
+ prerender
940
+ );
941
+ } else if (pathname.endsWith(".rsc")) {
942
+ variantsFor(routeOf(pathname.slice(0, -".rsc".length))).rsc = prerender;
943
+ } else {
944
+ variantsFor(pathname).html = prerender;
945
+ }
946
+ }
947
+ return groups;
948
+ }
949
+ var NON_CACHEABLE_APP_PAGE_HEADERS = /* @__PURE__ */ new Set([
950
+ "content-type",
951
+ "vary",
952
+ "x-nextjs-prerender",
953
+ "x-nextjs-postponed"
954
+ ]);
955
+ function appPageCacheHeaders(initialHeaders) {
956
+ const headers = {};
957
+ for (const [name, value] of Object.entries(initialHeaders)) {
958
+ if (!NON_CACHEABLE_APP_PAGE_HEADERS.has(name.toLowerCase())) {
959
+ headers[name] = value;
960
+ }
961
+ }
962
+ return headers;
963
+ }
964
+ var NEXT_CACHE_TAGS_HEADER = "x-next-cache-tags";
965
+ function headerTags(value) {
966
+ const headers = value?.headers;
967
+ const header = headers?.[NEXT_CACHE_TAGS_HEADER];
968
+ return typeof header === "string" ? header.split(",").filter(Boolean) : [];
969
+ }
970
+ var INIT_CACHE_TAG_MANIFEST = "_cdk-nextjs-tag-manifest.json";
971
+
816
972
  // src/runtime/manifest.ts
817
973
  var ADAPTER_MANIFEST_VERSION = 1;
818
974
  var ADAPTER_DIR_NAME = "cdk-nextjs-adapter";
@@ -834,18 +990,6 @@ var ERROR_PAGE_SUFFIXES = [
834
990
  ];
835
991
  var RUNTIME_DIR_NAME = "cdk-nextjs-runtime";
836
992
 
837
- // src/utils/base-path.ts
838
- function normalizeBasePath(basePath) {
839
- return (basePath || "").replace(/^\/+/, "").replace(/\/+$/, "");
840
- }
841
- function hasPathPrefix(path, prefix) {
842
- return !!prefix && (path === prefix || path.startsWith(`${prefix}/`));
843
- }
844
- function basePathPrefix(basePath) {
845
- const bare = normalizeBasePath(basePath);
846
- return bare ? `/${bare}` : "";
847
- }
848
-
849
993
  // src/adapter/function-groups.ts
850
994
  var DEFAULT_FUNCTION_GROUP = "default";
851
995
  var FUNCTION_GROUPS_ENV_VAR = "CDK_NEXTJS_FUNCTION_GROUPS";
@@ -1532,6 +1676,12 @@ function withBasePath(path, basePath) {
1532
1676
  return path === "/" ? basePath || "/" : `${basePath}${path}`;
1533
1677
  }
1534
1678
 
1679
+ // src/utils/deployment-build-id.ts
1680
+ function deploymentBuildId(manifest) {
1681
+ const { deploymentId } = manifest.config;
1682
+ return deploymentId ? `${manifest.buildId}-${deploymentId}` : manifest.buildId;
1683
+ }
1684
+
1535
1685
  // src/adapter/build-outputs.ts
1536
1686
  var ENV_FILES = [".env", ".env.production"];
1537
1687
  var RUNTIME_NEXT_MODULES = [
@@ -1671,6 +1821,7 @@ function buildAdapterManifest(ctx, options = {}) {
1671
1821
  }))
1672
1822
  } : {}
1673
1823
  };
1824
+ assertBuildIdFits(manifest);
1674
1825
  const groups = assignment ? Object.entries(assignment.templates).map(([name, templates]) => ({
1675
1826
  name,
1676
1827
  dirName: groupStagingDirName(name),
@@ -1689,6 +1840,18 @@ function buildAdapterManifest(ctx, options = {}) {
1689
1840
  ];
1690
1841
  return { manifest, groups };
1691
1842
  }
1843
+ function assertBuildIdFits(manifest) {
1844
+ const bytes = Buffer.byteLength(deploymentBuildId(manifest));
1845
+ if (bytes > MAX_BUILD_ID_BYTES) {
1846
+ const { what, fix } = manifest.config.deploymentId ? {
1847
+ what: "The build ID and deploymentId together are",
1848
+ fix: "Use a shorter deploymentId or generateBuildId."
1849
+ } : { what: "The build ID is", fix: "Use a shorter generateBuildId." };
1850
+ throw new Error(
1851
+ `${LOG_PREFIX} ${what} ${bytes} bytes; cdk-nextjs stores cache entries under it and allows at most ${MAX_BUILD_ID_BYTES}. ${fix}`
1852
+ );
1853
+ }
1854
+ }
1692
1855
  function collectGroupStagingPlan(ctx, invocable, entrypoints, templates) {
1693
1856
  const basePath = ctx.config.basePath || "";
1694
1857
  const ownedEntrypoints = [
@@ -1798,7 +1961,7 @@ function collectStaticFiles(ctx) {
1798
1961
  const { repoRoot } = ctx;
1799
1962
  const basePath = ctx.config.basePath || "";
1800
1963
  const staticFiles = /* @__PURE__ */ new Map();
1801
- const pagesDir = join(ctx.distDir, "server", "pages") + sep;
1964
+ const pagesDir = join(ctx.distDir, "server", "pages") + sep2;
1802
1965
  for (const output of ctx.outputs.staticFiles) {
1803
1966
  const key = toPosix(relative(repoRoot, output.filePath));
1804
1967
  const pathnames = routablePathnames(
@@ -1828,9 +1991,9 @@ function routablePathnames(pathname, basePath, fromPagesRouter) {
1828
1991
  }
1829
1992
  function stageServedStaticFiles(ctx, staging) {
1830
1993
  const { repoRoot, distDir } = ctx;
1831
- const clientStaticDir = join(distDir, "static") + sep;
1994
+ const clientStaticDir = join(distDir, "static") + sep2;
1832
1995
  for (const output of ctx.outputs.staticFiles) {
1833
- const servedByS3 = output.filePath.startsWith(clientStaticDir) || !output.filePath.startsWith(distDir + sep);
1996
+ const servedByS3 = output.filePath.startsWith(clientStaticDir) || !output.filePath.startsWith(distDir + sep2);
1834
1997
  if (servedByS3) {
1835
1998
  continue;
1836
1999
  }
@@ -1956,7 +2119,7 @@ async function stageFiles(staging, stagingDir) {
1956
2119
  }
1957
2120
  await mkdir(dirname(dest), { recursive: true });
1958
2121
  const resolved = resolve(dirname(dest), linkTarget);
1959
- if (resolved === stagingDir || resolved.startsWith(stagingDir + sep)) {
2122
+ if (resolved === stagingDir || resolved.startsWith(stagingDir + sep2)) {
1960
2123
  await symlink(linkTarget, dest);
1961
2124
  } else {
1962
2125
  await cp(source, dest, { recursive: true, dereference: true });
@@ -2044,7 +2207,7 @@ function assertStagingKey(key, source) {
2044
2207
  }
2045
2208
  }
2046
2209
  function toPosix(path) {
2047
- return sep === "/" ? path : path.split(sep).join("/");
2210
+ return sep2 === "/" ? path : path.split(sep2).join("/");
2048
2211
  }
2049
2212
  function sortedUnique(values) {
2050
2213
  return [...new Set(values)].sort();
@@ -2054,7 +2217,8 @@ function sortedUnique(values) {
2054
2217
  var import_debug = __toESM(require_src());
2055
2218
  import { existsSync as existsSync2 } from "node:fs";
2056
2219
  import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "node:fs/promises";
2057
- import { join as join2 } from "node:path";
2220
+ import { createRequire as createRequire2 } from "node:module";
2221
+ import { dirname as dirname2, join as join2 } from "node:path";
2058
2222
  import { CachedRouteKind as CachedRouteKind2 } from "next/dist/server/response-cache/index.js";
2059
2223
 
2060
2224
  // src/adapter/cache-kinds.ts
@@ -2076,113 +2240,6 @@ function cacheKindResolver(outputs) {
2076
2240
  };
2077
2241
  }
2078
2242
 
2079
- // src/adapter/cache-utils.ts
2080
- function preprocessValue(value) {
2081
- if (value == null) {
2082
- return value;
2083
- }
2084
- if (Buffer.isBuffer(value)) {
2085
- return {
2086
- __type: "Buffer",
2087
- base64: value.toString("base64")
2088
- };
2089
- }
2090
- if (value instanceof Map) {
2091
- const entries = {};
2092
- for (const [key, val] of value.entries()) {
2093
- entries[key] = preprocessValue(val);
2094
- }
2095
- return {
2096
- __type: "Map",
2097
- data: entries
2098
- };
2099
- }
2100
- if (Array.isArray(value)) {
2101
- return value.map((item) => preprocessValue(item));
2102
- }
2103
- if (typeof value === "object") {
2104
- const processed = {};
2105
- for (const [key, val] of Object.entries(value)) {
2106
- processed[key] = preprocessValue(val);
2107
- }
2108
- return processed;
2109
- }
2110
- return value;
2111
- }
2112
- function serializeCacheValue(value) {
2113
- const processed = preprocessValue(value);
2114
- return JSON.stringify(processed);
2115
- }
2116
- function prerenderPathToCacheKey(pathname, basePath) {
2117
- const route = (hasPathPrefix(pathname, basePath) ? pathname.slice(basePath.length) : pathname).replace(/^\/+/, "");
2118
- return route === "" ? "index" : route;
2119
- }
2120
- var INDEX_SUFFIX = "/index";
2121
- var PAGES_DATA_PATHNAME = /^(.*)\/_next\/data\/[^/]+\/(.+)\.json$/;
2122
- function groupPrerenders(prerenders) {
2123
- const htmlPathnames = new Set(
2124
- prerenders.map((p) => p.pathname).filter(
2125
- (p) => !p.endsWith(".rsc") && !p.includes(".segments/") && !PAGES_DATA_PATHNAME.test(p)
2126
- )
2127
- );
2128
- const groups = /* @__PURE__ */ new Map();
2129
- const variantsFor = (base) => {
2130
- let variants = groups.get(base);
2131
- if (!variants) {
2132
- variants = { segments: [] };
2133
- groups.set(base, variants);
2134
- }
2135
- return variants;
2136
- };
2137
- const routeOf = (base) => {
2138
- if (base.endsWith(INDEX_SUFFIX) && !htmlPathnames.has(base)) {
2139
- const parent = base.slice(0, -INDEX_SUFFIX.length) || "/";
2140
- if (htmlPathnames.has(parent)) {
2141
- return parent;
2142
- }
2143
- }
2144
- return base;
2145
- };
2146
- for (const prerender of prerenders) {
2147
- const { pathname } = prerender;
2148
- const dataRoute = PAGES_DATA_PATHNAME.exec(pathname);
2149
- if (dataRoute) {
2150
- variantsFor(routeOf(`${dataRoute[1]}/${dataRoute[2]}`)).data = prerender;
2151
- } else if (pathname.includes(".segments/")) {
2152
- variantsFor(routeOf(pathname.split(".segments/")[0])).segments.push(
2153
- prerender
2154
- );
2155
- } else if (pathname.endsWith(".rsc")) {
2156
- variantsFor(routeOf(pathname.slice(0, -".rsc".length))).rsc = prerender;
2157
- } else {
2158
- variantsFor(pathname).html = prerender;
2159
- }
2160
- }
2161
- return groups;
2162
- }
2163
- var NON_CACHEABLE_APP_PAGE_HEADERS = /* @__PURE__ */ new Set([
2164
- "content-type",
2165
- "vary",
2166
- "x-nextjs-prerender",
2167
- "x-nextjs-postponed"
2168
- ]);
2169
- function appPageCacheHeaders(initialHeaders) {
2170
- const headers = {};
2171
- for (const [name, value] of Object.entries(initialHeaders)) {
2172
- if (!NON_CACHEABLE_APP_PAGE_HEADERS.has(name.toLowerCase())) {
2173
- headers[name] = value;
2174
- }
2175
- }
2176
- return headers;
2177
- }
2178
- var NEXT_CACHE_TAGS_HEADER = "x-next-cache-tags";
2179
- function headerTags(value) {
2180
- const headers = value?.headers;
2181
- const header = headers?.[NEXT_CACHE_TAGS_HEADER];
2182
- return typeof header === "string" ? header.split(",").filter(Boolean) : [];
2183
- }
2184
- var INIT_CACHE_TAG_MANIFEST = "_cdk-nextjs-tag-manifest.json";
2185
-
2186
2243
  // src/adapter/init-cache.ts
2187
2244
  var debug = (0, import_debug.default)("cdk-nextjs:adapter");
2188
2245
  async function writeInitCache(ctx, cacheDir) {
@@ -2192,6 +2249,8 @@ async function writeInitCache(ctx, cacheDir) {
2192
2249
  debug(`Prerender groups: ${prerenderGroups.size} groups`);
2193
2250
  const cacheKindOf = cacheKindResolver(ctx.outputs);
2194
2251
  const tagManifest = {};
2252
+ const scopesKeysByRoute = nextScopesKeysByRoute(ctx.projectDir);
2253
+ const unscopedRoutes = [];
2195
2254
  for (const [basePath, variants] of prerenderGroups) {
2196
2255
  try {
2197
2256
  const kind = cacheKindOf(variants);
@@ -2212,12 +2271,12 @@ async function writeInitCache(ctx, cacheDir) {
2212
2271
  }
2213
2272
  let cacheEntry;
2214
2273
  if (kind === CachedRouteKind2.APP_PAGE) {
2215
- const html = await readPrerenderAsText(htmlPrerender);
2274
+ const html = (await readPrerender(htmlPrerender))?.toString();
2216
2275
  if (!html) {
2217
2276
  debug(`SKIP: No HTML content for APP_PAGE ${basePath}`);
2218
2277
  continue;
2219
2278
  }
2220
- const rscData = await readPrerenderAsBuffer(rscPrerender);
2279
+ const rscData = await readPrerender(rscPrerender);
2221
2280
  const segmentData = await getSegmentData(segmentPrerenders);
2222
2281
  const headers = appPageCacheHeaders(
2223
2282
  htmlPrerender?.fallback?.initialHeaders || rscPrerender?.fallback?.initialHeaders || {}
@@ -2238,7 +2297,7 @@ async function writeInitCache(ctx, cacheDir) {
2238
2297
  }
2239
2298
  };
2240
2299
  } else if (kind === CachedRouteKind2.APP_ROUTE) {
2241
- const body = await readPrerenderAsBuffer(htmlPrerender);
2300
+ const body = await readPrerender(htmlPrerender);
2242
2301
  if (!body) {
2243
2302
  debug(`SKIP: No body content for APP_ROUTE ${basePath}`);
2244
2303
  continue;
@@ -2261,12 +2320,12 @@ async function writeInitCache(ctx, cacheDir) {
2261
2320
  );
2262
2321
  continue;
2263
2322
  }
2264
- const html = await readPrerenderAsText(htmlPrerender);
2323
+ const html = (await readPrerender(htmlPrerender))?.toString();
2265
2324
  if (!html) {
2266
2325
  debug(`SKIP: No HTML content for PAGES ${basePath}`);
2267
2326
  continue;
2268
2327
  }
2269
- const pageDataJson = await readPrerenderAsText(dataPrerender);
2328
+ const pageDataJson = (await readPrerender(dataPrerender))?.toString();
2270
2329
  cacheEntry = {
2271
2330
  lastModified: Date.now(),
2272
2331
  value: {
@@ -2287,14 +2346,16 @@ async function writeInitCache(ctx, cacheDir) {
2287
2346
  debug(`SKIP: Unsupported route kind ${kind} for ${basePath}`);
2288
2347
  continue;
2289
2348
  }
2290
- const cacheKey = prerenderPathToCacheKey(
2291
- basePath,
2292
- ctx.config.basePath || ""
2349
+ const routeCacheKey = routeCacheKeyFromFilePath(
2350
+ prerenderFilePath(htmlPrerender)
2293
2351
  );
2294
- const cacheFilePath = join2(cacheDir, `${cacheKey}.json`);
2295
- await mkdir2(join2(cacheDir, cacheKey.split("/").slice(0, -1).join("/")), {
2296
- recursive: true
2297
- });
2352
+ if (!routeCacheKey && scopesKeysByRoute) {
2353
+ unscopedRoutes.push(basePath);
2354
+ continue;
2355
+ }
2356
+ const cacheKey = routeCacheKey ?? prerenderPathToCacheKey(basePath, ctx.config.basePath || "");
2357
+ const cacheFilePath = join2(cacheDir, cacheObjectName(cacheKey));
2358
+ await mkdir2(dirname2(cacheFilePath), { recursive: true });
2298
2359
  await writeFile2(cacheFilePath, serializeCacheValue(cacheEntry));
2299
2360
  for (const tag of headerTags(cacheEntry.value)) {
2300
2361
  (tagManifest[tag] ??= []).push(cacheKey);
@@ -2304,6 +2365,11 @@ async function writeInitCache(ctx, cacheDir) {
2304
2365
  console.error(`Error processing prerender group ${basePath}:`, error);
2305
2366
  }
2306
2367
  }
2368
+ if (unscopedRoutes.length > 0) {
2369
+ throw new Error(
2370
+ `${LOG_PREFIX} Next.js ${ctx.nextVersion} wrote ${unscopedRoutes.length} prerender(s) outside <distDir>/server/route-cache/, so cdk-nextjs cannot tell the cache key it reads them back with: ${unscopedRoutes.slice(0, 5).join(", ")}${unscopedRoutes.length > 5 ? ", ..." : ""}`
2371
+ );
2372
+ }
2307
2373
  const taggedKeys = Object.keys(tagManifest).length;
2308
2374
  if (taggedKeys > 0) {
2309
2375
  await writeFile2(
@@ -2313,23 +2379,28 @@ async function writeInitCache(ctx, cacheDir) {
2313
2379
  debug(`Wrote ${INIT_CACHE_TAG_MANIFEST} with ${taggedKeys} tags`);
2314
2380
  }
2315
2381
  }
2316
- async function readPrerenderAsText(prerender) {
2317
- if (prerender?.fallback && "filePath" in prerender.fallback && prerender.fallback.filePath && existsSync2(prerender.fallback.filePath)) {
2318
- return readFile(prerender.fallback.filePath, "utf-8");
2382
+ function nextScopesKeysByRoute(projectDir) {
2383
+ try {
2384
+ createRequire2(join2(projectDir, "package.json")).resolve(
2385
+ "next/dist/server/lib/route-cache-key.js"
2386
+ );
2387
+ return true;
2388
+ } catch {
2389
+ return false;
2319
2390
  }
2320
- return void 0;
2321
2391
  }
2322
- async function readPrerenderAsBuffer(prerender) {
2323
- if (prerender?.fallback && "filePath" in prerender.fallback && prerender.fallback.filePath && existsSync2(prerender.fallback.filePath)) {
2324
- return readFile(prerender.fallback.filePath);
2325
- }
2326
- return void 0;
2392
+ function prerenderFilePath(prerender) {
2393
+ return prerender?.fallback && "filePath" in prerender.fallback ? prerender.fallback.filePath : void 0;
2394
+ }
2395
+ async function readPrerender(prerender) {
2396
+ const filePath = prerenderFilePath(prerender);
2397
+ return filePath && existsSync2(filePath) ? readFile(filePath) : void 0;
2327
2398
  }
2328
2399
  async function getSegmentData(segmentPrerenders) {
2329
2400
  const segmentData = /* @__PURE__ */ new Map();
2330
2401
  for (const segmentPrerender of segmentPrerenders) {
2331
- if (segmentPrerender.fallback && "filePath" in segmentPrerender.fallback && segmentPrerender.fallback.filePath && existsSync2(segmentPrerender.fallback.filePath)) {
2332
- const segmentContent = await readFile(segmentPrerender.fallback.filePath);
2402
+ const segmentContent = await readPrerender(segmentPrerender);
2403
+ if (segmentContent) {
2333
2404
  const segmentName = "/" + segmentPrerender.pathname.split(".segments/")[1].replace(/\.segment\.rsc$/, "");
2334
2405
  segmentData.set(segmentName, segmentContent);
2335
2406
  }