vinext 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -10
- package/dist/build/app-page-static-eligibility.d.ts +15 -0
- package/dist/build/app-page-static-eligibility.js +82 -0
- package/dist/build/client-build-config.d.ts +10 -0
- package/dist/build/client-build-config.js +18 -2
- package/dist/build/pages-client-assets-module.d.ts +8 -0
- package/dist/build/pages-client-assets-module.js +21 -1
- package/dist/build/prerender-paths.js +8 -81
- package/dist/build/prerender.js +33 -8
- package/dist/build/run-prerender.js +1 -1
- package/dist/build/server-manifest.d.ts +7 -0
- package/dist/build/server-manifest.js +11 -1
- package/dist/build/ssr-manifest.d.ts +2 -0
- package/dist/build/ssr-manifest.js +6 -2
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/check.js +7 -6
- package/dist/cli.js +2 -1
- package/dist/client/client-redirect-matcher.d.ts +14 -0
- package/dist/client/client-redirect-matcher.js +37 -0
- package/dist/client/client-redirects.d.ts +27 -0
- package/dist/client/client-redirects.js +28 -0
- package/dist/client/client-rewrites.d.ts +2 -1
- package/dist/client/client-rewrites.js +1 -1
- package/dist/client/navigation-runtime.d.ts +11 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.d.ts +26 -11
- package/dist/config/config-matchers.js +81 -82
- package/dist/config/destination-params.d.ts +47 -0
- package/dist/config/destination-params.js +116 -0
- package/dist/config/next-config.d.ts +14 -0
- package/dist/config/next-config.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +6 -0
- package/dist/entries/app-rsc-entry.js +84 -49
- package/dist/entries/pages-client-entry.js +3 -1
- package/dist/entries/pages-server-entry.js +13 -37
- package/dist/index.d.ts +7 -2
- package/dist/index.js +193 -73
- package/dist/init-cloudflare.d.ts +9 -0
- package/dist/init-cloudflare.js +107 -31
- package/dist/init-platform.d.ts +4 -0
- package/dist/init-platform.js +27 -2
- package/dist/init.d.ts +1 -0
- package/dist/init.js +67 -3
- package/dist/plugins/ast-scope.d.ts +7 -0
- package/dist/plugins/ast-scope.js +59 -3
- package/dist/plugins/cache-flight-codec.d.ts +19 -0
- package/dist/plugins/cache-flight-codec.js +68 -0
- package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
- package/dist/plugins/commonjs-esm-facade.js +80 -0
- package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +9 -8
- package/dist/plugins/fonts.d.ts +8 -11
- package/dist/plugins/fonts.js +71 -34
- package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +3 -4
- package/dist/plugins/middleware-server-only.d.ts +5 -4
- package/dist/plugins/middleware-server-only.js +8 -5
- package/dist/plugins/pages-node-externals.js +1 -1
- package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
- package/dist/plugins/rsc-scan-glob-imports.js +50 -0
- package/dist/plugins/strip-server-exports.js +262 -98
- package/dist/plugins/transform-result.d.ts +9 -5
- package/dist/plugins/transitive-externals.js +1 -1
- package/dist/plugins/typeof-window.d.ts +2 -2
- package/dist/plugins/use-cache-callable.js +8 -2
- package/dist/plugins/worker-server-only-imports.d.ts +20 -0
- package/dist/plugins/worker-server-only-imports.js +31 -0
- package/dist/plugins/worker-use-server.d.ts +25 -0
- package/dist/plugins/worker-use-server.js +129 -0
- package/dist/routing/file-matcher.d.ts +1 -0
- package/dist/routing/file-matcher.js +1 -1
- package/dist/server/api-handler.d.ts +1 -0
- package/dist/server/api-handler.js +19 -8
- package/dist/server/app-browser-action-result.d.ts +1 -1
- package/dist/server/app-browser-action-result.js +9 -2
- package/dist/server/app-browser-entry.js +11 -7
- package/dist/server/app-browser-history-controller.d.ts +2 -0
- package/dist/server/app-browser-history-controller.js +8 -0
- package/dist/server/app-browser-interception-context.js +26 -2
- package/dist/server/app-browser-navigation-controller.d.ts +7 -1
- package/dist/server/app-browser-navigation-controller.js +94 -11
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +2 -0
- package/dist/server/app-fallback-renderer.js +1 -0
- package/dist/server/app-history-state.d.ts +3 -0
- package/dist/server/app-history-state.js +7 -0
- package/dist/server/app-middleware.d.ts +11 -0
- package/dist/server/app-middleware.js +19 -3
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-boundary-render.d.ts +5 -0
- package/dist/server/app-page-boundary-render.js +6 -4
- package/dist/server/app-page-cache-finalizer.d.ts +11 -0
- package/dist/server/app-page-cache-finalizer.js +41 -7
- package/dist/server/app-page-cache-render.d.ts +18 -0
- package/dist/server/app-page-cache-render.js +63 -43
- package/dist/server/app-page-cache.d.ts +6 -1
- package/dist/server/app-page-cache.js +28 -8
- package/dist/server/app-page-dispatch.d.ts +12 -2
- package/dist/server/app-page-dispatch.js +51 -33
- package/dist/server/app-page-element-builder.d.ts +7 -0
- package/dist/server/app-page-element-builder.js +27 -42
- package/dist/server/app-page-execution.d.ts +15 -12
- package/dist/server/app-page-execution.js +30 -40
- package/dist/server/app-page-head.d.ts +5 -0
- package/dist/server/app-page-head.js +9 -3
- package/dist/server/app-page-ppr-runtime.js +1 -1
- package/dist/server/app-page-probe.d.ts +0 -109
- package/dist/server/app-page-probe.js +3 -136
- package/dist/server/app-page-render-identity.d.ts +0 -1
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render-observation.js +3 -3
- package/dist/server/app-page-render.d.ts +13 -5
- package/dist/server/app-page-render.js +219 -119
- package/dist/server/app-page-request.d.ts +0 -1
- package/dist/server/app-page-request.js +54 -17
- package/dist/server/app-page-response.d.ts +4 -3
- package/dist/server/app-page-response.js +4 -3
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +57 -35
- package/dist/server/app-page-rsc-render-status.d.ts +48 -0
- package/dist/server/app-page-rsc-render-status.js +54 -0
- package/dist/server/app-page-stream.d.ts +15 -8
- package/dist/server/app-page-stream.js +6 -0
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -3
- package/dist/server/app-prerender-endpoints.js +9 -6
- package/dist/server/app-request-stage-dispatch.js +7 -3
- package/dist/server/app-request-stage-independent-entry.js +29 -9
- package/dist/server/app-response-header-provenance.d.ts +2 -0
- package/dist/server/app-response-header-provenance.js +10 -4
- package/dist/server/app-response-stage-entry.js +10 -2
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +6 -3
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-runtime.d.ts +4 -1
- package/dist/server/app-route-handler-runtime.js +45 -24
- package/dist/server/app-router-entry.js +27 -8
- package/dist/server/app-rsc-cache-busting.js +1 -1
- package/dist/server/app-rsc-errors.js +20 -16
- package/dist/server/app-rsc-handler.d.ts +11 -0
- package/dist/server/app-rsc-handler.js +291 -125
- package/dist/server/app-rsc-request-normalization.d.ts +3 -1
- package/dist/server/app-rsc-request-normalization.js +4 -2
- package/dist/server/app-rsc-response-stage.d.ts +1 -1
- package/dist/server/app-rsc-response-stage.js +10 -6
- package/dist/server/app-rsc-route-matching.d.ts +9 -1
- package/dist/server/app-rsc-route-matching.js +36 -19
- package/dist/server/app-segment-config.d.ts +2 -1
- package/dist/server/app-server-action-execution.d.ts +2 -1
- package/dist/server/app-server-action-execution.js +62 -23
- package/dist/server/app-ssr-entry.d.ts +3 -2
- package/dist/server/app-ssr-entry.js +8 -5
- package/dist/server/app-ssr-stream.d.ts +6 -1
- package/dist/server/app-ssr-stream.js +34 -15
- package/dist/server/app-worker-stages.d.ts +7 -2
- package/dist/server/app-worker-stages.js +4 -4
- package/dist/server/cacheability-request.js +11 -7
- package/dist/server/client-build-files.d.ts +16 -0
- package/dist/server/client-build-files.js +36 -0
- package/dist/server/client-reuse-manifest.js +1 -1
- package/dist/server/config-headers.js +2 -0
- package/dist/server/dev-server.js +50 -29
- package/dist/server/dev-stack-sourcemap.js +18 -23
- package/dist/server/document-initial-head.d.ts +0 -11
- package/dist/server/document-initial-head.js +1 -1
- package/dist/server/forwarded-middleware-context.d.ts +5 -0
- package/dist/server/forwarded-middleware-context.js +22 -0
- package/dist/server/headers.d.ts +18 -1
- package/dist/server/headers.js +19 -2
- package/dist/server/http-conditional.d.ts +11 -0
- package/dist/server/http-conditional.js +10 -1
- package/dist/server/http-error-responses.d.ts +1 -1
- package/dist/server/image-optimization.d.ts +54 -0
- package/dist/server/image-optimization.js +118 -9
- package/dist/server/isr-cache.d.ts +10 -0
- package/dist/server/isr-cache.js +43 -8
- package/dist/server/metadata-route-response.js +9 -8
- package/dist/server/middleware-matcher-pattern.js +8 -4
- package/dist/server/middleware-matcher.d.ts +2 -0
- package/dist/server/middleware-matcher.js +23 -2
- package/dist/server/middleware-response-headers.js +2 -0
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +33 -20
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +3 -1
- package/dist/server/multi-stage.d.ts +19 -0
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/node-response-signal.d.ts +16 -0
- package/dist/server/node-response-signal.js +26 -0
- package/dist/server/normalize-path.d.ts +0 -13
- package/dist/server/normalize-path.js +1 -34
- package/dist/server/open-redirect.d.ts +47 -0
- package/dist/server/open-redirect.js +74 -1
- package/dist/server/pages-api-route.d.ts +3 -2
- package/dist/server/pages-api-route.js +4 -3
- package/dist/server/pages-cache-tag.d.ts +12 -0
- package/dist/server/pages-cache-tag.js +19 -0
- package/dist/server/pages-client-assets.d.ts +2 -0
- package/dist/server/pages-data-route.d.ts +10 -0
- package/dist/server/pages-data-route.js +19 -2
- package/dist/server/pages-document-asset-props.d.ts +5 -1
- package/dist/server/pages-document-asset-props.js +13 -5
- package/dist/server/pages-document-splice.d.ts +19 -0
- package/dist/server/pages-document-splice.js +55 -0
- package/dist/server/pages-i18n.d.ts +12 -1
- package/dist/server/pages-i18n.js +18 -4
- package/dist/server/pages-node-compat.d.ts +16 -5
- package/dist/server/pages-node-compat.js +20 -12
- package/dist/server/pages-page-data.d.ts +32 -24
- package/dist/server/pages-page-data.js +50 -120
- package/dist/server/pages-page-handler.d.ts +0 -6
- package/dist/server/pages-page-handler.js +209 -130
- package/dist/server/pages-page-response.d.ts +12 -6
- package/dist/server/pages-page-response.js +41 -34
- package/dist/server/pages-request-pipeline.d.ts +10 -2
- package/dist/server/pages-request-pipeline.js +37 -18
- package/dist/server/pages-request-stage-entry.d.ts +6 -0
- package/dist/server/pages-request-stage-entry.js +79 -16
- package/dist/server/pages-response-stage-entry.js +4 -0
- package/dist/server/pages-revalidate.js +1 -1
- package/dist/server/pages-router-entry.js +4 -3
- package/dist/server/prerender-manifest.d.ts +3 -3
- package/dist/server/prerender-manifest.js +4 -3
- package/dist/server/prerender-work-unit-setup.js +4 -0
- package/dist/server/prod-server.d.ts +20 -7
- package/dist/server/prod-server.js +343 -129
- package/dist/server/proper-error.d.ts +4 -0
- package/dist/server/proper-error.js +31 -0
- package/dist/server/request-pipeline.d.ts +24 -9
- package/dist/server/request-pipeline.js +24 -9
- package/dist/server/response-compression.d.ts +43 -0
- package/dist/server/response-compression.js +111 -0
- package/dist/server/revalidation-request.d.ts +1 -0
- package/dist/server/revalidation-request.js +1 -1
- package/dist/server/rsc-stream-hints.js +31 -2
- package/dist/server/seed-cache.d.ts +1 -0
- package/dist/server/seed-cache.js +15 -11
- package/dist/server/server-reference-loader.d.ts +4 -0
- package/dist/server/server-reference-loader.js +29 -0
- package/dist/server/socket-error-backstop.d.ts +23 -1
- package/dist/server/socket-error-backstop.js +69 -17
- package/dist/server/static-file-signal.d.ts +15 -4
- package/dist/server/static-file-signal.js +46 -12
- package/dist/server/worker-image-optimization.d.ts +34 -0
- package/dist/server/worker-image-optimization.js +86 -0
- package/dist/server/worker-stages.d.ts +1 -1
- package/dist/server/worker-stages.js +2 -2
- package/dist/server/worker-utils.d.ts +8 -3
- package/dist/server/worker-utils.js +12 -7
- package/dist/shims/app-router-scroll.js +4 -1
- package/dist/shims/app-router-tree-context.d.ts +5 -0
- package/dist/shims/app-router-tree-context.js +27 -0
- package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
- package/dist/shims/cache-adapter-instantiate.js +118 -0
- package/dist/shims/cache-callable-runtime.d.ts +6 -42
- package/dist/shims/cache-callable-runtime.js +65 -174
- package/dist/shims/cache-flight-arguments.d.ts +22 -0
- package/dist/shims/cache-flight-arguments.js +56 -0
- package/dist/shims/cache-handler.js +1 -9
- package/dist/shims/cache-request-state.d.ts +15 -0
- package/dist/shims/cache-request-state.js +31 -8
- package/dist/shims/cache-runtime.d.ts +12 -20
- package/dist/shims/cache-runtime.js +166 -225
- package/dist/shims/cache.d.ts +4 -4
- package/dist/shims/cache.js +44 -16
- package/dist/shims/cacheability-classification.d.ts +7 -0
- package/dist/shims/cdn-cache.d.ts +8 -0
- package/dist/shims/cdn-cache.js +17 -1
- package/dist/shims/disable-smooth-scroll.d.ts +3 -0
- package/dist/shims/disable-smooth-scroll.js +36 -0
- package/dist/shims/document-placeholders.d.ts +19 -0
- package/dist/shims/document-placeholders.js +45 -0
- package/dist/shims/document.js +21 -5
- package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
- package/dist/shims/dynamic-preload-chunks.js +13 -9
- package/dist/shims/dynamic.d.ts +1 -1
- package/dist/shims/dynamic.js +60 -8
- package/dist/shims/error-boundary-navigation.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.js +19 -1
- package/dist/shims/error-boundary.d.ts +10 -10
- package/dist/shims/error-boundary.js +26 -7
- package/dist/shims/fetch-cache-state.d.ts +66 -0
- package/dist/shims/fetch-cache-state.js +92 -0
- package/dist/shims/fetch-cache.d.ts +3 -61
- package/dist/shims/fetch-cache.js +161 -108
- package/dist/shims/form.js +1 -1
- package/dist/shims/head.d.ts +10 -6
- package/dist/shims/head.js +15 -13
- package/dist/shims/headers.d.ts +13 -0
- package/dist/shims/headers.js +47 -4
- package/dist/shims/image.js +2 -1
- package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
- package/dist/shims/internal/app-page-props-cache-key.js +24 -15
- package/dist/shims/internal/headers-state.d.ts +0 -1
- package/dist/shims/internal/work-store-scope.d.ts +5 -0
- package/dist/shims/internal/work-store-scope.js +24 -0
- package/dist/shims/metadata.js +3 -3
- package/dist/shims/navigation-errors.d.ts +6 -0
- package/dist/shims/navigation-errors.js +11 -1
- package/dist/shims/navigation.d.ts +7 -0
- package/dist/shims/navigation.js +45 -9
- package/dist/shims/request-context.d.ts +11 -0
- package/dist/shims/request-state-types.d.ts +2 -1
- package/dist/shims/router.js +12 -20
- package/dist/shims/server.d.ts +16 -0
- package/dist/shims/server.js +24 -4
- package/dist/shims/thenable-params.d.ts +4 -0
- package/dist/shims/thenable-params.js +25 -3
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/shims/url-utils.d.ts +2 -0
- package/dist/shims/url-utils.js +3 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/hash.d.ts +7 -0
- package/dist/utils/hash.js +11 -1
- package/dist/utils/mdx-scan.d.ts +5 -0
- package/dist/utils/mdx-scan.js +8 -1
- package/dist/utils/package-name.d.ts +6 -0
- package/dist/utils/package-name.js +12 -1
- package/dist/utils/plugin-rsc-version.d.ts +4 -0
- package/dist/utils/plugin-rsc-version.js +15 -0
- package/dist/utils/prerender-output-paths.js +15 -4
- package/dist/utils/public-file-path.d.ts +10 -2
- package/dist/utils/public-file-path.js +13 -9
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.d.ts +3 -0
- package/dist/utils/react-version.js +1 -1
- package/dist/utils/regex-safety.d.ts +10 -0
- package/dist/utils/regex-safety.js +70 -18
- package/package.json +9 -5
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/server/proper-error.ts
|
|
2
|
+
function isPlainObject(value) {
|
|
3
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") return false;
|
|
4
|
+
const prototype = Object.getPrototypeOf(value);
|
|
5
|
+
return prototype === null || Object.prototype.hasOwnProperty.call(prototype, "isPrototypeOf");
|
|
6
|
+
}
|
|
7
|
+
/** JSON.stringify that replaces circular references with "[Circular]". */
|
|
8
|
+
function safeStringifyLite(value) {
|
|
9
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
10
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
11
|
+
if (typeof nested === "object" && nested !== null) {
|
|
12
|
+
if (seen.has(nested)) return "[Circular]";
|
|
13
|
+
seen.add(nested);
|
|
14
|
+
}
|
|
15
|
+
return nested;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function isError(value) {
|
|
19
|
+
return typeof value === "object" && value !== null && "name" in value && "message" in value;
|
|
20
|
+
}
|
|
21
|
+
/** Turn a thrown value into an Error, as Next.js does before reporting it. */
|
|
22
|
+
function getProperError(error) {
|
|
23
|
+
if (isError(error)) return error;
|
|
24
|
+
if (process.env.NODE_ENV === "development") {
|
|
25
|
+
if (typeof error === "undefined") return /* @__PURE__ */ new Error("An undefined error was thrown, see here for more info: https://nextjs.org/docs/messages/threw-undefined");
|
|
26
|
+
if (error === null) return /* @__PURE__ */ new Error("A null error was thrown, see here for more info: https://nextjs.org/docs/messages/threw-undefined");
|
|
27
|
+
}
|
|
28
|
+
return new Error(isPlainObject(error) ? safeStringifyLite(error) : String(error));
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { getProperError };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
2
2
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
3
3
|
import { StaticFileSignalContext, createStaticFileSignal } from "./static-file-signal.js";
|
|
4
|
-
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
4
|
+
import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
|
|
5
5
|
//#region src/server/request-pipeline.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* Apply the URL Standard's pathname canonicalization without decoding and
|
|
@@ -39,10 +39,21 @@ export declare function canonicalizeRequestUrlPathname(url: string): string;
|
|
|
39
39
|
* Backslashes are equivalent to forward slashes in the URL spec
|
|
40
40
|
* (e.g. `/\evil.com` is treated as `//evil.com` by browsers).
|
|
41
41
|
*
|
|
42
|
-
* Next.js
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* Like Next.js, any raw path containing a literal backslash or a repeated
|
|
43
|
+
* slash first gets a 308 to the collapsed path (`//evil.com` → `/evil.com`,
|
|
44
|
+
* `//` → `/`); see `getRepeatedSlashRedirect`. We check the RAW
|
|
45
|
+
* pathname before normalization so this fires before normalizePath collapses
|
|
46
|
+
* `//`.
|
|
47
|
+
*
|
|
48
|
+
* Request-based (Worker) entries only see `request.url` after WHATWG URL
|
|
49
|
+
* parsing, which has already turned `\` into `/`; the raw request target is
|
|
50
|
+
* not available there. `/\evil.com` still redirects (it arrives as
|
|
51
|
+
* `//evil.com`), but a lone backslash such as `/foo\bar` arrives as
|
|
52
|
+
* `/foo/bar` and is routed without the redirect. On Cloudflare, requests that
|
|
53
|
+
* match a static asset are also answered by the asset layer before the
|
|
54
|
+
* Worker runs. Node entries (dev and `vinext start`) check the raw target.
|
|
55
|
+
*
|
|
56
|
+
* Percent-encoded variants get a 404, as in Next.js, because:
|
|
46
57
|
* - `%5C` decodes to `\` (browsers treat `/\evil.com` as `//evil.com`).
|
|
47
58
|
* - `%2F` decodes to `/` (so `/%2F/evil.com` effectively becomes `//evil.com`).
|
|
48
59
|
* These forms survive segment-wise decoding that re-encodes path delimiters
|
|
@@ -51,13 +62,17 @@ export declare function canonicalizeRequestUrlPathname(url: string): string;
|
|
|
51
62
|
* `isOpenRedirectShaped` for the full list of rejected leading-segment forms.
|
|
52
63
|
*
|
|
53
64
|
* @param rawPathname - The raw pathname from the URL, before any normalization
|
|
54
|
-
* @
|
|
65
|
+
* @param search - The raw query string (including `?`), kept in the 308 Location
|
|
66
|
+
* @returns A 308 or 404 Response, or null to continue
|
|
55
67
|
*/
|
|
56
|
-
export declare function guardProtocolRelativeUrl(rawPathname: string): Response | null;
|
|
68
|
+
export declare function guardProtocolRelativeUrl(rawPathname: string, search?: string): Response | null;
|
|
57
69
|
export type HeaderRecord = Record<string, string | string[]>;
|
|
58
70
|
type ResolvePublicFileRouteOptions = {
|
|
59
71
|
cleanPathname: string;
|
|
60
|
-
|
|
72
|
+
/** `requestHeaders` holds middleware's request-header override instructions. */
|
|
73
|
+
middlewareContext: StaticFileSignalContext & {
|
|
74
|
+
requestHeaders?: Headers | null;
|
|
75
|
+
};
|
|
61
76
|
pathname: string;
|
|
62
77
|
publicFiles: ReadonlySet<string>;
|
|
63
78
|
request: Request;
|
|
@@ -175,4 +190,4 @@ export declare function cloneRequestWithHeaders(request: Request, headers: Heade
|
|
|
175
190
|
*/
|
|
176
191
|
export declare function cloneRequestWithUrl(request: Request, url: string): Request;
|
|
177
192
|
//#endregion
|
|
178
|
-
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, createStaticFileSignal, hasBasePath, isOpenRedirectShaped, stripBasePath };
|
|
193
|
+
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, createStaticFileSignal, getRepeatedSlashRedirect, hasBasePath, isOpenRedirectShaped, sendRepeatedSlashRedirect, stripBasePath };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "../utils/protocol-headers.js";
|
|
2
|
-
import { getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
|
|
2
|
+
import { buildRequestHeadersFromMiddlewareResponse, getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
|
|
3
3
|
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
4
4
|
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
5
5
|
import { forbiddenResponse, methodNotAllowedResponse, notFoundResponse } from "./http-error-responses.js";
|
|
6
|
-
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
6
|
+
import { getRepeatedSlashRedirect, isOpenRedirectShaped, repeatedSlashRedirectResponse, sendRepeatedSlashRedirect } from "./open-redirect.js";
|
|
7
7
|
import { createStaticFileSignal } from "./static-file-signal.js";
|
|
8
8
|
//#region src/server/request-pipeline.ts
|
|
9
9
|
const PATHNAME_CANONICALIZATION_BASE = new URL("http://vinext.invalid/");
|
|
@@ -52,10 +52,21 @@ function canonicalizeRequestUrlPathname(url) {
|
|
|
52
52
|
* Backslashes are equivalent to forward slashes in the URL spec
|
|
53
53
|
* (e.g. `/\evil.com` is treated as `//evil.com` by browsers).
|
|
54
54
|
*
|
|
55
|
-
* Next.js
|
|
56
|
-
*
|
|
55
|
+
* Like Next.js, any raw path containing a literal backslash or a repeated
|
|
56
|
+
* slash first gets a 308 to the collapsed path (`//evil.com` → `/evil.com`,
|
|
57
|
+
* `//` → `/`); see `getRepeatedSlashRedirect`. We check the RAW
|
|
58
|
+
* pathname before normalization so this fires before normalizePath collapses
|
|
59
|
+
* `//`.
|
|
57
60
|
*
|
|
58
|
-
*
|
|
61
|
+
* Request-based (Worker) entries only see `request.url` after WHATWG URL
|
|
62
|
+
* parsing, which has already turned `\` into `/`; the raw request target is
|
|
63
|
+
* not available there. `/\evil.com` still redirects (it arrives as
|
|
64
|
+
* `//evil.com`), but a lone backslash such as `/foo\bar` arrives as
|
|
65
|
+
* `/foo/bar` and is routed without the redirect. On Cloudflare, requests that
|
|
66
|
+
* match a static asset are also answered by the asset layer before the
|
|
67
|
+
* Worker runs. Node entries (dev and `vinext start`) check the raw target.
|
|
68
|
+
*
|
|
69
|
+
* Percent-encoded variants get a 404, as in Next.js, because:
|
|
59
70
|
* - `%5C` decodes to `\` (browsers treat `/\evil.com` as `//evil.com`).
|
|
60
71
|
* - `%2F` decodes to `/` (so `/%2F/evil.com` effectively becomes `//evil.com`).
|
|
61
72
|
* These forms survive segment-wise decoding that re-encodes path delimiters
|
|
@@ -64,9 +75,12 @@ function canonicalizeRequestUrlPathname(url) {
|
|
|
64
75
|
* `isOpenRedirectShaped` for the full list of rejected leading-segment forms.
|
|
65
76
|
*
|
|
66
77
|
* @param rawPathname - The raw pathname from the URL, before any normalization
|
|
67
|
-
* @
|
|
78
|
+
* @param search - The raw query string (including `?`), kept in the 308 Location
|
|
79
|
+
* @returns A 308 or 404 Response, or null to continue
|
|
68
80
|
*/
|
|
69
|
-
function guardProtocolRelativeUrl(rawPathname) {
|
|
81
|
+
function guardProtocolRelativeUrl(rawPathname, search = "") {
|
|
82
|
+
const slashRedirect = repeatedSlashRedirectResponse(rawPathname + search);
|
|
83
|
+
if (slashRedirect) return slashRedirect;
|
|
70
84
|
if (isOpenRedirectShaped(rawPathname)) return notFoundResponse();
|
|
71
85
|
return null;
|
|
72
86
|
}
|
|
@@ -86,7 +100,8 @@ function resolvePublicFileRoute(options) {
|
|
|
86
100
|
if (options.pathname.endsWith(".rsc")) return null;
|
|
87
101
|
if (!options.publicFiles.has(options.cleanPathname)) return null;
|
|
88
102
|
if (options.request.method !== "GET" && options.request.method !== "HEAD") return methodNotAllowedResponse("GET, HEAD", { headers: options.middlewareContext.headers ?? void 0 });
|
|
89
|
-
|
|
103
|
+
const { requestHeaders } = options.middlewareContext;
|
|
104
|
+
return createStaticFileSignal(options.cleanPathname, options.middlewareContext, requestHeaders ? buildRequestHeadersFromMiddlewareResponse(options.request.headers, requestHeaders) : null);
|
|
90
105
|
}
|
|
91
106
|
function normalizeTrailingSlashPathname(pathname, trailingSlash) {
|
|
92
107
|
if (pathname === "/" || pathname === "/api" || pathname.startsWith("/api/")) return null;
|
|
@@ -418,4 +433,4 @@ function cloneRequestWithUrl(request, url) {
|
|
|
418
433
|
return attachRequestCfMetadata(cloned, request);
|
|
419
434
|
}
|
|
420
435
|
//#endregion
|
|
421
|
-
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, attachRequestCfMetadata, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
|
|
436
|
+
export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, attachRequestCfMetadata, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, getRepeatedSlashRedirect, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, sendRepeatedSlashRedirect, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { NegotiatedEncoding } from "./accept-encoding.js";
|
|
2
|
+
import { IncomingMessage } from "node:http";
|
|
3
|
+
//#region src/server/response-compression.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Whether a Content-Type is compressible, matching `compressible()`. This
|
|
6
|
+
* covers `text/x-component` (RSC payloads) through the `text/` rule.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isCompressibleContentType(contentType: string | null | undefined): boolean;
|
|
9
|
+
/** Whether a Cache-Control value forbids content codings (`no-transform`). */
|
|
10
|
+
export declare function hasNoTransform(cacheControl: string | null | undefined): boolean;
|
|
11
|
+
/** Responses whose known length is below this many bytes are sent uncompressed. */
|
|
12
|
+
export declare const COMPRESS_THRESHOLD = 1024;
|
|
13
|
+
export type ResponseCompressionHeaders = {
|
|
14
|
+
contentType: string | null | undefined;
|
|
15
|
+
cacheControl: string | null | undefined;
|
|
16
|
+
contentEncoding: string | null | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Body length in bytes when it is known before the headers are written (a
|
|
19
|
+
* Content-Length header, or a buffered or empty body). Undefined for
|
|
20
|
+
* streamed bodies, which are compressed regardless of size.
|
|
21
|
+
*/
|
|
22
|
+
contentLength: number | undefined;
|
|
23
|
+
};
|
|
24
|
+
export type ResponseCompression = {
|
|
25
|
+
/** Whether the response must carry `Vary: Accept-Encoding`. */
|
|
26
|
+
varyAcceptEncoding: boolean;
|
|
27
|
+
/** The coding to apply. `identity` sends the body as is. */
|
|
28
|
+
encoding: NegotiatedEncoding;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Decide how to compress a response, in the middleware's order: the
|
|
32
|
+
* Content-Type filter and `Cache-Control: no-transform` skip compression
|
|
33
|
+
* without `Vary`; every other response gets `Vary: Accept-Encoding`, then is
|
|
34
|
+
* sent uncompressed when it is under the size threshold, already encoded, or
|
|
35
|
+
* a HEAD response, or when the client accepts no supported coding.
|
|
36
|
+
*
|
|
37
|
+
* Unlike the middleware, which offers only gzip and deflate, negotiation also
|
|
38
|
+
* offers zstd and br (see negotiateEncoding).
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveResponseCompression(req: IncomingMessage, compress: boolean, headers: ResponseCompressionHeaders): ResponseCompression;
|
|
41
|
+
/** Parse a Content-Length header value the way the middleware compares it. */
|
|
42
|
+
export declare function parseContentLengthHeader(value: string | null | undefined): number | undefined;
|
|
43
|
+
//#endregion
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { negotiateEncoding } from "./accept-encoding.js";
|
|
2
|
+
//#region src/server/response-compression.ts
|
|
3
|
+
/**
|
|
4
|
+
* Response compression rules for the Node production server, ported from the
|
|
5
|
+
* `compression` middleware that `next start` installs for every response
|
|
6
|
+
* unless `compress: false` (next/dist/compiled/compression, see
|
|
7
|
+
* packages/next/src/server/lib/router-server.ts).
|
|
8
|
+
*/
|
|
9
|
+
const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
|
|
10
|
+
const COMPRESSIBLE_TYPE_REGEXP = /^text\/|\+(?:json|text|xml)$/i;
|
|
11
|
+
/**
|
|
12
|
+
* mime-db entries marked `compressible: true` that COMPRESSIBLE_TYPE_REGEXP
|
|
13
|
+
* does not match, taken from the mime-db bundled with Next.js's compression
|
|
14
|
+
* middleware. That mime-db marks no type `compressible: false` that the
|
|
15
|
+
* regexp matches, so this set plus the regexp reproduces `compressible()`.
|
|
16
|
+
*/
|
|
17
|
+
const MIME_DB_COMPRESSIBLE_TYPES = /* @__PURE__ */ new Set([
|
|
18
|
+
"application/dart",
|
|
19
|
+
"application/ecmascript",
|
|
20
|
+
"application/javascript",
|
|
21
|
+
"application/json",
|
|
22
|
+
"application/postscript",
|
|
23
|
+
"application/raml+yaml",
|
|
24
|
+
"application/rtf",
|
|
25
|
+
"application/tar",
|
|
26
|
+
"application/toml",
|
|
27
|
+
"application/vnd.dart",
|
|
28
|
+
"application/vnd.ms-fontobject",
|
|
29
|
+
"application/vnd.ms-opentype",
|
|
30
|
+
"application/wasm",
|
|
31
|
+
"application/x-httpd-php",
|
|
32
|
+
"application/x-javascript",
|
|
33
|
+
"application/x-ns-proxy-autoconfig",
|
|
34
|
+
"application/x-sh",
|
|
35
|
+
"application/x-tar",
|
|
36
|
+
"application/x-virtualbox-hdd",
|
|
37
|
+
"application/x-virtualbox-ova",
|
|
38
|
+
"application/x-virtualbox-ovf",
|
|
39
|
+
"application/x-virtualbox-vbox",
|
|
40
|
+
"application/x-virtualbox-vdi",
|
|
41
|
+
"application/x-virtualbox-vhd",
|
|
42
|
+
"application/x-virtualbox-vmdk",
|
|
43
|
+
"application/x-www-form-urlencoded",
|
|
44
|
+
"application/xml",
|
|
45
|
+
"application/xml-dtd",
|
|
46
|
+
"font/otf",
|
|
47
|
+
"font/ttf",
|
|
48
|
+
"image/bmp",
|
|
49
|
+
"image/vnd.adobe.photoshop",
|
|
50
|
+
"image/x-icon",
|
|
51
|
+
"image/x-ms-bmp",
|
|
52
|
+
"message/rfc822",
|
|
53
|
+
"model/gltf-binary",
|
|
54
|
+
"x-shader/x-fragment",
|
|
55
|
+
"x-shader/x-vertex"
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* Whether a Content-Type is compressible, matching `compressible()`. This
|
|
59
|
+
* covers `text/x-component` (RSC payloads) through the `text/` rule.
|
|
60
|
+
*/
|
|
61
|
+
function isCompressibleContentType(contentType) {
|
|
62
|
+
if (!contentType) return false;
|
|
63
|
+
const match = EXTRACT_TYPE_REGEXP.exec(contentType);
|
|
64
|
+
const mime = match ? match[1].toLowerCase() : "";
|
|
65
|
+
return MIME_DB_COMPRESSIBLE_TYPES.has(mime) || COMPRESSIBLE_TYPE_REGEXP.test(mime);
|
|
66
|
+
}
|
|
67
|
+
const NO_TRANSFORM_REGEXP = /(?:^|,)\s*?no-transform\s*?(?:,|$)/;
|
|
68
|
+
/** Whether a Cache-Control value forbids content codings (`no-transform`). */
|
|
69
|
+
function hasNoTransform(cacheControl) {
|
|
70
|
+
return !!cacheControl && NO_TRANSFORM_REGEXP.test(cacheControl);
|
|
71
|
+
}
|
|
72
|
+
/** Responses whose known length is below this many bytes are sent uncompressed. */
|
|
73
|
+
const COMPRESS_THRESHOLD = 1024;
|
|
74
|
+
const NO_COMPRESSION = {
|
|
75
|
+
varyAcceptEncoding: false,
|
|
76
|
+
encoding: "identity"
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Decide how to compress a response, in the middleware's order: the
|
|
80
|
+
* Content-Type filter and `Cache-Control: no-transform` skip compression
|
|
81
|
+
* without `Vary`; every other response gets `Vary: Accept-Encoding`, then is
|
|
82
|
+
* sent uncompressed when it is under the size threshold, already encoded, or
|
|
83
|
+
* a HEAD response, or when the client accepts no supported coding.
|
|
84
|
+
*
|
|
85
|
+
* Unlike the middleware, which offers only gzip and deflate, negotiation also
|
|
86
|
+
* offers zstd and br (see negotiateEncoding).
|
|
87
|
+
*/
|
|
88
|
+
function resolveResponseCompression(req, compress, headers) {
|
|
89
|
+
if (!compress) return NO_COMPRESSION;
|
|
90
|
+
if (!isCompressibleContentType(headers.contentType)) return NO_COMPRESSION;
|
|
91
|
+
if (hasNoTransform(headers.cacheControl)) return NO_COMPRESSION;
|
|
92
|
+
const identity = {
|
|
93
|
+
varyAcceptEncoding: true,
|
|
94
|
+
encoding: "identity"
|
|
95
|
+
};
|
|
96
|
+
if (headers.contentLength !== void 0 && headers.contentLength < 1024) return identity;
|
|
97
|
+
if ((headers.contentEncoding || "identity") !== "identity") return identity;
|
|
98
|
+
if (req.method === "HEAD") return identity;
|
|
99
|
+
return {
|
|
100
|
+
varyAcceptEncoding: true,
|
|
101
|
+
encoding: negotiateEncoding(req)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Parse a Content-Length header value the way the middleware compares it. */
|
|
105
|
+
function parseContentLengthHeader(value) {
|
|
106
|
+
if (value === null || value === void 0) return void 0;
|
|
107
|
+
const length = Number(value);
|
|
108
|
+
return Number.isNaN(length) ? void 0 : length;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { COMPRESS_THRESHOLD, hasNoTransform, isCompressibleContentType, parseContentLengthHeader, resolveResponseCompression };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
//#region src/server/revalidation-request.d.ts
|
|
3
3
|
export declare function getRevalidateSecret(): string;
|
|
4
|
+
export declare function safeEqual(first: string, second: string): boolean;
|
|
4
5
|
export declare function isRevalidateSecret(value: string | null | undefined): boolean;
|
|
5
6
|
/** Match Next.js: the header value must equal the preview/revalidation secret. */
|
|
6
7
|
export declare function isOnDemandRevalidateRequest(headerValue: string | string[] | null | undefined): boolean;
|
|
@@ -34,4 +34,4 @@ function isOnDemandRevalidateRequest(headerValue) {
|
|
|
34
34
|
return typeof headerValue === "string" && isRevalidateSecret(headerValue);
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
|
-
export { PRERENDER_REVALIDATE_HEADER, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret };
|
|
37
|
+
export { PRERENDER_REVALIDATE_HEADER, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, safeEqual };
|
|
@@ -55,6 +55,15 @@ function indexOfByte(bytes, byte, from) {
|
|
|
55
55
|
for (let index = from; index < bytes.byteLength; index++) if (bytes[index] === byte) return index;
|
|
56
56
|
return -1;
|
|
57
57
|
}
|
|
58
|
+
function utf8CompleteEnd(bytes, from) {
|
|
59
|
+
const length = bytes.byteLength;
|
|
60
|
+
for (let index = length - 1; index >= from && index >= length - 3; index--) {
|
|
61
|
+
const byte = bytes[index];
|
|
62
|
+
if ((byte & 192) === 128) continue;
|
|
63
|
+
return index + (byte >= 240 ? 4 : byte >= 224 ? 3 : byte >= 192 ? 2 : 1) > length ? index : length;
|
|
64
|
+
}
|
|
65
|
+
return length;
|
|
66
|
+
}
|
|
58
67
|
function parseHexBytes(bytes, start, end) {
|
|
59
68
|
if (start === end) return null;
|
|
60
69
|
let value = 0;
|
|
@@ -101,16 +110,30 @@ function normalizeReactFlightPreloadHints(stream) {
|
|
|
101
110
|
let carry = null;
|
|
102
111
|
let rawBytesRemaining = 0;
|
|
103
112
|
let passThrough = false;
|
|
113
|
+
let inPlainRow = false;
|
|
104
114
|
return stream.pipeThrough(new TransformStream({
|
|
105
115
|
transform(chunk, controller) {
|
|
106
116
|
if (passThrough) {
|
|
107
117
|
controller.enqueue(chunk);
|
|
108
118
|
return;
|
|
109
119
|
}
|
|
120
|
+
let start = 0;
|
|
121
|
+
if (inPlainRow) {
|
|
122
|
+
const end = indexOfByte(chunk, NEWLINE_BYTE, 0);
|
|
123
|
+
if (end === -1) {
|
|
124
|
+
const bytes = carry === null ? chunk : concatBytes(carry, chunk);
|
|
125
|
+
const complete = utf8CompleteEnd(bytes, 0);
|
|
126
|
+
carry = complete === bytes.byteLength ? null : bytes.slice(complete);
|
|
127
|
+
if (complete > 0) controller.enqueue(complete === bytes.byteLength ? bytes : bytes.subarray(0, complete));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
inPlainRow = false;
|
|
131
|
+
start = (carry === null ? 0 : carry.byteLength) + end + 1;
|
|
132
|
+
}
|
|
110
133
|
const bytes = carry === null ? chunk : concatBytes(carry, chunk);
|
|
111
134
|
carry = null;
|
|
112
135
|
const byteLength = bytes.byteLength;
|
|
113
|
-
let offset =
|
|
136
|
+
let offset = start;
|
|
114
137
|
let emittedThrough = 0;
|
|
115
138
|
let output = bytes;
|
|
116
139
|
let ownsOutput = bytes !== chunk;
|
|
@@ -146,7 +169,13 @@ function normalizeReactFlightPreloadHints(stream) {
|
|
|
146
169
|
break;
|
|
147
170
|
}
|
|
148
171
|
const newline = indexOfByte(bytes, NEWLINE_BYTE, offset);
|
|
149
|
-
if (newline === -1)
|
|
172
|
+
if (newline === -1) {
|
|
173
|
+
if (!(tagByte === HINT_TAG_BYTE && (colon + 2 === byteLength || bytes[colon + 2] === LINK_HINT_CODE_BYTE))) {
|
|
174
|
+
inPlainRow = true;
|
|
175
|
+
offset = utf8CompleteEnd(bytes, offset);
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
150
179
|
if (tagByte === HINT_TAG_BYTE && bytes[colon + 2] === LINK_HINT_CODE_BYTE) {
|
|
151
180
|
const line = bytes.subarray(offset, newline + 1);
|
|
152
181
|
const normalized = normalizeReactFlightHintLine(line);
|
|
@@ -14,6 +14,7 @@ type PrerenderCacheSeedMetadata = {
|
|
|
14
14
|
};
|
|
15
15
|
type PrerenderCacheSeedOptions = {
|
|
16
16
|
buildAppPageHtmlKey?: (pathname: string) => string;
|
|
17
|
+
buildAppPageFallbackShellKey?: (pathname: string, suffix: "html" | "rsc") => string;
|
|
17
18
|
buildAppPageRscKey?: (pathname: string) => string;
|
|
18
19
|
buildAppRouteKey?: (pathname: string) => string;
|
|
19
20
|
writeAppPageEntry?: (key: string, data: CachedAppPageValue, metadata: PrerenderCacheSeedMetadata) => Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
-
import { appIsrCacheKey, isrSet, isrSetPrerenderedAppPage } from "./isr-cache.js";
|
|
2
|
+
import { appIsrCacheKey, appIsrFallbackShellKey, isrSet, isrSetPrerenderedAppPage } from "./isr-cache.js";
|
|
3
3
|
import { addPregeneratedConcretePath, addPregeneratedRoute, clearPregeneratedConcretePaths, normalizePregeneratedPathname } from "./pregenerated-concrete-paths.js";
|
|
4
4
|
import { getRenderedAppRoutes, getRenderedMetadataRoutes, isFallbackShellArtifactPath, readPrerenderManifest } from "./prerender-manifest.js";
|
|
5
5
|
import { buildAppPageCacheTags, buildAppRouteCacheTags } from "./app-page-cache.js";
|
|
@@ -61,17 +61,21 @@ async function seedMemoryCacheFromPrerender(serverDir, options) {
|
|
|
61
61
|
const appRoutes = getRenderedAppRoutes(routes);
|
|
62
62
|
for (const route of appRoutes) {
|
|
63
63
|
const concretePathname = route.path ?? route.route;
|
|
64
|
-
|
|
64
|
+
const isFallbackShell = isFallbackShellArtifactPath(concretePathname, route);
|
|
65
|
+
if (!isFallbackShell) addPregeneratedConcretePath(route.route, concretePathname);
|
|
65
66
|
const artifactPathname = route.path ?? route.route;
|
|
66
67
|
const cachePathname = normalizePregeneratedPathname(artifactPathname);
|
|
67
|
-
const htmlKey = options?.buildAppPageHtmlKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "html", buildId);
|
|
68
|
-
const rscKey = options?.buildAppPageRscKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "rsc", buildId);
|
|
68
|
+
const htmlKey = isFallbackShell ? options?.buildAppPageFallbackShellKey?.(cachePathname, "html") ?? appIsrFallbackShellKey(cachePathname, "html", buildId) : options?.buildAppPageHtmlKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "html", buildId);
|
|
69
|
+
const rscKey = isFallbackShell ? options?.buildAppPageFallbackShellKey?.(cachePathname, "rsc") ?? appIsrFallbackShellKey(cachePathname, "rsc", buildId) : options?.buildAppPageRscKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "rsc", buildId);
|
|
69
70
|
const revalidateSeconds = typeof route.revalidate === "number" ? route.revalidate : void 0;
|
|
70
71
|
const expireSeconds = typeof route.expire === "number" ? route.expire : void 0;
|
|
71
72
|
const staleSeconds = typeof route.stale === "number" && route.stale >= 0 ? route.stale : void 0;
|
|
72
73
|
const tags = buildAppPageCacheTags(cachePathname, route.tags ?? []);
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const status = route.responseStatus;
|
|
75
|
+
const location = route.headers?.location;
|
|
76
|
+
const rscHeaders = status !== void 0 && location !== void 0 ? { location } : void 0;
|
|
77
|
+
if (await seedHtml(writeAppPageEntry, prerenderDir, htmlKey, artifactPathname, trailingSlash, route.headers, status, revalidateSeconds, expireSeconds, staleSeconds, tags)) {
|
|
78
|
+
await seedRsc(writeAppPageEntry, prerenderDir, rscKey, artifactPathname, rscHeaders, status, revalidateSeconds, expireSeconds, staleSeconds, tags);
|
|
75
79
|
seeded++;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
@@ -107,7 +111,7 @@ function createDefaultAppPageEntryWriter() {
|
|
|
107
111
|
* Seed the HTML cache entry for a single route.
|
|
108
112
|
* Returns true if the file existed and was seeded.
|
|
109
113
|
*/
|
|
110
|
-
async function seedHtml(writeAppPageEntry, prerenderDir, key, pathname, trailingSlash, headers, revalidateSeconds, expireSeconds, staleSeconds, tags) {
|
|
114
|
+
async function seedHtml(writeAppPageEntry, prerenderDir, key, pathname, trailingSlash, headers, status, revalidateSeconds, expireSeconds, staleSeconds, tags) {
|
|
111
115
|
const relPath = getOutputPath(pathname, trailingSlash);
|
|
112
116
|
const fullPath = path.join(prerenderDir, relPath);
|
|
113
117
|
if (!fs.existsSync(fullPath)) return false;
|
|
@@ -117,7 +121,7 @@ async function seedHtml(writeAppPageEntry, prerenderDir, key, pathname, trailing
|
|
|
117
121
|
rscData: void 0,
|
|
118
122
|
headers,
|
|
119
123
|
postponed: void 0,
|
|
120
|
-
status
|
|
124
|
+
status
|
|
121
125
|
}, {
|
|
122
126
|
expireSeconds,
|
|
123
127
|
revalidateSeconds,
|
|
@@ -130,7 +134,7 @@ async function seedHtml(writeAppPageEntry, prerenderDir, key, pathname, trailing
|
|
|
130
134
|
* Seed the RSC cache entry for a single route.
|
|
131
135
|
* No-op if the .rsc file doesn't exist on disk.
|
|
132
136
|
*/
|
|
133
|
-
async function seedRsc(writeAppPageEntry, prerenderDir, key, pathname, revalidateSeconds, expireSeconds, staleSeconds, tags) {
|
|
137
|
+
async function seedRsc(writeAppPageEntry, prerenderDir, key, pathname, headers, status, revalidateSeconds, expireSeconds, staleSeconds, tags) {
|
|
134
138
|
const relPath = getRscOutputPath(pathname);
|
|
135
139
|
const fullPath = path.join(prerenderDir, relPath);
|
|
136
140
|
if (!fs.existsSync(fullPath)) return;
|
|
@@ -139,9 +143,9 @@ async function seedRsc(writeAppPageEntry, prerenderDir, key, pathname, revalidat
|
|
|
139
143
|
kind: "APP_PAGE",
|
|
140
144
|
html: "",
|
|
141
145
|
rscData: rscBuffer.buffer.slice(rscBuffer.byteOffset, rscBuffer.byteOffset + rscBuffer.byteLength),
|
|
142
|
-
headers
|
|
146
|
+
headers,
|
|
143
147
|
postponed: void 0,
|
|
144
|
-
status
|
|
148
|
+
status
|
|
145
149
|
}, {
|
|
146
150
|
expireSeconds,
|
|
147
151
|
revalidateSeconds,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { runOutsideRequestScopes } from "../shims/internal/als-registry.js";
|
|
2
|
+
//#region src/server/server-reference-loader.ts
|
|
3
|
+
/**
|
|
4
|
+
* Keep server-reference module evaluation out of the request that first
|
|
5
|
+
* names the module.
|
|
6
|
+
*
|
|
7
|
+
* React resolves every server reference it decodes through
|
|
8
|
+
* `@vitejs/plugin-rsc`'s `globalThis.__vite_rsc_server_require__`, which
|
|
9
|
+
* imports the referenced module on first use. Some references are resolved
|
|
10
|
+
* only once the action consumes them — promise and async-iterable arguments,
|
|
11
|
+
* and the encrypted closure captures an inline action decrypts in its own
|
|
12
|
+
* body — so they load inside the caller's request, past the loads the action
|
|
13
|
+
* dispatcher already runs outside it. A dynamic `import()` carries
|
|
14
|
+
* AsyncLocalStorage into the module's top-level evaluation, so module-scope
|
|
15
|
+
* `cookies()`/`headers()` would capture that caller's request for every later
|
|
16
|
+
* caller in the isolate. Next.js evaluates action modules outside the request
|
|
17
|
+
* store, where those calls throw.
|
|
18
|
+
*/
|
|
19
|
+
const ISOLATED = Symbol.for("vinext.serverReferenceRequire.isolated");
|
|
20
|
+
/** Wrap plugin-rsc's server-reference loader once; must run after it is installed. */
|
|
21
|
+
function isolateServerReferenceLoads() {
|
|
22
|
+
const load = globalThis.__vite_rsc_server_require__;
|
|
23
|
+
if (typeof load !== "function" || ISOLATED in load) return;
|
|
24
|
+
const isolated = (id) => runOutsideRequestScopes(() => load(id));
|
|
25
|
+
Object.defineProperty(isolated, ISOLATED, { value: true });
|
|
26
|
+
globalThis.__vite_rsc_server_require__ = isolated;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { isolateServerReferenceLoads };
|
|
@@ -23,11 +23,33 @@ export declare function peerDisconnectCode(err: unknown): string | undefined;
|
|
|
23
23
|
* Exported for unit testing in isolation.
|
|
24
24
|
*/
|
|
25
25
|
export declare function isBenignAssetImportError(err: unknown): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Pure predicate: returns `true` when `err` is Node's
|
|
28
|
+
* `Readable.toWeb()` adapter enqueueing into a request body stream that was
|
|
29
|
+
* already cancelled. `pull()` calls `req.resume()`, which schedules `flow()`
|
|
30
|
+
* on `nextTick`; if the body is cancelled before that tick runs, `flow()`
|
|
31
|
+
* still emits the chunks already buffered in `req` and the adapter's
|
|
32
|
+
* `onData` throws inside the `nextTick` callback. srvx (used by
|
|
33
|
+
* `@vitejs/plugin-rsc` in dev) builds `request.body` this way, so any
|
|
34
|
+
* cancelled request body, e.g. an oversized Server Action, can crash the
|
|
35
|
+
* dev server. Node main drops that data instead (`if (wasCanceled) return;`
|
|
36
|
+
* in `onData`), so absorbing it matches upstream behaviour. Exported for
|
|
37
|
+
* unit testing in isolation.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isCancelledRequestBodyEnqueueError(err: unknown): boolean;
|
|
26
40
|
/**
|
|
27
41
|
* Test-only: returns whether the backstop has been installed in this
|
|
28
42
|
* process. Used by the unit test to assert idempotent install via the
|
|
29
43
|
* Symbol.for guard. Not part of the public API.
|
|
30
44
|
*/
|
|
31
45
|
export declare function isSocketErrorBackstopInstalled(): boolean;
|
|
32
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Installs the backstop once per process. Pass `{ runningServer: true }`
|
|
48
|
+
* from a long-lived production server to log, rather than re-throw,
|
|
49
|
+
* errors outside the absorbed set. The flag is process-global and sticky,
|
|
50
|
+
* so it also applies when `index.ts` installed the listeners first.
|
|
51
|
+
*/
|
|
52
|
+
export declare function installSocketErrorBackstop(options?: {
|
|
53
|
+
runningServer?: boolean;
|
|
54
|
+
}): void;
|
|
33
55
|
//#endregion
|