vinext 1.3.1 → 1.4.1
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 +25 -22
- package/THIRD_PARTY_LICENSES.md +47 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -2
- package/dist/build/prerender.js +4 -3
- package/dist/build/report.d.ts +0 -14
- package/dist/build/report.js +1 -51
- package/dist/check.js +4 -7
- package/dist/cli.js +3 -0
- package/dist/client/dev-error-overlay-store.d.ts +1 -0
- package/dist/client/dev-error-overlay.d.ts +2 -0
- package/dist/client/dev-error-overlay.js +20 -4
- package/dist/client/navigation-runtime.d.ts +13 -1
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.js +2 -0
- package/dist/config/destination-params.d.ts +2 -1
- package/dist/config/destination-params.js +52 -2
- package/dist/config/next-config.d.ts +25 -0
- package/dist/config/next-config.js +82 -4
- 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 -49
- 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 +36 -9
- package/dist/index.js +112 -90
- 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/strip-server-exports.js +2 -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 +21 -5
- package/dist/server/app-bfcache-identity.js +1 -1
- package/dist/server/app-browser-entry.js +86 -66
- 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 +7 -7
- package/dist/server/app-browser-server-action-client.d.ts +2 -0
- package/dist/server/app-browser-server-action-client.js +5 -4
- 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 +4 -14
- 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 +43 -4
- package/dist/server/app-page-boundary-render.js +10 -14
- 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 -11
- package/dist/server/app-page-dispatch.js +84 -155
- package/dist/server/app-page-element-builder.d.ts +2 -2
- package/dist/server/app-page-element-builder.js +13 -12
- package/dist/server/app-page-execution.d.ts +17 -52
- package/dist/server/app-page-execution.js +102 -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 -13
- package/dist/server/app-page-render.js +103 -234
- 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 +86 -63
- 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 +12 -8
- package/dist/server/app-page-stream.js +4 -3
- 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-request-stage-independent-entry.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 +11 -5
- 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.d.ts +1 -1
- package/dist/server/app-route-handler-runtime.js +28 -11
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +12 -12
- package/dist/server/app-rsc-cache-busting.js +31 -72
- package/dist/server/app-rsc-error-handler.d.ts +2 -1
- package/dist/server/app-rsc-error-handler.js +10 -2
- package/dist/server/app-rsc-errors.d.ts +8 -0
- package/dist/server/app-rsc-errors.js +11 -1
- package/dist/server/app-rsc-handler.d.ts +5 -5
- package/dist/server/app-rsc-handler.js +80 -83
- package/dist/server/app-rsc-request-normalization.d.ts +10 -16
- package/dist/server/app-rsc-request-normalization.js +12 -18
- package/dist/server/app-rsc-response-finalizer.js +2 -2
- package/dist/server/app-rsc-response-stage.js +9 -15
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +15 -8
- package/dist/server/app-ssr-entry.d.ts +8 -0
- package/dist/server/app-ssr-entry.js +22 -16
- 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 +37 -6
- package/dist/server/app-worker-stages.js +81 -9
- package/dist/server/artifact-compatibility.d.ts +0 -1
- package/dist/server/artifact-compatibility.js +1 -10
- package/dist/server/bfcache-identity.d.ts +7 -0
- package/dist/server/bfcache-identity.js +10 -1
- package/dist/server/cache-proof.d.ts +1 -115
- package/dist/server/cache-proof.js +1 -544
- package/dist/server/cacheability-manifest.js +1 -2
- package/dist/server/cacheability-request.js +13 -8
- package/dist/server/dev-origin-check.d.ts +2 -1
- package/dist/server/dev-origin-check.js +2 -1
- package/dist/server/dev-server.js +6 -4
- package/dist/server/headers.d.ts +6 -3
- package/dist/server/headers.js +9 -5
- package/dist/server/image-optimization.d.ts +5 -0
- package/dist/server/image-optimization.js +6 -1
- package/dist/server/isr-cache.d.ts +8 -2
- package/dist/server/isr-cache.js +30 -2
- package/dist/server/metadata-route-response.js +32 -15
- package/dist/server/middleware-matcher.js +19 -6
- package/dist/server/middleware-runtime.js +6 -2
- package/dist/server/multi-stage.d.ts +57 -12
- 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.d.ts +1 -1
- package/dist/server/pages-node-compat.js +54 -27
- package/dist/server/pages-page-data.js +2 -1
- package/dist/server/pages-page-handler.js +28 -26
- package/dist/server/pages-page-response.d.ts +9 -0
- package/dist/server/pages-page-response.js +18 -4
- 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 +36 -14
- package/dist/server/pages-response-stage-entry.js +9 -3
- package/dist/server/pages-router-entry.js +3 -2
- package/dist/server/prod-server.d.ts +2 -2
- package/dist/server/prod-server.js +47 -9
- package/dist/server/request-error-log.d.ts +9 -0
- package/dist/server/request-error-log.js +13 -0
- package/dist/server/request-pipeline.d.ts +15 -4
- package/dist/server/request-pipeline.js +43 -15
- package/dist/server/rsc-completion-metadata.js +3 -0
- package/dist/server/shared-cache-identity.d.ts +23 -0
- package/dist/server/shared-cache-identity.js +36 -0
- package/dist/server/static-file-cache.d.ts +9 -0
- package/dist/server/static-file-cache.js +13 -2
- 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 +23 -26
- package/dist/shims/cache-request-state.js +22 -57
- package/dist/shims/cache-runtime.d.ts +3 -1
- package/dist/shims/cache-runtime.js +94 -48
- package/dist/shims/cache.d.ts +16 -10
- package/dist/shims/cache.js +47 -36
- package/dist/shims/cacheability-classification.d.ts +2 -0
- package/dist/shims/cdn-cache.d.ts +22 -5
- package/dist/shims/client-hook-error.d.ts +2 -0
- package/dist/shims/client-hook-error.js +16 -1
- 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 +39 -25
- package/dist/shims/form.js +2 -2
- package/dist/shims/headers.d.ts +10 -24
- package/dist/shims/headers.js +22 -70
- package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
- package/dist/shims/internal/navigation-untracked.js +1 -1
- package/dist/shims/link.js +8 -11
- 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 +184 -31
- package/dist/shims/navigation.react-server.js +2 -2
- package/dist/shims/request-state-types.d.ts +3 -2
- package/dist/shims/slot.d.ts +16 -0
- package/dist/shims/slot.js +44 -9
- package/dist/shims/unified-request-context.d.ts +7 -1
- package/dist/shims/unified-request-context.js +3 -3
- package/dist/utils/cache-control-metadata.d.ts +5 -0
- package/dist/utils/cache-control-metadata.js +9 -1
- package/dist/utils/cache-life-profiles.d.ts +33 -0
- package/dist/utils/cache-life-profiles.js +111 -0
- package/dist/utils/middleware-request-headers.d.ts +14 -3
- package/dist/utils/middleware-request-headers.js +54 -7
- 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
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER } from "./headers.js";
|
|
2
|
-
import { isPromiseLike } from "../utils/promise.js";
|
|
3
|
-
import { runWithConnectionProbe } from "../shims/headers.js";
|
|
4
|
-
import { isHTTPAccessFallbackError, isThrownNavigationError } from "../shims/navigation-errors.js";
|
|
5
|
-
import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, createRscRedirectLocation } from "./app-rsc-cache-busting.js";
|
|
6
|
-
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
7
2
|
import { copyLinkHeaderProvenance, getFrameworkLinkHeader, markFrameworkLinkHeaders } from "./app-response-header-provenance.js";
|
|
3
|
+
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
4
|
+
import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, createRscRedirectLocation, toPathAbsolutePathname } from "./app-rsc-cache-busting.js";
|
|
5
|
+
import { isHTTPAccessFallbackError, isThrownNavigationError } from "../shims/navigation-errors.js";
|
|
8
6
|
import { applyEdgeRuntimeHeader } from "./app-page-response.js";
|
|
9
7
|
import { parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
|
|
10
8
|
import { renderSsrErrorMetaTags } from "./app-ssr-error-meta.js";
|
|
@@ -61,6 +59,12 @@ const APP_PAGE_SLOT_ERROR_NOT_FOUND_BOUNDARY = Symbol.for("vinext.appPage.slotEr
|
|
|
61
59
|
* which no slot tag marks either.
|
|
62
60
|
*/
|
|
63
61
|
const APP_PAGE_PAGE_ERROR = Symbol.for("vinext.appPage.pageError");
|
|
62
|
+
/**
|
|
63
|
+
* Marker we tag onto an error a route layout's own component threw, holding
|
|
64
|
+
* that layout's index. Its boundaries wrap only its `children`, so the
|
|
65
|
+
* boundary above the layout catches it, as for a slot the layout renders.
|
|
66
|
+
*/
|
|
67
|
+
const APP_PAGE_LAYOUT_ERROR_LAYOUT_INDEX = Symbol.for("vinext.appPage.layoutErrorLayoutIndex");
|
|
64
68
|
function tagAppPageHttpAccessFallbackError(error, tags) {
|
|
65
69
|
if (!isHTTPAccessFallbackError(error)) return error;
|
|
66
70
|
const source = error;
|
|
@@ -85,9 +89,17 @@ function tagAppPageSlotError(error, layoutIndex, notFoundBoundary) {
|
|
|
85
89
|
function tagAppPagePageError(error) {
|
|
86
90
|
return tagAppPageHttpAccessFallbackError(error, { [APP_PAGE_PAGE_ERROR]: { value: true } });
|
|
87
91
|
}
|
|
92
|
+
function tagAppPageLayoutError(error, layoutIndex) {
|
|
93
|
+
return tagAppPageHttpAccessFallbackError(error, { [APP_PAGE_LAYOUT_ERROR_LAYOUT_INDEX]: { value: layoutIndex } });
|
|
94
|
+
}
|
|
88
95
|
function isAppPagePageError(error) {
|
|
89
96
|
return !!error && typeof error === "object" && Reflect.get(error, APP_PAGE_PAGE_ERROR) === true;
|
|
90
97
|
}
|
|
98
|
+
function getAppPageLayoutErrorLayoutIndex(error) {
|
|
99
|
+
if (!error || typeof error !== "object") return null;
|
|
100
|
+
const layoutIndex = Reflect.get(error, APP_PAGE_LAYOUT_ERROR_LAYOUT_INDEX);
|
|
101
|
+
return typeof layoutIndex === "number" ? layoutIndex : null;
|
|
102
|
+
}
|
|
91
103
|
function getAppPageSlotErrorLayoutIndex(error) {
|
|
92
104
|
if (!error || typeof error !== "object") return null;
|
|
93
105
|
const layoutIndex = Reflect.get(error, APP_PAGE_SLOT_ERROR_LAYOUT_INDEX);
|
|
@@ -149,8 +161,10 @@ function resolveAppPageSpecialError(error) {
|
|
|
149
161
|
* runs only in the RSC render. A notFound() / forbidden() / unauthorized()
|
|
150
162
|
* a parallel slot's page threw is caught above the layout rendering it,
|
|
151
163
|
* unless the slot's own not-found catches its notFound() and every other copy
|
|
152
|
-
* is the page's or another slot's.
|
|
153
|
-
*
|
|
164
|
+
* is the page's or another slot's. One a route layout threw is caught above
|
|
165
|
+
* that layout, and the outermost such layout wins. A copy nothing tagged,
|
|
166
|
+
* such as a template's or one from a component a layout or the page renders,
|
|
167
|
+
* marks the error unattributed.
|
|
154
168
|
*/
|
|
155
169
|
function resolveAppPageShellSpecialError(error, renderedSpecialErrors) {
|
|
156
170
|
const specialError = resolveAppPageSpecialError(error);
|
|
@@ -163,14 +177,32 @@ function resolveAppPageShellSpecialError(error, renderedSpecialErrors) {
|
|
|
163
177
|
if (specialError.kind !== "http-access-fallback") return specialError;
|
|
164
178
|
const skipsSlotCaughtNotFound = specialError.statusCode === 404 && renderedSpecialErrors.every((rendered) => isAppPagePageError(rendered) || getAppPageSlotErrorLayoutIndex(rendered) !== null);
|
|
165
179
|
let slotLayoutIndex = null;
|
|
180
|
+
let layoutIndex = null;
|
|
181
|
+
let unattributed = false;
|
|
166
182
|
for (const rendered of renderedSpecialErrors) {
|
|
183
|
+
const throwingLayoutIndex = getAppPageLayoutErrorLayoutIndex(rendered);
|
|
184
|
+
if (throwingLayoutIndex !== null) {
|
|
185
|
+
if (layoutIndex === null || throwingLayoutIndex < layoutIndex) layoutIndex = throwingLayoutIndex;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
167
188
|
if (skipsSlotCaughtNotFound && getAppPageSlotErrorNotFoundBoundary(rendered) !== null) continue;
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
189
|
+
const slotOwnerIndex = getAppPageSlotErrorLayoutIndex(rendered);
|
|
190
|
+
if (slotOwnerIndex !== null) {
|
|
191
|
+
if (slotLayoutIndex === null || slotOwnerIndex < slotLayoutIndex) slotLayoutIndex = slotOwnerIndex;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (!isAppPagePageError(rendered) && resolveAppPageSpecialError(rendered)?.fromMetadata !== true) unattributed = true;
|
|
195
|
+
}
|
|
196
|
+
if (layoutIndex !== null && slotLayoutIndex !== null) {
|
|
197
|
+
if (layoutIndex <= slotLayoutIndex) slotLayoutIndex = null;
|
|
198
|
+
else layoutIndex = null;
|
|
170
199
|
}
|
|
171
|
-
|
|
200
|
+
if (slotLayoutIndex === null && layoutIndex === null && !unattributed) return specialError;
|
|
201
|
+
return {
|
|
172
202
|
...specialError,
|
|
173
|
-
slotLayoutIndex
|
|
203
|
+
...slotLayoutIndex !== null ? { slotLayoutIndex } : {},
|
|
204
|
+
...layoutIndex !== null ? { layoutIndex } : {},
|
|
205
|
+
...unattributed ? { unattributed: true } : {}
|
|
174
206
|
};
|
|
175
207
|
}
|
|
176
208
|
/**
|
|
@@ -194,6 +226,7 @@ function resolveAppPageCaughtNotFoundBoundaries(error, renderedSpecialErrors) {
|
|
|
194
226
|
let caughtInSlot = false;
|
|
195
227
|
for (const rendered of renderedSpecialErrors) {
|
|
196
228
|
if (resolveAppPageSpecialError(rendered)?.fromMetadata === true) return null;
|
|
229
|
+
if (getAppPageLayoutErrorLayoutIndex(rendered) !== null) return null;
|
|
197
230
|
const boundary = getAppPageSlotErrorNotFoundBoundary(rendered);
|
|
198
231
|
if (boundary !== null) {
|
|
199
232
|
caughtInSlot = true;
|
|
@@ -230,21 +263,65 @@ function resolveAppPageSpecialErrorStoredHeaders(specialError, basePath) {
|
|
|
230
263
|
if (specialError.kind !== "redirect") return void 0;
|
|
231
264
|
return { location: applyAppPageRedirectBasePath(specialError.location, basePath) };
|
|
232
265
|
}
|
|
266
|
+
const REDIRECT_BASES = [{
|
|
267
|
+
host: "a.redirect-base.vinext.invalid",
|
|
268
|
+
protocol: "https:"
|
|
269
|
+
}, {
|
|
270
|
+
host: "b.redirect-base.vinext.invalid",
|
|
271
|
+
protocol: "http:"
|
|
272
|
+
}];
|
|
273
|
+
function resolveAgainstPlaceholder(location, requestUrl, placeholder) {
|
|
274
|
+
const base = new URL(requestUrl);
|
|
275
|
+
base.protocol = placeholder.protocol;
|
|
276
|
+
base.host = placeholder.host;
|
|
277
|
+
base.port = "";
|
|
278
|
+
const resolved = new URL(location, base);
|
|
279
|
+
return {
|
|
280
|
+
relative: resolved.origin === base.origin,
|
|
281
|
+
resolved
|
|
282
|
+
};
|
|
283
|
+
}
|
|
233
284
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* fully-qualified URL like `https://example.com/about`.
|
|
285
|
+
* A URL reference as the URL parser reads it, with every byte a header value
|
|
286
|
+
* cannot carry percent-encoded. The parser trims C0 controls and spaces and
|
|
287
|
+
* drops tabs and newlines; percent-decoding reverses the rest, including in a
|
|
288
|
+
* host, so the client resolves it as it would the raw target.
|
|
239
289
|
*/
|
|
240
|
-
function
|
|
290
|
+
function toHeaderSafeUrlReference(location) {
|
|
291
|
+
return location.replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g, "").replace(/[\t\n\r]/g, "").replace(/[^\u0021-\u007e]+/g, (chars) => Array.from(new TextEncoder().encode(chars), (byte) => `%${byte.toString(16).toUpperCase().padStart(2, "0")}`).join(""));
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The redirect target embedded in the flight digest and its streamed
|
|
295
|
+
* side-channel header. An absolute target stays in the digest as `redirect()`
|
|
296
|
+
* received it, as in Next.js's digest, so every host that shares the cache
|
|
297
|
+
* entry is sent to the same place; the header carries the same reference made
|
|
298
|
+
* header-safe, so a protocol-relative or scheme-relative target still resolves
|
|
299
|
+
* against the page each client fetched. A relative target resolves against the
|
|
300
|
+
* request's path to a path-only form, which the client resolves against its
|
|
301
|
+
* own origin; a resolved pathname that begins with "//" keeps a "/." prefix
|
|
302
|
+
* so that form stays a path rather than naming another host. Resolving
|
|
303
|
+
* against two placeholder origins tells these apart the way the URL parser
|
|
304
|
+
* does, never depends on the request's origin, and cannot mistake an absolute
|
|
305
|
+
* target naming one placeholder for a relative one.
|
|
306
|
+
*/
|
|
307
|
+
function resolveRedirectTargets(location, requestUrl) {
|
|
241
308
|
try {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
309
|
+
const [first, second] = REDIRECT_BASES.map((placeholder) => resolveAgainstPlaceholder(location, requestUrl, placeholder));
|
|
310
|
+
if (!first.relative || !second.relative) return {
|
|
311
|
+
digestUrl: location,
|
|
312
|
+
headerUrl: toHeaderSafeUrlReference(location)
|
|
313
|
+
};
|
|
314
|
+
const { pathname, search, hash } = first.resolved;
|
|
315
|
+
const target = `${toPathAbsolutePathname(pathname)}${search}${hash}`;
|
|
316
|
+
return {
|
|
317
|
+
digestUrl: target,
|
|
318
|
+
headerUrl: target
|
|
319
|
+
};
|
|
246
320
|
} catch {
|
|
247
|
-
return
|
|
321
|
+
return {
|
|
322
|
+
digestUrl: location,
|
|
323
|
+
headerUrl: toHeaderSafeUrlReference(location)
|
|
324
|
+
};
|
|
248
325
|
}
|
|
249
326
|
}
|
|
250
327
|
function buildMetadataRedirectHtmlResponse(options) {
|
|
@@ -263,7 +340,7 @@ async function buildAppPageSpecialErrorResponse(options) {
|
|
|
263
340
|
if (options.specialError.kind === "redirect") {
|
|
264
341
|
options.clearRequestContext();
|
|
265
342
|
const prefixedLocation = applyAppPageRedirectBasePath(options.specialError.location, options.basePath);
|
|
266
|
-
const digestUrl =
|
|
343
|
+
const { digestUrl, headerUrl } = resolveRedirectTargets(prefixedLocation, options.request.url);
|
|
267
344
|
const digest = formatNextRedirectDigest({
|
|
268
345
|
type: options.specialError.type ?? "replace",
|
|
269
346
|
url: digestUrl,
|
|
@@ -282,7 +359,7 @@ async function buildAppPageSpecialErrorResponse(options) {
|
|
|
282
359
|
mergeMiddlewareResponseHeaders(headers, options.middlewareContext?.headers ?? null);
|
|
283
360
|
applyRscCompatibilityIdHeader(headers);
|
|
284
361
|
applyRscDeploymentIdHeader(headers);
|
|
285
|
-
headers.set(VINEXT_RSC_REDIRECT_HEADER,
|
|
362
|
+
headers.set(VINEXT_RSC_REDIRECT_HEADER, headerUrl);
|
|
286
363
|
headers.set(VINEXT_RSC_REDIRECT_TYPE_HEADER, options.specialError.type ?? "replace");
|
|
287
364
|
const pendingCookies = options.getAndClearPendingCookies?.() ?? [];
|
|
288
365
|
for (const cookie of pendingCookies) headers.append("Set-Cookie", cookie);
|
|
@@ -320,76 +397,6 @@ async function buildAppPageSpecialErrorResponse(options) {
|
|
|
320
397
|
const responseStatus = options.specialError.fromMetadata === true && options.serveStreamingMetadata !== false ? 200 : options.specialError.statusCode;
|
|
321
398
|
return mergeAppPageSpecialErrorHeaders(new Response(getAppPageStatusText(options.specialError.statusCode), { status: responseStatus }), options.middlewareContext);
|
|
322
399
|
}
|
|
323
|
-
/** See `LayoutFlags` type docblock in app-elements.ts for lifecycle. */
|
|
324
|
-
async function probeAppPageLayouts(options) {
|
|
325
|
-
const layoutFlags = {};
|
|
326
|
-
const cls = options.classification ?? null;
|
|
327
|
-
return {
|
|
328
|
-
response: await options.runWithSuppressedHookWarning(async () => {
|
|
329
|
-
for (let layoutIndex = options.layoutCount - 1; layoutIndex >= 0; layoutIndex--) {
|
|
330
|
-
const buildTimeResult = cls?.buildTimeClassifications?.get(layoutIndex);
|
|
331
|
-
if (cls && buildTimeResult) {
|
|
332
|
-
const layoutId = cls.getLayoutId(layoutIndex);
|
|
333
|
-
layoutFlags[layoutId] = buildTimeResult === "static" ? "s" : "d";
|
|
334
|
-
const errorResponse = await probeLayoutForErrors(options, layoutIndex);
|
|
335
|
-
if (errorResponse) return errorResponse;
|
|
336
|
-
const observationDynamic = cls.isLayoutObservationDynamic?.(layoutId) === true;
|
|
337
|
-
layoutFlags[layoutId] = buildTimeResult === "dynamic" || observationDynamic ? "d" : "s";
|
|
338
|
-
if (cls.debugClassification) {
|
|
339
|
-
if (observationDynamic && buildTimeResult === "static") cls.debugClassification(layoutId, {
|
|
340
|
-
layer: "runtime-probe",
|
|
341
|
-
outcome: "dynamic"
|
|
342
|
-
});
|
|
343
|
-
else cls.debugClassification(layoutId, cls.buildTimeReasons?.get(layoutIndex) ?? { layer: "no-classifier" });
|
|
344
|
-
}
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (cls) {
|
|
348
|
-
const layoutId = cls.getLayoutId(layoutIndex);
|
|
349
|
-
try {
|
|
350
|
-
const { dynamicDetected } = await cls.runWithIsolatedDynamicScope(async () => {
|
|
351
|
-
const outcome = await runWithConnectionProbe(() => options.probeLayoutAt(layoutIndex));
|
|
352
|
-
return outcome.completed ? outcome.result : null;
|
|
353
|
-
});
|
|
354
|
-
const observationDynamic = cls.isLayoutObservationDynamic?.(layoutId) === true;
|
|
355
|
-
const layoutDynamic = dynamicDetected || observationDynamic;
|
|
356
|
-
layoutFlags[layoutId] = layoutDynamic ? "d" : "s";
|
|
357
|
-
if (cls.debugClassification) cls.debugClassification(layoutId, {
|
|
358
|
-
layer: "runtime-probe",
|
|
359
|
-
outcome: layoutDynamic ? "dynamic" : "static"
|
|
360
|
-
});
|
|
361
|
-
} catch (error) {
|
|
362
|
-
layoutFlags[layoutId] = "d";
|
|
363
|
-
if (cls.debugClassification) cls.debugClassification(layoutId, {
|
|
364
|
-
layer: "runtime-probe",
|
|
365
|
-
outcome: "dynamic",
|
|
366
|
-
error: error instanceof Error ? error.message : String(error)
|
|
367
|
-
});
|
|
368
|
-
const errorResponse = await options.onLayoutError(error, layoutIndex);
|
|
369
|
-
if (errorResponse) return errorResponse;
|
|
370
|
-
}
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
const errorResponse = await probeLayoutForErrors(options, layoutIndex);
|
|
374
|
-
if (errorResponse) return errorResponse;
|
|
375
|
-
}
|
|
376
|
-
return null;
|
|
377
|
-
}),
|
|
378
|
-
layoutFlags
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
async function probeLayoutForErrors(options, layoutIndex) {
|
|
382
|
-
const outcome = await runWithConnectionProbe(async () => {
|
|
383
|
-
try {
|
|
384
|
-
const layoutResult = options.probeLayoutAt(layoutIndex);
|
|
385
|
-
if (isPromiseLike(layoutResult)) await layoutResult;
|
|
386
|
-
} catch (error) {
|
|
387
|
-
return options.onLayoutError(error, layoutIndex);
|
|
388
|
-
}
|
|
389
|
-
return null;
|
|
390
|
-
});
|
|
391
|
-
return outcome.completed ? outcome.result : null;
|
|
392
|
-
}
|
|
393
400
|
async function readAppPageBinaryStream(stream) {
|
|
394
401
|
const reader = stream.getReader();
|
|
395
402
|
const chunks = [];
|
|
@@ -434,4 +441,4 @@ function buildAppPageFontLinkHeader(preloads) {
|
|
|
434
441
|
return preloads.map((preload) => `<${preload.href}>; rel=preload; as=font; type=${preload.type}; crossorigin`).join(", ");
|
|
435
442
|
}
|
|
436
443
|
//#endregion
|
|
437
|
-
export { APP_PAGE_NOT_FOUND_BOUNDARY_ID, bufferAppPageBinaryStream, buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse,
|
|
444
|
+
export { APP_PAGE_NOT_FOUND_BOUNDARY_ID, bufferAppPageBinaryStream, buildAppPageFontLinkHeader, buildAppPageSpecialErrorResponse, readAppPageBinaryStream, resolveAppPageCaughtNotFoundBoundaries, resolveAppPageShellSpecialError, resolveAppPageSpecialError, resolveAppPageSpecialErrorStoredHeaders, tagAppPageLayoutError, tagAppPageMetadataError, tagAppPagePageError, tagAppPageSlotError, teeAppPageRscStreamForCapture };
|
|
@@ -21,6 +21,12 @@ export type AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPage
|
|
|
21
21
|
layoutParams?: readonly AppPageParams[] | null;
|
|
22
22
|
layoutModule?: TModule | null;
|
|
23
23
|
layoutModules?: readonly (TModule | null | undefined)[] | null;
|
|
24
|
+
/**
|
|
25
|
+
* Whether each layout is in its page's own loader-tree segment, from the
|
|
26
|
+
* filesystem: route groups are segments, and an intercept's
|
|
27
|
+
* `layoutTreePositions` don't count its page's. Unmarked layouts are above it.
|
|
28
|
+
*/
|
|
29
|
+
layoutsBesidePage?: readonly boolean[] | null;
|
|
24
30
|
layoutTreePositions?: readonly number[] | null;
|
|
25
31
|
moduleRoutePrefixSegments?: readonly string[] | null;
|
|
26
32
|
moduleRouteSegments?: readonly string[] | null;
|
|
@@ -59,6 +65,11 @@ type AppPageHeadSlot<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
|
59
65
|
/** Directory depth of the folder holding the `@slot` directory. */
|
|
60
66
|
ownerTreePosition?: number | null;
|
|
61
67
|
page?: TModule | null;
|
|
68
|
+
/**
|
|
69
|
+
* The active page's tree position below the slot root, route groups
|
|
70
|
+
* included, in the coordinates of `configLayoutTreePositions`.
|
|
71
|
+
*/
|
|
72
|
+
pageTreePosition?: number | null;
|
|
62
73
|
routeSegments?: readonly string[] | null;
|
|
63
74
|
};
|
|
64
75
|
type ResolveActiveParallelRouteHeadInputsOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
@@ -179,6 +190,8 @@ type AppPageSearchParamsCollection = {
|
|
|
179
190
|
hasSearchParams: boolean;
|
|
180
191
|
pageSearchParams: AppPageSearchParams;
|
|
181
192
|
};
|
|
193
|
+
/** Whether each intercepting layout is in its intercepting page's own directory. */
|
|
194
|
+
export declare function resolveInterceptLayoutsBesidePage(layoutSegments: readonly (readonly string[])[] | null | undefined, branchSegments: readonly string[] | null | undefined): boolean[];
|
|
182
195
|
export declare function resolveActiveParallelRouteHeadInputs<TModule extends AppPageHeadModule>(options: ResolveActiveParallelRouteHeadInputsOptions<TModule>): ActiveParallelRouteHeadInput<TModule>[];
|
|
183
196
|
/**
|
|
184
197
|
* Whether a slot intercept keeps the source route's head. Next.js's client
|
|
@@ -3,8 +3,8 @@ import { runWithFetchDedupe } from "../shims/fetch-cache.js";
|
|
|
3
3
|
import { searchParamsToRecord } from "../utils/query.js";
|
|
4
4
|
import { mergeMetadataEntries, mergeParentMetadataEntries, mergeViewport, postProcessMetadata, resolveModuleMetadata as resolveModuleMetadata$1, resolveModuleViewport } from "../shims/metadata.js";
|
|
5
5
|
import { tagAppPageMetadataError } from "./app-page-execution.js";
|
|
6
|
-
import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
7
6
|
import { createAppMetadataModuleRoute, traceGenerateMetadata } from "./app-metadata-tracing.js";
|
|
7
|
+
import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
8
8
|
//#region src/server/app-page-head.ts
|
|
9
9
|
/**
|
|
10
10
|
* Wrapped {@link _resolveModuleMetadata} that tags any thrown error with the
|
|
@@ -25,6 +25,10 @@ async function resolveModuleMetadata(moduleRoute, ...args) {
|
|
|
25
25
|
throw tagAppPageMetadataError(error);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
/** Whether each intercepting layout is in its intercepting page's own directory. */
|
|
29
|
+
function resolveInterceptLayoutsBesidePage(layoutSegments, branchSegments) {
|
|
30
|
+
return (layoutSegments ?? []).map((segments) => segments.length === branchSegments?.length);
|
|
31
|
+
}
|
|
28
32
|
function resolveActiveParallelRouteHeadInputs(options) {
|
|
29
33
|
const slotEntries = Object.entries(options.slots ?? {});
|
|
30
34
|
return slotEntries.map(([slotKey, slot]) => {
|
|
@@ -56,6 +60,7 @@ function resolveActiveParallelRouteHeadInputs(options) {
|
|
|
56
60
|
...resolveParallelLayoutParams(options.interceptBranchSegments ?? segments, segments.length, options.interceptParams ?? options.params)
|
|
57
61
|
};
|
|
58
62
|
})],
|
|
63
|
+
layoutsBesidePage: [...slot.layout ? [false] : [], ...resolveInterceptLayoutsBesidePage(options.interceptLayoutSegments, options.interceptBranchSegments)],
|
|
59
64
|
layoutTreePositions: [...slot.layout ? [0] : [], ...interceptLayouts.filter(isPresent).map(() => options.routeSegments.length)],
|
|
60
65
|
pageModule: options.interceptPage,
|
|
61
66
|
params: options.interceptParams ?? options.params,
|
|
@@ -72,6 +77,8 @@ function resolveActiveParallelRouteHeadInputs(options) {
|
|
|
72
77
|
}
|
|
73
78
|
const slotRouteSegments = slot.routeSegments ?? options.routeSegments;
|
|
74
79
|
const moduleRouteSegments = slot.routeSegments ?? [];
|
|
80
|
+
const layoutTreePositions = [...slot.layout ? [0] : [], ...slot.configLayoutTreePositions ?? []];
|
|
81
|
+
const pageTreePosition = slot.pageTreePosition;
|
|
75
82
|
return {
|
|
76
83
|
head: {
|
|
77
84
|
layoutModules: [slot.layout, ...slot.configLayouts ?? []].filter(isPresent),
|
|
@@ -79,7 +86,8 @@ function resolveActiveParallelRouteHeadInputs(options) {
|
|
|
79
86
|
...ownerParams,
|
|
80
87
|
...resolveParallelLayoutParams(slot.routeSegments ?? options.routeSegments, treePosition, options.slotParams?.[slotKey] ?? options.params)
|
|
81
88
|
}))],
|
|
82
|
-
|
|
89
|
+
...pageTreePosition == null ? {} : { layoutsBesidePage: layoutTreePositions.map((treePosition) => treePosition >= pageTreePosition) },
|
|
90
|
+
layoutTreePositions,
|
|
83
91
|
pageModule: slot.page,
|
|
84
92
|
params: slotParams,
|
|
85
93
|
...ownerRouteSegments.length > 0 ? { moduleRoutePrefixSegments: ownerRouteSegments } : {},
|
|
@@ -219,14 +227,20 @@ function createLayoutInputs(layoutModules, layoutTreePositions) {
|
|
|
219
227
|
}
|
|
220
228
|
return layoutInputs;
|
|
221
229
|
}
|
|
222
|
-
/** The walk's accumulated metadata, with its resolved
|
|
230
|
+
/** The walk's accumulated metadata, with its resolved titles as the shown strings. */
|
|
223
231
|
function toShownMetadata(accumulated) {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
232
|
+
const shown = (title) => typeof title === "object" && title ? title.absolute || void 0 : title;
|
|
233
|
+
const result = { ...accumulated };
|
|
234
|
+
if (typeof accumulated.title === "object") result.title = shown(accumulated.title);
|
|
235
|
+
if (accumulated.openGraph) result.openGraph = {
|
|
236
|
+
...accumulated.openGraph,
|
|
237
|
+
title: shown(accumulated.openGraph.title)
|
|
238
|
+
};
|
|
239
|
+
if (accumulated.twitter) result.twitter = {
|
|
240
|
+
...accumulated.twitter,
|
|
241
|
+
title: shown(accumulated.twitter.title)
|
|
229
242
|
};
|
|
243
|
+
return result;
|
|
230
244
|
}
|
|
231
245
|
function getParallelRouteModules(parallelRoute) {
|
|
232
246
|
return [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
|
|
@@ -236,6 +250,7 @@ function parallelRouteHasDynamicMetadata(parallelRoute) {
|
|
|
236
250
|
}
|
|
237
251
|
function createRouteHeadBranch(layoutInputs, routeSegments, params, pageModule) {
|
|
238
252
|
const segments = layoutInputs.map((layoutInput) => ({
|
|
253
|
+
besidePage: layoutInput.treePosition >= routeSegments.length,
|
|
239
254
|
module: layoutInput.module,
|
|
240
255
|
moduleRoute: createAppMetadataModuleRoute(routeSegments.slice(0, layoutInput.treePosition), "layout"),
|
|
241
256
|
page: false,
|
|
@@ -260,6 +275,7 @@ function createParallelRouteHeadBranch(parallelRoute, fallbackParams, fallbackRo
|
|
|
260
275
|
const layoutTreePositions = parallelRoute.layoutTreePositions ?? [];
|
|
261
276
|
const layoutParams = parallelRoute.layoutParams ?? [];
|
|
262
277
|
const segments = getParallelRouteModules(parallelRoute).map((module, index) => ({
|
|
278
|
+
besidePage: parallelRoute.layoutsBesidePage?.[index] ?? false,
|
|
263
279
|
module,
|
|
264
280
|
moduleRoute: createAppMetadataModuleRoute([...moduleRoutePrefixSegments, ...moduleRouteSegments.slice(0, layoutTreePositions[index] ?? 0)], "layout"),
|
|
265
281
|
page: false,
|
|
@@ -296,14 +312,14 @@ function createHiddenHeadBranch(hidden, slot, fallbackParams, fallbackRouteSegme
|
|
|
296
312
|
* With `deferParentErrors`, `error` never waits on `parent`: what can only be
|
|
297
313
|
* known after it goes to `deferredError` instead.
|
|
298
314
|
*/
|
|
299
|
-
function startAppPageHeadItem(kind, segment, parent, deferParentErrors = false) {
|
|
315
|
+
function startAppPageHeadItem(kind, segment, parent, deferParentErrors = false, stashed = parent) {
|
|
300
316
|
const observedParent = observeParent(parent);
|
|
301
317
|
const parentRead = new Promise((resolve) => {
|
|
302
318
|
observedParent.onRead = () => resolve(null);
|
|
303
319
|
});
|
|
304
320
|
const result = kind.resolve(segment, observedParent);
|
|
305
321
|
result.catch(() => null);
|
|
306
|
-
const resolved = parent.then((resolvedParent) => result.then((value) => value ? kind.merge(resolvedParent, value) : resolvedParent));
|
|
322
|
+
const resolved = parent.then((resolvedParent) => stashed.then((resolvedStashed) => result.then((value) => value ? kind.merge(resolvedParent, value, resolvedStashed) : resolvedParent)));
|
|
307
323
|
resolved.catch(() => null);
|
|
308
324
|
const mergeError = result.then((value) => value ? parent.then(() => resolved.then(() => null), () => null) : null, () => null);
|
|
309
325
|
const ownError = result.then(() => null, (error) => {
|
|
@@ -362,8 +378,12 @@ function walkAppPageHead(kind, route, slots, hiddenBranches, outletIndex) {
|
|
|
362
378
|
let resolved = Promise.resolve(kind.initial);
|
|
363
379
|
let slotBranchParent = null;
|
|
364
380
|
let afterOutlet = false;
|
|
365
|
-
const
|
|
366
|
-
|
|
381
|
+
const lastBranch = slots.at(-1) ?? route;
|
|
382
|
+
const lastPage = lastBranch.segments.at(-1)?.page ? lastBranch.segments.at(-1) : void 0;
|
|
383
|
+
const lastPageLayout = lastPage && lastBranch.segments.at(-2);
|
|
384
|
+
let beforeLastPageLayout;
|
|
385
|
+
const start = (segment, parent, stashed) => {
|
|
386
|
+
const item = startAppPageHeadItem(kind, segment, parent, !slotBranchParent && afterOutlet, stashed);
|
|
367
387
|
if (item.deferredError) deferredErrors.push(item.deferredError);
|
|
368
388
|
if (slotBranchParent) {
|
|
369
389
|
const error = slotBranchParent.then(() => item.error, () => null);
|
|
@@ -379,7 +399,8 @@ function walkAppPageHead(kind, route, slots, hiddenBranches, outletIndex) {
|
|
|
379
399
|
return item;
|
|
380
400
|
};
|
|
381
401
|
const startShown = (segment) => {
|
|
382
|
-
|
|
402
|
+
if (segment === lastPageLayout && segment.besidePage) beforeLastPageLayout = resolved;
|
|
403
|
+
const item = start(segment, resolved, segment === lastPage ? beforeLastPageLayout : void 0);
|
|
383
404
|
resolved = item.resolved;
|
|
384
405
|
return item.result;
|
|
385
406
|
};
|
|
@@ -489,9 +510,16 @@ function prepareAppPageHeadInner(options) {
|
|
|
489
510
|
const segmentSearchParamsObserver = (segment) => segment.page ? options.searchParamsObserver : void 0;
|
|
490
511
|
const metadataWalk = walkAppPageHead({
|
|
491
512
|
initial: {},
|
|
492
|
-
merge: (resolved, metadata) => {
|
|
513
|
+
merge: (resolved, metadata, stashed) => {
|
|
493
514
|
try {
|
|
494
|
-
return mergeParentMetadataEntries([{ metadata: resolved }, { metadata }]
|
|
515
|
+
return mergeParentMetadataEntries(resolved === stashed ? [{ metadata: resolved }, { metadata }] : [
|
|
516
|
+
{ metadata: stashed },
|
|
517
|
+
{
|
|
518
|
+
isPage: true,
|
|
519
|
+
metadata: resolved
|
|
520
|
+
},
|
|
521
|
+
{ metadata }
|
|
522
|
+
]);
|
|
495
523
|
} catch (error) {
|
|
496
524
|
throw tagAppPageMetadataError(error);
|
|
497
525
|
}
|
|
@@ -540,4 +568,4 @@ function prepareAppPageHeadInner(options) {
|
|
|
540
568
|
};
|
|
541
569
|
}
|
|
542
570
|
//#endregion
|
|
543
|
-
export { collectAppPageSearchParams, hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveOrderedAppPageMetadata, slotInterceptKeepsSourceHead };
|
|
571
|
+
export { collectAppPageSearchParams, hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveInterceptLayoutsBesidePage, resolveOrderedAppPageMetadata, slotInterceptKeepsSourceHead };
|
|
@@ -33,6 +33,7 @@ type RouteNotFoundHeadPlanRoute<TModule extends AppPageHeadModule> = {
|
|
|
33
33
|
notFounds?: readonly (TModule | null | undefined)[] | null;
|
|
34
34
|
notFoundTreePosition?: number | null;
|
|
35
35
|
routeSegments?: readonly string[] | null;
|
|
36
|
+
segmentNotFoundTreePositions?: readonly number[] | null;
|
|
36
37
|
};
|
|
37
38
|
/**
|
|
38
39
|
* The not-found head a route's own head falls back to. Like Next.js's
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isStaticGenerationAccessError } from "../shims/internal/headers-state.js";
|
|
2
2
|
import { mergeViewport, resolveModuleViewport } from "../shims/metadata.js";
|
|
3
3
|
import { resolveAppPageSpecialError } from "./app-page-execution.js";
|
|
4
|
-
import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
5
4
|
import { createAppMetadataModuleRoute } from "./app-metadata-tracing.js";
|
|
5
|
+
import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
6
6
|
import { resolveOrderedAppPageMetadata } from "./app-page-head.js";
|
|
7
7
|
import { resolveAppPageParentHttpAccessBoundary } from "./app-page-boundary.js";
|
|
8
8
|
//#region src/server/app-page-http-access-fallback-metadata.ts
|
|
@@ -124,7 +124,7 @@ function createRouteNotFoundHeadPlanOptions(options) {
|
|
|
124
124
|
if (!boundaryModule) {
|
|
125
125
|
const parentBoundary = resolveAppPageParentHttpAccessBoundary({
|
|
126
126
|
layoutIndex: layoutModules.length,
|
|
127
|
-
rootNotFoundModule: options.rootNotFoundModule,
|
|
127
|
+
rootNotFoundModule: route.segmentNotFoundTreePositions ? null : options.rootNotFoundModule,
|
|
128
128
|
routeNotFoundModules: route.notFounds,
|
|
129
129
|
statusCode: 404
|
|
130
130
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/server/app-page-layout-gate.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Holds an RSC response until the layouts above the route's first loading
|
|
4
|
+
* boundary have rendered, so that a layout's redirect(), notFound(),
|
|
5
|
+
* forbidden() or unauthorized() still answers the request: a redirect through
|
|
6
|
+
* the `X-Vinext-Rsc-Redirect` side channel, and the others with their
|
|
7
|
+
* boundary's status. The layouts render once, in the render itself. A
|
|
8
|
+
* signal from a component a layout renders streams in the payload, as in
|
|
9
|
+
* Next.js.
|
|
10
|
+
*/
|
|
11
|
+
export type AppPageLayoutGate = {
|
|
12
|
+
/** Holds the response until the layout with `layoutId` has rendered. */
|
|
13
|
+
hold(layoutId: string, layoutIndex: number): void;
|
|
14
|
+
/** Reports how the layout with `layoutId` rendered. */
|
|
15
|
+
settle(layoutId: string, failure?: {
|
|
16
|
+
error: unknown;
|
|
17
|
+
}): void;
|
|
18
|
+
/**
|
|
19
|
+
* The outermost held layout that threw, once every held layout `isRendered`
|
|
20
|
+
* includes has rendered, or null. An error elsewhere in the render doesn't
|
|
21
|
+
* stop the wait, as Flight goes on to render the other entries, but the
|
|
22
|
+
* render ending does: `ended` settles when its stream closes or errors.
|
|
23
|
+
*/
|
|
24
|
+
wait(isRendered: (layoutId: string) => boolean, ended: Promise<unknown>): Promise<AppPageLayoutGateFailure | null>;
|
|
25
|
+
};
|
|
26
|
+
type AppPageLayoutGateFailure = {
|
|
27
|
+
error: unknown;
|
|
28
|
+
layoutIndex: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function createAppPageLayoutGate(): AppPageLayoutGate;
|
|
31
|
+
export declare function registerAppPageLayoutGate(elements: object, gate: AppPageLayoutGate): void;
|
|
32
|
+
/** The gate of the layouts in `element`, an App Router elements record. */
|
|
33
|
+
export declare function getAppPageLayoutGate(element: unknown): AppPageLayoutGate | null;
|
|
34
|
+
//#endregion
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/server/app-page-layout-gate.ts
|
|
2
|
+
function createAppPageLayoutGate() {
|
|
3
|
+
const held = /* @__PURE__ */ new Map();
|
|
4
|
+
const settled = /* @__PURE__ */ new Set();
|
|
5
|
+
let failure = null;
|
|
6
|
+
let ended = false;
|
|
7
|
+
let check = null;
|
|
8
|
+
return {
|
|
9
|
+
hold(layoutId, layoutIndex) {
|
|
10
|
+
held.set(layoutId, layoutIndex);
|
|
11
|
+
},
|
|
12
|
+
settle(layoutId, layoutFailure) {
|
|
13
|
+
const layoutIndex = held.get(layoutId);
|
|
14
|
+
if (layoutIndex === void 0 || settled.has(layoutId)) return;
|
|
15
|
+
settled.add(layoutId);
|
|
16
|
+
if (layoutFailure) {
|
|
17
|
+
if (failure === null || layoutIndex < failure.layoutIndex) failure = {
|
|
18
|
+
error: layoutFailure.error,
|
|
19
|
+
layoutIndex
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
check?.();
|
|
23
|
+
},
|
|
24
|
+
wait(isRendered, renderEnded) {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
check = () => {
|
|
27
|
+
for (const layoutId of held.keys()) if (!ended && !settled.has(layoutId) && isRendered(layoutId)) return;
|
|
28
|
+
check = null;
|
|
29
|
+
resolve(failure);
|
|
30
|
+
};
|
|
31
|
+
const end = () => {
|
|
32
|
+
ended = true;
|
|
33
|
+
check?.();
|
|
34
|
+
};
|
|
35
|
+
renderEnded.then(end, end);
|
|
36
|
+
check();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const appPageLayoutGates = /* @__PURE__ */ new WeakMap();
|
|
42
|
+
function registerAppPageLayoutGate(elements, gate) {
|
|
43
|
+
appPageLayoutGates.set(elements, gate);
|
|
44
|
+
}
|
|
45
|
+
/** The gate of the layouts in `element`, an App Router elements record. */
|
|
46
|
+
function getAppPageLayoutGate(element) {
|
|
47
|
+
return typeof element === "object" && element !== null ? appPageLayoutGates.get(element) ?? null : null;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { createAppPageLayoutGate, getAppPageLayoutGate, registerAppPageLayoutGate };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
2
2
|
//#region src/server/app-page-params.d.ts
|
|
3
3
|
export declare function getAppPageSegmentParamName(segment: string): string | null;
|
|
4
|
-
export declare function resolveAppPageSegmentParamScopeKeys(routeSegments: readonly string[] | null | undefined, treePosition: number): readonly string[];
|
|
5
4
|
export declare function resolveAppPageSegmentParams(routeSegments: readonly string[] | null | undefined, treePosition: number, matchedParams: AppPageParams): AppPageParams;
|
|
6
5
|
export declare function resolveAppPageBranchParams(branchSegments: readonly string[], treePosition: number, matchedParams: AppPageParams, scopedSegments?: readonly string[]): AppPageParams;
|
|
7
6
|
//#endregion
|
|
@@ -8,19 +8,6 @@ function getAppPageSegmentParamName(segment) {
|
|
|
8
8
|
function isEmptyOptionalCatchAll(segment, paramValue) {
|
|
9
9
|
return segment.startsWith("[[...") && Array.isArray(paramValue) && paramValue.length === 0;
|
|
10
10
|
}
|
|
11
|
-
function resolveAppPageSegmentParamScopeKeys(routeSegments, treePosition) {
|
|
12
|
-
const paramNames = [];
|
|
13
|
-
const seen = /* @__PURE__ */ new Set();
|
|
14
|
-
const segments = routeSegments ?? [];
|
|
15
|
-
const end = Math.min(Math.max(treePosition, 0), segments.length);
|
|
16
|
-
for (let index = 0; index < end; index++) {
|
|
17
|
-
const paramName = getAppPageSegmentParamName(segments[index]);
|
|
18
|
-
if (!paramName || seen.has(paramName)) continue;
|
|
19
|
-
seen.add(paramName);
|
|
20
|
-
paramNames.push(paramName);
|
|
21
|
-
}
|
|
22
|
-
return paramNames;
|
|
23
|
-
}
|
|
24
11
|
function resolveAppPageSegmentParams(routeSegments, treePosition, matchedParams) {
|
|
25
12
|
const segmentParams = {};
|
|
26
13
|
const segments = routeSegments ?? [];
|
|
@@ -43,4 +30,4 @@ function resolveAppPageBranchParams(branchSegments, treePosition, matchedParams,
|
|
|
43
30
|
return scopedParams;
|
|
44
31
|
}
|
|
45
32
|
//#endregion
|
|
46
|
-
export { getAppPageSegmentParamName, resolveAppPageBranchParams,
|
|
33
|
+
export { getAppPageSegmentParamName, resolveAppPageBranchParams, resolveAppPageSegmentParams };
|
|
@@ -1,31 +1,13 @@
|
|
|
1
|
-
import { LayoutFlags } from "./app-elements-wire.js";
|
|
2
|
-
import { AppPageSpecialError, LayoutClassificationOptions } from "./app-page-execution.js";
|
|
3
1
|
//#region src/server/app-page-probe.d.ts
|
|
4
2
|
type ProbeReactServerSubtreeOptions = Readonly<{
|
|
5
3
|
maxDepth?: number;
|
|
6
4
|
maxNodes?: number;
|
|
7
5
|
}>;
|
|
8
6
|
/**
|
|
9
|
-
* Invokes server
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* failures only make static-layout skip fall back to render-and-send.
|
|
7
|
+
* Invokes the server components a layout renders, so the error-path layout
|
|
8
|
+
* probe sees an HTTP access fallback thrown below the layout's own function
|
|
9
|
+
* body. The real RSC render remains authoritative.
|
|
13
10
|
*/
|
|
14
11
|
export declare function probeReactServerSubtree(node: unknown, options?: ProbeReactServerSubtreeOptions): Promise<void>;
|
|
15
|
-
type ProbeAppPageBeforeRenderResult = {
|
|
16
|
-
response: Response | null;
|
|
17
|
-
layoutFlags: LayoutFlags;
|
|
18
|
-
};
|
|
19
|
-
type ProbeAppPageBeforeRenderOptions = {
|
|
20
|
-
skipProbes?: boolean;
|
|
21
|
-
layoutCount: number;
|
|
22
|
-
probeLayoutAt: (layoutIndex: number) => unknown;
|
|
23
|
-
renderLayoutSpecialError: (specialError: AppPageSpecialError, layoutIndex: number) => Promise<Response>;
|
|
24
|
-
resolveSpecialError: (error: unknown) => AppPageSpecialError | null;
|
|
25
|
-
runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;
|
|
26
|
-
/** When provided, enables per-layout static/dynamic classification. */
|
|
27
|
-
classification?: LayoutClassificationOptions | null;
|
|
28
|
-
};
|
|
29
|
-
export declare function probeAppPageBeforeRender(options: ProbeAppPageBeforeRenderOptions): Promise<ProbeAppPageBeforeRenderResult>;
|
|
30
12
|
//#endregion
|
|
31
13
|
export {};
|