cdk-nextjs 0.7.5 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +37 -37
- package/docs/adapter-runtime.md +1 -1
- package/docs/harness-coverage.md +9 -8
- package/lib/adapter/adapter.mjs +220 -149
- package/lib/adapter/aws-cache-store.d.ts +28 -4
- package/lib/adapter/aws-cache-store.js +52 -21
- package/lib/adapter/aws-cache-store.test.js +36 -1
- package/lib/adapter/build-outputs.js +23 -1
- package/lib/adapter/build-outputs.test.js +18 -1
- package/lib/adapter/cache-handler.d.ts +4 -3
- package/lib/adapter/cache-handler.js +1 -1
- package/lib/adapter/cache-handler.mjs +131 -61
- package/lib/adapter/cache-handler.test.js +31 -1
- package/lib/adapter/cache-utils.d.ts +68 -2
- package/lib/adapter/cache-utils.js +108 -2
- package/lib/adapter/cache-utils.test.js +45 -1
- package/lib/adapter/init-cache.d.ts +1 -1
- package/lib/adapter/init-cache.js +60 -37
- package/lib/adapter/init-cache.test.js +55 -2
- package/lib/adapter/local-file-cache-handler.d.ts +2 -2
- package/lib/adapter/local-file-cache-handler.js +4 -5
- package/lib/adapter/s3-cache-handler.d.ts +23 -7
- package/lib/adapter/s3-cache-handler.js +63 -27
- package/lib/adapter/s3-cache-handler.test.js +168 -1
- package/lib/adapter/use-cache-default-handler.mjs +32 -9
- package/lib/adapter/use-cache-remote-handler.js +2 -3
- package/lib/adapter/use-cache-remote-handler.mjs +81 -61
- package/lib/nextjs-api.js +1 -1
- package/lib/nextjs-build/nextjs-build.d.ts +0 -7
- package/lib/nextjs-build/nextjs-build.js +4 -11
- package/lib/nextjs-build/nextjs-build.test.js +4 -3
- package/lib/nextjs-cache.js +1 -1
- package/lib/nextjs-compute/nextjs-containers.js +1 -1
- package/lib/nextjs-compute/nextjs-functions.js +1 -1
- package/lib/nextjs-distribution.js +1 -1
- package/lib/nextjs-post-deploy.js +1 -1
- package/lib/nextjs-static-assets.js +1 -1
- package/lib/root-constructs/nextjs-base-construct.js +1 -1
- package/lib/root-constructs/nextjs-global-containers.js +1 -1
- package/lib/root-constructs/nextjs-global-functions.js +1 -1
- package/lib/root-constructs/nextjs-regional-containers.js +1 -1
- package/lib/root-constructs/nextjs-regional-functions.js +1 -1
- package/lib/runtime/image.d.ts +19 -0
- package/lib/runtime/image.js +28 -2
- package/lib/runtime/image.test.js +43 -1
- package/lib/runtime/lambda.mjs +14 -3
- package/lib/runtime/server.mjs +14 -3
- package/lib/utils/deployment-build-id.d.ts +11 -0
- package/lib/utils/deployment-build-id.js +15 -0
- package/package.json +3 -3
- package/scripts/e2e-harness/README.md +1 -1
package/lib/adapter/adapter.mjs
CHANGED
|
@@ -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") +
|
|
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") +
|
|
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 +
|
|
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 +
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2291
|
-
|
|
2292
|
-
ctx.config.basePath || ""
|
|
2349
|
+
const routeCacheKey = routeCacheKeyFromFilePath(
|
|
2350
|
+
prerenderFilePath(htmlPrerender)
|
|
2293
2351
|
);
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
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
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
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
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
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
|
-
|
|
2332
|
-
|
|
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
|
}
|
|
@@ -25,6 +25,8 @@ export declare function resolveAwsCacheConfig(): AwsCacheConfig;
|
|
|
25
25
|
* which is what keeps it disjoint from {@link useCacheS3Key}.
|
|
26
26
|
*/
|
|
27
27
|
export declare function buildS3Key(buildId: string, cacheKey: string): string;
|
|
28
|
+
/** `{buildId}/` + {@link cacheObjectName}: the S3 object an entry is stored in. */
|
|
29
|
+
export declare function s3ObjectKey(buildId: string, cacheKey: string): string;
|
|
28
30
|
/**
|
|
29
31
|
* The folder under the build prefix that `'use cache'` entries are stored in.
|
|
30
32
|
* @see useCacheS3Key
|
|
@@ -36,8 +38,9 @@ export declare const USE_CACHE_KEY_PREFIX = "_use-cache";
|
|
|
36
38
|
* Hashed because a `'use cache'` key is the serialized arguments of the call and
|
|
37
39
|
* has no length limit, where an S3 key stops at 1024 bytes. The `.entry` suffix
|
|
38
40
|
* rather than `.json` is what makes a collision with the incremental cache
|
|
39
|
-
* impossible rather than unlikely: every
|
|
40
|
-
* `.json`, including one for a route that happens to be named
|
|
41
|
+
* impossible rather than unlikely: every object {@link s3ObjectKey} names ends
|
|
42
|
+
* in `.json` or `.long`, including one for a route that happens to be named
|
|
43
|
+
* `/_use-cache/…`.
|
|
41
44
|
* Still under the build prefix, so post-deploy pruning drops them with the build.
|
|
42
45
|
*/
|
|
43
46
|
export declare function useCacheS3Key(buildId: string, keyHash: string): string;
|
|
@@ -113,9 +116,29 @@ export declare const BATCH_GET_MAX_KEYS = 100;
|
|
|
113
116
|
* revalidation log, the reads scale with instances instead of requests or tags.
|
|
114
117
|
*/
|
|
115
118
|
export declare const DEFAULT_TAG_REFRESH_MS = 1000;
|
|
119
|
+
/** DynamoDB's limit on a sort key, in UTF-8 bytes. */
|
|
120
|
+
export declare const MAX_SORT_KEY_BYTES = 1024;
|
|
121
|
+
/**
|
|
122
|
+
* `#<sha256(tag)>`, standing in for a tag in a sort key it would push past
|
|
123
|
+
* {@link MAX_SORT_KEY_BYTES}. Next.js caps the tags an app names, but not an
|
|
124
|
+
* implicit `_N_T_/…` tag, which is as long as its path, and a rejected write
|
|
125
|
+
* left `revalidatePath` on a long path recorded nowhere.
|
|
126
|
+
*/
|
|
127
|
+
export declare function hashedTag(tag: string): string;
|
|
128
|
+
/**
|
|
129
|
+
* The longest tag a sort key spells out; past it, {@link sortKeyTag} hashes
|
|
130
|
+
* it. One cap for every row the tag is in, so a row's writer and reader agree
|
|
131
|
+
* without each working out what else shares its key. It leaves room for the
|
|
132
|
+
* most any row puts beside the tag: a mapping row's `#<buildId>/#<sha256>`,
|
|
133
|
+
* with the build ID capped at `MAX_BUILD_ID_BYTES`.
|
|
134
|
+
*/
|
|
135
|
+
export declare const MAX_TAG_BYTES = 768;
|
|
136
|
+
/** `tag` as a sort key spells it: itself, or {@link hashedTag} past {@link MAX_TAG_BYTES}. */
|
|
137
|
+
export declare function sortKeyTag(tag: string): string;
|
|
116
138
|
/**
|
|
117
139
|
* The bare-tag marker rows of the revalidation table (`pk = buildId`,
|
|
118
|
-
* `sk = tag
|
|
140
|
+
* `sk = tag`, or {@link hashedTag} past {@link MAX_TAG_BYTES}), which record when a tag
|
|
141
|
+
* was last revalidated and how.
|
|
119
142
|
*
|
|
120
143
|
* The table also holds the incremental cache's `tag#s3Key` mapping rows, which
|
|
121
144
|
* are its own business and stay in `S3CacheHandler`: only the markers mean the
|
|
@@ -166,7 +189,8 @@ export interface RevalidationLogRow {
|
|
|
166
189
|
/**
|
|
167
190
|
* The revalidation log: one row per tag revalidation (`pk = <buildId>#log`,
|
|
168
191
|
* `sk = <Date.now(), zero-padded>#<tag>`), which expires after
|
|
169
|
-
* {@link REVALIDATION_LOG_TTL_MS}.
|
|
192
|
+
* {@link REVALIDATION_LOG_TTL_MS}. A tag past {@link MAX_TAG_BYTES} is
|
|
193
|
+
* {@link hashedTag} there, and spelled out in the row's `longTag` instead.
|
|
170
194
|
*
|
|
171
195
|
* The marker rows stay the source of truth. The log exists so an instance
|
|
172
196
|
* can ask "what was revalidated since I last looked?" in one `Query`, instead
|