vinext 0.1.2 → 0.1.4
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/dist/build/client-build-config.d.ts +11 -2
- package/dist/build/client-build-config.js +17 -6
- package/dist/build/prerender.d.ts +9 -1
- package/dist/build/prerender.js +42 -12
- package/dist/build/run-prerender.d.ts +10 -2
- package/dist/build/run-prerender.js +15 -1
- package/dist/client/app-nav-failure-handler.d.ts +8 -0
- package/dist/client/app-nav-failure-handler.js +44 -0
- package/dist/client/pages-router-link-navigation.d.ts +33 -7
- package/dist/client/pages-router-link-navigation.js +32 -2
- package/dist/client/vinext-next-data.d.ts +18 -1
- package/dist/client/vinext-next-data.js +2 -0
- package/dist/client/window-next.d.ts +2 -1
- package/dist/client/window-next.js +12 -1
- package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
- package/dist/config/config-matchers.d.ts +11 -1
- package/dist/config/config-matchers.js +87 -16
- package/dist/config/next-config.d.ts +46 -4
- package/dist/config/next-config.js +147 -48
- package/dist/config/tsconfig-paths.js +14 -1
- package/dist/deploy.d.ts +30 -11
- package/dist/deploy.js +200 -112
- package/dist/entries/app-browser-entry.d.ts +9 -3
- package/dist/entries/app-browser-entry.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +65 -5
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/app-ssr-entry.js +1 -1
- package/dist/entries/pages-client-entry.js +66 -20
- package/dist/entries/pages-server-entry.js +47 -31
- package/dist/index.js +417 -102
- package/dist/plugins/dynamic-preload-metadata.js +2 -4
- package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
- package/dist/plugins/extensionless-dynamic-import.js +152 -0
- package/dist/plugins/fonts.js +5 -4
- package/dist/plugins/optimize-imports.d.ts +2 -1
- package/dist/plugins/optimize-imports.js +11 -9
- package/dist/plugins/postcss.js +7 -7
- package/dist/plugins/strip-server-exports.d.ts +9 -7
- package/dist/plugins/strip-server-exports.js +493 -46
- package/dist/plugins/typeof-window.d.ts +14 -0
- package/dist/plugins/typeof-window.js +150 -0
- package/dist/routing/app-route-graph.d.ts +2 -1
- package/dist/routing/app-route-graph.js +46 -16
- package/dist/routing/file-matcher.d.ts +10 -1
- package/dist/routing/file-matcher.js +22 -1
- package/dist/routing/pages-router.js +3 -3
- package/dist/routing/utils.d.ts +35 -6
- package/dist/routing/utils.js +59 -7
- package/dist/server/api-handler.d.ts +6 -1
- package/dist/server/api-handler.js +21 -15
- package/dist/server/app-browser-action-result.d.ts +19 -6
- package/dist/server/app-browser-action-result.js +20 -11
- package/dist/server/app-browser-entry.js +175 -91
- package/dist/server/app-browser-error.d.ts +10 -6
- package/dist/server/app-browser-error.js +43 -8
- package/dist/server/app-browser-hydration.d.ts +2 -0
- package/dist/server/app-browser-hydration.js +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +5 -3
- package/dist/server/app-browser-navigation-controller.js +23 -2
- package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
- package/dist/server/app-browser-server-action-navigation.js +9 -0
- package/dist/server/app-browser-state.d.ts +1 -1
- package/dist/server/app-browser-state.js +19 -11
- package/dist/server/app-browser-stream.js +86 -43
- package/dist/server/app-browser-visible-commit.d.ts +1 -1
- package/dist/server/app-elements-wire.d.ts +6 -1
- package/dist/server/app-elements-wire.js +14 -4
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-fallback-renderer.d.ts +1 -0
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-optimistic-routing.js +2 -2
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +27 -14
- package/dist/server/app-page-cache-render.d.ts +53 -0
- package/dist/server/app-page-cache-render.js +91 -0
- package/dist/server/app-page-cache.d.ts +16 -2
- package/dist/server/app-page-cache.js +62 -1
- package/dist/server/app-page-dispatch.d.ts +26 -0
- package/dist/server/app-page-dispatch.js +149 -92
- package/dist/server/app-page-element-builder.d.ts +1 -0
- package/dist/server/app-page-element-builder.js +5 -2
- package/dist/server/app-page-execution.d.ts +6 -1
- package/dist/server/app-page-execution.js +21 -1
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +4 -0
- package/dist/server/app-page-render-observation.d.ts +3 -1
- package/dist/server/app-page-render-observation.js +17 -1
- package/dist/server/app-page-render.d.ts +12 -1
- package/dist/server/app-page-render.js +42 -4
- package/dist/server/app-page-request.d.ts +2 -0
- package/dist/server/app-page-request.js +2 -1
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +14 -5
- package/dist/server/app-page-stream.d.ts +15 -3
- package/dist/server/app-page-stream.js +11 -5
- package/dist/server/app-pages-bridge.d.ts +23 -1
- package/dist/server/app-pages-bridge.js +26 -17
- package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
- package/dist/server/app-ppr-fallback-shell-render.js +26 -0
- package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
- package/dist/server/app-ppr-fallback-shell.js +8 -1
- package/dist/server/app-route-handler-dispatch.js +9 -2
- package/dist/server/app-route-handler-policy.d.ts +1 -0
- package/dist/server/app-router-entry.js +5 -0
- package/dist/server/app-rsc-cache-busting.js +2 -0
- package/dist/server/app-rsc-handler.d.ts +28 -0
- package/dist/server/app-rsc-handler.js +195 -59
- package/dist/server/app-rsc-route-matching.d.ts +3 -0
- package/dist/server/app-rsc-route-matching.js +8 -2
- package/dist/server/app-segment-config.d.ts +9 -1
- package/dist/server/app-segment-config.js +12 -3
- package/dist/server/app-server-action-execution.d.ts +1 -0
- package/dist/server/app-server-action-execution.js +47 -15
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +84 -39
- package/dist/server/before-interactive-head.d.ts +17 -0
- package/dist/server/before-interactive-head.js +35 -0
- package/dist/server/cache-control.js +4 -0
- package/dist/server/csp.js +1 -4
- package/dist/server/dev-server.d.ts +2 -2
- package/dist/server/dev-server.js +321 -83
- package/dist/server/hybrid-route-priority.d.ts +22 -0
- package/dist/server/hybrid-route-priority.js +33 -0
- package/dist/server/image-optimization.d.ts +18 -9
- package/dist/server/image-optimization.js +37 -23
- package/dist/server/implicit-tags.d.ts +2 -1
- package/dist/server/implicit-tags.js +4 -1
- package/dist/server/middleware-matcher.js +12 -3
- package/dist/server/middleware-runtime.d.ts +3 -4
- package/dist/server/middleware-runtime.js +2 -0
- package/dist/server/navigation-planner.d.ts +135 -41
- package/dist/server/navigation-planner.js +138 -0
- package/dist/server/navigation-trace.d.ts +9 -1
- package/dist/server/navigation-trace.js +9 -1
- package/dist/server/operation-token.d.ts +40 -0
- package/dist/server/operation-token.js +85 -0
- package/dist/server/pages-api-route.d.ts +6 -0
- package/dist/server/pages-api-route.js +13 -2
- package/dist/server/pages-asset-tags.d.ts +2 -1
- package/dist/server/pages-asset-tags.js +6 -2
- package/dist/server/pages-data-route.d.ts +9 -2
- package/dist/server/pages-data-route.js +18 -6
- package/dist/server/pages-dev-module-url.d.ts +4 -0
- package/dist/server/pages-dev-module-url.js +15 -0
- package/dist/server/pages-document-initial-props.d.ts +4 -15
- package/dist/server/pages-document-initial-props.js +27 -56
- package/dist/server/pages-get-initial-props.d.ts +54 -4
- package/dist/server/pages-get-initial-props.js +43 -1
- package/dist/server/pages-i18n.js +2 -2
- package/dist/server/pages-node-compat.js +2 -2
- package/dist/server/pages-page-data.d.ts +11 -2
- package/dist/server/pages-page-data.js +207 -34
- package/dist/server/pages-page-handler.d.ts +4 -2
- package/dist/server/pages-page-handler.js +62 -23
- package/dist/server/pages-page-response.d.ts +4 -1
- package/dist/server/pages-page-response.js +11 -8
- package/dist/server/pages-readiness.js +1 -1
- package/dist/server/pages-request-pipeline.d.ts +8 -7
- package/dist/server/pages-request-pipeline.js +126 -47
- package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
- package/dist/server/pregenerated-concrete-paths.js +2 -19
- package/dist/server/prerender-manifest.d.ts +33 -0
- package/dist/server/prerender-manifest.js +54 -0
- package/dist/server/prerender-route-params.d.ts +1 -2
- package/dist/server/prod-server.d.ts +3 -1
- package/dist/server/prod-server.js +50 -13
- package/dist/server/request-pipeline.d.ts +3 -15
- package/dist/server/request-pipeline.js +58 -47
- package/dist/server/rsc-stream-hints.d.ts +5 -1
- package/dist/server/rsc-stream-hints.js +6 -1
- package/dist/server/seed-cache.js +10 -18
- package/dist/server/static-file-cache.js +16 -4
- package/dist/shims/app-router-scroll-state.d.ts +3 -1
- package/dist/shims/app-router-scroll-state.js +14 -2
- package/dist/shims/app-router-scroll.d.ts +3 -0
- package/dist/shims/app-router-scroll.js +28 -18
- package/dist/shims/before-interactive-context.d.ts +14 -3
- package/dist/shims/cache-runtime.js +3 -2
- package/dist/shims/cache.d.ts +1 -0
- package/dist/shims/cache.js +1 -1
- package/dist/shims/cdn-cache.d.ts +5 -5
- package/dist/shims/document.d.ts +15 -20
- package/dist/shims/document.js +5 -8
- package/dist/shims/dynamic-preload-chunks.js +6 -4
- package/dist/shims/error-boundary.d.ts +2 -0
- package/dist/shims/error-boundary.js +7 -0
- package/dist/shims/error.js +3 -2
- package/dist/shims/error.react-server.d.ts +9 -0
- package/dist/shims/error.react-server.js +6 -0
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +45 -20
- package/dist/shims/hash-scroll.js +6 -1
- package/dist/shims/headers.js +29 -4
- package/dist/shims/image.js +9 -2
- package/dist/shims/internal/als-registry.js +28 -1
- package/dist/shims/internal/app-route-detection.js +8 -17
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
- package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
- package/dist/shims/internal/navigation-untracked.d.ts +35 -0
- package/dist/shims/internal/navigation-untracked.js +55 -0
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +6 -7
- package/dist/shims/internal/pages-data-fetch-dedup.js +67 -14
- package/dist/shims/internal/pages-data-target.d.ts +7 -2
- package/dist/shims/internal/pages-data-target.js +17 -8
- package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
- package/dist/shims/internal/pages-router-accessor.js +13 -0
- package/dist/shims/internal/router-context.d.ts +2 -1
- package/dist/shims/internal/router-context.js +3 -1
- package/dist/shims/link.js +47 -19
- package/dist/shims/metadata.js +4 -4
- package/dist/shims/navigation.d.ts +8 -2
- package/dist/shims/navigation.js +63 -31
- package/dist/shims/ppr-fallback-shell.d.ts +5 -1
- package/dist/shims/ppr-fallback-shell.js +28 -7
- package/dist/shims/router.d.ts +18 -3
- package/dist/shims/router.js +512 -142
- package/dist/shims/script.js +8 -4
- package/dist/shims/server.d.ts +16 -1
- package/dist/shims/server.js +44 -12
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/built-asset-url.d.ts +4 -0
- package/dist/utils/built-asset-url.js +11 -0
- package/dist/utils/commonjs-loader.d.ts +16 -0
- package/dist/utils/commonjs-loader.js +100 -0
- package/dist/utils/deployment-id.d.ts +8 -0
- package/dist/utils/deployment-id.js +22 -0
- package/dist/utils/has-trailing-comma.d.ts +24 -0
- package/dist/utils/has-trailing-comma.js +62 -0
- package/dist/utils/html-limited-bots.d.ts +18 -1
- package/dist/utils/html-limited-bots.js +23 -1
- package/dist/utils/parse-cookie.d.ts +13 -0
- package/dist/utils/parse-cookie.js +52 -0
- package/dist/utils/path.d.ts +7 -1
- package/dist/utils/path.js +9 -1
- package/dist/utils/text-stream.d.ts +1 -1
- package/dist/utils/text-stream.js +2 -2
- package/dist/utils/vite-version.d.ts +12 -1
- package/dist/utils/vite-version.js +9 -1
- package/package.json +2 -2
- package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
- package/dist/shims/internal/parse-cookie-header.js +0 -30
|
@@ -109,11 +109,42 @@ async function importServerEntryModule(entryPath) {
|
|
|
109
109
|
}
|
|
110
110
|
/** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
|
|
111
111
|
function readNodeStream(req) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
let cancelled = false;
|
|
113
|
+
let cleanup = () => {};
|
|
114
|
+
return new ReadableStream({
|
|
115
|
+
start(controller) {
|
|
116
|
+
cleanup = () => {
|
|
117
|
+
req.off("data", onData);
|
|
118
|
+
req.off("end", onEnd);
|
|
119
|
+
req.off("error", onError);
|
|
120
|
+
};
|
|
121
|
+
const onData = (chunk) => {
|
|
122
|
+
if (cancelled) return;
|
|
123
|
+
controller.enqueue(new Uint8Array(chunk));
|
|
124
|
+
if ((controller.desiredSize ?? 0) <= 0) req.pause();
|
|
125
|
+
};
|
|
126
|
+
const onEnd = () => {
|
|
127
|
+
cleanup();
|
|
128
|
+
if (!cancelled) controller.close();
|
|
129
|
+
};
|
|
130
|
+
const onError = (error) => {
|
|
131
|
+
cleanup();
|
|
132
|
+
if (!cancelled) controller.error(error);
|
|
133
|
+
};
|
|
134
|
+
req.on("data", onData);
|
|
135
|
+
req.on("end", onEnd);
|
|
136
|
+
req.on("error", onError);
|
|
137
|
+
req.pause();
|
|
138
|
+
},
|
|
139
|
+
pull() {
|
|
140
|
+
if (!cancelled) req.resume();
|
|
141
|
+
},
|
|
142
|
+
cancel() {
|
|
143
|
+
cancelled = true;
|
|
144
|
+
cleanup();
|
|
145
|
+
req.resume();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
117
148
|
}
|
|
118
149
|
/** Content types that benefit from compression. */
|
|
119
150
|
const COMPRESSIBLE_TYPES = new Set([
|
|
@@ -201,6 +232,7 @@ function toWebHeaders(headersRecord) {
|
|
|
201
232
|
}
|
|
202
233
|
function appendWebHeader(headers, key, value) {
|
|
203
234
|
if (value === void 0) return;
|
|
235
|
+
if (key.startsWith(":")) return;
|
|
204
236
|
if (Array.isArray(value)) {
|
|
205
237
|
for (const item of value) headers.append(key, item);
|
|
206
238
|
return;
|
|
@@ -513,7 +545,7 @@ function nodeToWebRequest(req, urlOverride, prerenderSecret) {
|
|
|
513
545
|
headers
|
|
514
546
|
};
|
|
515
547
|
if (hasBody) {
|
|
516
|
-
init.body =
|
|
548
|
+
init.body = readNodeStream(req);
|
|
517
549
|
init.duplex = "half";
|
|
518
550
|
}
|
|
519
551
|
return new Request(url, init);
|
|
@@ -783,7 +815,7 @@ async function startAppRouterServer(options) {
|
|
|
783
815
|
}
|
|
784
816
|
}
|
|
785
817
|
if (isImageOptimizationPath(pathname)) {
|
|
786
|
-
const params = parseImageParams(new URL(rawUrl, "http://localhost"), [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES]);
|
|
818
|
+
const params = parseImageParams(new URL(rawUrl, "http://localhost"), [...imageConfig?.deviceSizes ?? DEFAULT_DEVICE_SIZES, ...imageConfig?.imageSizes ?? DEFAULT_IMAGE_SIZES], imageConfig?.qualities);
|
|
787
819
|
if (!params) {
|
|
788
820
|
res.writeHead(400);
|
|
789
821
|
res.end("Bad Request");
|
|
@@ -891,6 +923,7 @@ async function startPagesRouterServer(options) {
|
|
|
891
923
|
const pagesImageConfig = vinextConfig?.images ? {
|
|
892
924
|
dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
|
|
893
925
|
dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
|
|
926
|
+
qualities: vinextConfig.images.qualities,
|
|
894
927
|
contentDispositionType: vinextConfig.images.contentDispositionType,
|
|
895
928
|
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
|
|
896
929
|
} : void 0;
|
|
@@ -960,7 +993,7 @@ async function startPagesRouterServer(options) {
|
|
|
960
993
|
return;
|
|
961
994
|
}
|
|
962
995
|
if (isImageOptimizationPath(pathname) || isImageOptimizationPath(staticLookupPath)) {
|
|
963
|
-
const params = parseImageParams(new URL(rawUrl, "http://localhost"), allowedImageWidths);
|
|
996
|
+
const params = parseImageParams(new URL(rawUrl, "http://localhost"), allowedImageWidths, pagesImageConfig?.qualities);
|
|
964
997
|
if (!params) {
|
|
965
998
|
res.writeHead(400);
|
|
966
999
|
res.end("Bad Request");
|
|
@@ -991,6 +1024,7 @@ async function startPagesRouterServer(options) {
|
|
|
991
1024
|
}
|
|
992
1025
|
}
|
|
993
1026
|
let isDataReq = false;
|
|
1027
|
+
const originalRenderUrl = url;
|
|
994
1028
|
if (isNextDataPathname(pathname)) {
|
|
995
1029
|
const dataMatch = pagesBuildId ? parseNextDataPathname(pathname, pagesBuildId) : null;
|
|
996
1030
|
if (!dataMatch) {
|
|
@@ -1005,7 +1039,7 @@ async function startPagesRouterServer(options) {
|
|
|
1005
1039
|
const protocol = resolveRequestProtocol(req);
|
|
1006
1040
|
const hostHeader = resolveRequestHost(req, `${host}:${port}`);
|
|
1007
1041
|
const rawReqHeaders = nodeHeadersToWebHeaders(req.headers);
|
|
1008
|
-
const isDataRequest =
|
|
1042
|
+
const isDataRequest = isDataReq;
|
|
1009
1043
|
const reqHeaders = filterInternalHeaders(rawReqHeaders);
|
|
1010
1044
|
const method = req.method ?? "GET";
|
|
1011
1045
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
@@ -1028,10 +1062,13 @@ async function startPagesRouterServer(options) {
|
|
|
1028
1062
|
rawSearch: rawQs,
|
|
1029
1063
|
matchPageRoute: matchPageRoute ?? null,
|
|
1030
1064
|
runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
|
|
1031
|
-
renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders,
|
|
1065
|
+
renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, {
|
|
1066
|
+
...options,
|
|
1067
|
+
originalUrl: originalRenderUrl
|
|
1068
|
+
}) : null,
|
|
1032
1069
|
handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
|
|
1033
|
-
|
|
1034
|
-
if (requestPathname === "/" || requestPathname.startsWith("/api/") || requestPathname.startsWith(`/_next/static/`)) return false;
|
|
1070
|
+
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase) => {
|
|
1071
|
+
if (req.method !== "GET" && req.method !== "HEAD" || requestPathname === "/" || requestPathname === "/api" || requestPathname.startsWith("/api/") || phase === "direct" && requestPathname.startsWith(`/_next/static/`)) return false;
|
|
1035
1072
|
return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);
|
|
1036
1073
|
}
|
|
1037
1074
|
});
|
|
@@ -1082,4 +1119,4 @@ async function startPagesRouterServer(options) {
|
|
|
1082
1119
|
};
|
|
1083
1120
|
}
|
|
1084
1121
|
//#endregion
|
|
1085
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
1122
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
@@ -76,7 +76,8 @@ type ApplyConfigHeadersOptions = {
|
|
|
76
76
|
* (basePath: true) rule for backward compatibility — callers that need to
|
|
77
77
|
* support `basePath: false` headers must pass this in.
|
|
78
78
|
*/
|
|
79
|
-
basePathState?: BasePathMatchState;
|
|
79
|
+
basePathState?: BasePathMatchState; /** Existing framework-generated headers that matching config rules may replace. */
|
|
80
|
+
overwriteExisting?: ReadonlySet<string>;
|
|
80
81
|
};
|
|
81
82
|
type StaticFileSignalContext = {
|
|
82
83
|
headers: Headers | null;
|
|
@@ -157,19 +158,6 @@ declare function validateCsrfOrigin(request: Request, allowedOrigins?: string[])
|
|
|
157
158
|
*/
|
|
158
159
|
declare function validateServerActionPayload(body: string | FormData): Promise<Response | null>;
|
|
159
160
|
declare function isOriginAllowed(origin: string, allowed: string[]): boolean;
|
|
160
|
-
/**
|
|
161
|
-
* Validate an image optimization URL parameter.
|
|
162
|
-
*
|
|
163
|
-
* Ensures the URL is a relative path that doesn't escape the origin:
|
|
164
|
-
* - Must start with "/" but not "//"
|
|
165
|
-
* - Backslashes are normalized (browsers treat `\` as `/`)
|
|
166
|
-
* - Origin validation as defense-in-depth
|
|
167
|
-
*
|
|
168
|
-
* @param rawUrl - The raw `url` query parameter value
|
|
169
|
-
* @param requestUrl - The full request URL for origin comparison
|
|
170
|
-
* @returns An error Response if validation fails, or the normalized image URL
|
|
171
|
-
*/
|
|
172
|
-
declare function validateImageUrl(rawUrl: string | null, requestUrl: string): Response | string;
|
|
173
161
|
/**
|
|
174
162
|
* Strip internal `x-middleware-*` headers from a Headers object.
|
|
175
163
|
*
|
|
@@ -219,4 +207,4 @@ declare function cloneRequestWithHeaders(request: Request, headers: Headers): Re
|
|
|
219
207
|
*/
|
|
220
208
|
declare function cloneRequestWithUrl(request: Request, url: string): Request;
|
|
221
209
|
//#endregion
|
|
222
|
-
export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin,
|
|
210
|
+
export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
|
|
@@ -122,7 +122,7 @@ function applyConfigHeadersToResponse(responseHeaders, options) {
|
|
|
122
122
|
for (const header of matched) {
|
|
123
123
|
const lowerName = header.key.toLowerCase();
|
|
124
124
|
if (lowerName === "vary" || lowerName === "set-cookie") responseHeaders.append(header.key, header.value);
|
|
125
|
-
else if (!responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
|
|
125
|
+
else if (options.overwriteExisting?.has(lowerName) || !responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
@@ -197,9 +197,10 @@ function normalizeTrailingSlash(pathname, basePath, trailingSlash, search) {
|
|
|
197
197
|
if (isOpenRedirectShaped(pathname)) return notFoundResponse();
|
|
198
198
|
const normalizedPathname = normalizeTrailingSlashPathname(pathname, trailingSlash);
|
|
199
199
|
if (normalizedPathname === null) return null;
|
|
200
|
+
const encodedPathname = normalizedPathname.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@/%]/gu, encodeURIComponent);
|
|
200
201
|
return new Response(null, {
|
|
201
202
|
status: 308,
|
|
202
|
-
headers: { Location: basePath +
|
|
203
|
+
headers: { Location: basePath + encodedPathname + search }
|
|
203
204
|
});
|
|
204
205
|
}
|
|
205
206
|
/**
|
|
@@ -246,45 +247,74 @@ function validateCsrfOrigin(request, allowedOrigins = []) {
|
|
|
246
247
|
* Regular user form fields are ignored entirely.
|
|
247
248
|
*/
|
|
248
249
|
async function validateServerActionPayload(body) {
|
|
249
|
-
const
|
|
250
|
+
const maxNumericFields = 4096;
|
|
251
|
+
const maxContainerReferences = 16384;
|
|
252
|
+
const maxContainerDepth = 1024;
|
|
253
|
+
const containerRefRe = /"\$([QWi])([0-9a-f]+)(?=[:"])/gi;
|
|
250
254
|
const fieldRefs = /* @__PURE__ */ new Map();
|
|
255
|
+
let referenceCount = 0;
|
|
256
|
+
const invalidPayloadResponse = () => new Response("Invalid server action payload", {
|
|
257
|
+
status: 400,
|
|
258
|
+
headers: { "Content-Type": "text/plain" }
|
|
259
|
+
});
|
|
251
260
|
const collectRefs = (fieldKey, text) => {
|
|
261
|
+
if (fieldRefs.has(fieldKey)) return true;
|
|
262
|
+
if (fieldRefs.size >= maxNumericFields) return false;
|
|
252
263
|
const refs = /* @__PURE__ */ new Set();
|
|
253
264
|
let match;
|
|
254
265
|
containerRefRe.lastIndex = 0;
|
|
255
|
-
while ((match = containerRefRe.exec(text)) !== null)
|
|
266
|
+
while ((match = containerRefRe.exec(text)) !== null) {
|
|
267
|
+
const previousSize = refs.size;
|
|
268
|
+
refs.add(String(Number.parseInt(match[2], 16)));
|
|
269
|
+
if (refs.size !== previousSize && ++referenceCount > maxContainerReferences) return false;
|
|
270
|
+
}
|
|
256
271
|
fieldRefs.set(fieldKey, refs);
|
|
272
|
+
return true;
|
|
257
273
|
};
|
|
258
|
-
if (typeof body === "string")
|
|
259
|
-
|
|
274
|
+
if (typeof body === "string") {
|
|
275
|
+
if (!collectRefs("0", body)) return invalidPayloadResponse();
|
|
276
|
+
} else for (const [key, value] of body.entries()) {
|
|
260
277
|
if (!/^\d+$/.test(key)) continue;
|
|
261
278
|
if (typeof value === "string") {
|
|
262
|
-
collectRefs(key, value);
|
|
279
|
+
if (!collectRefs(key, value)) return invalidPayloadResponse();
|
|
263
280
|
continue;
|
|
264
281
|
}
|
|
265
|
-
if (typeof value?.text === "function")
|
|
282
|
+
if (typeof value?.text === "function") {
|
|
283
|
+
if (!collectRefs(key, await value.text())) return invalidPayloadResponse();
|
|
284
|
+
}
|
|
266
285
|
}
|
|
267
286
|
if (fieldRefs.size === 0) return null;
|
|
268
287
|
const knownFields = new Set(fieldRefs.keys());
|
|
269
|
-
for (const refs of fieldRefs.values()) for (const ref of refs) if (!knownFields.has(ref)) return
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
stack.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
+
for (const refs of fieldRefs.values()) for (const ref of refs) if (!knownFields.has(ref)) return invalidPayloadResponse();
|
|
289
|
+
const state = /* @__PURE__ */ new Map();
|
|
290
|
+
for (const root of fieldRefs.keys()) {
|
|
291
|
+
if (state.has(root)) continue;
|
|
292
|
+
const stack = [{
|
|
293
|
+
node: root,
|
|
294
|
+
refs: [...fieldRefs.get(root) ?? []],
|
|
295
|
+
nextRef: 0
|
|
296
|
+
}];
|
|
297
|
+
state.set(root, "visiting");
|
|
298
|
+
while (stack.length > 0) {
|
|
299
|
+
if (stack.length > maxContainerDepth) return invalidPayloadResponse();
|
|
300
|
+
const frame = stack[stack.length - 1];
|
|
301
|
+
const ref = frame.refs[frame.nextRef++];
|
|
302
|
+
if (ref === void 0) {
|
|
303
|
+
state.set(frame.node, "visited");
|
|
304
|
+
stack.pop();
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const refState = state.get(ref);
|
|
308
|
+
if (refState === "visiting") return invalidPayloadResponse();
|
|
309
|
+
if (refState === "visited") continue;
|
|
310
|
+
state.set(ref, "visiting");
|
|
311
|
+
stack.push({
|
|
312
|
+
node: ref,
|
|
313
|
+
refs: [...fieldRefs.get(ref) ?? []],
|
|
314
|
+
nextRef: 0
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
288
318
|
return null;
|
|
289
319
|
}
|
|
290
320
|
/**
|
|
@@ -329,25 +359,6 @@ function isOriginAllowed(origin, allowed) {
|
|
|
329
359
|
return false;
|
|
330
360
|
}
|
|
331
361
|
/**
|
|
332
|
-
* Validate an image optimization URL parameter.
|
|
333
|
-
*
|
|
334
|
-
* Ensures the URL is a relative path that doesn't escape the origin:
|
|
335
|
-
* - Must start with "/" but not "//"
|
|
336
|
-
* - Backslashes are normalized (browsers treat `\` as `/`)
|
|
337
|
-
* - Origin validation as defense-in-depth
|
|
338
|
-
*
|
|
339
|
-
* @param rawUrl - The raw `url` query parameter value
|
|
340
|
-
* @param requestUrl - The full request URL for origin comparison
|
|
341
|
-
* @returns An error Response if validation fails, or the normalized image URL
|
|
342
|
-
*/
|
|
343
|
-
function validateImageUrl(rawUrl, requestUrl) {
|
|
344
|
-
const imgUrl = rawUrl?.replaceAll("\\", "/") ?? null;
|
|
345
|
-
if (!imgUrl || !imgUrl.startsWith("/") || imgUrl.startsWith("//")) return new Response(!rawUrl ? "Missing url parameter" : "Only relative URLs allowed", { status: 400 });
|
|
346
|
-
const url = new URL(requestUrl);
|
|
347
|
-
if (new URL(imgUrl, url.origin).origin !== url.origin) return new Response("Only relative URLs allowed", { status: 400 });
|
|
348
|
-
return imgUrl;
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
362
|
* Strip internal `x-middleware-*` headers from a Headers object.
|
|
352
363
|
*
|
|
353
364
|
* Middleware uses `x-middleware-*` headers as internal signals (e.g.
|
|
@@ -465,4 +476,4 @@ function cloneRequestWithUrl(request, url) {
|
|
|
465
476
|
return cloned;
|
|
466
477
|
}
|
|
467
478
|
//#endregion
|
|
468
|
-
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin,
|
|
479
|
+
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
//#region src/server/rsc-stream-hints.d.ts
|
|
2
2
|
declare function normalizeReactFlightPreloadHints(stream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
|
|
3
3
|
type RscRawRenderer = (model: unknown, options?: unknown) => ReadableStream<Uint8Array>;
|
|
4
|
+
type RscRawPrerenderer = (model: unknown, options?: unknown) => Promise<{
|
|
5
|
+
prelude: ReadableStream<Uint8Array>;
|
|
6
|
+
}>;
|
|
4
7
|
declare function createRscRenderer(render: RscRawRenderer): RscRawRenderer;
|
|
8
|
+
declare function createRscPrerenderer(prerender: RscRawPrerenderer): RscRawPrerenderer;
|
|
5
9
|
//#endregion
|
|
6
|
-
export { createRscRenderer, normalizeReactFlightPreloadHints };
|
|
10
|
+
export { RscRawPrerenderer, RscRawRenderer, createRscPrerenderer, createRscRenderer, normalizeReactFlightPreloadHints };
|
|
@@ -32,5 +32,10 @@ function normalizeReactFlightPreloadHints(stream) {
|
|
|
32
32
|
function createRscRenderer(render) {
|
|
33
33
|
return (model, options) => normalizeReactFlightPreloadHints(render(model, options));
|
|
34
34
|
}
|
|
35
|
+
function createRscPrerenderer(prerender) {
|
|
36
|
+
return async (model, options) => {
|
|
37
|
+
return { prelude: normalizeReactFlightPreloadHints((await prerender(model, options)).prelude) };
|
|
38
|
+
};
|
|
39
|
+
}
|
|
35
40
|
//#endregion
|
|
36
|
-
export { createRscRenderer, normalizeReactFlightPreloadHints };
|
|
41
|
+
export { createRscPrerenderer, createRscRenderer, normalizeReactFlightPreloadHints };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { normalizePathnameForRouteMatch } from "../routing/utils.js";
|
|
2
|
-
import { normalizePath } from "./normalize-path.js";
|
|
3
1
|
import { isrCacheKey, isrSetPrerenderedAppPage } from "./isr-cache.js";
|
|
4
2
|
import { buildAppPageCacheTags } from "./app-page-cache.js";
|
|
5
3
|
import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
|
|
4
|
+
import { addPregeneratedConcretePath, clearPregeneratedConcretePaths, normalizePregeneratedPathname } from "./pregenerated-concrete-paths.js";
|
|
5
|
+
import { getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest } from "./prerender-manifest.js";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
//#region src/server/seed-cache.ts
|
|
@@ -46,26 +46,21 @@ import path from "node:path";
|
|
|
46
46
|
* @returns The number of routes seeded (0 if no manifest or no renderable routes).
|
|
47
47
|
*/
|
|
48
48
|
async function seedMemoryCacheFromPrerender(serverDir, options) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
54
|
-
} catch (err) {
|
|
55
|
-
console.warn("[vinext] Failed to parse vinext-prerender.json, skipping cache seeding:", err);
|
|
56
|
-
return 0;
|
|
57
|
-
}
|
|
49
|
+
clearPregeneratedConcretePaths();
|
|
50
|
+
const manifest = readPrerenderManifest(path.join(serverDir, "vinext-prerender.json"));
|
|
51
|
+
if (!manifest) return 0;
|
|
58
52
|
const { buildId, routes } = manifest;
|
|
59
53
|
if (!buildId || !Array.isArray(routes)) return 0;
|
|
60
54
|
const trailingSlash = manifest.trailingSlash ?? false;
|
|
61
55
|
const prerenderDir = path.join(serverDir, "prerendered-routes");
|
|
62
56
|
const writeAppPageEntry = options?.writeAppPageEntry ?? createDefaultAppPageEntryWriter();
|
|
63
57
|
let seeded = 0;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
const appRoutes = getRenderedAppRoutes(routes);
|
|
59
|
+
for (const route of appRoutes) {
|
|
60
|
+
const concretePathname = route.path ?? route.route;
|
|
61
|
+
if (!isFallbackShellArtifactPath(concretePathname, route)) addPregeneratedConcretePath(route.route, concretePathname);
|
|
67
62
|
const artifactPathname = route.path ?? route.route;
|
|
68
|
-
const cachePathname =
|
|
63
|
+
const cachePathname = normalizePregeneratedPathname(artifactPathname);
|
|
69
64
|
const baseKey = isrCacheKey("app", cachePathname, buildId);
|
|
70
65
|
const htmlKey = options?.buildAppPageHtmlKey?.(cachePathname) ?? baseKey + ":html";
|
|
71
66
|
const rscKey = options?.buildAppPageRscKey?.(cachePathname) ?? baseKey + ":rsc";
|
|
@@ -79,9 +74,6 @@ async function seedMemoryCacheFromPrerender(serverDir, options) {
|
|
|
79
74
|
}
|
|
80
75
|
return seeded;
|
|
81
76
|
}
|
|
82
|
-
function normalizePrerenderCachePathname(pathname) {
|
|
83
|
-
return normalizePath(normalizePathnameForRouteMatch(pathname));
|
|
84
|
-
}
|
|
85
77
|
function createDefaultAppPageEntryWriter() {
|
|
86
78
|
return (key, data, metadata) => isrSetPrerenderedAppPage(key, data, metadata);
|
|
87
79
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizePathSeparators } from "../utils/path.js";
|
|
2
|
-
import "../utils/asset-prefix.js";
|
|
2
|
+
import { ASSET_PREFIX_URL_DIR } from "../utils/asset-prefix.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
//#region src/server/static-file-cache.ts
|
|
@@ -23,6 +23,7 @@ const CONTENT_TYPES = {
|
|
|
23
23
|
".css": "text/css",
|
|
24
24
|
".html": "text/html",
|
|
25
25
|
".json": "application/json",
|
|
26
|
+
".txt": "text/plain; charset=utf-8",
|
|
26
27
|
".png": "image/png",
|
|
27
28
|
".jpg": "image/jpeg",
|
|
28
29
|
".jpeg": "image/jpeg",
|
|
@@ -165,9 +166,20 @@ var StaticFileCache = class StaticFileCache {
|
|
|
165
166
|
function etagFromFilenameHash(relativePath, ext) {
|
|
166
167
|
const basename = path.basename(relativePath, ext);
|
|
167
168
|
const lastDash = basename.lastIndexOf("-");
|
|
168
|
-
if (lastDash
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
if (lastDash !== -1 && lastDash !== basename.length - 1) {
|
|
170
|
+
const suffix = basename.slice(lastDash + 1);
|
|
171
|
+
if (suffix.length >= 6 && suffix.length <= 12 && /^[A-Za-z0-9_-]+$/.test(suffix)) return `W/"${suffix}"`;
|
|
172
|
+
}
|
|
173
|
+
const normalizedPath = normalizePathSeparators(relativePath);
|
|
174
|
+
const managedMediaSegment = `${ASSET_PREFIX_URL_DIR}/media/`;
|
|
175
|
+
if (normalizedPath.startsWith(managedMediaSegment) || normalizedPath.includes(`/${managedMediaSegment}`)) {
|
|
176
|
+
const lastDot = basename.lastIndexOf(".");
|
|
177
|
+
if (lastDot !== -1) {
|
|
178
|
+
const suffix = basename.slice(lastDot + 1);
|
|
179
|
+
if (/^[0-9a-f]{8}$/.test(suffix)) return `W/"${suffix}"`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
171
183
|
}
|
|
172
184
|
function buildVariant(info, baseHeaders, encoding) {
|
|
173
185
|
return {
|
|
@@ -3,11 +3,13 @@ type AppRouterScrollIntent = Readonly<{
|
|
|
3
3
|
commitId: number | null;
|
|
4
4
|
hash: string | null;
|
|
5
5
|
id: number;
|
|
6
|
+
targetHoistedInHead: boolean;
|
|
6
7
|
}>;
|
|
7
8
|
declare function beginAppRouterScrollIntent(hash: string | null): AppRouterScrollIntent;
|
|
8
9
|
declare function clearAppRouterScrollIntent(): void;
|
|
9
10
|
declare function getPendingAppRouterScrollIntent(): AppRouterScrollIntent | null;
|
|
10
11
|
declare function claimAppRouterScrollIntentForCommit(expected: AppRouterScrollIntent | null | undefined, commitId: number): void;
|
|
12
|
+
declare function markAppRouterScrollIntentHeadHoisted(expected: AppRouterScrollIntent | null | undefined, commitId: number): void;
|
|
11
13
|
declare function consumeAppRouterScrollIntent(expected: AppRouterScrollIntent | null | undefined, commitId?: number): AppRouterScrollIntent | null;
|
|
12
14
|
//#endregion
|
|
13
|
-
export { AppRouterScrollIntent, beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent };
|
|
15
|
+
export { AppRouterScrollIntent, beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted };
|
|
@@ -14,7 +14,8 @@ function beginAppRouterScrollIntent(hash) {
|
|
|
14
14
|
const intent = {
|
|
15
15
|
commitId: null,
|
|
16
16
|
hash,
|
|
17
|
-
id: store.nextId
|
|
17
|
+
id: store.nextId,
|
|
18
|
+
targetHoistedInHead: false
|
|
18
19
|
};
|
|
19
20
|
store.pending = intent;
|
|
20
21
|
return intent;
|
|
@@ -35,6 +36,17 @@ function claimAppRouterScrollIntentForCommit(expected, commitId) {
|
|
|
35
36
|
commitId
|
|
36
37
|
};
|
|
37
38
|
}
|
|
39
|
+
function markAppRouterScrollIntentHeadHoisted(expected, commitId) {
|
|
40
|
+
const store = getScrollIntentStore();
|
|
41
|
+
const intent = store.pending;
|
|
42
|
+
if (expected === null || expected === void 0 || intent === null) return;
|
|
43
|
+
if (intent.id !== expected.id) return;
|
|
44
|
+
if (intent.commitId !== commitId) return;
|
|
45
|
+
store.pending = {
|
|
46
|
+
...intent,
|
|
47
|
+
targetHoistedInHead: true
|
|
48
|
+
};
|
|
49
|
+
}
|
|
38
50
|
function consumeAppRouterScrollIntent(expected, commitId) {
|
|
39
51
|
if (expected === null || expected === void 0) return null;
|
|
40
52
|
const store = getScrollIntentStore();
|
|
@@ -46,4 +58,4 @@ function consumeAppRouterScrollIntent(expected, commitId) {
|
|
|
46
58
|
return intent;
|
|
47
59
|
}
|
|
48
60
|
//#endregion
|
|
49
|
-
export { beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent };
|
|
61
|
+
export { beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted };
|
|
@@ -5,9 +5,12 @@ declare class AppRouterScrollTargetInner extends React$1.Component<{
|
|
|
5
5
|
children: React$1.ReactNode;
|
|
6
6
|
commitId: number | null;
|
|
7
7
|
}> {
|
|
8
|
+
scheduledCommitId: number | null;
|
|
9
|
+
schedulePotentialScroll: () => void;
|
|
8
10
|
handlePotentialScroll: () => void;
|
|
9
11
|
componentDidMount(): void;
|
|
10
12
|
componentDidUpdate(): void;
|
|
13
|
+
componentWillUnmount(): void;
|
|
11
14
|
render(): React$1.ReactNode;
|
|
12
15
|
}
|
|
13
16
|
declare function AppRouterScrollCommitProvider({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { decodeHashFragment } from "./hash-scroll.js";
|
|
3
|
-
import { consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent } from "./app-router-scroll-state.js";
|
|
3
|
+
import { consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted } from "./app-router-scroll-state.js";
|
|
4
4
|
import * as React$1 from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
import * as ReactDOM from "react-dom";
|
|
@@ -46,8 +46,7 @@ function topOfElementInViewport(element, viewportHeight) {
|
|
|
46
46
|
function getHashFragmentDomNode(hash) {
|
|
47
47
|
const fragment = decodeHashFragment(hash.startsWith("#") ? hash.slice(1) : hash);
|
|
48
48
|
if (fragment === "top") return document.body;
|
|
49
|
-
|
|
50
|
-
return element instanceof HTMLElement ? element : null;
|
|
49
|
+
return document.getElementById(fragment) ?? document.getElementsByName(fragment)[0] ?? null;
|
|
51
50
|
}
|
|
52
51
|
function isInDocumentHead(node) {
|
|
53
52
|
const head = node.ownerDocument?.head;
|
|
@@ -55,7 +54,7 @@ function isInDocumentHead(node) {
|
|
|
55
54
|
}
|
|
56
55
|
function findNextScrollTarget(node) {
|
|
57
56
|
if (!(node instanceof Element)) return null;
|
|
58
|
-
if (isInDocumentHead(node)) return
|
|
57
|
+
if (isInDocumentHead(node)) return null;
|
|
59
58
|
let target = node;
|
|
60
59
|
while (!(target instanceof HTMLElement) || shouldSkipElement(target)) {
|
|
61
60
|
if (target.nextElementSibling === null) return null;
|
|
@@ -82,34 +81,45 @@ function scrollToElement(target, hash) {
|
|
|
82
81
|
});
|
|
83
82
|
}
|
|
84
83
|
var AppRouterScrollTargetInner = class extends React$1.Component {
|
|
84
|
+
scheduledCommitId = null;
|
|
85
|
+
schedulePotentialScroll = () => {
|
|
86
|
+
const commitId = this.props.commitId;
|
|
87
|
+
this.scheduledCommitId = commitId;
|
|
88
|
+
queueMicrotask(() => {
|
|
89
|
+
if (this.scheduledCommitId !== commitId) return;
|
|
90
|
+
this.handlePotentialScroll();
|
|
91
|
+
});
|
|
92
|
+
};
|
|
85
93
|
handlePotentialScroll = () => {
|
|
86
94
|
const intent = getPendingAppRouterScrollIntent();
|
|
87
95
|
if (intent === null) return;
|
|
88
96
|
if (this.props.commitId === null || intent.commitId !== this.props.commitId) return;
|
|
89
|
-
let
|
|
90
|
-
if (intent.hash !== null)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (next.kind === "document-top") {
|
|
97
|
-
if (consumeAppRouterScrollIntent(intent, this.props.commitId) === null) return;
|
|
98
|
-
document.documentElement.scrollTop = 0;
|
|
97
|
+
let node;
|
|
98
|
+
if (intent.hash !== null) node = getHashFragmentDomNode(intent.hash);
|
|
99
|
+
else node = null;
|
|
100
|
+
if (node === null) {
|
|
101
|
+
node = findDOMNode(this);
|
|
102
|
+
if (node !== null && isInDocumentHead(node)) {
|
|
103
|
+
markAppRouterScrollIntentHeadHoisted(intent, this.props.commitId);
|
|
99
104
|
return;
|
|
100
105
|
}
|
|
101
|
-
target = next.element;
|
|
102
106
|
}
|
|
107
|
+
const next = findNextScrollTarget(node);
|
|
108
|
+
if (next === null) return;
|
|
109
|
+
const target = next.element;
|
|
103
110
|
const consumed = consumeAppRouterScrollIntent(intent, this.props.commitId);
|
|
104
111
|
if (consumed === null) return;
|
|
105
112
|
scrollToElement(target, consumed.hash);
|
|
106
|
-
target.focus(
|
|
113
|
+
target.focus();
|
|
107
114
|
};
|
|
108
115
|
componentDidMount() {
|
|
109
|
-
this.
|
|
116
|
+
this.schedulePotentialScroll();
|
|
110
117
|
}
|
|
111
118
|
componentDidUpdate() {
|
|
112
|
-
this.
|
|
119
|
+
this.schedulePotentialScroll();
|
|
120
|
+
}
|
|
121
|
+
componentWillUnmount() {
|
|
122
|
+
this.scheduledCommitId = null;
|
|
113
123
|
}
|
|
114
124
|
render() {
|
|
115
125
|
return this.props.children;
|
|
@@ -2,17 +2,28 @@ import React from "react";
|
|
|
2
2
|
|
|
3
3
|
//#region src/shims/before-interactive-context.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* A `<Script strategy="beforeInteractive">` captured during SSR.
|
|
6
6
|
*
|
|
7
7
|
* The Script shim hands these records to the SSR pipeline through
|
|
8
8
|
* `BeforeInteractiveContext` instead of rendering the `<script>` tag inline.
|
|
9
9
|
* The pipeline then emits the captured tag immediately after `<head>` opens,
|
|
10
10
|
* so the script runs before any React-hoisted stylesheets or modulepreload
|
|
11
11
|
* links. Matches the standard no-flash dark-mode pattern.
|
|
12
|
+
*
|
|
13
|
+
* Both inline (`children`/`dangerouslySetInnerHTML`) and external (`src`)
|
|
14
|
+
* beforeInteractive scripts flow through here, mirroring Next.js which registers
|
|
15
|
+
* inline and src beforeInteractive scripts equally in the App Router runtime
|
|
16
|
+
* (`(self.__next_s=...).push(...)`). An entry has `src` set for external
|
|
17
|
+
* scripts and `innerHTML` set for inline scripts (never both).
|
|
12
18
|
*/
|
|
13
19
|
type BeforeInteractiveInlineScript = {
|
|
14
|
-
/** Optional id attribute. */id?: string; /**
|
|
15
|
-
|
|
20
|
+
/** Optional id attribute. */id?: string; /** External script URL. Set for src beforeInteractive scripts. */
|
|
21
|
+
src?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Pre-escaped inline content (already passed through `escapeInlineContent`).
|
|
24
|
+
* Set for inline beforeInteractive scripts; omitted for src scripts.
|
|
25
|
+
*/
|
|
26
|
+
innerHTML?: string; /** Nonce to emit on the `<script>` tag, when CSP is enabled. */
|
|
16
27
|
nonce?: string;
|
|
17
28
|
/**
|
|
18
29
|
* Additional HTML attributes to emit on the tag. Booleans render as the
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getOrCreateAls } from "./internal/als-registry.js";
|
|
2
2
|
import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
|
|
3
3
|
import { VINEXT_RSC_MARKER_HEADER } from "../server/headers.js";
|
|
4
|
+
import { trackPprFallbackShellCacheTask } from "./ppr-fallback-shell.js";
|
|
4
5
|
import { markDynamicUsage } from "./headers.js";
|
|
5
6
|
import { _registerCacheContextAccessor, _setRequestScopedCacheLife, cacheLifeProfiles, getDataCacheHandler } from "./cache.js";
|
|
6
7
|
import { addCollectedRequestTags, getCurrentFetchSoftTags } from "./fetch-cache.js";
|
|
@@ -243,7 +244,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
|
|
|
243
244
|
const cacheVariant = variant ?? "";
|
|
244
245
|
const omitAppPageSearchParamsFromFirstArg = options.appPageDefaultExport === true;
|
|
245
246
|
const isDev = typeof process !== "undefined" && process.env.NODE_ENV === "development";
|
|
246
|
-
const cachedFn =
|
|
247
|
+
const cachedFn = (...args) => trackPprFallbackShellCacheTask(async () => {
|
|
247
248
|
const rsc = await getRscModule();
|
|
248
249
|
const keySeed = getUseCacheKeySeed();
|
|
249
250
|
let cacheKey;
|
|
@@ -320,7 +321,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
|
|
|
320
321
|
});
|
|
321
322
|
} catch {}
|
|
322
323
|
return result;
|
|
323
|
-
};
|
|
324
|
+
}, cacheVariant);
|
|
324
325
|
Object.defineProperty(cachedFn, "length", {
|
|
325
326
|
value: fn.length,
|
|
326
327
|
configurable: true
|