vinext 1.3.1 → 1.4.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.
- package/README.md +5 -2
- package/THIRD_PARTY_LICENSES.md +47 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -2
- package/dist/build/prerender.js +1 -1
- package/dist/build/report.d.ts +0 -14
- package/dist/build/report.js +1 -51
- package/dist/check.js +1 -1
- package/dist/cli.js +3 -0
- package/dist/client/dev-error-overlay.js +1 -1
- package/dist/client/navigation-runtime.d.ts +13 -1
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/next-config.d.ts +19 -0
- package/dist/config/next-config.js +64 -1
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-codec.js +327 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-data.js +123 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/index.js +29 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/internal.js +134 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-codec.js +50 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data-generated.js +455 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data.js +147 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/big5-added.json.js +169 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp936.json.js +2584 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp949.json.js +2220 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp950.json.js +227 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/eucjp.json.js +425 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gb18030-ranges.json.js +427 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gbk-added.json.js +229 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/shiftjis.json.js +292 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf16.js +106 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf7.js +182 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/bom-handling.js +42 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/extend-node.js +147 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/index.js +94 -0
- package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/streams.js +95 -0
- package/dist/deps/.pnpm/safer-buffer@2.1.2/deps/safer-buffer/safer.js +45 -0
- package/dist/entries/app-rsc-entry.js +17 -48
- package/dist/entries/app-rsc-manifest.js +3 -4
- package/dist/entries/app-ssr-entry.js +1 -0
- package/dist/entries/pages-server-entry.js +33 -8
- package/dist/index.js +83 -70
- package/dist/init-cloudflare.d.ts +1 -3
- package/dist/init-cloudflare.js +10 -48
- package/dist/init-platform.d.ts +2 -0
- package/dist/init-platform.js +4 -0
- package/dist/plugins/extensionless-dynamic-import.js +6 -2
- package/dist/plugins/next-intl-catalog.d.ts +12 -0
- package/dist/plugins/next-intl-catalog.js +85 -0
- package/dist/plugins/pages-node-externals.d.ts +1 -0
- package/dist/plugins/pages-node-externals.js +1 -1
- package/dist/plugins/use-cache-callable.d.ts +1 -1
- package/dist/plugins/use-cache-callable.js +136 -94
- package/dist/routing/app-route-graph.d.ts +6 -0
- package/dist/routing/app-route-graph.js +9 -1
- package/dist/server/api-handler.js +19 -4
- package/dist/server/app-bfcache-identity.js +1 -1
- package/dist/server/app-browser-entry.js +79 -63
- package/dist/server/app-browser-history-controller.d.ts +5 -4
- package/dist/server/app-browser-history-controller.js +93 -15
- package/dist/server/app-browser-navigation-controller.d.ts +2 -2
- package/dist/server/app-browser-navigation-controller.js +1 -2
- package/dist/server/app-browser-rsc-redirect.js +4 -4
- package/dist/server/app-browser-server-action-client.d.ts +2 -0
- package/dist/server/app-browser-server-action-client.js +4 -3
- package/dist/server/app-browser-state.d.ts +2 -4
- package/dist/server/app-browser-state.js +9 -4
- package/dist/server/app-browser-supplemental-refresh.js +16 -1
- package/dist/server/app-browser-visible-commit.d.ts +1 -1
- package/dist/server/app-browser-visible-commit.js +2 -12
- package/dist/server/app-elements-wire.d.ts +6 -38
- package/dist/server/app-elements-wire.js +4 -41
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-history-state.d.ts +2 -2
- package/dist/server/app-history-state.js +5 -2
- package/dist/server/app-mounted-layouts.d.ts +21 -0
- package/dist/server/app-mounted-layouts.js +67 -0
- package/dist/server/app-optimistic-routing.d.ts +19 -1
- package/dist/server/app-optimistic-routing.js +42 -2
- package/dist/server/app-page-boundary-render.js +7 -6
- package/dist/server/app-page-boundary.js +2 -2
- package/dist/server/app-page-cache-finalizer.d.ts +12 -1
- package/dist/server/app-page-cache-finalizer.js +70 -29
- package/dist/server/app-page-cache.d.ts +13 -1
- package/dist/server/app-page-cache.js +31 -9
- package/dist/server/app-page-dispatch.d.ts +5 -10
- package/dist/server/app-page-dispatch.js +84 -154
- package/dist/server/app-page-element-builder.d.ts +2 -2
- package/dist/server/app-page-element-builder.js +10 -9
- package/dist/server/app-page-execution.d.ts +17 -52
- package/dist/server/app-page-execution.js +99 -95
- package/dist/server/app-page-head.d.ts +13 -0
- package/dist/server/app-page-head.js +44 -16
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +1 -0
- package/dist/server/app-page-http-access-fallback-metadata.js +2 -2
- package/dist/server/app-page-layout-gate.d.ts +35 -0
- package/dist/server/app-page-layout-gate.js +50 -0
- package/dist/server/app-page-params.d.ts +0 -1
- package/dist/server/app-page-params.js +1 -14
- package/dist/server/app-page-probe.d.ts +3 -21
- package/dist/server/app-page-probe.js +4 -37
- package/dist/server/app-page-render.d.ts +2 -12
- package/dist/server/app-page-render.js +89 -220
- package/dist/server/app-page-request.d.ts +1 -3
- package/dist/server/app-page-request.js +1 -1
- package/dist/server/app-page-response.js +3 -3
- package/dist/server/app-page-route-wiring.d.ts +13 -6
- package/dist/server/app-page-route-wiring.js +67 -61
- package/dist/server/app-page-rsc-render-status.d.ts +27 -1
- package/dist/server/app-page-rsc-render-status.js +121 -13
- package/dist/server/app-page-stream.d.ts +2 -0
- package/dist/server/app-page-stream.js +2 -2
- package/dist/server/app-pages-bridge.d.ts +4 -0
- package/dist/server/app-pages-bridge.js +7 -3
- package/dist/server/app-render-dependency.d.ts +11 -4
- package/dist/server/app-render-dependency.js +86 -38
- package/dist/server/app-request-stage-dispatch.js +1 -1
- package/dist/server/app-route-handler-cache.js +2 -2
- package/dist/server/app-route-handler-dispatch.js +14 -9
- package/dist/server/app-route-handler-execution.js +8 -3
- package/dist/server/app-route-handler-policy.d.ts +1 -1
- package/dist/server/app-route-handler-policy.js +3 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-handler-runtime.js +17 -4
- package/dist/server/app-rsc-cache-busting.d.ts +2 -2
- package/dist/server/app-rsc-cache-busting.js +4 -4
- package/dist/server/app-rsc-error-handler.d.ts +2 -1
- package/dist/server/app-rsc-error-handler.js +9 -1
- package/dist/server/app-rsc-errors.d.ts +8 -0
- package/dist/server/app-rsc-errors.js +8 -1
- package/dist/server/app-rsc-handler.d.ts +5 -5
- package/dist/server/app-rsc-handler.js +30 -24
- package/dist/server/app-rsc-request-normalization.d.ts +0 -4
- package/dist/server/app-rsc-request-normalization.js +1 -4
- package/dist/server/app-rsc-response-finalizer.js +2 -2
- package/dist/server/app-rsc-response-stage.js +5 -3
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +7 -4
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +9 -8
- package/dist/server/app-ssr-stream.js +1 -1
- package/dist/server/app-visited-response-cache.d.ts +36 -1
- package/dist/server/app-visited-response-cache.js +46 -5
- package/dist/server/app-worker-stages.d.ts +25 -3
- package/dist/server/app-worker-stages.js +45 -6
- package/dist/server/artifact-compatibility.d.ts +0 -1
- package/dist/server/artifact-compatibility.js +1 -10
- package/dist/server/cache-proof.d.ts +1 -115
- package/dist/server/cache-proof.js +1 -544
- package/dist/server/cacheability-request.js +13 -8
- package/dist/server/dev-server.js +4 -3
- package/dist/server/headers.d.ts +6 -3
- package/dist/server/headers.js +9 -5
- package/dist/server/isr-cache.d.ts +8 -2
- package/dist/server/isr-cache.js +26 -1
- package/dist/server/metadata-route-response.js +32 -15
- package/dist/server/multi-stage.d.ts +52 -11
- package/dist/server/multi-stage.js +13 -1
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/pages-api-route.js +28 -19
- package/dist/server/pages-media-type.d.ts +21 -1
- package/dist/server/pages-media-type.js +69 -5
- package/dist/server/pages-node-compat.js +52 -27
- package/dist/server/pages-page-handler.js +11 -20
- package/dist/server/pages-request-pipeline.d.ts +8 -0
- package/dist/server/pages-request-pipeline.js +2 -2
- package/dist/server/pages-request-stage-entry.js +14 -12
- package/dist/server/pages-response-stage-entry.js +9 -3
- package/dist/server/prod-server.js +5 -1
- package/dist/server/request-pipeline.d.ts +6 -0
- package/dist/server/request-pipeline.js +21 -1
- package/dist/server/shared-cache-identity.d.ts +23 -0
- package/dist/server/shared-cache-identity.js +36 -0
- package/dist/server/worker-stages.d.ts +16 -3
- package/dist/server/worker-stages.js +23 -4
- package/dist/shims/cache-handler.js +2 -2
- package/dist/shims/cache-request-state.d.ts +17 -20
- package/dist/shims/cache-request-state.js +16 -21
- package/dist/shims/cache-runtime.js +27 -11
- package/dist/shims/cache.d.ts +8 -7
- package/dist/shims/cache.js +17 -19
- package/dist/shims/cacheability-classification.d.ts +2 -0
- package/dist/shims/cdn-cache.d.ts +22 -5
- package/dist/shims/client-page-root.js +2 -2
- package/dist/shims/dynamic.js +9 -6
- package/dist/shims/error-boundary.js +2 -2
- package/dist/shims/fetch-cache-state.d.ts +5 -1
- package/dist/shims/fetch-cache-state.js +1 -3
- package/dist/shims/fetch-cache.d.ts +2 -1
- package/dist/shims/fetch-cache.js +34 -24
- package/dist/shims/form.js +2 -2
- package/dist/shims/headers.d.ts +8 -23
- package/dist/shims/headers.js +17 -67
- package/dist/shims/internal/navigation-untracked.js +1 -1
- package/dist/shims/link.js +3 -1
- package/dist/shims/metadata.d.ts +23 -21
- package/dist/shims/metadata.js +62 -24
- package/dist/shims/navigation-server.js +1 -1
- package/dist/shims/navigation.d.ts +74 -7
- package/dist/shims/navigation.js +183 -29
- package/dist/shims/navigation.react-server.js +2 -2
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/slot.d.ts +12 -0
- package/dist/shims/slot.js +37 -9
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +2 -3
- package/dist/utils/cache-control-metadata.d.ts +5 -0
- package/dist/utils/cache-control-metadata.js +9 -1
- package/dist/utils/project.d.ts +2 -2
- package/dist/utils/project.js +3 -16
- package/dist/utils/regex.d.ts +5 -0
- package/dist/utils/regex.js +11 -1
- package/dist/utils/text-stream.d.ts +10 -5
- package/dist/utils/text-stream.js +6 -6
- package/package.json +2 -1
- package/dist/build/layout-classification-types.d.ts +0 -60
- package/dist/build/layout-classification-types.js +0 -1
- package/dist/build/layout-classification.d.ts +0 -62
- package/dist/build/layout-classification.js +0 -98
- package/dist/build/route-classification-injector.d.ts +0 -33
- package/dist/build/route-classification-injector.js +0 -59
- package/dist/build/route-classification-manifest.d.ts +0 -54
- package/dist/build/route-classification-manifest.js +0 -143
- package/dist/server/app-browser-client-reuse-manifest.d.ts +0 -11
- package/dist/server/app-browser-client-reuse-manifest.js +0 -101
- package/dist/server/app-layout-param-observation.d.ts +0 -42
- package/dist/server/app-layout-param-observation.js +0 -168
- package/dist/server/client-reuse-manifest.d.ts +0 -103
- package/dist/server/client-reuse-manifest.js +0 -209
- package/dist/server/skip-cache-proof.d.ts +0 -60
- package/dist/server/skip-cache-proof.js +0 -168
- package/dist/server/static-layout-client-reuse-proof.d.ts +0 -14
- package/dist/server/static-layout-client-reuse-proof.js +0 -33
- package/dist/utils/sorted-array.d.ts +0 -8
- package/dist/utils/sorted-array.js +0 -20
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { fnv1a64 } from "../utils/hash.js";
|
|
2
|
+
import { AppElementsWire } from "./app-elements-wire.js";
|
|
3
|
+
import "./app-elements.js";
|
|
4
|
+
//#region src/server/app-mounted-layouts.ts
|
|
5
|
+
/**
|
|
6
|
+
* A client navigation names the layouts it already holds, so a render that's
|
|
7
|
+
* never stored can leave them out, as Next.js leaves out the segments above
|
|
8
|
+
* where a navigation diverges from its router state tree.
|
|
9
|
+
*
|
|
10
|
+
* Each entry hashes a layout's segment identity, which binds the layout to its
|
|
11
|
+
* route prefix and that prefix's params, so a layout whose params changed never
|
|
12
|
+
* matches.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_MOUNTED_LAYOUTS = 64;
|
|
15
|
+
const MOUNTED_LAYOUT_HASH = /^[0-9a-f]{16}$/;
|
|
16
|
+
function hashMountedLayoutIdentity(identity) {
|
|
17
|
+
return fnv1a64(identity);
|
|
18
|
+
}
|
|
19
|
+
function serializeMountedLayouts(identities) {
|
|
20
|
+
if (identities.length === 0) return null;
|
|
21
|
+
return identities.slice(0, MAX_MOUNTED_LAYOUTS).map(hashMountedLayoutIdentity).join(",");
|
|
22
|
+
}
|
|
23
|
+
function parseMountedLayoutsHeader(value) {
|
|
24
|
+
if (!value) return null;
|
|
25
|
+
const hashes = /* @__PURE__ */ new Set();
|
|
26
|
+
for (const entry of value.split(",", 65)) {
|
|
27
|
+
const hash = entry.trim();
|
|
28
|
+
if (!MOUNTED_LAYOUT_HASH.test(hash)) return null;
|
|
29
|
+
hashes.add(hash);
|
|
30
|
+
}
|
|
31
|
+
if (hashes.size === 0 || hashes.size > MAX_MOUNTED_LAYOUTS) return null;
|
|
32
|
+
return hashes;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The header for the outermost layouts a client state holds. Only that prefix
|
|
36
|
+
* can be left out of a render, as a layout below one that re-renders renders
|
|
37
|
+
* inside it.
|
|
38
|
+
*/
|
|
39
|
+
function createMountedLayoutsHeader(elements, layoutIds) {
|
|
40
|
+
const identities = AppElementsWire.readMetadata(elements).bfcacheSegmentIdentities;
|
|
41
|
+
const held = [];
|
|
42
|
+
for (const layoutId of layoutIds) {
|
|
43
|
+
const identity = identities[layoutId];
|
|
44
|
+
if (identity === void 0 || !Object.hasOwn(elements, layoutId)) break;
|
|
45
|
+
held.push(identity);
|
|
46
|
+
}
|
|
47
|
+
return serializeMountedLayouts(held);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether a client state still holds, unchanged, every layout a payload left
|
|
51
|
+
* out. A navigation that started from another state, or a payload stored for
|
|
52
|
+
* another client, can name layouts this one doesn't hold.
|
|
53
|
+
*/
|
|
54
|
+
function holdsSkippedLayouts(current, next) {
|
|
55
|
+
const nextMetadata = AppElementsWire.readMetadata(next);
|
|
56
|
+
if (nextMetadata.skippedLayoutIds.length === 0) return true;
|
|
57
|
+
const currentIdentities = AppElementsWire.readMetadata(current.elements).bfcacheSegmentIdentities;
|
|
58
|
+
const currentLayoutIds = new Set(current.layoutIds);
|
|
59
|
+
for (const layoutId of nextMetadata.skippedLayoutIds) {
|
|
60
|
+
if (!currentLayoutIds.has(layoutId) || !Object.hasOwn(current.elements, layoutId)) return false;
|
|
61
|
+
const identity = nextMetadata.bfcacheSegmentIdentities[layoutId];
|
|
62
|
+
if (identity === void 0 || identity !== currentIdentities[layoutId]) return false;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { createMountedLayoutsHeader, hashMountedLayoutIdentity, holdsSkippedLayouts, parseMountedLayoutsHeader };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BrowserRouteManifest, BrowserRouteManifestRoute } from "../routing/browser-route-manifest.js";
|
|
2
|
-
import { OptimisticRouteTrieMatch } from "../routing/optimistic-route-trie.js";
|
|
3
2
|
import { AppElements } from "./app-elements-wire.js";
|
|
4
3
|
import "./app-elements.js";
|
|
4
|
+
import { OptimisticRouteTrieMatch } from "../routing/optimistic-route-trie.js";
|
|
5
5
|
//#region src/server/app-optimistic-routing.d.ts
|
|
6
6
|
type OptimisticRouteMatch = OptimisticRouteTrieMatch<BrowserRouteManifestRoute>;
|
|
7
7
|
export type OptimisticRouteTemplate = {
|
|
@@ -90,5 +90,23 @@ export declare function resolveOptimisticNavigationPayload(options: {
|
|
|
90
90
|
routeManifest: BrowserRouteManifest;
|
|
91
91
|
templates: ReadonlyMap<string, OptimisticRouteTemplate>;
|
|
92
92
|
}): OptimisticNavigationPayload | null;
|
|
93
|
+
export type LearnedOptimisticRouteTemplate = {
|
|
94
|
+
key: string;
|
|
95
|
+
template: OptimisticRouteTemplate;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Optimistic route templates and the prefetch sources they were learned from.
|
|
99
|
+
* `clear()` retires every template, so none can outlive the prefetch payload
|
|
100
|
+
* it was derived from, and drops any result a learning started before the
|
|
101
|
+
* clear resolves to. Sources that are still cached are learned again on the
|
|
102
|
+
* next optimistic navigation.
|
|
103
|
+
*/
|
|
104
|
+
export declare function createOptimisticRouteTemplateStore(): {
|
|
105
|
+
templates: ReadonlyMap<string, OptimisticRouteTemplate>;
|
|
106
|
+
clear(): void;
|
|
107
|
+
hasSource(sourceKey: string): boolean;
|
|
108
|
+
learn(sourceKey: string, learnTemplate: () => Promise<LearnedOptimisticRouteTemplate | null>): Promise<void>;
|
|
109
|
+
pendingLearning(): Promise<void>[];
|
|
110
|
+
};
|
|
93
111
|
//#endregion
|
|
94
112
|
export {};
|
|
@@ -3,10 +3,10 @@ import { stripBasePath } from "../utils/base-path.js";
|
|
|
3
3
|
import { decodeMatchedParams, splitPathnameForRouteMatch } from "../routing/utils.js";
|
|
4
4
|
import { extractRawRoutePatternParams, matchRoutePattern } from "../routing/route-pattern.js";
|
|
5
5
|
import { buildOptimisticRouteTrie, matchOptimisticRouteTrie } from "../routing/optimistic-route-trie.js";
|
|
6
|
+
import { stripRscCacheBustingSearchParam, stripRscSuffix } from "./app-rsc-cache-busting.js";
|
|
6
7
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
7
8
|
import "./app-elements.js";
|
|
8
9
|
import { createNestedBfcacheSlotSegmentId, deriveBfcacheSegmentIdentity, isNestedBfcacheSlotSegmentId } from "./bfcache-identity.js";
|
|
9
|
-
import { stripRscCacheBustingSearchParam, stripRscSuffix } from "./app-rsc-cache-busting.js";
|
|
10
10
|
import { canonicalizeAppPageParams, resolveAppPagePatternStateKey, resolveAppPageSemanticSegmentStateKey } from "./app-page-segment-state.js";
|
|
11
11
|
import { Suspense, createElement, isValidElement } from "react";
|
|
12
12
|
//#region src/server/app-optimistic-routing.ts
|
|
@@ -310,5 +310,45 @@ function resolveOptimisticNavigationPayload(options) {
|
|
|
310
310
|
urlParts: urlParts.normalized
|
|
311
311
|
};
|
|
312
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Optimistic route templates and the prefetch sources they were learned from.
|
|
315
|
+
* `clear()` retires every template, so none can outlive the prefetch payload
|
|
316
|
+
* it was derived from, and drops any result a learning started before the
|
|
317
|
+
* clear resolves to. Sources that are still cached are learned again on the
|
|
318
|
+
* next optimistic navigation.
|
|
319
|
+
*/
|
|
320
|
+
function createOptimisticRouteTemplateStore() {
|
|
321
|
+
const templates = /* @__PURE__ */ new Map();
|
|
322
|
+
const learnedSources = /* @__PURE__ */ new Set();
|
|
323
|
+
const learning = /* @__PURE__ */ new Map();
|
|
324
|
+
let generation = 0;
|
|
325
|
+
return {
|
|
326
|
+
templates,
|
|
327
|
+
clear() {
|
|
328
|
+
generation += 1;
|
|
329
|
+
templates.clear();
|
|
330
|
+
learnedSources.clear();
|
|
331
|
+
learning.clear();
|
|
332
|
+
},
|
|
333
|
+
hasSource(sourceKey) {
|
|
334
|
+
return learnedSources.has(sourceKey) || learning.has(sourceKey);
|
|
335
|
+
},
|
|
336
|
+
learn(sourceKey, learnTemplate) {
|
|
337
|
+
const learnedGeneration = generation;
|
|
338
|
+
const promise = learnTemplate().then((learned) => {
|
|
339
|
+
if (learned === null || learnedGeneration !== generation) return;
|
|
340
|
+
templates.set(learned.key, learned.template);
|
|
341
|
+
learnedSources.add(sourceKey);
|
|
342
|
+
}).finally(() => {
|
|
343
|
+
if (learning.get(sourceKey) === promise) learning.delete(sourceKey);
|
|
344
|
+
});
|
|
345
|
+
learning.set(sourceKey, promise);
|
|
346
|
+
return promise;
|
|
347
|
+
},
|
|
348
|
+
pendingLearning() {
|
|
349
|
+
return [...learning.values()];
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
313
353
|
//#endregion
|
|
314
|
-
export { canCommitOptimisticRouteTemplate, createOptimisticRouteElements, createOptimisticRouteTemplate, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, keepOptimisticStreamingMetadata, matchOptimisticRouteManifestRoute, resolveOptimisticNavigationParamsForHref, resolveOptimisticNavigationPayload };
|
|
354
|
+
export { canCommitOptimisticRouteTemplate, createOptimisticRouteElements, createOptimisticRouteTemplate, createOptimisticRouteTemplateStore, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, keepOptimisticStreamingMetadata, matchOptimisticRouteManifestRoute, resolveOptimisticNavigationParamsForHref, resolveOptimisticNavigationPayload };
|
|
@@ -2,26 +2,26 @@ import { stripBasePath } from "../utils/base-path.js";
|
|
|
2
2
|
import { isNavigationSignalError } from "../utils/navigation-signal.js";
|
|
3
3
|
import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
|
|
4
4
|
import { isRenderDynamicLatched, isStaticGenerationAccessError } from "../shims/internal/headers-state.js";
|
|
5
|
+
import { copyLinkHeaderProvenance } from "./app-response-header-provenance.js";
|
|
6
|
+
import { stripRscSuffix } from "./app-rsc-cache-busting.js";
|
|
5
7
|
import { createArtifactCompatibilityEnvelope } from "./artifact-compatibility.js";
|
|
6
8
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
7
9
|
import "./app-elements.js";
|
|
8
10
|
import DefaultGlobalError from "../shims/default-global-error.js";
|
|
9
11
|
import { ErrorBoundary, GlobalErrorBoundary, SerializedErrorBoundary } from "../shims/error-boundary.js";
|
|
12
|
+
import { buildClientHookErrorMessage } from "../shims/client-hook-error.js";
|
|
10
13
|
import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
|
|
11
14
|
import { withUseCacheLayoutMarker } from "../shims/internal/app-page-props-cache-key.js";
|
|
12
15
|
import { MetadataHead, ViewportHead } from "../shims/metadata.js";
|
|
13
|
-
import { stripRscSuffix } from "./app-rsc-cache-busting.js";
|
|
14
|
-
import { copyLinkHeaderProvenance } from "./app-response-header-provenance.js";
|
|
15
16
|
import { buildRscRedirectFlightStream } from "./app-rsc-redirect-flight.js";
|
|
16
17
|
import { bufferAppPageBinaryStream, buildAppPageSpecialErrorResponse, resolveAppPageSpecialError } from "./app-page-execution.js";
|
|
17
|
-
import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
18
|
-
import { createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage } from "./app-page-route-wiring.js";
|
|
19
|
-
import { buildClientHookErrorMessage } from "../shims/client-hook-error.js";
|
|
20
18
|
import { createAppPageMetadataSearchParamsObserver } from "./app-page-search-params-observation.js";
|
|
21
|
-
import {
|
|
19
|
+
import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
20
|
+
import { hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveInterceptLayoutsBesidePage, slotInterceptKeepsSourceHead } from "./app-page-head.js";
|
|
22
21
|
import { renderAppPageBoundaryResponse, resolveAppPageErrorBoundary, resolveAppPageHttpAccessBoundaryModule, wrapAppPageBoundaryElement } from "./app-page-boundary.js";
|
|
23
22
|
import { createRouteNotFoundHeadPlanOptions, recoverAppPageRouteHead, resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport } from "./app-page-http-access-fallback-metadata.js";
|
|
24
23
|
import "./app-rsc-route-matching.js";
|
|
24
|
+
import { createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage } from "./app-page-route-wiring.js";
|
|
25
25
|
import { resolveSlotParamOverrides } from "./app-page-element-builder.js";
|
|
26
26
|
import { createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlResponse } from "./app-page-stream.js";
|
|
27
27
|
import { Fragment, Suspense, createElement } from "react";
|
|
@@ -301,6 +301,7 @@ async function renderAppPageHttpAccessFallback(options) {
|
|
|
301
301
|
head: {
|
|
302
302
|
layoutModules: intercept?.interceptLayouts ?? [],
|
|
303
303
|
layoutParams: (intercept?.interceptLayoutSegments ?? []).map((layoutSegments) => resolveAppPageBranchParams(intercept?.interceptBranchSegments ?? layoutSegments, layoutSegments.length, effectiveParams, layoutSegments)),
|
|
304
|
+
layoutsBesidePage: resolveInterceptLayoutsBesidePage(intercept?.interceptLayoutSegments, intercept?.interceptBranchSegments),
|
|
304
305
|
pageModule: intercept?.interceptPage ?? null,
|
|
305
306
|
params: effectiveParams,
|
|
306
307
|
routeSegments: intercept?.interceptSourcePageSegments ?? segments
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { VINEXT_RSC_VARY_HEADER } from "./app-rsc-vary.js";
|
|
2
2
|
import { runWithFetchDedupe } from "../shims/fetch-cache.js";
|
|
3
|
-
import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader } from "./app-rsc-cache-busting.js";
|
|
4
3
|
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
4
|
+
import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader } from "./app-rsc-cache-busting.js";
|
|
5
|
+
import { isAppPageRouteGroupSegment } from "./app-page-segment-state.js";
|
|
5
6
|
import { applyEdgeRuntimeHeader } from "./app-page-response.js";
|
|
6
7
|
import { resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
7
|
-
import { isAppPageRouteGroupSegment } from "./app-page-segment-state.js";
|
|
8
8
|
//#region src/server/app-page-boundary.ts
|
|
9
9
|
function resolveAppPageHttpAccessBoundaryComponent(options) {
|
|
10
10
|
return options.getDefaultExport(resolveAppPageHttpAccessBoundaryModule({
|
|
@@ -2,6 +2,7 @@ import { RenderObservation } from "./cache-proof.js";
|
|
|
2
2
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
3
3
|
import { AppPageRenderObservationState } from "./app-page-render-observation.js";
|
|
4
4
|
import { AppPageCacheSetter } from "./isr-cache.js";
|
|
5
|
+
import { CdnAppPageDocument } from "../shims/cdn-cache.js";
|
|
5
6
|
import { AppPageRscRenderStatus } from "./app-page-rsc-render-status.js";
|
|
6
7
|
//#region src/server/app-page-cache-finalizer.d.ts
|
|
7
8
|
type AppPageDebugLogger = (event: string, detail: string) => void;
|
|
@@ -30,6 +31,8 @@ type FinalizeAppPageCacheabilityEvaluationOptions = {
|
|
|
30
31
|
/** An RSC render's status, from its document's shell. */
|
|
31
32
|
resolveRscRenderStatus?: () => Promise<AppPageRscRenderStatus>;
|
|
32
33
|
revalidateSeconds: number | null;
|
|
34
|
+
/** Hands the CDN adapter the page's document to store beside the response. */
|
|
35
|
+
captureAdmittedDocument?: (document: CdnAppPageDocument | null) => void;
|
|
33
36
|
};
|
|
34
37
|
type FinalizeAppPageHtmlCacheResponseOptions = {
|
|
35
38
|
bypassInterceptionContextCache?: boolean;
|
|
@@ -66,15 +69,20 @@ type FinalizeAppPageHtmlCacheResponseOptions = {
|
|
|
66
69
|
};
|
|
67
70
|
type ScheduleAppPageRscCacheWriteOptions = {
|
|
68
71
|
bypassInterceptionContextCache?: boolean;
|
|
72
|
+
/** Hands the CDN adapter the page's document to store beside the response. */
|
|
73
|
+
captureAdmittedDocument?: (document: CdnAppPageDocument | null) => void;
|
|
69
74
|
capturedRscDataPromise: Promise<ArrayBuffer> | null;
|
|
70
75
|
cleanPathname: string;
|
|
71
76
|
consumeDynamicUsage: () => boolean;
|
|
72
77
|
consumeRenderObservationState?: () => AppPageRenderObservationState;
|
|
78
|
+
/** Observes the document the render's status resolution rendered, if any. */
|
|
79
|
+
createHtmlRenderObservation?: BuildAppPageCacheRenderObservation;
|
|
73
80
|
createRscRenderObservation: BuildAppPageCacheRenderObservation;
|
|
74
81
|
dynamicUsedDuringBuild: boolean;
|
|
75
82
|
getPageTags: () => string[];
|
|
76
83
|
getRequestCacheLife?: () => AppPageRequestCacheLife | null;
|
|
77
84
|
isrDebug?: AppPageDebugLogger;
|
|
85
|
+
isrHtmlKey?: (pathname: string) => string;
|
|
78
86
|
isrRscKey: AppPageRscCacheKeyBuilder;
|
|
79
87
|
isrSet: AppPageCacheSetter;
|
|
80
88
|
interceptionContext?: string | null;
|
|
@@ -82,7 +90,10 @@ type ScheduleAppPageRscCacheWriteOptions = {
|
|
|
82
90
|
mountedSlotsHeader?: string | null;
|
|
83
91
|
omitPendingDynamicCacheState?: boolean;
|
|
84
92
|
renderMode?: AppRscRenderMode;
|
|
85
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* The render's status, from its document's shell, and the page's document
|
|
95
|
+
* when it rendered one to store. Without it the render is a 200.
|
|
96
|
+
*/
|
|
86
97
|
resolveRscRenderStatus?: () => Promise<AppPageRscRenderStatus>;
|
|
87
98
|
preserveClientResponseHeaders?: boolean;
|
|
88
99
|
expireSeconds?: number;
|
|
@@ -67,55 +67,79 @@ function applyRscRenderStatus(outcome, renderStatus) {
|
|
|
67
67
|
...renderStatus.headers ? { headers: renderStatus.headers } : {}
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
+
/** Hand an adapter the document; its failure never holds back admission. */
|
|
71
|
+
function settleAdmittedDocument(capture, document) {
|
|
72
|
+
try {
|
|
73
|
+
capture?.(document);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error("[vinext] CDN adapter document capture error:", error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
70
78
|
function finalizeEvaluatedAppPageResponse(response, options) {
|
|
71
79
|
if (!isRouteCacheabilityEvaluation()) return null;
|
|
72
80
|
const complete = deferRouteCacheability();
|
|
73
|
-
if (!complete)
|
|
81
|
+
if (!complete) {
|
|
82
|
+
settleAdmittedDocument(options.captureAdmittedDocument, null);
|
|
83
|
+
return response;
|
|
84
|
+
}
|
|
74
85
|
captureRouteCacheabilityResponsePolicy(captureCdnResponsePolicyHeaders(response.headers));
|
|
86
|
+
const admit = (renderStatus) => {
|
|
87
|
+
const observationState = options.consumeRenderObservationState?.();
|
|
88
|
+
const cacheControl = resolveAppPageCacheControl({
|
|
89
|
+
expireSeconds: options.expireSeconds,
|
|
90
|
+
requestCacheLife: options.getRequestCacheLife?.(),
|
|
91
|
+
revalidateSeconds: options.revalidateSeconds
|
|
92
|
+
});
|
|
93
|
+
const outcome = cacheControl ? {
|
|
94
|
+
cacheable: true,
|
|
95
|
+
cacheControl: appPageCacheControlHeader(cacheControl),
|
|
96
|
+
...observationState && !observationState.requestApis.includes("searchParams") ? { searchParamsUnread: true } : {},
|
|
97
|
+
...cacheControl.stale !== void 0 ? { staleTimeSeconds: cacheControl.stale } : {},
|
|
98
|
+
tags: options.getPageTags()
|
|
99
|
+
} : {
|
|
100
|
+
cacheable: false,
|
|
101
|
+
reason: "render did not produce a cache policy"
|
|
102
|
+
};
|
|
103
|
+
const admitted = renderStatus ? applyRscRenderStatus(outcome, renderStatus) : outcome;
|
|
104
|
+
settleAdmittedDocument(options.captureAdmittedDocument, admitted.cacheable && admitted.searchParamsUnread === true && renderStatus?.kind === "page" ? renderStatus.document ?? null : null);
|
|
105
|
+
complete(admitted);
|
|
106
|
+
};
|
|
75
107
|
let completed = false;
|
|
76
108
|
const finish = () => {
|
|
77
109
|
if (completed) return;
|
|
78
110
|
completed = true;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (options.capturedDynamicUsageBeforeContextCleanup?.() === true || options.consumeDynamicUsage()) outcome = {
|
|
111
|
+
let ineligible;
|
|
112
|
+
if (options.capturedDynamicUsageBeforeContextCleanup?.() === true || options.consumeDynamicUsage()) ineligible = {
|
|
82
113
|
cacheable: false,
|
|
83
114
|
dynamicUsage: true,
|
|
84
115
|
reason: "dynamic API used during render"
|
|
85
116
|
};
|
|
86
|
-
else if (options.isStaticEligible === false)
|
|
117
|
+
else if (options.isStaticEligible === false) ineligible = {
|
|
87
118
|
cacheable: false,
|
|
88
119
|
reason: "route is not statically generated"
|
|
89
120
|
};
|
|
90
|
-
else if (response.headers.has("set-cookie") || hasExplicitNonCacheableResponsePolicy(response.headers))
|
|
121
|
+
else if (response.headers.has("set-cookie") || hasExplicitNonCacheableResponsePolicy(response.headers)) ineligible = {
|
|
91
122
|
cacheable: false,
|
|
92
123
|
reason: "response explicitly opts out of shared caching"
|
|
93
124
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
outcome = cacheControl ? {
|
|
101
|
-
cacheable: true,
|
|
102
|
-
cacheControl: appPageCacheControlHeader(cacheControl),
|
|
103
|
-
...observationState && !observationState.requestApis.includes("searchParams") ? { searchParamsUnread: true } : {},
|
|
104
|
-
tags: options.getPageTags()
|
|
105
|
-
} : {
|
|
106
|
-
cacheable: false,
|
|
107
|
-
reason: "render did not produce a cache policy"
|
|
108
|
-
};
|
|
125
|
+
if (ineligible) {
|
|
126
|
+
options.consumeRenderObservationState?.();
|
|
127
|
+
settleAdmittedDocument(options.captureAdmittedDocument, null);
|
|
128
|
+
complete(ineligible);
|
|
129
|
+
return;
|
|
109
130
|
}
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
options.resolveRscRenderStatus().then((renderStatus) => complete(applyRscRenderStatus(cacheableOutcome, renderStatus)), () => complete({
|
|
113
|
-
cacheable: false,
|
|
114
|
-
reason: "render status could not be resolved"
|
|
115
|
-
}));
|
|
131
|
+
if (!options.resolveRscRenderStatus) {
|
|
132
|
+
admit();
|
|
116
133
|
return;
|
|
117
134
|
}
|
|
118
|
-
|
|
135
|
+
options.resolveRscRenderStatus().then(admit, () => {
|
|
136
|
+
options.consumeRenderObservationState?.();
|
|
137
|
+
settleAdmittedDocument(options.captureAdmittedDocument, null);
|
|
138
|
+
complete({
|
|
139
|
+
cacheable: false,
|
|
140
|
+
reason: "render status could not be resolved"
|
|
141
|
+
});
|
|
142
|
+
});
|
|
119
143
|
};
|
|
120
144
|
if (!response.body) {
|
|
121
145
|
finish();
|
|
@@ -230,6 +254,7 @@ function finalizeAppPageRscCacheResponse(response, options) {
|
|
|
230
254
|
options.capturedRscDataPromise?.catch(() => {});
|
|
231
255
|
return probeResponse;
|
|
232
256
|
}
|
|
257
|
+
settleAdmittedDocument(options.captureAdmittedDocument, null);
|
|
233
258
|
scheduleAppPageRscCacheWrite(options);
|
|
234
259
|
const isUncacheableVariant = Boolean(options.mountedSlotsHeader) || options.bypassInterceptionContextCache === true;
|
|
235
260
|
if (options.preserveClientResponseHeaders === true && !isUncacheableVariant) return response;
|
|
@@ -253,6 +278,7 @@ function scheduleAppPageRscCacheWrite(options) {
|
|
|
253
278
|
options.isrDebug?.("RSC cache write skipped (dynamic usage during render)", rscKey);
|
|
254
279
|
return;
|
|
255
280
|
}
|
|
281
|
+
const renderStatus = await options.resolveRscRenderStatus?.() ?? { kind: "page" };
|
|
256
282
|
const cacheControl = resolveAppPageCacheControl({
|
|
257
283
|
expireSeconds: options.expireSeconds,
|
|
258
284
|
requestCacheLife: options.getRequestCacheLife?.(),
|
|
@@ -272,7 +298,6 @@ function scheduleAppPageRscCacheWrite(options) {
|
|
|
272
298
|
options.isrDebug?.("RSC cache write skipped (searchParams not proven unread)", rscKey);
|
|
273
299
|
return;
|
|
274
300
|
}
|
|
275
|
-
const renderStatus = await options.resolveRscRenderStatus?.() ?? { kind: "page" };
|
|
276
301
|
if (renderStatus.kind === "unstorable") {
|
|
277
302
|
options.isrDebug?.("RSC cache write skipped (unstorable special error)", rscKey);
|
|
278
303
|
return;
|
|
@@ -282,6 +307,22 @@ function scheduleAppPageRscCacheWrite(options) {
|
|
|
282
307
|
tags: pageTags
|
|
283
308
|
});
|
|
284
309
|
options.isrDebug?.("RSC cache written", rscKey);
|
|
310
|
+
const document = renderStatus.kind === "page" ? renderStatus.document : void 0;
|
|
311
|
+
if (!document || !options.isrHtmlKey || !options.createHtmlRenderObservation) return;
|
|
312
|
+
const htmlKey = options.isrHtmlKey(options.cleanPathname);
|
|
313
|
+
const htmlRenderObservation = options.createHtmlRenderObservation({
|
|
314
|
+
cacheTags: pageTags,
|
|
315
|
+
state: observationState
|
|
316
|
+
});
|
|
317
|
+
if (!hasQueryInvariantRenderProof(htmlRenderObservation)) {
|
|
318
|
+
options.isrDebug?.("HTML cache write skipped (searchParams not proven unread)", htmlKey);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
await options.isrSet(htmlKey, buildAppPageCacheValue(document.html, void 0, 200, htmlRenderObservation, document.linkHeader ? { link: document.linkHeader } : void 0), {
|
|
322
|
+
cacheControl,
|
|
323
|
+
tags: pageTags
|
|
324
|
+
});
|
|
325
|
+
options.isrDebug?.("HTML cache written", htmlKey);
|
|
285
326
|
} catch (cacheError) {
|
|
286
327
|
console.error("[vinext] ISR RSC cache write error:", cacheError);
|
|
287
328
|
}
|
|
@@ -83,7 +83,19 @@ type ReadAppPageCacheResponseOptions = {
|
|
|
83
83
|
renderMode?: AppRscRenderMode;
|
|
84
84
|
expireSeconds?: number;
|
|
85
85
|
revalidateSeconds: number;
|
|
86
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Whether this is an RSC request Next.js renders dynamically under PPR (one
|
|
88
|
+
* that is not a prefetch, for a path not generated at build time), and so
|
|
89
|
+
* never regenerates the page's document.
|
|
90
|
+
*/
|
|
91
|
+
isDynamicRscRequest?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Renders the page for its cache entries. `renderDocument` asks for the
|
|
94
|
+
* document as an HTML request renders it, to be stored beside the RSC payload.
|
|
95
|
+
*/
|
|
96
|
+
renderFreshPageForCache: (options: {
|
|
97
|
+
renderDocument: boolean;
|
|
98
|
+
}) => Promise<AppPageCacheRenderResult>;
|
|
87
99
|
scheduleBackgroundRegeneration: AppPageBackgroundRegenerator;
|
|
88
100
|
};
|
|
89
101
|
type ReadAppPageFallbackShellCacheResponseOptions = {
|
|
@@ -6,12 +6,12 @@ import { buildAppPageCacheValue, isrCacheControl, resolveRouteExpireSeconds } fr
|
|
|
6
6
|
import { resolveClientStaleTimeSeconds } from "../utils/cache-control-metadata.js";
|
|
7
7
|
import { encodeCacheTag } from "../utils/encode-cache-tag.js";
|
|
8
8
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
9
|
-
import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader } from "./app-rsc-cache-busting.js";
|
|
10
|
-
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
11
9
|
import { markFrameworkLinkHeaders } from "./app-response-header-provenance.js";
|
|
12
|
-
import {
|
|
10
|
+
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
11
|
+
import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader } from "./app-rsc-cache-busting.js";
|
|
13
12
|
import { isAppPprDynamicFallbackShellHtml } from "./app-ppr-fallback-shell.js";
|
|
14
13
|
import { hasQueryInvariantRenderProof } from "./cache-proof.js";
|
|
14
|
+
import { applyClientStaleTimeHeader, applyEdgeRuntimeHeader } from "./app-page-response.js";
|
|
15
15
|
import { finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse, scheduleAppPageRscCacheWrite } from "./app-page-cache-finalizer.js";
|
|
16
16
|
import { buildPageCacheTags } from "./implicit-tags.js";
|
|
17
17
|
//#region src/server/app-page-cache.ts
|
|
@@ -92,6 +92,25 @@ function resolveRegenerationFailureCacheControl(previous) {
|
|
|
92
92
|
...previous.stale === void 0 ? {} : { stale: previous.stale }
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Next.js stamps every prerender with the stale time it collected. A render
|
|
97
|
+
* that made no `cacheLife` claim keeps the `INFINITE_CACHE` sentinel, which
|
|
98
|
+
* resolves to `experimental.staleTimes.static` (`createSelectStaleTime` in
|
|
99
|
+
* `server/app-render/stale-time.ts`, applied by
|
|
100
|
+
* `applyMetadataFromPrerenderResult` in `app-render.tsx`). Its client only
|
|
101
|
+
* caches a navigation response for that long under
|
|
102
|
+
* `experimental.cachedNavigations` (`fetch-server-response.ts`); otherwise the
|
|
103
|
+
* value only dates prefetches. vinext's client reuses navigation responses for
|
|
104
|
+
* any advertised stale time, so the unclaimed value is only advertised when
|
|
105
|
+
* cached navigations are on.
|
|
106
|
+
*/
|
|
107
|
+
function resolveUnclaimedPrerenderStaleTimeSeconds(isRoutePPREnabled) {
|
|
108
|
+
if (isRoutePPREnabled !== true) return void 0;
|
|
109
|
+
if (String(process.env.__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS) !== "true") return void 0;
|
|
110
|
+
const raw = process.env.__NEXT_CLIENT_ROUTER_STATIC_STALETIME;
|
|
111
|
+
const seconds = raw === void 0 || raw === "" ? 300 : Number(raw);
|
|
112
|
+
return Number.isFinite(seconds) && seconds >= 0 ? Math.floor(seconds) : void 0;
|
|
113
|
+
}
|
|
95
114
|
function isRedirectStatus(status) {
|
|
96
115
|
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
97
116
|
}
|
|
@@ -108,7 +127,7 @@ function buildAppPageCachedResponse(cachedValue, options) {
|
|
|
108
127
|
expireSeconds: options.expireSeconds,
|
|
109
128
|
cacheControlMeta: options.cacheControl
|
|
110
129
|
});
|
|
111
|
-
const staleTimeSeconds = resolveClientStaleTimeSeconds(options.cacheControl);
|
|
130
|
+
const staleTimeSeconds = resolveClientStaleTimeSeconds(options.cacheControl) ?? resolveUnclaimedPrerenderStaleTimeSeconds(options.isRoutePPREnabled);
|
|
112
131
|
if (options.isRscRequest) {
|
|
113
132
|
if (!cachedValue.rscData) return null;
|
|
114
133
|
const rscHeaders = buildAppPageCachedHeaders({
|
|
@@ -276,8 +295,10 @@ async function readAppPageCacheResponse(options) {
|
|
|
276
295
|
console.error(`[vinext] Failed to keep the previous entry for ${key}:`, storeError);
|
|
277
296
|
}
|
|
278
297
|
};
|
|
298
|
+
const regeneratesDocument = !options.isRscRequest || options.isDynamicRscRequest !== true && isrKey === options.isrRscKey(options.cleanPathname, null);
|
|
299
|
+
const htmlKey = options.isRscRequest ? regeneratesDocument ? options.isrHtmlKey(options.cleanPathname) : null : isrKey;
|
|
279
300
|
const regenerate = async () => {
|
|
280
|
-
const revalidatedPage = await options.renderFreshPageForCache();
|
|
301
|
+
const revalidatedPage = await options.renderFreshPageForCache({ renderDocument: regeneratesDocument });
|
|
281
302
|
const cacheControl = resolveRegeneratedAppPageCacheControl({
|
|
282
303
|
expireSeconds: options.expireSeconds,
|
|
283
304
|
renderCacheControl: revalidatedPage.cacheControl,
|
|
@@ -285,7 +306,8 @@ async function readAppPageCacheResponse(options) {
|
|
|
285
306
|
});
|
|
286
307
|
if (options.isRoutePPREnabled !== true && (revalidatedPage.usedDynamicApi || cacheControl.revalidate === 0)) throw new Error(`Page changed from static to dynamic at runtime ${options.cleanPathname}
|
|
287
308
|
see more here https://nextjs.org/docs/messages/app-static-to-dynamic-error`);
|
|
288
|
-
|
|
309
|
+
const documentKey = options.isRscRequest && revalidatedPage.html === "" ? null : htmlKey;
|
|
310
|
+
if (!hasQueryInvariantRenderProof(revalidatedPage.rscRenderObservation) || documentKey !== null && !hasQueryInvariantRenderProof(revalidatedPage.htmlRenderObservation)) {
|
|
289
311
|
options.isrDebug?.("regen write skipped (searchParams not proven unread)", isrKey);
|
|
290
312
|
return;
|
|
291
313
|
}
|
|
@@ -304,15 +326,15 @@ see more here https://nextjs.org/docs/messages/app-static-to-dynamic-error`);
|
|
|
304
326
|
};
|
|
305
327
|
const status = revalidatedPage.status ?? 200;
|
|
306
328
|
if (!await store(rscKey, buildAppPageCacheValue("", revalidatedPage.rscData, status, revalidatedPage.rscRenderObservation, revalidatedPage.headers))) return;
|
|
307
|
-
if (
|
|
308
|
-
if (!await store(
|
|
329
|
+
if (documentKey !== null) {
|
|
330
|
+
if (!await store(documentKey, buildAppPageCacheValue(revalidatedPage.html, void 0, status, revalidatedPage.htmlRenderObservation, revalidatedPage.linkHeader ? {
|
|
309
331
|
...revalidatedPage.headers,
|
|
310
332
|
link: revalidatedPage.linkHeader
|
|
311
333
|
} : revalidatedPage.headers))) return;
|
|
312
334
|
}
|
|
313
335
|
options.isrDebug?.("regen complete", options.cleanPathname);
|
|
314
336
|
};
|
|
315
|
-
options.scheduleBackgroundRegeneration(isrKey, async () => {
|
|
337
|
+
options.scheduleBackgroundRegeneration(htmlKey ?? isrKey, async () => {
|
|
316
338
|
try {
|
|
317
339
|
await regenerate();
|
|
318
340
|
} catch (error) {
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { ClassificationReason } from "../build/layout-classification-types.js";
|
|
2
|
-
import { ClientReuseManifestParseResult } from "./client-reuse-manifest.js";
|
|
3
1
|
import { AppOutgoingElements } from "./app-elements-wire.js";
|
|
4
2
|
import "./app-elements.js";
|
|
5
3
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
6
4
|
import { NavigationContext } from "../shims/navigation-context-state.js";
|
|
7
5
|
import "../shims/navigation.js";
|
|
8
|
-
import { AppPageFontPreload
|
|
6
|
+
import { AppPageFontPreload } from "./app-page-execution.js";
|
|
9
7
|
import { AppPageMiddlewareContext } from "./app-page-response.js";
|
|
10
8
|
import { AppPageFallbackRouteHead, AppPagePreparedRouteHead } from "./app-page-head.js";
|
|
11
|
-
import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
|
|
12
9
|
import { RootParams } from "../shims/root-params.js";
|
|
13
10
|
import { AppPageSsrHandler } from "./app-page-stream.js";
|
|
14
11
|
import { AppRenderErrorContextOverrides } from "./app-rsc-error-handler.js";
|
|
@@ -107,8 +104,6 @@ type AppPageDispatchSlot = {
|
|
|
107
104
|
slotParamNames?: readonly string[] | null;
|
|
108
105
|
};
|
|
109
106
|
export type AppPageDispatchRoute = {
|
|
110
|
-
__buildTimeClassifications?: LayoutClassificationOptions["buildTimeClassifications"];
|
|
111
|
-
__buildTimeReasons?: LayoutClassificationOptions["buildTimeReasons"];
|
|
112
107
|
error?: AppPageModule | null;
|
|
113
108
|
errors?: readonly (AppPageModule | null | undefined)[];
|
|
114
109
|
forbidden?: AppPageModule | null;
|
|
@@ -161,9 +156,11 @@ export type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
|
161
156
|
* `next.config`. Undefined falls back to the React default downstream.
|
|
162
157
|
*/
|
|
163
158
|
reactMaxHeadersLength?: number;
|
|
164
|
-
buildPageElement: (route: TRoute, params: AppPageParams, opts: AppPageDispatchInterceptOptions | undefined, searchParams: URLSearchParams,
|
|
159
|
+
buildPageElement: (route: TRoute, params: AppPageParams, opts: AppPageDispatchInterceptOptions | undefined, searchParams: URLSearchParams, options?: {
|
|
165
160
|
isForceStatic?: boolean;
|
|
166
161
|
isUncacheableRender?: boolean;
|
|
162
|
+
/** Layouts the navigating client holds, which the render may leave out. */
|
|
163
|
+
mountedLayouts?: ReadonlySet<string> | null;
|
|
167
164
|
observeMetadataSearchParamsAccess?: boolean;
|
|
168
165
|
observePageSearchParamsAccess?: boolean;
|
|
169
166
|
serveStreamingMetadata?: boolean;
|
|
@@ -171,12 +168,10 @@ export type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
|
171
168
|
/** Receives the route's head once the render starts resolving it. */
|
|
172
169
|
onHeadPrepared?: (head: AppPagePreparedRouteHead) => void;
|
|
173
170
|
}) => Promise<AppPageElement>;
|
|
174
|
-
clientReuseManifest?: ClientReuseManifestParseResult;
|
|
175
171
|
cleanPathname: string;
|
|
176
172
|
displayPathname?: string;
|
|
177
173
|
clearRequestContext: () => void;
|
|
178
174
|
createRscOnErrorHandler: (pathname: string, routePath: string, overrides?: AppRenderErrorContextOverrides) => AppPageBoundaryOnError;
|
|
179
|
-
debugClassification?: (layoutId: string, reason: ClassificationReason) => void;
|
|
180
175
|
draftModeSecret: string;
|
|
181
176
|
dynamicConfig?: string;
|
|
182
177
|
dynamicStaleTimeSeconds?: number;
|
|
@@ -249,7 +244,7 @@ export type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
|
249
244
|
skipStaticParamsValidation?: boolean;
|
|
250
245
|
staticParamsValidationParams?: AppPageParams;
|
|
251
246
|
rootParams?: RootParams;
|
|
252
|
-
probeLayoutAt: (layoutIndex: number
|
|
247
|
+
probeLayoutAt: (layoutIndex: number) => unknown;
|
|
253
248
|
expireSeconds?: number;
|
|
254
249
|
renderErrorBoundaryPage: (error: unknown, errorOrigin?: "rsc" | "ssr") => Promise<Response | null>;
|
|
255
250
|
renderHttpAccessFallbackPage: (statusCode: number, opts: {
|