vinext 1.4.1 → 1.4.3
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/dist/build/action-owner-manifest.js +1 -0
- package/dist/build/prerender-paths.d.ts +11 -1
- package/dist/build/prerender-paths.js +36 -10
- package/dist/check.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/client/dev-error-overlay.d.ts +2 -1
- package/dist/client/dev-error-overlay.js +8 -3
- package/dist/config/config-matchers.js +8 -4
- package/dist/config/destination-params.js +5 -2
- package/dist/config/next-config.d.ts +25 -1
- package/dist/config/next-config.js +31 -1
- package/dist/entries/app-rsc-entry.js +65 -30
- package/dist/entries/app-rsc-manifest.js +4 -0
- package/dist/entries/pages-server-entry.js +19 -4
- package/dist/index.js +37 -37
- package/dist/init-cloudflare.js +18 -2
- package/dist/init-platform.js +2 -2
- package/dist/init.js +42 -5
- package/dist/plugins/css-target.d.ts +18 -0
- package/dist/plugins/css-target.js +93 -0
- package/dist/routing/app-route-graph.d.ts +10 -0
- package/dist/routing/app-route-graph.js +15 -1
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +10 -8
- package/dist/server/app-action-forwarding.js +3 -1
- package/dist/server/app-browser-entry.js +37 -41
- package/dist/server/app-browser-error.d.ts +1 -1
- package/dist/server/app-browser-error.js +13 -4
- package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
- package/dist/server/app-browser-supplemental-refresh.js +36 -4
- package/dist/server/app-elements.d.ts +3 -0
- package/dist/server/app-elements.js +14 -1
- package/dist/server/app-fallback-renderer.js +2 -2
- package/dist/server/app-middleware.d.ts +2 -0
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.d.ts +12 -1
- package/dist/server/app-optimistic-routing.js +38 -16
- package/dist/server/app-page-boundary-render.js +4 -2
- package/dist/server/app-page-cache-render.js +1 -1
- package/dist/server/app-page-dispatch.d.ts +11 -1
- package/dist/server/app-page-dispatch.js +78 -31
- package/dist/server/app-page-element-builder.d.ts +11 -20
- package/dist/server/app-page-element-builder.js +39 -50
- package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
- package/dist/server/app-page-intercept-segment-config.js +90 -0
- package/dist/server/app-page-navigation-params.d.ts +23 -0
- package/dist/server/app-page-navigation-params.js +49 -0
- package/dist/server/app-page-render.d.ts +5 -2
- package/dist/server/app-page-render.js +17 -3
- package/dist/server/app-page-request.d.ts +24 -2
- package/dist/server/app-page-request.js +10 -10
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +19 -13
- package/dist/server/app-page-segment-plan.js +9 -1
- package/dist/server/app-page-segment-state.d.ts +9 -0
- package/dist/server/app-page-segment-state.js +54 -1
- package/dist/server/app-pages-bridge.d.ts +6 -0
- package/dist/server/app-pages-bridge.js +8 -3
- package/dist/server/app-request-stage-independent-entry.js +2 -3
- package/dist/server/app-response-stage-entry.js +1 -0
- package/dist/server/app-route-module-loader.d.ts +2 -0
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-router-entry.js +2 -3
- package/dist/server/app-rsc-cache-busting.d.ts +8 -0
- package/dist/server/app-rsc-cache-busting.js +13 -1
- package/dist/server/app-rsc-combined-handler.js +2 -1
- package/dist/server/app-rsc-handler.d.ts +14 -1
- package/dist/server/app-rsc-handler.js +111 -62
- package/dist/server/app-rsc-response-finalizer.d.ts +18 -3
- package/dist/server/app-rsc-response-finalizer.js +61 -7
- package/dist/server/app-rsc-route-matching.d.ts +14 -0
- package/dist/server/app-rsc-route-matching.js +42 -18
- package/dist/server/app-server-action-execution.js +16 -10
- package/dist/server/app-ssr-entry.js +8 -0
- package/dist/server/cache-control.d.ts +2 -0
- package/dist/server/cache-control.js +3 -1
- package/dist/server/config-headers.d.ts +7 -0
- package/dist/server/config-headers.js +20 -1
- package/dist/server/image-animation.d.ts +9 -0
- package/dist/server/image-animation.js +118 -0
- package/dist/server/image-local-patterns.d.ts +21 -0
- package/dist/server/image-local-patterns.js +59 -0
- package/dist/server/image-optimization.d.ts +48 -27
- package/dist/server/image-optimization.js +316 -22
- package/dist/server/image-source-reader.d.ts +34 -0
- package/dist/server/image-source-reader.js +126 -0
- package/dist/server/metadata-route-response.d.ts +6 -1
- package/dist/server/metadata-route-response.js +46 -10
- package/dist/server/middleware-matcher.d.ts +1 -0
- package/dist/server/middleware-matcher.js +1 -1
- package/dist/server/middleware-runtime.d.ts +5 -0
- package/dist/server/middleware-runtime.js +48 -15
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/nitro-public-files-plugin.js +20 -8
- package/dist/server/nitro-request-target-plugin.d.ts +17 -0
- package/dist/server/nitro-request-target-plugin.js +22 -0
- package/dist/server/open-redirect.d.ts +2 -1
- package/dist/server/open-redirect.js +2 -1
- package/dist/server/pages-client-assets.d.ts +1 -0
- package/dist/server/pages-data-route.d.ts +5 -1
- package/dist/server/pages-data-route.js +14 -6
- package/dist/server/pages-i18n.d.ts +7 -0
- package/dist/server/pages-i18n.js +14 -1
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-handler.js +4 -4
- package/dist/server/pages-request-pipeline.d.ts +9 -0
- package/dist/server/pages-request-pipeline.js +35 -15
- package/dist/server/pages-request-stage-entry.js +8 -2
- package/dist/server/pages-router-entry.js +3 -0
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +64 -62
- package/dist/server/request-pipeline.d.ts +5 -0
- package/dist/server/request-pipeline.js +11 -1
- package/dist/server/response-stage-cacheability.d.ts +7 -0
- package/dist/server/response-stage-cacheability.js +6 -5
- package/dist/server/srvx-middleware-origin.d.ts +13 -0
- package/dist/server/srvx-middleware-origin.js +19 -0
- package/dist/server/srvx-request-target.d.ts +12 -0
- package/dist/server/srvx-request-target.js +20 -0
- package/dist/server/static-file-cache.js +10 -22
- package/dist/server/worker-image-optimization.d.ts +10 -3
- package/dist/server/worker-image-optimization.js +43 -9
- package/dist/server/worker-revalidation-context.js +1 -0
- package/dist/server/worker-utils.d.ts +8 -0
- package/dist/server/worker-utils.js +19 -2
- package/dist/shims/error-boundary.d.ts +6 -2
- package/dist/shims/error-boundary.js +4 -2
- package/dist/shims/image-props.d.ts +6 -0
- package/dist/shims/image-props.js +39 -3
- package/dist/shims/image.js +3 -3
- package/dist/shims/request-context.d.ts +3 -1
- package/dist/shims/slot.d.ts +3 -0
- package/dist/shims/slot.js +67 -9
- package/dist/shims/url-utils.js +6 -1
- package/dist/utils/base-path.d.ts +7 -0
- package/dist/utils/base-path.js +11 -1
- package/dist/utils/client-entry-manifest.d.ts +5 -0
- package/dist/utils/client-entry-manifest.js +1 -0
- package/dist/utils/client-runtime-metadata.d.ts +5 -0
- package/dist/utils/client-runtime-metadata.js +33 -7
- package/package.json +1 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { collectAppPageStaticGenerationRuntimes, collectAppPageStaticParamsWalkSegments, hasAppPageGenerateStaticParamsAtLastDynamicSegment, isAppPageStaticEligible, isEdgeRuntime, resolveAppPageSegmentConfig, resolveAppPageStaticGenerationRuntime } from "./app-segment-config.js";
|
|
2
|
+
import { SIBLING_PAGE_INTERCEPT_SLOT_KEY } from "./app-rsc-route-matching.js";
|
|
3
|
+
//#region src/server/app-page-intercept-segment-config.ts
|
|
4
|
+
/**
|
|
5
|
+
* Next.js renders an interception as its own route, whose loader tree holds
|
|
6
|
+
* the owner's layouts and the intercepting page, never the source's page, so
|
|
7
|
+
* its segment config comes from those modules alone. A sibling intercept's
|
|
8
|
+
* branch takes the children position below its owner folder; a slot
|
|
9
|
+
* intercept's branch takes its slot, beside the other slots of the owner's
|
|
10
|
+
* layouts, and children renders the owner's default.
|
|
11
|
+
*/
|
|
12
|
+
function resolveAppPageInterceptSegmentConfig(route, interceptOpts) {
|
|
13
|
+
const routeLayouts = route.layouts ?? [];
|
|
14
|
+
const routeSegments = route.routeSegments ?? [];
|
|
15
|
+
const isSiblingIntercept = interceptOpts.interceptSlotKey === SIBLING_PAGE_INTERCEPT_SLOT_KEY;
|
|
16
|
+
const interceptedSlot = isSiblingIntercept ? void 0 : route.slots?.[interceptOpts.interceptSlotKey];
|
|
17
|
+
const branchSegments = interceptOpts.interceptBranchSegments ?? [];
|
|
18
|
+
const interceptLayouts = interceptOpts.interceptLayouts ?? [];
|
|
19
|
+
const interceptLayoutPositions = (interceptOpts.interceptLayoutSegments ?? []).map((segments) => segments.length);
|
|
20
|
+
const ownerPosition = isSiblingIntercept ? interceptOpts.interceptSourcePageSegments ? interceptOpts.interceptSourcePageSegments.length - branchSegments.length : routeSegments.length : interceptedSlot?.ownerTreePosition ?? route.layoutTreePositions?.[interceptedSlot?.layoutIndex ?? -1] ?? routeSegments.length;
|
|
21
|
+
const ownerLayoutIndexes = routeLayouts.flatMap((_, index) => (route.layoutTreePositions?.[index] ?? 0) <= ownerPosition ? [index] : []);
|
|
22
|
+
const ownerLayouts = ownerLayoutIndexes.map((index) => routeLayouts[index]);
|
|
23
|
+
const ownerLayoutPositions = ownerLayoutIndexes.map((index) => route.layoutTreePositions?.[index] ?? 0);
|
|
24
|
+
const ownerSegments = routeSegments.slice(0, ownerPosition);
|
|
25
|
+
const page = interceptOpts.interceptPage;
|
|
26
|
+
const retainedBranches = Object.entries(route.slots ?? {}).flatMap(([slotKey, slot]) => slotKey !== interceptOpts.interceptSlotKey && (slot.ownerTreePosition ?? route.layoutTreePositions?.[slot.layoutIndex ?? 0] ?? 0) <= ownerPosition ? [{
|
|
27
|
+
configLayouts: slot.page ? slot.configLayouts : null,
|
|
28
|
+
configLayoutTreePositions: slot.page ? slot.configLayoutTreePositions : null,
|
|
29
|
+
isDefault: !slot.page,
|
|
30
|
+
layout: slot.page ? slot.layout : null,
|
|
31
|
+
name: slot.name,
|
|
32
|
+
ownerTreePosition: slot.ownerTreePosition,
|
|
33
|
+
page: slot.page ?? slot.default,
|
|
34
|
+
routeSegments: slot.routeSegments
|
|
35
|
+
}] : []);
|
|
36
|
+
const tree = isSiblingIntercept ? {
|
|
37
|
+
layouts: [...ownerLayouts, ...interceptLayouts],
|
|
38
|
+
layoutTreePositions: [...ownerLayoutPositions, ...interceptLayoutPositions.map((position) => ownerPosition + position)],
|
|
39
|
+
page,
|
|
40
|
+
parallelBranches: retainedBranches,
|
|
41
|
+
routeSegments: [...ownerSegments, ...branchSegments]
|
|
42
|
+
} : {
|
|
43
|
+
layouts: ownerLayouts,
|
|
44
|
+
layoutTreePositions: ownerLayoutPositions,
|
|
45
|
+
page: null,
|
|
46
|
+
parallelBranches: [...retainedBranches, {
|
|
47
|
+
configLayouts: interceptLayouts,
|
|
48
|
+
configLayoutTreePositions: interceptLayoutPositions,
|
|
49
|
+
layout: interceptedSlot?.layout,
|
|
50
|
+
name: interceptedSlot?.name,
|
|
51
|
+
ownerTreePosition: ownerPosition,
|
|
52
|
+
page,
|
|
53
|
+
routeSegments: branchSegments
|
|
54
|
+
}],
|
|
55
|
+
routeSegments: ownerSegments
|
|
56
|
+
};
|
|
57
|
+
const config = resolveAppPageSegmentConfig({
|
|
58
|
+
layouts: [
|
|
59
|
+
...ownerLayouts,
|
|
60
|
+
...isSiblingIntercept ? [] : [interceptedSlot?.layout],
|
|
61
|
+
...interceptLayouts
|
|
62
|
+
],
|
|
63
|
+
page,
|
|
64
|
+
parallelBranches: retainedBranches,
|
|
65
|
+
parallelPages: retainedBranches.map((branch) => branch.page)
|
|
66
|
+
});
|
|
67
|
+
const walkSegments = collectAppPageStaticParamsWalkSegments(tree);
|
|
68
|
+
const hasGenerateStaticParams = hasAppPageGenerateStaticParamsAtLastDynamicSegment(tree);
|
|
69
|
+
const isDynamicRoute = walkSegments.some((segment) => segment.dynamic);
|
|
70
|
+
const isStaticGenerationEdgeRuntime = isEdgeRuntime(resolveAppPageStaticGenerationRuntime(collectAppPageStaticGenerationRuntimes(tree)));
|
|
71
|
+
return {
|
|
72
|
+
dynamicConfig: config.dynamicConfig,
|
|
73
|
+
dynamicStaleTimeSeconds: config.dynamicStaleTimeSeconds,
|
|
74
|
+
fetchCacheMode: config.fetchCache ?? null,
|
|
75
|
+
hasAnyGenerateStaticParams: walkSegments.some((segment) => segment.generateStaticParams),
|
|
76
|
+
hasGenerateStaticParams,
|
|
77
|
+
isEdgeRuntime: isEdgeRuntime(config.runtime),
|
|
78
|
+
isStaticEligible: isAppPageStaticEligible({
|
|
79
|
+
dynamicConfig: config.dynamicConfig,
|
|
80
|
+
hasGenerateStaticParams,
|
|
81
|
+
isDynamicRoute,
|
|
82
|
+
isStaticGenerationEdgeRuntime,
|
|
83
|
+
revalidateSeconds: config.revalidateSeconds
|
|
84
|
+
}),
|
|
85
|
+
isStaticGenerationEdgeRuntime,
|
|
86
|
+
revalidateSeconds: config.revalidateSeconds
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { resolveAppPageInterceptSegmentConfig };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AppPageParams } from "./app-page-boundary.js";
|
|
2
|
+
//#region src/server/app-page-navigation-params.d.ts
|
|
3
|
+
export type AppPageNavigationParamSlot = {
|
|
4
|
+
__loadDefault?: unknown;
|
|
5
|
+
__loadPage?: unknown;
|
|
6
|
+
default?: unknown;
|
|
7
|
+
page?: unknown;
|
|
8
|
+
slotPatternParts?: readonly string[] | null;
|
|
9
|
+
slotParamNames?: readonly string[] | null;
|
|
10
|
+
};
|
|
11
|
+
type AppPageNavigationParamRoute = {
|
|
12
|
+
params?: readonly string[] | null;
|
|
13
|
+
slots?: Readonly<Record<string, AppPageNavigationParamSlot>> | null;
|
|
14
|
+
};
|
|
15
|
+
type AppPageNavigationParamInterceptOptions = {
|
|
16
|
+
interceptPage?: unknown;
|
|
17
|
+
interceptParams?: AppPageParams | null;
|
|
18
|
+
interceptSlotKey?: string | null;
|
|
19
|
+
};
|
|
20
|
+
export declare function resolveSlotParamOverrides(route: AppPageNavigationParamRoute, routePath: string): Readonly<Record<string, AppPageParams>> | null;
|
|
21
|
+
export declare function resolveAppPageNavigationParams(route: AppPageNavigationParamRoute, routeParams: AppPageParams, routePath: string, opts?: AppPageNavigationParamInterceptOptions | null): AppPageParams;
|
|
22
|
+
//#endregion
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { matchRoutePattern } from "../routing/route-pattern.js";
|
|
2
|
+
import "./app-rsc-route-matching.js";
|
|
3
|
+
//#region src/server/app-page-navigation-params.ts
|
|
4
|
+
function resolveSlotParamOverrides(route, routePath) {
|
|
5
|
+
const overrides = {};
|
|
6
|
+
const slots = route.slots;
|
|
7
|
+
if (slots) {
|
|
8
|
+
let urlParts = null;
|
|
9
|
+
const routeParamSet = collectParamNameSet(route.params);
|
|
10
|
+
for (const [slotKey, slot] of Object.entries(slots)) {
|
|
11
|
+
const patternParts = slot.slotPatternParts;
|
|
12
|
+
const paramNames = slot.slotParamNames;
|
|
13
|
+
if (!patternParts || patternParts.length === 0) continue;
|
|
14
|
+
if (paramNames && paramNames.every((name) => routeParamSet.has(name))) continue;
|
|
15
|
+
if (urlParts === null) urlParts = routePath.split("/").filter(Boolean);
|
|
16
|
+
const matched = matchRoutePattern(urlParts, patternParts);
|
|
17
|
+
if (!matched) continue;
|
|
18
|
+
overrides[slotKey] = matched;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return Object.keys(overrides).length > 0 ? overrides : null;
|
|
22
|
+
}
|
|
23
|
+
function mergeAppPageParams(target, source) {
|
|
24
|
+
for (const [key, value] of Object.entries(source)) target[key] = value;
|
|
25
|
+
}
|
|
26
|
+
function isDefaultExportModule(module) {
|
|
27
|
+
return typeof module === "object" && module !== null;
|
|
28
|
+
}
|
|
29
|
+
function hasDefaultExport(module) {
|
|
30
|
+
if (!isDefaultExportModule(module)) return false;
|
|
31
|
+
return module?.default !== null && module?.default !== void 0;
|
|
32
|
+
}
|
|
33
|
+
function resolveAppPageNavigationParams(route, routeParams, routePath, opts) {
|
|
34
|
+
const navigationParams = { ...routeParams };
|
|
35
|
+
const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
|
|
36
|
+
for (const [slotKey, slot] of Object.entries(route.slots ?? {})) {
|
|
37
|
+
const isInterceptedSlot = opts?.interceptSlotKey === slotKey && opts.interceptSlotKey !== "__vinext_page_intercept" && hasDefaultExport(opts.interceptPage);
|
|
38
|
+
if (!isInterceptedSlot && !hasDefaultExport(slot.page) && !hasDefaultExport(slot.default) && !slot.__loadPage && !slot.__loadDefault) continue;
|
|
39
|
+
mergeAppPageParams(navigationParams, isInterceptedSlot ? opts?.interceptParams ?? routeParams : slotParamOverrides?.[slotKey] ?? routeParams);
|
|
40
|
+
}
|
|
41
|
+
return navigationParams;
|
|
42
|
+
}
|
|
43
|
+
function collectParamNameSet(params) {
|
|
44
|
+
const set = /* @__PURE__ */ new Set();
|
|
45
|
+
if (params) for (const name of params) set.add(name);
|
|
46
|
+
return set;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { resolveAppPageNavigationParams, resolveSlotParamOverrides };
|
|
@@ -150,9 +150,12 @@ type RenderAppPageLifecycleOptions = RenderAppPageLifecycleOptionsBase & ({
|
|
|
150
150
|
* static, draft mode, `force-dynamic`, `revalidate = 0`, or a dynamic API read
|
|
151
151
|
* before the response left the render. Responses that leave the render before
|
|
152
152
|
* its response policy, such as error boundaries and special errors, get the
|
|
153
|
-
* same header as the normal render.
|
|
153
|
+
* same header as the normal render. One not yet known dynamic is never stored
|
|
154
|
+
* either, and its body can still reach a dynamic API after its headers leave,
|
|
155
|
+
* so it is sent like a dynamic render (see resolveUnstoredEarlyResponseCacheControl
|
|
156
|
+
* for the responses that aren't).
|
|
154
157
|
*/
|
|
155
|
-
export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "dynamicStaleTimeSeconds" | "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">, requestCacheLife?: AppPageRequestCacheLife | null): Response;
|
|
158
|
+
export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "dynamicStaleTimeSeconds" | "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isPrerender" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">, requestCacheLife?: AppPageRequestCacheLife | null): Response;
|
|
156
159
|
export declare function renderAppPageLifecycle(options: RenderAppPageLifecycleOptions): Promise<Response>;
|
|
157
160
|
//#endregion
|
|
158
161
|
export {};
|
|
@@ -24,10 +24,10 @@ import { appendRscCompletionMetadata } from "./rsc-completion-metadata.js";
|
|
|
24
24
|
import { isBailoutToCSRError } from "../shims/navigation-errors.js";
|
|
25
25
|
import { applyClientStaleTimeHeader, applyEdgeRuntimeHeader, buildAppPageHtmlResponse, buildAppPageRscResponse, resolveAppPageHtmlResponsePolicy, resolveAppPageRscResponsePolicy, resolveUncacheableCacheControl } from "./app-page-response.js";
|
|
26
26
|
import { buildAppPageFontLinkHeader, readAppPageBinaryStream, resolveAppPageCaughtNotFoundBoundaries, resolveAppPageShellSpecialError, resolveAppPageSpecialError, resolveAppPageSpecialErrorStoredHeaders, teeAppPageRscStreamForCapture } from "./app-page-execution.js";
|
|
27
|
+
import { buildAppPageHtmlDocumentResponse, buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
|
|
27
28
|
import { getAppPageLayoutGate } from "./app-page-layout-gate.js";
|
|
28
29
|
import { hasDigest } from "./app-rsc-errors.js";
|
|
29
30
|
import { recordAppPageRenderError, traceAppPageRender } from "./app-page-tracing.js";
|
|
30
|
-
import { buildAppPageHtmlDocumentResponse, buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
|
|
31
31
|
import { createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope } from "./app-page-render-observation.js";
|
|
32
32
|
import { createAppPageRscRenderStatusResolver } from "./app-page-rsc-render-status.js";
|
|
33
33
|
import { finalizeAppPageCacheabilityEvaluationResponse, finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse } from "./app-page-cache-finalizer.js";
|
|
@@ -70,11 +70,14 @@ function applyRequestCacheLife(options) {
|
|
|
70
70
|
* static, draft mode, `force-dynamic`, `revalidate = 0`, or a dynamic API read
|
|
71
71
|
* before the response left the render. Responses that leave the render before
|
|
72
72
|
* its response policy, such as error boundaries and special errors, get the
|
|
73
|
-
* same header as the normal render.
|
|
73
|
+
* same header as the normal render. One not yet known dynamic is never stored
|
|
74
|
+
* either, and its body can still reach a dynamic API after its headers leave,
|
|
75
|
+
* so it is sent like a dynamic render (see resolveUnstoredEarlyResponseCacheControl
|
|
76
|
+
* for the responses that aren't).
|
|
74
77
|
*/
|
|
75
78
|
function applyIneligibleRouteCachePolicy(response, options, requestCacheLife = null) {
|
|
76
|
-
const cacheControl = resolveEarlyResponseCacheControl(options);
|
|
77
79
|
const isRscRedirect = options.isRscRequest && response.headers.has("X-Vinext-Rsc-Redirect");
|
|
80
|
+
const cacheControl = resolveEarlyResponseCacheControl(options) ?? (isRscRedirect ? null : resolveUnstoredEarlyResponseCacheControl(response, options));
|
|
78
81
|
if (!cacheControl) {
|
|
79
82
|
const staleTimeSeconds = isRscRedirect ? resolveClientStaleTimeSeconds(requestCacheLife) : void 0;
|
|
80
83
|
if (staleTimeSeconds === void 0) return response;
|
|
@@ -108,6 +111,17 @@ function resolveEarlyResponseCacheControl(options) {
|
|
|
108
111
|
const ignoresDynamicUsage = (options.isForceStatic || options.isDynamicError) && (options.revalidateSeconds === null || options.revalidateSeconds === Infinity);
|
|
109
112
|
return options.revalidateSeconds === 0 || !ignoresDynamicUsage && (options.peekDynamicUsage?.() ?? peekDynamicUsage()) ? uncacheable : null;
|
|
110
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* The normal render can still become a cache entry, so it sends its pending
|
|
116
|
+
* policy until the stream proves it static. An early response never does, and
|
|
117
|
+
* nothing checks it again, so one with a body takes the never-cache header
|
|
118
|
+
* unless it already carries a policy, such as middleware's. A force-static or
|
|
119
|
+
* dynamic = "error" render can't read the request, and dev and the build's
|
|
120
|
+
* prerender, which reads `no-store` as a dynamic render, keep no policy.
|
|
121
|
+
*/
|
|
122
|
+
function resolveUnstoredEarlyResponseCacheControl(response, options) {
|
|
123
|
+
return options.isProduction && options.isPrerender !== true && !options.isForceStatic && !options.isDynamicError && response.body !== null && !response.headers.has("cache-control") ? NEVER_CACHE_CONTROL : null;
|
|
124
|
+
}
|
|
111
125
|
function resolveAppPageCacheWriteRevalidateSeconds(options) {
|
|
112
126
|
if (!options.isStaticEligible) return null;
|
|
113
127
|
if (options.revalidateSeconds === null && (options.isForceStatic || options.isDynamicError)) return Infinity;
|
|
@@ -100,6 +100,25 @@ type AppPageInterceptMatch<TPage = unknown> = {
|
|
|
100
100
|
targetPatternParts?: readonly string[];
|
|
101
101
|
targetRouteGraphId?: string | null;
|
|
102
102
|
};
|
|
103
|
+
export type AppPageInterceptState<TRoute, TPage> = {
|
|
104
|
+
kind: "none";
|
|
105
|
+
} | {
|
|
106
|
+
kind: "current-route";
|
|
107
|
+
intercept: AppPageInterceptMatch<TPage>;
|
|
108
|
+
} | {
|
|
109
|
+
kind: "source-route";
|
|
110
|
+
intercept: AppPageInterceptMatch<TPage>;
|
|
111
|
+
sourceRoute: TRoute;
|
|
112
|
+
};
|
|
113
|
+
type AppPageInterceptStateLookup<TRoute, TPage> = {
|
|
114
|
+
cleanPathname: string;
|
|
115
|
+
currentRoute: TRoute;
|
|
116
|
+
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
117
|
+
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
118
|
+
/** The state this request already resolved, which is then reused. */
|
|
119
|
+
interceptState?: AppPageInterceptState<TRoute, TPage>;
|
|
120
|
+
isRscRequest: boolean;
|
|
121
|
+
};
|
|
103
122
|
type ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts> = {
|
|
104
123
|
cleanPathname: string;
|
|
105
124
|
currentParams: AppPageParams;
|
|
@@ -107,6 +126,7 @@ type ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOp
|
|
|
107
126
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
108
127
|
getRouteParamNames: (route: TRoute) => readonly string[];
|
|
109
128
|
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
129
|
+
interceptState?: AppPageInterceptState<TRoute, TPage>;
|
|
110
130
|
isRscRequest: boolean;
|
|
111
131
|
toInterceptOpts: (intercept: AppPageInterceptMatch<TPage>) => TInterceptOpts;
|
|
112
132
|
};
|
|
@@ -128,10 +148,11 @@ type ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement> = {
|
|
|
128
148
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
129
149
|
getRouteParamNames: (route: TRoute) => readonly string[];
|
|
130
150
|
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
151
|
+
interceptState?: AppPageInterceptState<TRoute, TPage>;
|
|
131
152
|
isRscRequest: boolean;
|
|
132
153
|
resolveNavigationParams: (route: TRoute, params: AppPageParams, pathname: string, interceptOpts: TInterceptOpts) => AppPageParams;
|
|
133
|
-
renderInterceptResponse: (route: TRoute, element: TElement) => Promise<Response> | Response;
|
|
134
|
-
resolveSearchParams?: (route: TRoute, searchParams: URLSearchParams) => Awaitable<URLSearchParams>;
|
|
154
|
+
renderInterceptResponse: (route: TRoute, element: TElement, navigationParams: AppPageParams) => Promise<Response> | Response;
|
|
155
|
+
resolveSearchParams?: (route: TRoute, searchParams: URLSearchParams, interceptOpts: TInterceptOpts) => Awaitable<URLSearchParams>;
|
|
135
156
|
searchParams: URLSearchParams;
|
|
136
157
|
setNavigationContext: (context: {
|
|
137
158
|
params: AppPageParams;
|
|
@@ -146,6 +167,7 @@ type ResolveAppPageInterceptResult<TInterceptOpts> = {
|
|
|
146
167
|
};
|
|
147
168
|
export declare function resolveAppPageGenerateStaticParamsSources(options: ResolveAppPageGenerateStaticParamsSourcesOptions): GenerateStaticParamsSource[];
|
|
148
169
|
export declare function validateAppPageDynamicParams(options: ValidateAppPageDynamicParamsOptions): Promise<Response | null>;
|
|
170
|
+
export declare function resolveAppPageInterceptState<TRoute, TPage>(options: AppPageInterceptStateLookup<TRoute, TPage>): Promise<AppPageInterceptState<TRoute, TPage>>;
|
|
149
171
|
export declare function resolveAppPageInterceptionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageInterceptionRerenderTargetResult<TRoute, TInterceptOpts>>;
|
|
150
172
|
export declare function resolveAppPageActionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageActionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageActionRerenderTargetResult<TRoute, TInterceptOpts>>;
|
|
151
173
|
export declare function resolveAppPageIntercept<TRoute, TPage, TInterceptOpts, TElement>(options: ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement>): Promise<ResolveAppPageInterceptResult<TInterceptOpts>>;
|
|
@@ -278,6 +278,7 @@ async function validateAppPageDynamicParams(options) {
|
|
|
278
278
|
return null;
|
|
279
279
|
}
|
|
280
280
|
async function resolveAppPageInterceptState(options) {
|
|
281
|
+
if (options.interceptState) return options.interceptState;
|
|
281
282
|
if (!options.isRscRequest) return { kind: "none" };
|
|
282
283
|
const intercept = options.findIntercept(options.cleanPathname);
|
|
283
284
|
if (!intercept) return { kind: "none" };
|
|
@@ -301,10 +302,9 @@ async function resolveAppPageInterceptionRerenderTarget(options) {
|
|
|
301
302
|
cleanPathname: options.cleanPathname,
|
|
302
303
|
currentRoute: options.currentRoute,
|
|
303
304
|
findIntercept: options.findIntercept,
|
|
304
|
-
getRouteParamNames: options.getRouteParamNames,
|
|
305
305
|
getSourceRoute: options.getSourceRoute,
|
|
306
|
-
|
|
307
|
-
|
|
306
|
+
interceptState: options.interceptState,
|
|
307
|
+
isRscRequest: options.isRscRequest
|
|
308
308
|
});
|
|
309
309
|
if (interceptState.kind === "source-route") {
|
|
310
310
|
const sourceMatchedParams = interceptState.intercept.sourceMatchedParams ?? interceptState.intercept.matchedParams;
|
|
@@ -333,10 +333,9 @@ async function resolveAppPageIntercept(options) {
|
|
|
333
333
|
cleanPathname: options.cleanPathname,
|
|
334
334
|
currentRoute: options.currentRoute,
|
|
335
335
|
findIntercept: options.findIntercept,
|
|
336
|
-
getRouteParamNames: options.getRouteParamNames,
|
|
337
336
|
getSourceRoute: options.getSourceRoute,
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
interceptState: options.interceptState,
|
|
338
|
+
isRscRequest: options.isRscRequest
|
|
340
339
|
});
|
|
341
340
|
if (interceptState.kind === "source-route") {
|
|
342
341
|
const renderRoute = interceptState.sourceRoute;
|
|
@@ -346,17 +345,18 @@ async function resolveAppPageIntercept(options) {
|
|
|
346
345
|
...sourceMatchedParams,
|
|
347
346
|
...interceptState.intercept.matchedParams
|
|
348
347
|
};
|
|
349
|
-
const renderSearchParams = options.resolveSearchParams ? await options.resolveSearchParams(renderRoute, options.searchParams) : options.searchParams;
|
|
348
|
+
const renderSearchParams = options.resolveSearchParams ? await options.resolveSearchParams(renderRoute, options.searchParams, interceptOpts) : options.searchParams;
|
|
350
349
|
const renderParams = pickRouteParams(sourceMatchedParams, options.getRouteParamNames(interceptState.sourceRoute));
|
|
350
|
+
const resolvedNavigationParams = options.resolveNavigationParams(renderRoute, navigationParams, options.cleanPathname, interceptOpts);
|
|
351
351
|
options.setNavigationContext({
|
|
352
|
-
params:
|
|
352
|
+
params: resolvedNavigationParams,
|
|
353
353
|
pathname: options.cleanPathname,
|
|
354
354
|
searchParams: renderSearchParams
|
|
355
355
|
});
|
|
356
356
|
const interceptElement = await options.buildPageElement(renderRoute, renderParams, interceptOpts, renderSearchParams);
|
|
357
357
|
return {
|
|
358
358
|
interceptOpts: void 0,
|
|
359
|
-
response: await options.renderInterceptResponse(renderRoute, interceptElement)
|
|
359
|
+
response: await options.renderInterceptResponse(renderRoute, interceptElement, resolvedNavigationParams)
|
|
360
360
|
};
|
|
361
361
|
}
|
|
362
362
|
return {
|
|
@@ -385,4 +385,4 @@ async function buildAppPageElement(options) {
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
//#endregion
|
|
388
|
-
export { buildAppPageElement, resolveAppPageActionRerenderTarget, resolveAppPageGenerateStaticParamsSources, resolveAppPageIntercept, resolveAppPageInterceptionRerenderTarget, validateAppPageDynamicParams };
|
|
388
|
+
export { buildAppPageElement, resolveAppPageActionRerenderTarget, resolveAppPageGenerateStaticParamsSources, resolveAppPageIntercept, resolveAppPageInterceptState, resolveAppPageInterceptionRerenderTarget, validateAppPageDynamicParams };
|
|
@@ -35,6 +35,8 @@ export type AppPageRouteWiringSlot<TModule extends AppPageModule = AppPageModule
|
|
|
35
35
|
configLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
36
36
|
configLayoutTreePositions?: readonly number[] | null;
|
|
37
37
|
error?: TErrorModule | null;
|
|
38
|
+
errors?: readonly (TErrorModule | null | undefined)[] | null;
|
|
39
|
+
errorTreePositions?: readonly number[] | null;
|
|
38
40
|
layout?: TModule | null;
|
|
39
41
|
layoutIndex: number;
|
|
40
42
|
loading?: TModule | null;
|
|
@@ -99,6 +101,8 @@ export type AppPageRouteWiringRoute<TModule extends AppPageModule = AppPageModul
|
|
|
99
101
|
id: string;
|
|
100
102
|
ownerTreePath: string;
|
|
101
103
|
state: AppElementsSlotBinding["state"];
|
|
104
|
+
/** A slot sub-route whose children its route segments don't describe. */
|
|
105
|
+
synthetic?: boolean;
|
|
102
106
|
} | null;
|
|
103
107
|
/**
|
|
104
108
|
* Static sibling segment names at each dynamic URL level for this route. Used
|
|
@@ -216,11 +220,7 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
|
|
|
216
220
|
isRscRequest?: boolean;
|
|
217
221
|
/**
|
|
218
222
|
* The render can never be stored and, in production, is never sent with
|
|
219
|
-
* static cache headers (see app-page-dispatch.ts)
|
|
220
|
-
* prefetch without a loading boundary may skip its layouts. A render that
|
|
221
|
-
* may be stored keeps them, so a layout's dynamic API read still keeps it
|
|
222
|
-
* out of the cache. An explicit CDN cache policy on a dynamic route can
|
|
223
|
-
* still store the route tree, as a CDN would store Next.js's.
|
|
223
|
+
* static cache headers (see app-page-dispatch.ts).
|
|
224
224
|
*/
|
|
225
225
|
isUncacheableRender?: boolean;
|
|
226
226
|
/**
|
|
@@ -232,6 +232,12 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
|
|
|
232
232
|
mountedLayouts?: ReadonlySet<string> | null;
|
|
233
233
|
mountedSlotIds?: ReadonlySet<string> | null;
|
|
234
234
|
renderIdentity?: AppPageRenderIdentity;
|
|
235
|
+
/**
|
|
236
|
+
* The render has no PPR (see app-page-dispatch.ts), so a loading-shell
|
|
237
|
+
* prefetch without a loading boundary sends only the route tree. Without it,
|
|
238
|
+
* the layouts render, as Next.js renders them with isRoutePPREnabled.
|
|
239
|
+
*/
|
|
240
|
+
routeTreeOnlyPrefetchShell?: boolean;
|
|
235
241
|
renderMode?: AppRscRenderMode;
|
|
236
242
|
routePath: string;
|
|
237
243
|
semanticPageIdentity?: Readonly<{
|
|
@@ -241,6 +247,12 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
|
|
|
241
247
|
sourceRouteGraphId: string;
|
|
242
248
|
}> | null;
|
|
243
249
|
semanticInterceptionTargetRouteId?: string | null;
|
|
250
|
+
/**
|
|
251
|
+
* A sibling intercept's segment state keys, app root first, down to the
|
|
252
|
+
* segment its page element renders. Its BFCache segment levels follow these
|
|
253
|
+
* rather than the source route's segments.
|
|
254
|
+
*/
|
|
255
|
+
siblingInterceptSegmentKeys?: readonly string[] | null;
|
|
244
256
|
sourcePageSegments?: readonly string[] | null;
|
|
245
257
|
};
|
|
246
258
|
export type AppPageTemplateEntry<TModule extends AppPageModule = AppPageModule> = {
|
|
@@ -514,7 +514,7 @@ function buildAppPageElements(options) {
|
|
|
514
514
|
[pageElementId]: null,
|
|
515
515
|
[routeId]: null
|
|
516
516
|
};
|
|
517
|
-
if (isPrefetchLoadingShell && !shouldRenderPrefetchLoadingShell && options.
|
|
517
|
+
if (isPrefetchLoadingShell && !shouldRenderPrefetchLoadingShell && options.routeTreeOnlyPrefetchShell === true) return {
|
|
518
518
|
...metadataEntries,
|
|
519
519
|
...options.route.staticSiblings && options.route.staticSiblings.length > 0 ? { [APP_STATIC_SIBLINGS_KEY]: options.route.staticSiblings } : {},
|
|
520
520
|
[pageElementId]: null,
|
|
@@ -669,9 +669,11 @@ function buildAppPageElements(options) {
|
|
|
669
669
|
branchAccessBoundaries.set(treePosition, entries);
|
|
670
670
|
}
|
|
671
671
|
const branchErrors = /* @__PURE__ */ new Map();
|
|
672
|
-
|
|
672
|
+
const slotErrorModules = !overrideOrPageComponent ? void 0 : hasSlotTreeOverride ? slotOverride?.errorModules : slot.errors;
|
|
673
|
+
const slotErrorTreePositions = hasSlotTreeOverride ? slotOverride?.errorTreePositions : slot.errorTreePositions;
|
|
674
|
+
for (const [index, errorModule] of (slotErrorModules ?? []).entries()) {
|
|
673
675
|
const component = getErrorBoundaryExport(errorModule);
|
|
674
|
-
const treePosition =
|
|
676
|
+
const treePosition = slotErrorTreePositions?.[index];
|
|
675
677
|
if (component && treePosition !== void 0) branchErrors.set(treePosition, component);
|
|
676
678
|
}
|
|
677
679
|
const resolveSlotNotFoundBoundary = (treePosition) => {
|
|
@@ -753,12 +755,6 @@ function buildAppPageElements(options) {
|
|
|
753
755
|
])).filter((treePosition) => !prefetchSlotLoadingEntry || !isOwnedAtRoutePrefetchCutoff && treePosition <= prefetchSlotLoadingEntry.treePosition).sort((left, right) => left - right);
|
|
754
756
|
for (let index = branchTreePositions.length - 1; index >= 0; index--) {
|
|
755
757
|
const treePosition = branchTreePositions[index];
|
|
756
|
-
const nestedBfcacheSegment = nestedBfcacheSegments.find((segment) => segment.treePosition === treePosition);
|
|
757
|
-
if (nestedBfcacheSegment) slotElement = /* @__PURE__ */ jsx(BfcacheSegmentBoundary, {
|
|
758
|
-
id: nestedBfcacheSegment.id,
|
|
759
|
-
stateKey: nestedBfcacheSegment.stateKey,
|
|
760
|
-
children: slotElement
|
|
761
|
-
});
|
|
762
758
|
const loadingComponents = prefetchSlotLoadingEntry ? [] : branchLoadings.get(treePosition) ?? [];
|
|
763
759
|
for (let loadingIndex = loadingComponents.length - 1; loadingIndex >= 0; loadingIndex--) {
|
|
764
760
|
const LoadingComponent = loadingComponents[loadingIndex];
|
|
@@ -786,6 +782,12 @@ function buildAppPageElements(options) {
|
|
|
786
782
|
fallback: SegmentErrorComponent,
|
|
787
783
|
children: slotElement
|
|
788
784
|
});
|
|
785
|
+
const nestedBfcacheSegment = nestedBfcacheSegments.find((segment) => segment.treePosition === treePosition);
|
|
786
|
+
if (nestedBfcacheSegment) slotElement = /* @__PURE__ */ jsx(BfcacheSegmentBoundary, {
|
|
787
|
+
id: nestedBfcacheSegment.id,
|
|
788
|
+
stateKey: nestedBfcacheSegment.stateKey,
|
|
789
|
+
children: slotElement
|
|
790
|
+
});
|
|
789
791
|
const layoutEntriesAtPosition = branchLayouts.get(treePosition) ?? [];
|
|
790
792
|
for (let layoutIndex = layoutEntriesAtPosition.length - 1; layoutIndex >= 0; layoutIndex--) {
|
|
791
793
|
const layoutEntry = layoutEntriesAtPosition[layoutIndex];
|
|
@@ -878,17 +880,19 @@ function buildAppPageElements(options) {
|
|
|
878
880
|
children: routeChildren
|
|
879
881
|
});
|
|
880
882
|
const firstLayoutWithElementTreePosition = layoutEntries.find((entry) => getDefaultExport(entry.layoutModule) !== null)?.treePosition;
|
|
881
|
-
const
|
|
882
|
-
const
|
|
883
|
+
const siblingInterceptSegmentKeys = options.siblingInterceptSegmentKeys ?? null;
|
|
884
|
+
const keepsSegmentLevelLists = String(process.env.__NEXT_CACHE_COMPONENTS) === "true" && firstLayoutWithElementTreePosition !== void 0 && !options.route.childrenSlot?.synthetic && (options.semanticPageIdentity === void 0 || siblingInterceptSegmentKeys !== null);
|
|
885
|
+
const spineSegmentCount = siblingInterceptSegmentKeys?.length ?? routeSegments.length;
|
|
886
|
+
const spineTreePositions = keepsSegmentLevelLists ? Array.from(/* @__PURE__ */ new Set([...orderedTreePositions, ...Array.from({ length: Math.max(0, spineSegmentCount - firstLayoutWithElementTreePosition) }, (_, offset) => firstLayoutWithElementTreePosition + 1 + offset)])).sort((left, right) => left - right) : orderedTreePositions;
|
|
883
887
|
const renderedTreePositions = isPrefetchLoadingShell ? spineTreePositions.filter(includesPrefetchTreePosition) : spineTreePositions;
|
|
884
888
|
const firstLayoutBelowShell = keepsSegmentLevelLists && isPrefetchLoadingShell ? layoutEntries.find((entry) => !includesPrefetchTreePosition(entry.treePosition) && getDefaultExport(entry.layoutModule) !== null) : void 0;
|
|
885
889
|
let childSegmentElementId = firstLayoutBelowShell?.id ?? pageElementId;
|
|
886
|
-
let childSegmentTreePosition = firstLayoutBelowShell?.treePosition ??
|
|
890
|
+
let childSegmentTreePosition = firstLayoutBelowShell?.treePosition ?? spineSegmentCount + 1;
|
|
887
891
|
const retainChildSegment = (treePosition, children) => treePosition === childSegmentTreePosition - 1 ? /* @__PURE__ */ jsx(BfcacheChildrenBoundary, {
|
|
888
892
|
id: childSegmentElementId,
|
|
889
893
|
children
|
|
890
894
|
}) : /* @__PURE__ */ jsx(BfcacheChildrenBoundary, {
|
|
891
|
-
stateKey: createBfcacheChildSegmentStateKey(resolveAppPageTemplateStateKey(routeSegments, treePosition, options.matchedParams)),
|
|
895
|
+
stateKey: createBfcacheChildSegmentStateKey(siblingInterceptSegmentKeys ? JSON.stringify(siblingInterceptSegmentKeys.slice(0, treePosition + 1)) : resolveAppPageTemplateStateKey(routeSegments, treePosition, options.matchedParams)),
|
|
892
896
|
children
|
|
893
897
|
});
|
|
894
898
|
for (let index = renderedTreePositions.length - 1; index >= 0; index--) {
|
|
@@ -977,8 +981,10 @@ function buildAppPageElements(options) {
|
|
|
977
981
|
const globalErrorComponent = getErrorBoundaryExport(options.globalErrorModule);
|
|
978
982
|
routeChildren = /* @__PURE__ */ jsx(GlobalErrorBoundary, {
|
|
979
983
|
fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
|
|
984
|
+
wrapsGlobalErrorBoundary: globalErrorComponent !== null,
|
|
980
985
|
children: globalErrorComponent ? /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
981
986
|
fallback: globalErrorComponent,
|
|
987
|
+
isGlobalErrorBoundary: true,
|
|
982
988
|
children: routeChildren
|
|
983
989
|
}) : routeChildren
|
|
984
990
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
2
2
|
import "./app-elements.js";
|
|
3
3
|
import { createNestedBfcacheSlotSegmentId, deriveBfcacheSegmentIdentity } from "./bfcache-identity.js";
|
|
4
|
-
import { resolveAppPageRouteStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey } from "./app-page-segment-state.js";
|
|
4
|
+
import { APP_PAGE_DEFAULT_SEGMENT_KEY, APP_PAGE_SEGMENT_KEY, resolveAppPageRouteStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey } from "./app-page-segment-state.js";
|
|
5
5
|
//#region src/server/app-page-segment-plan.ts
|
|
6
6
|
function requireGraphSequenceId(ids, index, kind) {
|
|
7
7
|
const id = ids[index];
|
|
@@ -118,6 +118,14 @@ function createAppPageSegmentPlan(options) {
|
|
|
118
118
|
treePosition
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
|
+
if (semanticSegments.length > 0) {
|
|
122
|
+
const leafKey = slotBinding.state === "default" ? APP_PAGE_DEFAULT_SEGMENT_KEY : APP_PAGE_SEGMENT_KEY;
|
|
123
|
+
semanticSegments.push({
|
|
124
|
+
identityKey: JSON.stringify([...boundSegmentKeys, leafKey]),
|
|
125
|
+
stateKey: leafKey,
|
|
126
|
+
treePosition: branchSegments.length
|
|
127
|
+
});
|
|
128
|
+
}
|
|
121
129
|
}
|
|
122
130
|
const includedInPayload = options.includeSlot(ownerTreePosition, targetTreePosition);
|
|
123
131
|
const graphId = graphIds ? graphIds.slots[slotKey] : slot.id ?? slotId;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
2
2
|
//#region src/server/app-page-segment-state.d.ts
|
|
3
3
|
export declare const APP_PAGE_SEGMENT_KEY = "__PAGE__";
|
|
4
|
+
export declare const APP_PAGE_DEFAULT_SEGMENT_KEY = "__DEFAULT__";
|
|
4
5
|
export declare function isAppPageRouteGroupSegment(segment: string): boolean;
|
|
5
6
|
export declare function canonicalizeAppPageParams(params: AppPageParams): void;
|
|
7
|
+
/**
|
|
8
|
+
* Rebinds segment state keys and BFCache identities resolved with one set of
|
|
9
|
+
* params to another. Keys are JSON arrays of segment keys, nested to any
|
|
10
|
+
* depth, so each whole segment key is compared with the param tokens
|
|
11
|
+
* `resolveSingleSegmentStateKey` produced for the learned params; only those
|
|
12
|
+
* whose value differs are replaced. Returns null when no token changes.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createAppPageSegmentStateKeyRebinder(fromParams: AppPageParams, toParams: AppPageParams): ((stateKey: string) => string) | null;
|
|
6
15
|
export declare function resolveAppPageChildSegments(routeSegments: readonly string[], treePosition: number, params: AppPageParams): string[];
|
|
7
16
|
export declare function resolveAppPageSegmentStateKey(routeSegments: readonly string[], treePosition: number, params: AppPageParams): string;
|
|
8
17
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isCatchAllSegment, isOptionalCatchAllSegment } from "../routing/utils.js";
|
|
2
2
|
//#region src/server/app-page-segment-state.ts
|
|
3
3
|
const APP_PAGE_SEGMENT_KEY = "__PAGE__";
|
|
4
|
+
const APP_PAGE_DEFAULT_SEGMENT_KEY = "__DEFAULT__";
|
|
4
5
|
function isDynamicSegment(segment) {
|
|
5
6
|
return segment.startsWith("[") && segment.endsWith("]") && !segment.includes(".");
|
|
6
7
|
}
|
|
@@ -49,6 +50,58 @@ function resolveSingleSegmentStateKey(segment, params) {
|
|
|
49
50
|
if (!param) return segment;
|
|
50
51
|
return `${param.name}|${formatSegmentStateParamValue(param, params, segment)}|${param.type}`;
|
|
51
52
|
}
|
|
53
|
+
const SEGMENT_PARAM_TYPES = [
|
|
54
|
+
"d",
|
|
55
|
+
"c",
|
|
56
|
+
"oc"
|
|
57
|
+
];
|
|
58
|
+
function formatSegmentParamPattern(param) {
|
|
59
|
+
if (param.type === "oc") return `[[...${param.name}]]`;
|
|
60
|
+
if (param.type === "c") return `[...${param.name}]`;
|
|
61
|
+
return `[${param.name}]`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Rebinds segment state keys and BFCache identities resolved with one set of
|
|
65
|
+
* params to another. Keys are JSON arrays of segment keys, nested to any
|
|
66
|
+
* depth, so each whole segment key is compared with the param tokens
|
|
67
|
+
* `resolveSingleSegmentStateKey` produced for the learned params; only those
|
|
68
|
+
* whose value differs are replaced. Returns null when no token changes.
|
|
69
|
+
*/
|
|
70
|
+
function createAppPageSegmentStateKeyRebinder(fromParams, toParams) {
|
|
71
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const name of /* @__PURE__ */ new Set([...Object.keys(fromParams), ...Object.keys(toParams)])) for (const type of SEGMENT_PARAM_TYPES) {
|
|
73
|
+
const param = {
|
|
74
|
+
name,
|
|
75
|
+
type
|
|
76
|
+
};
|
|
77
|
+
const fallbackSegment = formatSegmentParamPattern(param);
|
|
78
|
+
const from = formatSegmentStateParamValue(param, fromParams, fallbackSegment);
|
|
79
|
+
const to = formatSegmentStateParamValue(param, toParams, fallbackSegment);
|
|
80
|
+
if (from !== to) tokens.set(`${name}|${from}|${type}`, `${name}|${to}|${type}`);
|
|
81
|
+
}
|
|
82
|
+
if (tokens.size === 0) return null;
|
|
83
|
+
const rebind = (stateKey) => {
|
|
84
|
+
const token = tokens.get(stateKey);
|
|
85
|
+
if (token !== void 0) return token;
|
|
86
|
+
if (!stateKey.startsWith("[")) return stateKey;
|
|
87
|
+
let parts;
|
|
88
|
+
try {
|
|
89
|
+
parts = JSON.parse(stateKey);
|
|
90
|
+
} catch {
|
|
91
|
+
return stateKey;
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(parts)) return stateKey;
|
|
94
|
+
let changed = false;
|
|
95
|
+
const rebound = parts.map((part) => {
|
|
96
|
+
if (typeof part !== "string") return part;
|
|
97
|
+
const next = rebind(part);
|
|
98
|
+
if (next !== part) changed = true;
|
|
99
|
+
return next;
|
|
100
|
+
});
|
|
101
|
+
return changed ? JSON.stringify(rebound) : stateKey;
|
|
102
|
+
};
|
|
103
|
+
return rebind;
|
|
104
|
+
}
|
|
52
105
|
function resolveAppPageChildSegments(routeSegments, treePosition, params) {
|
|
53
106
|
const rawSegments = routeSegments.slice(treePosition);
|
|
54
107
|
const resolvedSegments = [];
|
|
@@ -104,4 +157,4 @@ function resolveAppPageTemplateStateKey(routeSegments, treePosition, params) {
|
|
|
104
157
|
return statePath.length > 0 ? JSON.stringify(statePath) : "";
|
|
105
158
|
}
|
|
106
159
|
//#endregion
|
|
107
|
-
export { APP_PAGE_SEGMENT_KEY, canonicalizeAppPageParams, isAppPageRouteGroupSegment, resolveAppPageChildSegments, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey };
|
|
160
|
+
export { APP_PAGE_DEFAULT_SEGMENT_KEY, APP_PAGE_SEGMENT_KEY, canonicalizeAppPageParams, createAppPageSegmentStateKeyRebinder, isAppPageRouteGroupSegment, resolveAppPageChildSegments, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey };
|
|
@@ -57,6 +57,12 @@ type RenderPagesFallbackOptions = {
|
|
|
57
57
|
pathname?: string;
|
|
58
58
|
pagesDataRequest?: Request | null;
|
|
59
59
|
request: Request;
|
|
60
|
+
/**
|
|
61
|
+
* The basePath `request.url` still carries. The page renderer receives the
|
|
62
|
+
* URL without it, as in the Pages Router pipeline, so `req.url` and `asPath`
|
|
63
|
+
* leave it out like Next.js.
|
|
64
|
+
*/
|
|
65
|
+
requestBasePath?: string;
|
|
60
66
|
/** The shared cache entry answers every host; a request-time render is not stored. */
|
|
61
67
|
sharedAcrossHosts?: boolean;
|
|
62
68
|
url: URL;
|