vinext 0.0.46 → 0.0.48
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 +8 -6
- package/dist/build/layout-classification.js +3 -1
- package/dist/build/layout-classification.js.map +1 -1
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +80 -24
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/report.d.ts +9 -5
- package/dist/build/report.js +17 -7
- package/dist/build/report.js.map +1 -1
- package/dist/build/route-classification-injector.d.ts +35 -0
- package/dist/build/route-classification-injector.js +61 -0
- package/dist/build/route-classification-injector.js.map +1 -0
- package/dist/build/route-classification-manifest.d.ts +1 -1
- package/dist/build/run-prerender.d.ts +5 -0
- package/dist/build/run-prerender.js +4 -1
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/build/server-manifest.js +2 -7
- package/dist/build/server-manifest.js.map +1 -1
- package/dist/build/standalone.js +3 -5
- package/dist/build/standalone.js.map +1 -1
- package/dist/build/static-export.d.ts +1 -1
- package/dist/check.js +45 -29
- package/dist/check.js.map +1 -1
- package/dist/cli-args.d.ts +33 -0
- package/dist/cli-args.js +121 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.js +11 -20
- package/dist/cli.js.map +1 -1
- package/dist/cloudflare/kv-cache-handler.js +29 -9
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/config/config-matchers.js +46 -37
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/next-config.d.ts +4 -2
- package/dist/config/next-config.js +3 -0
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.d.ts +18 -2
- package/dist/deploy.js +47 -4
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +4 -3
- package/dist/entries/app-rsc-entry.js +379 -858
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-rsc-manifest.d.ts +1 -1
- package/dist/entries/app-rsc-manifest.js +6 -1
- package/dist/entries/app-rsc-manifest.js.map +1 -1
- package/dist/entries/pages-client-entry.js +3 -2
- package/dist/entries/pages-client-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +19 -61
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.d.ts +12 -3
- package/dist/entries/runtime-entry-module.js +15 -4
- package/dist/entries/runtime-entry-module.js.map +1 -1
- package/dist/index.js +40 -58
- package/dist/index.js.map +1 -1
- package/dist/plugins/fonts.js +54 -32
- package/dist/plugins/fonts.js.map +1 -1
- package/dist/plugins/og-assets.js +15 -16
- package/dist/plugins/og-assets.js.map +1 -1
- package/dist/plugins/rsc-client-shim-excludes.d.ts +2 -1
- package/dist/plugins/rsc-client-shim-excludes.js +11 -1
- package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
- package/dist/routing/app-route-graph.d.ts +195 -0
- package/dist/routing/app-route-graph.js +1022 -0
- package/dist/routing/app-route-graph.js.map +1 -0
- package/dist/routing/app-router.d.ts +14 -88
- package/dist/routing/app-router.js +21 -712
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/file-matcher.d.ts +3 -1
- package/dist/routing/file-matcher.js +6 -1
- package/dist/routing/file-matcher.js.map +1 -1
- package/dist/routing/pages-router.js +10 -19
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/route-matching.d.ts +28 -0
- package/dist/routing/route-matching.js +44 -0
- package/dist/routing/route-matching.js.map +1 -0
- package/dist/routing/route-pattern.js +4 -1
- package/dist/routing/route-pattern.js.map +1 -1
- package/dist/routing/route-trie.d.ts +8 -0
- package/dist/routing/route-trie.js +12 -1
- package/dist/routing/route-trie.js.map +1 -1
- package/dist/routing/route-validation.js +3 -4
- package/dist/routing/route-validation.js.map +1 -1
- package/dist/routing/utils.d.ts +8 -1
- package/dist/routing/utils.js +25 -2
- package/dist/routing/utils.js.map +1 -1
- package/dist/server/app-browser-entry.js +145 -294
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-error.d.ts +3 -4
- package/dist/server/app-browser-error.js +8 -4
- package/dist/server/app-browser-error.js.map +1 -1
- package/dist/server/app-browser-navigation-controller.d.ts +75 -0
- package/dist/server/app-browser-navigation-controller.js +290 -0
- package/dist/server/app-browser-navigation-controller.js.map +1 -0
- package/dist/server/app-browser-state.d.ts +33 -15
- package/dist/server/app-browser-state.js +52 -59
- package/dist/server/app-browser-state.js.map +1 -1
- package/dist/server/app-browser-visible-commit.d.ts +68 -0
- package/dist/server/app-browser-visible-commit.js +182 -0
- package/dist/server/app-browser-visible-commit.js.map +1 -0
- package/dist/server/app-client-reference-preloader.d.ts +15 -0
- package/dist/server/app-client-reference-preloader.js +46 -0
- package/dist/server/app-client-reference-preloader.js.map +1 -0
- package/dist/server/app-elements-wire.d.ts +130 -0
- package/dist/server/app-elements-wire.js +205 -0
- package/dist/server/app-elements-wire.js.map +1 -0
- package/dist/server/app-elements.d.ts +2 -84
- package/dist/server/app-elements.js +4 -107
- package/dist/server/app-elements.js.map +1 -1
- package/dist/server/app-fallback-renderer.d.ts +57 -0
- package/dist/server/app-fallback-renderer.js +79 -0
- package/dist/server/app-fallback-renderer.js.map +1 -0
- package/dist/server/app-hook-warning-suppression.d.ts +7 -0
- package/dist/server/app-hook-warning-suppression.js +12 -0
- package/dist/server/app-hook-warning-suppression.js.map +1 -0
- package/dist/server/app-middleware.d.ts +2 -1
- package/dist/server/app-middleware.js +34 -11
- package/dist/server/app-middleware.js.map +1 -1
- package/dist/server/app-mounted-slots-header.d.ts +17 -0
- package/dist/server/app-mounted-slots-header.js +21 -0
- package/dist/server/app-mounted-slots-header.js.map +1 -0
- package/dist/server/app-page-boundary-render.d.ts +3 -3
- package/dist/server/app-page-boundary-render.js +8 -5
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-boundary.js.map +1 -1
- package/dist/server/app-page-cache.d.ts +19 -4
- package/dist/server/app-page-cache.js +60 -22
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-dispatch.d.ts +9 -5
- package/dist/server/app-page-dispatch.js +41 -17
- package/dist/server/app-page-dispatch.js.map +1 -1
- package/dist/server/app-page-element-builder.d.ts +61 -0
- package/dist/server/app-page-element-builder.js +142 -0
- package/dist/server/app-page-element-builder.js.map +1 -0
- package/dist/server/app-page-execution.d.ts +23 -5
- package/dist/server/app-page-execution.js +39 -24
- package/dist/server/app-page-execution.js.map +1 -1
- package/dist/server/app-page-head.js +2 -1
- package/dist/server/app-page-head.js.map +1 -1
- package/dist/server/app-page-method.js +2 -5
- package/dist/server/app-page-method.js.map +1 -1
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +3 -3
- package/dist/server/app-page-params.js.map +1 -1
- package/dist/server/app-page-probe.d.ts +1 -1
- package/dist/server/app-page-probe.js +5 -1
- package/dist/server/app-page-probe.js.map +1 -1
- package/dist/server/app-page-render.d.ts +6 -2
- package/dist/server/app-page-render.js +118 -30
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-request.d.ts +19 -5
- package/dist/server/app-page-request.js +49 -7
- package/dist/server/app-page-request.js.map +1 -1
- package/dist/server/app-page-response.d.ts +1 -0
- package/dist/server/app-page-response.js +6 -9
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +20 -4
- package/dist/server/app-page-route-wiring.js +15 -12
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +7 -0
- package/dist/server/app-page-stream.js +9 -2
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-post-middleware-context.d.ts +16 -0
- package/dist/server/app-post-middleware-context.js +28 -0
- package/dist/server/app-post-middleware-context.js.map +1 -0
- package/dist/server/app-prerender-endpoints.js +3 -2
- package/dist/server/app-prerender-endpoints.js.map +1 -1
- package/dist/server/app-request-context.d.ts +22 -0
- package/dist/server/app-request-context.js +30 -0
- package/dist/server/app-request-context.js.map +1 -0
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +7 -2
- package/dist/server/app-route-handler-cache.js.map +1 -1
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +8 -5
- package/dist/server/app-route-handler-dispatch.js.map +1 -1
- package/dist/server/app-route-handler-execution.d.ts +2 -1
- package/dist/server/app-route-handler-execution.js +2 -2
- package/dist/server/app-route-handler-execution.js.map +1 -1
- package/dist/server/app-route-handler-policy.js +13 -13
- package/dist/server/app-route-handler-policy.js.map +1 -1
- package/dist/server/app-route-handler-response.d.ts +4 -2
- package/dist/server/app-route-handler-response.js +9 -7
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-route-handler-runtime.d.ts +9 -1
- package/dist/server/app-route-handler-runtime.js +11 -1
- package/dist/server/app-route-handler-runtime.js.map +1 -1
- package/dist/server/app-router-entry.js +9 -4
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +34 -0
- package/dist/server/app-rsc-cache-busting.js +137 -0
- package/dist/server/app-rsc-cache-busting.js.map +1 -0
- package/dist/server/app-rsc-error-handler.d.ts +21 -0
- package/dist/server/app-rsc-error-handler.js +30 -0
- package/dist/server/app-rsc-error-handler.js.map +1 -0
- package/dist/server/app-rsc-handler.d.ts +117 -0
- package/dist/server/app-rsc-handler.js +271 -0
- package/dist/server/app-rsc-handler.js.map +1 -0
- package/dist/server/app-rsc-request-normalization.d.ts +42 -0
- package/dist/server/app-rsc-request-normalization.js +67 -0
- package/dist/server/app-rsc-request-normalization.js.map +1 -0
- package/dist/server/app-rsc-response-finalizer.d.ts +30 -0
- package/dist/server/app-rsc-response-finalizer.js +38 -0
- package/dist/server/app-rsc-response-finalizer.js.map +1 -0
- package/dist/server/app-rsc-route-matching.js +8 -4
- package/dist/server/app-rsc-route-matching.js.map +1 -1
- package/dist/server/app-segment-config.d.ts +33 -0
- package/dist/server/app-segment-config.js +90 -0
- package/dist/server/app-segment-config.js.map +1 -0
- package/dist/server/app-server-action-execution.d.ts +2 -0
- package/dist/server/app-server-action-execution.js +45 -51
- package/dist/server/app-server-action-execution.js.map +1 -1
- package/dist/server/app-ssr-entry.js +21 -20
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/artifact-compatibility.d.ts +44 -0
- package/dist/server/artifact-compatibility.js +82 -0
- package/dist/server/artifact-compatibility.js.map +1 -0
- package/dist/server/cache-control.d.ts +24 -0
- package/dist/server/cache-control.js +33 -0
- package/dist/server/cache-control.js.map +1 -0
- package/dist/server/cache-proof.d.ts +200 -0
- package/dist/server/cache-proof.js +342 -0
- package/dist/server/cache-proof.js.map +1 -0
- package/dist/server/dev-error-overlay-store.d.ts +23 -0
- package/dist/server/dev-error-overlay-store.js +67 -0
- package/dist/server/dev-error-overlay-store.js.map +1 -0
- package/dist/server/dev-error-overlay.d.ts +15 -0
- package/dist/server/dev-error-overlay.js +548 -0
- package/dist/server/dev-error-overlay.js.map +1 -0
- package/dist/server/dev-origin-check.js +8 -4
- package/dist/server/dev-origin-check.js.map +1 -1
- package/dist/server/dev-server.js +1 -6
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/http-error-responses.d.ts +67 -0
- package/dist/server/http-error-responses.js +77 -0
- package/dist/server/http-error-responses.js.map +1 -0
- package/dist/server/image-optimization.js +2 -1
- package/dist/server/image-optimization.js.map +1 -1
- package/dist/server/instrumentation-runtime.d.ts +44 -0
- package/dist/server/instrumentation-runtime.js +29 -0
- package/dist/server/instrumentation-runtime.js.map +1 -0
- package/dist/server/isr-cache.d.ts +2 -7
- package/dist/server/isr-cache.js +7 -10
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/metadata-route-response.js +6 -5
- package/dist/server/metadata-route-response.js.map +1 -1
- package/dist/server/metadata-routes.d.ts +1 -0
- package/dist/server/metadata-routes.js +6 -0
- package/dist/server/metadata-routes.js.map +1 -1
- package/dist/server/middleware-matcher.js +2 -2
- package/dist/server/middleware-matcher.js.map +1 -1
- package/dist/server/middleware-response-headers.js +21 -0
- package/dist/server/middleware-response-headers.js.map +1 -1
- package/dist/server/middleware-runtime.js +3 -3
- package/dist/server/middleware-runtime.js.map +1 -1
- package/dist/server/navigation-trace.d.ts +33 -0
- package/dist/server/navigation-trace.js +35 -0
- package/dist/server/navigation-trace.js.map +1 -0
- package/dist/server/next-error-digest.d.ts +44 -0
- package/dist/server/next-error-digest.js +40 -0
- package/dist/server/next-error-digest.js.map +1 -0
- package/dist/server/pages-api-route.js +2 -1
- package/dist/server/pages-api-route.js.map +1 -1
- package/dist/server/pages-node-compat.js +4 -16
- package/dist/server/pages-node-compat.js.map +1 -1
- package/dist/server/pages-page-data.d.ts +2 -1
- package/dist/server/pages-page-data.js +6 -5
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-response.d.ts +3 -8
- package/dist/server/pages-page-response.js +46 -15
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/prod-server.d.ts +6 -0
- package/dist/server/prod-server.js +28 -21
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-pipeline.d.ts +42 -1
- package/dist/server/request-pipeline.js +97 -17
- package/dist/server/request-pipeline.js.map +1 -1
- package/dist/server/rsc-stream-hints.d.ts +3 -1
- package/dist/server/rsc-stream-hints.js +4 -1
- package/dist/server/rsc-stream-hints.js.map +1 -1
- package/dist/server/seed-cache.js +19 -8
- package/dist/server/seed-cache.js.map +1 -1
- package/dist/shims/cache-runtime.d.ts +2 -2
- package/dist/shims/cache-runtime.js +31 -17
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +15 -3
- package/dist/shims/cache.js +45 -20
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +17 -1
- package/dist/shims/error-boundary.js +31 -1
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts +4 -1
- package/dist/shims/fetch-cache.js +57 -16
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/head-state.js +2 -3
- package/dist/shims/head-state.js.map +1 -1
- package/dist/shims/headers.js +4 -44
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/i18n-state.js +2 -3
- package/dist/shims/i18n-state.js.map +1 -1
- package/dist/shims/image.js +93 -5
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/internal/als-registry.d.ts +15 -0
- package/dist/shims/internal/als-registry.js +55 -0
- package/dist/shims/internal/als-registry.js.map +1 -0
- package/dist/shims/internal/cookie-serialize.d.ts +46 -0
- package/dist/shims/internal/cookie-serialize.js +51 -0
- package/dist/shims/internal/cookie-serialize.js.map +1 -0
- package/dist/shims/link.js +31 -26
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +26 -1
- package/dist/shims/metadata.js +94 -4
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation-state.js +2 -3
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +2 -7
- package/dist/shims/navigation.js +44 -36
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/request-context.js +2 -4
- package/dist/shims/request-context.js.map +1 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router-state.js +2 -3
- package/dist/shims/router-state.js.map +1 -1
- package/dist/shims/router.js +2 -2
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/server.js +5 -30
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/slot.d.ts +1 -1
- package/dist/shims/slot.js +5 -4
- package/dist/shims/slot.js.map +1 -1
- package/dist/shims/thenable-params.d.ts +5 -2
- package/dist/shims/thenable-params.js +26 -6
- package/dist/shims/thenable-params.js.map +1 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +3 -14
- package/dist/shims/unified-request-context.js.map +1 -1
- package/dist/shims/use-merged-ref.d.ts +7 -0
- package/dist/shims/use-merged-ref.js +40 -0
- package/dist/shims/use-merged-ref.js.map +1 -0
- package/dist/utils/base-path.d.ts +7 -1
- package/dist/utils/base-path.js +12 -1
- package/dist/utils/base-path.js.map +1 -1
- package/dist/utils/cache-control-metadata.d.ts +6 -0
- package/dist/utils/cache-control-metadata.js +16 -0
- package/dist/utils/cache-control-metadata.js.map +1 -0
- package/dist/utils/safe-json-file.d.ts +18 -0
- package/dist/utils/safe-json-file.js +25 -0
- package/dist/utils/safe-json-file.js.map +1 -0
- package/dist/utils/text-stream.d.ts +29 -0
- package/dist/utils/text-stream.js +66 -0
- package/dist/utils/text-stream.js.map +1 -0
- package/package.json +5 -5
|
@@ -1,26 +1,14 @@
|
|
|
1
1
|
import { parseCookies } from "../config/config-matchers.js";
|
|
2
2
|
import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
|
|
3
|
+
import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
|
|
3
4
|
import { decode } from "node:querystring";
|
|
4
5
|
//#region src/server/pages-node-compat.ts
|
|
5
6
|
const MAX_PAGES_API_BODY_SIZE = 1 * 1024 * 1024;
|
|
6
7
|
async function readPagesRequestBodyWithLimit(request, maxBytes) {
|
|
7
8
|
if (!request.body) return "";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
let totalSize = 0;
|
|
12
|
-
for (;;) {
|
|
13
|
-
const result = await reader.read();
|
|
14
|
-
if (result.done) break;
|
|
15
|
-
totalSize += result.value.byteLength;
|
|
16
|
-
if (totalSize > maxBytes) {
|
|
17
|
-
await reader.cancel();
|
|
18
|
-
throw new PagesBodyParseError("Request body too large", 413);
|
|
19
|
-
}
|
|
20
|
-
chunks.push(decoder.decode(result.value, { stream: true }));
|
|
21
|
-
}
|
|
22
|
-
chunks.push(decoder.decode());
|
|
23
|
-
return chunks.join("");
|
|
9
|
+
return readStreamAsTextWithLimit(request.body, maxBytes, () => {
|
|
10
|
+
throw new PagesBodyParseError("Request body too large", 413);
|
|
11
|
+
});
|
|
24
12
|
}
|
|
25
13
|
async function parsePagesApiBody(request, maxBytes = MAX_PAGES_API_BODY_SIZE) {
|
|
26
14
|
if (Number.parseInt(request.headers.get("content-length") || "0", 10) > maxBytes) throw new PagesBodyParseError("Request body too large", 413);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages-node-compat.js","names":["decodeQueryString"],"sources":["../../src/server/pages-node-compat.ts"],"sourcesContent":["import { decode as decodeQueryString } from \"node:querystring\";\nimport { parseCookies } from \"../config/config-matchers.js\";\nimport { PagesBodyParseError, getMediaType, isJsonMediaType } from \"./pages-media-type.js\";\n\nconst MAX_PAGES_API_BODY_SIZE = 1 * 1024 * 1024;\n\n/**\n * @deprecated Use PagesBodyParseError from pages-media-type.ts instead.\n * Kept for backwards compatibility.\n */\nexport { PagesBodyParseError as PagesApiBodyParseError };\n\nexport type PagesRequestQuery = Record<string, string | string[]>;\n\nexport type PagesReqResRequest = {\n method: string;\n url: string;\n headers: Record<string, string>;\n query: PagesRequestQuery;\n body: unknown;\n cookies: Record<string, string>;\n};\n\nexport type PagesReqResHeaders = {\n [key: string]: string | number | boolean | string[];\n};\n\nexport type PagesReqResResponse = {\n statusCode: number;\n readonly headersSent: boolean;\n writeHead: (code: number, headers?: PagesReqResHeaders) => PagesReqResResponse;\n setHeader: (name: string, value: string | number | boolean | string[]) => PagesReqResResponse;\n getHeader: (name: string) => string | number | boolean | string[] | undefined;\n end: (data?: BodyInit | null) => void;\n status: (code: number) => PagesReqResResponse;\n json: (data: unknown) => void;\n send: (data: unknown) => void;\n redirect: (statusOrUrl: number | string, url?: string) => void;\n getHeaders: () => PagesReqResHeaders;\n};\n\ntype CreatePagesReqResOptions = {\n body: unknown;\n query: PagesRequestQuery;\n request: Request;\n url: string;\n};\n\ntype CreatePagesReqResResult = {\n req: PagesReqResRequest;\n res: PagesReqResResponse;\n responsePromise: Promise<Response>;\n};\n\nasync function readPagesRequestBodyWithLimit(request: Request, maxBytes: number): Promise<string> {\n if (!request.body) {\n return \"\";\n }\n\n const reader = request.body.getReader();\n const decoder = new TextDecoder();\n const chunks: string[] = [];\n let totalSize = 0;\n\n for (;;) {\n const result = await reader.read();\n if (result.done) {\n break;\n }\n\n totalSize += result.value.byteLength;\n if (totalSize > maxBytes) {\n await reader.cancel();\n throw new PagesBodyParseError(\"Request body too large\", 413);\n }\n\n chunks.push(decoder.decode(result.value, { stream: true }));\n }\n\n chunks.push(decoder.decode());\n return chunks.join(\"\");\n}\n\nexport async function parsePagesApiBody(\n request: Request,\n maxBytes = MAX_PAGES_API_BODY_SIZE,\n): Promise<unknown> {\n const contentLength = Number.parseInt(request.headers.get(\"content-length\") || \"0\", 10);\n if (contentLength > maxBytes) {\n throw new PagesBodyParseError(\"Request body too large\", 413);\n }\n\n let rawBody = \"\";\n try {\n rawBody = await readPagesRequestBodyWithLimit(request, maxBytes);\n } catch (err) {\n if (err instanceof PagesBodyParseError) {\n throw err;\n }\n throw new PagesBodyParseError(\"Request body too large\", 413);\n }\n\n const mediaType = getMediaType(request.headers.get(\"content-type\"));\n if (!rawBody) {\n return isJsonMediaType(mediaType)\n ? {}\n : mediaType === \"application/x-www-form-urlencoded\"\n ? decodeQueryString(rawBody)\n : undefined;\n }\n\n if (isJsonMediaType(mediaType)) {\n try {\n return JSON.parse(rawBody);\n } catch {\n throw new PagesBodyParseError(\"Invalid JSON\", 400);\n }\n }\n\n if (mediaType === \"application/x-www-form-urlencoded\") {\n return decodeQueryString(rawBody);\n }\n\n return rawBody;\n}\n\nexport function createPagesReqRes(options: CreatePagesReqResOptions): CreatePagesReqResResult {\n const headersObj: Record<string, string> = {};\n for (const [key, value] of options.request.headers) {\n headersObj[key.toLowerCase()] = value;\n }\n\n const req: PagesReqResRequest = {\n method: options.request.method,\n url: options.url,\n headers: headersObj,\n query: options.query,\n body: options.body,\n cookies: parseCookies(options.request.headers.get(\"cookie\")),\n };\n\n let resStatusCode = 200;\n const resHeaders: Record<string, string | number | boolean> = {};\n const setCookieHeaders: string[] = [];\n let resBody: BodyInit | null = null;\n let ended = false;\n let resolveResponse!: (value: Response) => void;\n const responsePromise = new Promise<Response>((resolve) => {\n resolveResponse = resolve;\n });\n\n const res: PagesReqResResponse = {\n get statusCode() {\n return resStatusCode;\n },\n set statusCode(code) {\n resStatusCode = code;\n },\n get headersSent() {\n return ended;\n },\n writeHead(code, headers) {\n resStatusCode = code;\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === \"set-cookie\") {\n if (Array.isArray(value)) {\n setCookieHeaders.push(...value.map(String));\n } else {\n setCookieHeaders.push(String(value));\n }\n } else {\n resHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n }\n return res;\n },\n setHeader(name, value) {\n if (name.toLowerCase() === \"set-cookie\") {\n // Node.js res.setHeader() replaces the existing value entirely.\n setCookieHeaders.length = 0;\n if (Array.isArray(value)) {\n setCookieHeaders.push(...value.map(String));\n } else {\n setCookieHeaders.push(String(value));\n }\n } else {\n resHeaders[name.toLowerCase()] = Array.isArray(value) ? value.join(\", \") : value;\n }\n return res;\n },\n getHeader(name) {\n if (name.toLowerCase() === \"set-cookie\") {\n return setCookieHeaders.length > 0 ? setCookieHeaders : undefined;\n }\n return resHeaders[name.toLowerCase()];\n },\n end(data) {\n if (ended) {\n return;\n }\n ended = true;\n if (data !== undefined && data !== null) {\n resBody = data;\n }\n const headers = new Headers();\n for (const [key, value] of Object.entries(resHeaders)) {\n headers.set(key, String(value));\n }\n for (const cookie of setCookieHeaders) {\n headers.append(\"set-cookie\", cookie);\n }\n resolveResponse(new Response(resBody, { status: resStatusCode, headers }));\n },\n status(code) {\n resStatusCode = code;\n return res;\n },\n json(data) {\n resHeaders[\"content-type\"] = \"application/json\";\n res.end(JSON.stringify(data));\n },\n send(data) {\n if (Buffer.isBuffer(data)) {\n if (!resHeaders[\"content-type\"]) {\n resHeaders[\"content-type\"] = \"application/octet-stream\";\n }\n resHeaders[\"content-length\"] = String(data.length);\n res.end(new Uint8Array(data));\n return;\n }\n\n if (typeof data === \"object\" && data !== null) {\n resHeaders[\"content-type\"] = \"application/json\";\n res.end(JSON.stringify(data));\n return;\n }\n\n if (!resHeaders[\"content-type\"]) {\n resHeaders[\"content-type\"] = \"text/plain\";\n }\n res.end(String(data));\n },\n redirect(statusOrUrl, url) {\n if (typeof statusOrUrl === \"string\") {\n res.writeHead(307, { Location: statusOrUrl });\n } else {\n res.writeHead(statusOrUrl, { Location: url ?? \"\" });\n }\n res.end();\n },\n getHeaders() {\n const headers: PagesReqResHeaders = { ...resHeaders };\n if (setCookieHeaders.length > 0) {\n headers[\"set-cookie\"] = setCookieHeaders;\n }\n return headers;\n },\n };\n\n return { req, res, responsePromise };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pages-node-compat.js","names":["decodeQueryString"],"sources":["../../src/server/pages-node-compat.ts"],"sourcesContent":["import { decode as decodeQueryString } from \"node:querystring\";\nimport { parseCookies } from \"../config/config-matchers.js\";\nimport { readStreamAsTextWithLimit } from \"../utils/text-stream.js\";\nimport { PagesBodyParseError, getMediaType, isJsonMediaType } from \"./pages-media-type.js\";\n\nconst MAX_PAGES_API_BODY_SIZE = 1 * 1024 * 1024;\n\n/**\n * @deprecated Use PagesBodyParseError from pages-media-type.ts instead.\n * Kept for backwards compatibility.\n */\nexport { PagesBodyParseError as PagesApiBodyParseError };\n\nexport type PagesRequestQuery = Record<string, string | string[]>;\n\nexport type PagesReqResRequest = {\n method: string;\n url: string;\n headers: Record<string, string>;\n query: PagesRequestQuery;\n body: unknown;\n cookies: Record<string, string>;\n};\n\nexport type PagesReqResHeaders = {\n [key: string]: string | number | boolean | string[];\n};\n\nexport type PagesReqResResponse = {\n statusCode: number;\n readonly headersSent: boolean;\n writeHead: (code: number, headers?: PagesReqResHeaders) => PagesReqResResponse;\n setHeader: (name: string, value: string | number | boolean | string[]) => PagesReqResResponse;\n getHeader: (name: string) => string | number | boolean | string[] | undefined;\n end: (data?: BodyInit | null) => void;\n status: (code: number) => PagesReqResResponse;\n json: (data: unknown) => void;\n send: (data: unknown) => void;\n redirect: (statusOrUrl: number | string, url?: string) => void;\n getHeaders: () => PagesReqResHeaders;\n};\n\ntype CreatePagesReqResOptions = {\n body: unknown;\n query: PagesRequestQuery;\n request: Request;\n url: string;\n};\n\ntype CreatePagesReqResResult = {\n req: PagesReqResRequest;\n res: PagesReqResResponse;\n responsePromise: Promise<Response>;\n};\n\nasync function readPagesRequestBodyWithLimit(request: Request, maxBytes: number): Promise<string> {\n if (!request.body) {\n return \"\";\n }\n\n return readStreamAsTextWithLimit(request.body, maxBytes, () => {\n throw new PagesBodyParseError(\"Request body too large\", 413);\n });\n}\n\nexport async function parsePagesApiBody(\n request: Request,\n maxBytes = MAX_PAGES_API_BODY_SIZE,\n): Promise<unknown> {\n const contentLength = Number.parseInt(request.headers.get(\"content-length\") || \"0\", 10);\n if (contentLength > maxBytes) {\n throw new PagesBodyParseError(\"Request body too large\", 413);\n }\n\n let rawBody = \"\";\n try {\n rawBody = await readPagesRequestBodyWithLimit(request, maxBytes);\n } catch (err) {\n if (err instanceof PagesBodyParseError) {\n throw err;\n }\n throw new PagesBodyParseError(\"Request body too large\", 413);\n }\n\n const mediaType = getMediaType(request.headers.get(\"content-type\"));\n if (!rawBody) {\n return isJsonMediaType(mediaType)\n ? {}\n : mediaType === \"application/x-www-form-urlencoded\"\n ? decodeQueryString(rawBody)\n : undefined;\n }\n\n if (isJsonMediaType(mediaType)) {\n try {\n return JSON.parse(rawBody);\n } catch {\n throw new PagesBodyParseError(\"Invalid JSON\", 400);\n }\n }\n\n if (mediaType === \"application/x-www-form-urlencoded\") {\n return decodeQueryString(rawBody);\n }\n\n return rawBody;\n}\n\nexport function createPagesReqRes(options: CreatePagesReqResOptions): CreatePagesReqResResult {\n const headersObj: Record<string, string> = {};\n for (const [key, value] of options.request.headers) {\n headersObj[key.toLowerCase()] = value;\n }\n\n const req: PagesReqResRequest = {\n method: options.request.method,\n url: options.url,\n headers: headersObj,\n query: options.query,\n body: options.body,\n cookies: parseCookies(options.request.headers.get(\"cookie\")),\n };\n\n let resStatusCode = 200;\n const resHeaders: Record<string, string | number | boolean> = {};\n const setCookieHeaders: string[] = [];\n let resBody: BodyInit | null = null;\n let ended = false;\n let resolveResponse!: (value: Response) => void;\n const responsePromise = new Promise<Response>((resolve) => {\n resolveResponse = resolve;\n });\n\n const res: PagesReqResResponse = {\n get statusCode() {\n return resStatusCode;\n },\n set statusCode(code) {\n resStatusCode = code;\n },\n get headersSent() {\n return ended;\n },\n writeHead(code, headers) {\n resStatusCode = code;\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === \"set-cookie\") {\n if (Array.isArray(value)) {\n setCookieHeaders.push(...value.map(String));\n } else {\n setCookieHeaders.push(String(value));\n }\n } else {\n resHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n }\n return res;\n },\n setHeader(name, value) {\n if (name.toLowerCase() === \"set-cookie\") {\n // Node.js res.setHeader() replaces the existing value entirely.\n setCookieHeaders.length = 0;\n if (Array.isArray(value)) {\n setCookieHeaders.push(...value.map(String));\n } else {\n setCookieHeaders.push(String(value));\n }\n } else {\n resHeaders[name.toLowerCase()] = Array.isArray(value) ? value.join(\", \") : value;\n }\n return res;\n },\n getHeader(name) {\n if (name.toLowerCase() === \"set-cookie\") {\n return setCookieHeaders.length > 0 ? setCookieHeaders : undefined;\n }\n return resHeaders[name.toLowerCase()];\n },\n end(data) {\n if (ended) {\n return;\n }\n ended = true;\n if (data !== undefined && data !== null) {\n resBody = data;\n }\n const headers = new Headers();\n for (const [key, value] of Object.entries(resHeaders)) {\n headers.set(key, String(value));\n }\n for (const cookie of setCookieHeaders) {\n headers.append(\"set-cookie\", cookie);\n }\n resolveResponse(new Response(resBody, { status: resStatusCode, headers }));\n },\n status(code) {\n resStatusCode = code;\n return res;\n },\n json(data) {\n resHeaders[\"content-type\"] = \"application/json\";\n res.end(JSON.stringify(data));\n },\n send(data) {\n if (Buffer.isBuffer(data)) {\n if (!resHeaders[\"content-type\"]) {\n resHeaders[\"content-type\"] = \"application/octet-stream\";\n }\n resHeaders[\"content-length\"] = String(data.length);\n res.end(new Uint8Array(data));\n return;\n }\n\n if (typeof data === \"object\" && data !== null) {\n resHeaders[\"content-type\"] = \"application/json\";\n res.end(JSON.stringify(data));\n return;\n }\n\n if (!resHeaders[\"content-type\"]) {\n resHeaders[\"content-type\"] = \"text/plain\";\n }\n res.end(String(data));\n },\n redirect(statusOrUrl, url) {\n if (typeof statusOrUrl === \"string\") {\n res.writeHead(307, { Location: statusOrUrl });\n } else {\n res.writeHead(statusOrUrl, { Location: url ?? \"\" });\n }\n res.end();\n },\n getHeaders() {\n const headers: PagesReqResHeaders = { ...resHeaders };\n if (setCookieHeaders.length > 0) {\n headers[\"set-cookie\"] = setCookieHeaders;\n }\n return headers;\n },\n };\n\n return { req, res, responsePromise };\n}\n"],"mappings":"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"}
|
|
@@ -74,7 +74,8 @@ type ResolvePagesPageDataOptions = {
|
|
|
74
74
|
i18n: PagesI18nRenderContext;
|
|
75
75
|
isrCacheKey: (router: string, pathname: string) => string;
|
|
76
76
|
isrGet: (key: string) => Promise<ISRCacheEntry | null>;
|
|
77
|
-
isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number, tags?: string[]) => Promise<void>;
|
|
77
|
+
isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number, tags?: string[], expireSeconds?: number) => Promise<void>;
|
|
78
|
+
expireSeconds?: number;
|
|
78
79
|
pageModule: PagesPageModule;
|
|
79
80
|
params: Record<string, unknown>;
|
|
80
81
|
query: Record<string, unknown>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildPagesCacheValue } from "./isr-cache.js";
|
|
2
|
+
import { buildCachedRevalidateCacheControl } from "./cache-control.js";
|
|
2
3
|
import { buildPagesNextDataScript } from "./pages-page-response.js";
|
|
3
4
|
//#region src/server/pages-page-data.ts
|
|
4
5
|
function buildPagesNotFoundResponse() {
|
|
@@ -20,11 +21,11 @@ function matchesPagesStaticPath(pathEntry, params) {
|
|
|
20
21
|
return String(value) === String(actual);
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
|
-
function buildPagesCacheResponse(html, cacheState, fontLinkHeader, revalidateSeconds) {
|
|
24
|
+
function buildPagesCacheResponse(html, cacheState, fontLinkHeader, revalidateSeconds, expireSeconds, cacheControl) {
|
|
24
25
|
const headers = {
|
|
25
26
|
"Content-Type": "text/html",
|
|
26
27
|
"X-Vinext-Cache": cacheState,
|
|
27
|
-
"Cache-Control": cacheState
|
|
28
|
+
"Cache-Control": buildCachedRevalidateCacheControl(cacheState, cacheControl?.revalidate ?? revalidateSeconds ?? 60, cacheControl === void 0 ? void 0 : cacheControl.expire ?? expireSeconds)
|
|
28
29
|
};
|
|
29
30
|
if (fontLinkHeader) headers.Link = fontLinkHeader;
|
|
30
31
|
return new Response(html, {
|
|
@@ -111,7 +112,7 @@ async function resolvePagesPageData(options) {
|
|
|
111
112
|
const cachedValue = cached?.value.value;
|
|
112
113
|
if (cachedValue?.kind === "PAGES" && cached && !cached.isStale && !options.scriptNonce) return {
|
|
113
114
|
kind: "response",
|
|
114
|
-
response: buildPagesCacheResponse(cachedValue.html, "HIT", options.fontLinkHeader,
|
|
115
|
+
response: buildPagesCacheResponse(cachedValue.html, "HIT", options.fontLinkHeader, void 0, options.expireSeconds, cached.value.cacheControl)
|
|
115
116
|
};
|
|
116
117
|
if (cachedValue?.kind === "PAGES" && cached && cached.isStale && !options.scriptNonce) {
|
|
117
118
|
options.triggerBackgroundRegeneration(cacheKey, async function() {
|
|
@@ -135,7 +136,7 @@ async function resolvePagesPageData(options) {
|
|
|
135
136
|
routePattern: options.routePattern,
|
|
136
137
|
safeJsonStringify: options.safeJsonStringify
|
|
137
138
|
});
|
|
138
|
-
await options.isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshResult.props), freshResult.revalidate);
|
|
139
|
+
await options.isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshResult.props), freshResult.revalidate, void 0, options.expireSeconds);
|
|
139
140
|
}
|
|
140
141
|
});
|
|
141
142
|
}, {
|
|
@@ -145,7 +146,7 @@ async function resolvePagesPageData(options) {
|
|
|
145
146
|
});
|
|
146
147
|
return {
|
|
147
148
|
kind: "response",
|
|
148
|
-
response: buildPagesCacheResponse(cachedValue.html, "STALE", options.fontLinkHeader)
|
|
149
|
+
response: buildPagesCacheResponse(cachedValue.html, "STALE", options.fontLinkHeader, void 0, options.expireSeconds, cached.value.cacheControl)
|
|
149
150
|
};
|
|
150
151
|
}
|
|
151
152
|
const result = await options.pageModule.getStaticProps({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages-page-data.js","names":[],"sources":["../../src/server/pages-page-data.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { Route } from \"../routing/pages-router.js\";\nimport type { CachedPagesValue } from \"vinext/shims/cache\";\nimport { buildPagesCacheValue, type ISRCacheEntry } from \"./isr-cache.js\";\nimport {\n buildPagesNextDataScript,\n type PagesGsspResponse,\n type PagesI18nRenderContext,\n} from \"./pages-page-response.js\";\n\ntype PagesRedirectResult = {\n destination: string;\n permanent?: boolean;\n statusCode?: number;\n};\n\ntype PagesStaticPathsEntry = {\n params: Record<string, unknown>;\n};\n\ntype PagesStaticPathsResult = {\n fallback?: boolean | \"blocking\";\n paths?: PagesStaticPathsEntry[];\n};\n\ntype PagesPagePropsResult = {\n props?: Record<string, unknown>;\n redirect?: PagesRedirectResult;\n notFound?: boolean;\n revalidate?: number;\n};\n\nexport type PagesMutableGsspResponse = {\n headersSent: boolean;\n} & PagesGsspResponse;\n\nexport type PagesGsspContextResponse = {\n req: unknown;\n res: PagesMutableGsspResponse;\n responsePromise: Promise<Response>;\n};\n\nexport type PagesPageModule = {\n default?: unknown;\n getStaticPaths?: (context: {\n locales: string[];\n defaultLocale: string;\n }) => Promise<PagesStaticPathsResult> | PagesStaticPathsResult;\n getServerSideProps?: (context: {\n params: Record<string, unknown>;\n req: unknown;\n res: PagesMutableGsspResponse;\n query: Record<string, unknown>;\n resolvedUrl: string;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n }) => Promise<PagesPagePropsResult> | PagesPagePropsResult;\n getStaticProps?: (context: {\n params: Record<string, unknown>;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n }) => Promise<PagesPagePropsResult> | PagesPagePropsResult;\n};\n\ntype RenderPagesIsrHtmlOptions = {\n buildId: string | null;\n cachedHtml: string;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n i18n: PagesI18nRenderContext;\n pageProps: Record<string, unknown>;\n params: Record<string, unknown>;\n renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;\n routePattern: string;\n safeJsonStringify: (value: unknown) => string;\n};\n\nexport type ResolvePagesPageDataOptions = {\n applyRequestContexts: () => void;\n buildId: string | null;\n createGsspReqRes: () => PagesGsspContextResponse;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n fontLinkHeader: string;\n i18n: PagesI18nRenderContext;\n isrCacheKey: (router: string, pathname: string) => string;\n isrGet: (key: string) => Promise<ISRCacheEntry | null>;\n isrSet: (\n key: string,\n data: CachedPagesValue,\n revalidateSeconds: number,\n tags?: string[],\n ) => Promise<void>;\n pageModule: PagesPageModule;\n params: Record<string, unknown>;\n query: Record<string, unknown>;\n route: Pick<Route, \"isDynamic\">;\n routePattern: string;\n routeUrl: string;\n runInFreshUnifiedContext: <T>(callback: () => Promise<T>) => Promise<T>;\n safeJsonStringify: (value: unknown) => string;\n sanitizeDestination: (destination: string) => string;\n scriptNonce?: string;\n triggerBackgroundRegeneration: (\n key: string,\n renderFn: () => Promise<void>,\n errorContext?: { routerKind: \"Pages Router\"; routePath: string; routeType: \"render\" },\n ) => void;\n renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;\n};\n\ntype ResolvePagesPageDataRenderResult = {\n kind: \"render\";\n gsspRes: PagesGsspResponse | null;\n isrRevalidateSeconds: number | null;\n pageProps: Record<string, unknown>;\n};\n\ntype ResolvePagesPageDataResponseResult = {\n kind: \"response\";\n response: Response;\n};\n\ntype ResolvePagesPageDataResult =\n | ResolvePagesPageDataRenderResult\n | ResolvePagesPageDataResponseResult;\n\nfunction buildPagesNotFoundResponse(): Response {\n return new Response(\"<!DOCTYPE html><html><body><h1>404 - Page not found</h1></body></html>\", {\n status: 404,\n headers: { \"Content-Type\": \"text/html\" },\n });\n}\n\nfunction buildPagesDataNotFoundResponse(): Response {\n return new Response(\"404\", { status: 404 });\n}\n\nfunction resolvePagesRedirectStatus(redirect: PagesRedirectResult): number {\n return redirect.statusCode != null ? redirect.statusCode : redirect.permanent ? 308 : 307;\n}\n\nfunction matchesPagesStaticPath(\n pathEntry: PagesStaticPathsEntry,\n params: Record<string, unknown>,\n): boolean {\n return Object.entries(pathEntry.params).every(([key, value]) => {\n const actual = params[key];\n if (Array.isArray(value)) {\n return Array.isArray(actual) && value.join(\"/\") === actual.join(\"/\");\n }\n return String(value) === String(actual);\n });\n}\n\nfunction buildPagesCacheResponse(\n html: string,\n cacheState: \"HIT\" | \"STALE\",\n fontLinkHeader: string,\n revalidateSeconds?: number,\n): Response {\n const headers: Record<string, string> = {\n \"Content-Type\": \"text/html\",\n \"X-Vinext-Cache\": cacheState,\n \"Cache-Control\":\n cacheState === \"HIT\"\n ? `s-maxage=${revalidateSeconds ?? 60}, stale-while-revalidate`\n : \"s-maxage=0, stale-while-revalidate\",\n };\n\n if (fontLinkHeader) {\n headers.Link = fontLinkHeader;\n }\n\n return new Response(html, {\n status: 200,\n headers,\n });\n}\n\nfunction rewritePagesCachedHtml(\n cachedHtml: string,\n freshBody: string,\n nextDataScript: string,\n): string {\n const bodyMarker = '<div id=\"__next\">';\n const bodyStart = cachedHtml.indexOf(bodyMarker);\n const contentStart = bodyStart >= 0 ? bodyStart + bodyMarker.length : -1;\n // This intentionally looks for the bare inline __NEXT_DATA__ marker.\n // Pages responses with scriptNonce are excluded from ISR writes, so cached\n // HTML should never contain nonce-prefixed __NEXT_DATA__ scripts here.\n const nextDataMarker = \"<script>window.__NEXT_DATA__\";\n const nextDataStart = cachedHtml.indexOf(nextDataMarker);\n\n if (contentStart >= 0 && nextDataStart >= 0) {\n const region = cachedHtml.slice(contentStart, nextDataStart);\n const lastCloseDiv = region.lastIndexOf(\"</div>\");\n const gap = lastCloseDiv >= 0 ? region.slice(lastCloseDiv + 6) : \"\";\n const nextDataEnd = cachedHtml.indexOf(\"</script>\", nextDataStart) + 9;\n const tail = cachedHtml.slice(nextDataEnd);\n\n return cachedHtml.slice(0, contentStart) + freshBody + \"</div>\" + gap + nextDataScript + tail;\n }\n\n return (\n '<!DOCTYPE html>\\n<html>\\n<head>\\n</head>\\n<body>\\n <div id=\"__next\">' +\n freshBody +\n \"</div>\\n \" +\n nextDataScript +\n \"\\n</body>\\n</html>\"\n );\n}\n\nexport async function renderPagesIsrHtml(options: RenderPagesIsrHtmlOptions): Promise<string> {\n const freshBody = await options.renderIsrPassToStringAsync(\n options.createPageElement(options.pageProps),\n );\n const nextDataScript = buildPagesNextDataScript({\n buildId: options.buildId,\n i18n: options.i18n,\n pageProps: options.pageProps,\n params: options.params,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n });\n\n return rewritePagesCachedHtml(options.cachedHtml, freshBody, nextDataScript);\n}\n\nexport async function resolvePagesPageData(\n options: ResolvePagesPageDataOptions,\n): Promise<ResolvePagesPageDataResult> {\n if (typeof options.pageModule.getStaticPaths === \"function\" && options.route.isDynamic) {\n const pathsResult = await options.pageModule.getStaticPaths({\n locales: options.i18n.locales ?? [],\n defaultLocale: options.i18n.defaultLocale ?? \"\",\n });\n const fallback = pathsResult?.fallback ?? false;\n\n if (fallback === false) {\n const paths = pathsResult?.paths ?? [];\n const isValidPath = paths.some((pathEntry) =>\n matchesPagesStaticPath(pathEntry, options.params),\n );\n\n if (!isValidPath) {\n return {\n kind: \"response\",\n response: buildPagesNotFoundResponse(),\n };\n }\n }\n }\n\n let pageProps: Record<string, unknown> = {};\n let gsspRes: PagesMutableGsspResponse | null = null;\n\n if (typeof options.pageModule.getServerSideProps === \"function\") {\n const { req, res, responsePromise } = options.createGsspReqRes();\n const result = await options.pageModule.getServerSideProps({\n params: options.params,\n req,\n res,\n query: options.query,\n resolvedUrl: options.routeUrl,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (res.headersSent) {\n return {\n kind: \"response\",\n response: await responsePromise,\n };\n }\n\n if (result?.props) {\n pageProps = result.props;\n }\n\n if (result?.redirect) {\n return {\n kind: \"response\",\n response: new Response(null, {\n status: resolvePagesRedirectStatus(result.redirect),\n headers: { Location: options.sanitizeDestination(result.redirect.destination) },\n }),\n };\n }\n\n if (result?.notFound) {\n return {\n kind: \"response\",\n response: buildPagesDataNotFoundResponse(),\n };\n }\n\n gsspRes = res;\n }\n\n let isrRevalidateSeconds: number | null = null;\n\n if (typeof options.pageModule.getStaticProps === \"function\") {\n const pathname = options.routeUrl.split(\"?\")[0];\n const cacheKey = options.isrCacheKey(\"pages\", pathname);\n const cached = await options.isrGet(cacheKey);\n const cachedValue = cached?.value.value;\n\n if (cachedValue?.kind === \"PAGES\" && cached && !cached.isStale && !options.scriptNonce) {\n return {\n kind: \"response\",\n response: buildPagesCacheResponse(\n cachedValue.html,\n \"HIT\",\n options.fontLinkHeader,\n (cachedValue as CachedPagesValue & { revalidate?: number }).revalidate,\n ),\n };\n }\n\n if (cachedValue?.kind === \"PAGES\" && cached && cached.isStale && !options.scriptNonce) {\n options.triggerBackgroundRegeneration(\n cacheKey,\n async function () {\n return options.runInFreshUnifiedContext(async () => {\n const freshResult = await options.pageModule.getStaticProps?.({\n params: options.params,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (\n freshResult?.props &&\n typeof freshResult.revalidate === \"number\" &&\n freshResult.revalidate > 0\n ) {\n options.applyRequestContexts();\n const freshHtml = await renderPagesIsrHtml({\n buildId: options.buildId,\n cachedHtml: cachedValue.html,\n createPageElement: options.createPageElement,\n i18n: options.i18n,\n pageProps: freshResult.props,\n params: options.params,\n renderIsrPassToStringAsync: options.renderIsrPassToStringAsync,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n });\n\n await options.isrSet(\n cacheKey,\n buildPagesCacheValue(freshHtml, freshResult.props),\n freshResult.revalidate,\n );\n }\n });\n },\n {\n routerKind: \"Pages Router\",\n routePath: options.routePattern,\n routeType: \"render\",\n },\n );\n\n return {\n kind: \"response\",\n response: buildPagesCacheResponse(cachedValue.html, \"STALE\", options.fontLinkHeader),\n };\n }\n\n const result = await options.pageModule.getStaticProps({\n params: options.params,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (result?.props) {\n pageProps = result.props;\n }\n\n if (result?.redirect) {\n return {\n kind: \"response\",\n response: new Response(null, {\n status: resolvePagesRedirectStatus(result.redirect),\n headers: { Location: options.sanitizeDestination(result.redirect.destination) },\n }),\n };\n }\n\n if (result?.notFound) {\n return {\n kind: \"response\",\n response: buildPagesDataNotFoundResponse(),\n };\n }\n\n if (typeof result?.revalidate === \"number\" && result.revalidate > 0) {\n isrRevalidateSeconds = result.revalidate;\n }\n }\n\n return {\n kind: \"render\",\n gsspRes,\n isrRevalidateSeconds,\n pageProps,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pages-page-data.js","names":[],"sources":["../../src/server/pages-page-data.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { Route } from \"../routing/pages-router.js\";\nimport type { CachedPagesValue, CacheControlMetadata } from \"vinext/shims/cache\";\nimport { buildCachedRevalidateCacheControl } from \"./cache-control.js\";\nimport { buildPagesCacheValue, type ISRCacheEntry } from \"./isr-cache.js\";\nimport {\n buildPagesNextDataScript,\n type PagesGsspResponse,\n type PagesI18nRenderContext,\n} from \"./pages-page-response.js\";\n\ntype PagesRedirectResult = {\n destination: string;\n permanent?: boolean;\n statusCode?: number;\n};\n\ntype PagesStaticPathsEntry = {\n params: Record<string, unknown>;\n};\n\ntype PagesStaticPathsResult = {\n fallback?: boolean | \"blocking\";\n paths?: PagesStaticPathsEntry[];\n};\n\ntype PagesPagePropsResult = {\n props?: Record<string, unknown>;\n redirect?: PagesRedirectResult;\n notFound?: boolean;\n revalidate?: number;\n};\n\nexport type PagesMutableGsspResponse = {\n headersSent: boolean;\n} & PagesGsspResponse;\n\nexport type PagesGsspContextResponse = {\n req: unknown;\n res: PagesMutableGsspResponse;\n responsePromise: Promise<Response>;\n};\n\nexport type PagesPageModule = {\n default?: unknown;\n getStaticPaths?: (context: {\n locales: string[];\n defaultLocale: string;\n }) => Promise<PagesStaticPathsResult> | PagesStaticPathsResult;\n getServerSideProps?: (context: {\n params: Record<string, unknown>;\n req: unknown;\n res: PagesMutableGsspResponse;\n query: Record<string, unknown>;\n resolvedUrl: string;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n }) => Promise<PagesPagePropsResult> | PagesPagePropsResult;\n getStaticProps?: (context: {\n params: Record<string, unknown>;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n }) => Promise<PagesPagePropsResult> | PagesPagePropsResult;\n};\n\ntype RenderPagesIsrHtmlOptions = {\n buildId: string | null;\n cachedHtml: string;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n i18n: PagesI18nRenderContext;\n pageProps: Record<string, unknown>;\n params: Record<string, unknown>;\n renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;\n routePattern: string;\n safeJsonStringify: (value: unknown) => string;\n};\n\nexport type ResolvePagesPageDataOptions = {\n applyRequestContexts: () => void;\n buildId: string | null;\n createGsspReqRes: () => PagesGsspContextResponse;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n fontLinkHeader: string;\n i18n: PagesI18nRenderContext;\n isrCacheKey: (router: string, pathname: string) => string;\n isrGet: (key: string) => Promise<ISRCacheEntry | null>;\n isrSet: (\n key: string,\n data: CachedPagesValue,\n revalidateSeconds: number,\n tags?: string[],\n expireSeconds?: number,\n ) => Promise<void>;\n expireSeconds?: number;\n pageModule: PagesPageModule;\n params: Record<string, unknown>;\n query: Record<string, unknown>;\n route: Pick<Route, \"isDynamic\">;\n routePattern: string;\n routeUrl: string;\n runInFreshUnifiedContext: <T>(callback: () => Promise<T>) => Promise<T>;\n safeJsonStringify: (value: unknown) => string;\n sanitizeDestination: (destination: string) => string;\n scriptNonce?: string;\n triggerBackgroundRegeneration: (\n key: string,\n renderFn: () => Promise<void>,\n errorContext?: { routerKind: \"Pages Router\"; routePath: string; routeType: \"render\" },\n ) => void;\n renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;\n};\n\ntype ResolvePagesPageDataRenderResult = {\n kind: \"render\";\n gsspRes: PagesGsspResponse | null;\n isrRevalidateSeconds: number | null;\n pageProps: Record<string, unknown>;\n};\n\ntype ResolvePagesPageDataResponseResult = {\n kind: \"response\";\n response: Response;\n};\n\ntype ResolvePagesPageDataResult =\n | ResolvePagesPageDataRenderResult\n | ResolvePagesPageDataResponseResult;\n\nfunction buildPagesNotFoundResponse(): Response {\n return new Response(\"<!DOCTYPE html><html><body><h1>404 - Page not found</h1></body></html>\", {\n status: 404,\n headers: { \"Content-Type\": \"text/html\" },\n });\n}\n\nfunction buildPagesDataNotFoundResponse(): Response {\n return new Response(\"404\", { status: 404 });\n}\n\nfunction resolvePagesRedirectStatus(redirect: PagesRedirectResult): number {\n return redirect.statusCode != null ? redirect.statusCode : redirect.permanent ? 308 : 307;\n}\n\nfunction matchesPagesStaticPath(\n pathEntry: PagesStaticPathsEntry,\n params: Record<string, unknown>,\n): boolean {\n return Object.entries(pathEntry.params).every(([key, value]) => {\n const actual = params[key];\n if (Array.isArray(value)) {\n return Array.isArray(actual) && value.join(\"/\") === actual.join(\"/\");\n }\n return String(value) === String(actual);\n });\n}\n\nfunction buildPagesCacheResponse(\n html: string,\n cacheState: \"HIT\" | \"STALE\",\n fontLinkHeader: string,\n revalidateSeconds?: number,\n expireSeconds?: number,\n cacheControl?: CacheControlMetadata,\n): Response {\n // Legacy cache entries written before cacheControl metadata existed can still\n // hit this path without a persisted revalidate value; keep the historic\n // 60-second fallback for that migration window.\n const effectiveRevalidateSeconds = cacheControl?.revalidate ?? revalidateSeconds ?? 60;\n const effectiveExpireSeconds =\n cacheControl === undefined ? undefined : (cacheControl.expire ?? expireSeconds);\n const headers: Record<string, string> = {\n \"Content-Type\": \"text/html\",\n \"X-Vinext-Cache\": cacheState,\n \"Cache-Control\": buildCachedRevalidateCacheControl(\n cacheState,\n effectiveRevalidateSeconds,\n effectiveExpireSeconds,\n ),\n };\n\n if (fontLinkHeader) {\n headers.Link = fontLinkHeader;\n }\n\n return new Response(html, {\n status: 200,\n headers,\n });\n}\n\nfunction rewritePagesCachedHtml(\n cachedHtml: string,\n freshBody: string,\n nextDataScript: string,\n): string {\n const bodyMarker = '<div id=\"__next\">';\n const bodyStart = cachedHtml.indexOf(bodyMarker);\n const contentStart = bodyStart >= 0 ? bodyStart + bodyMarker.length : -1;\n // This intentionally looks for the bare inline __NEXT_DATA__ marker.\n // Pages responses with scriptNonce are excluded from ISR writes, so cached\n // HTML should never contain nonce-prefixed __NEXT_DATA__ scripts here.\n const nextDataMarker = \"<script>window.__NEXT_DATA__\";\n const nextDataStart = cachedHtml.indexOf(nextDataMarker);\n\n if (contentStart >= 0 && nextDataStart >= 0) {\n const region = cachedHtml.slice(contentStart, nextDataStart);\n const lastCloseDiv = region.lastIndexOf(\"</div>\");\n const gap = lastCloseDiv >= 0 ? region.slice(lastCloseDiv + 6) : \"\";\n const nextDataEnd = cachedHtml.indexOf(\"</script>\", nextDataStart) + 9;\n const tail = cachedHtml.slice(nextDataEnd);\n\n return cachedHtml.slice(0, contentStart) + freshBody + \"</div>\" + gap + nextDataScript + tail;\n }\n\n return (\n '<!DOCTYPE html>\\n<html>\\n<head>\\n</head>\\n<body>\\n <div id=\"__next\">' +\n freshBody +\n \"</div>\\n \" +\n nextDataScript +\n \"\\n</body>\\n</html>\"\n );\n}\n\nexport async function renderPagesIsrHtml(options: RenderPagesIsrHtmlOptions): Promise<string> {\n const freshBody = await options.renderIsrPassToStringAsync(\n options.createPageElement(options.pageProps),\n );\n const nextDataScript = buildPagesNextDataScript({\n buildId: options.buildId,\n i18n: options.i18n,\n pageProps: options.pageProps,\n params: options.params,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n });\n\n return rewritePagesCachedHtml(options.cachedHtml, freshBody, nextDataScript);\n}\n\nexport async function resolvePagesPageData(\n options: ResolvePagesPageDataOptions,\n): Promise<ResolvePagesPageDataResult> {\n if (typeof options.pageModule.getStaticPaths === \"function\" && options.route.isDynamic) {\n const pathsResult = await options.pageModule.getStaticPaths({\n locales: options.i18n.locales ?? [],\n defaultLocale: options.i18n.defaultLocale ?? \"\",\n });\n const fallback = pathsResult?.fallback ?? false;\n\n if (fallback === false) {\n const paths = pathsResult?.paths ?? [];\n const isValidPath = paths.some((pathEntry) =>\n matchesPagesStaticPath(pathEntry, options.params),\n );\n\n if (!isValidPath) {\n return {\n kind: \"response\",\n response: buildPagesNotFoundResponse(),\n };\n }\n }\n }\n\n let pageProps: Record<string, unknown> = {};\n let gsspRes: PagesMutableGsspResponse | null = null;\n\n if (typeof options.pageModule.getServerSideProps === \"function\") {\n const { req, res, responsePromise } = options.createGsspReqRes();\n const result = await options.pageModule.getServerSideProps({\n params: options.params,\n req,\n res,\n query: options.query,\n resolvedUrl: options.routeUrl,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (res.headersSent) {\n return {\n kind: \"response\",\n response: await responsePromise,\n };\n }\n\n if (result?.props) {\n pageProps = result.props;\n }\n\n if (result?.redirect) {\n return {\n kind: \"response\",\n response: new Response(null, {\n status: resolvePagesRedirectStatus(result.redirect),\n headers: { Location: options.sanitizeDestination(result.redirect.destination) },\n }),\n };\n }\n\n if (result?.notFound) {\n return {\n kind: \"response\",\n response: buildPagesDataNotFoundResponse(),\n };\n }\n\n gsspRes = res;\n }\n\n let isrRevalidateSeconds: number | null = null;\n\n if (typeof options.pageModule.getStaticProps === \"function\") {\n const pathname = options.routeUrl.split(\"?\")[0];\n const cacheKey = options.isrCacheKey(\"pages\", pathname);\n const cached = await options.isrGet(cacheKey);\n const cachedValue = cached?.value.value;\n\n if (cachedValue?.kind === \"PAGES\" && cached && !cached.isStale && !options.scriptNonce) {\n return {\n kind: \"response\",\n response: buildPagesCacheResponse(\n cachedValue.html,\n \"HIT\",\n options.fontLinkHeader,\n undefined,\n options.expireSeconds,\n cached.value.cacheControl,\n ),\n };\n }\n\n if (cachedValue?.kind === \"PAGES\" && cached && cached.isStale && !options.scriptNonce) {\n options.triggerBackgroundRegeneration(\n cacheKey,\n async function () {\n return options.runInFreshUnifiedContext(async () => {\n const freshResult = await options.pageModule.getStaticProps?.({\n params: options.params,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (\n freshResult?.props &&\n typeof freshResult.revalidate === \"number\" &&\n freshResult.revalidate > 0\n ) {\n options.applyRequestContexts();\n const freshHtml = await renderPagesIsrHtml({\n buildId: options.buildId,\n cachedHtml: cachedValue.html,\n createPageElement: options.createPageElement,\n i18n: options.i18n,\n pageProps: freshResult.props,\n params: options.params,\n renderIsrPassToStringAsync: options.renderIsrPassToStringAsync,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n });\n\n await options.isrSet(\n cacheKey,\n buildPagesCacheValue(freshHtml, freshResult.props),\n freshResult.revalidate,\n undefined,\n options.expireSeconds,\n );\n }\n });\n },\n {\n routerKind: \"Pages Router\",\n routePath: options.routePattern,\n routeType: \"render\",\n },\n );\n\n return {\n kind: \"response\",\n response: buildPagesCacheResponse(\n cachedValue.html,\n \"STALE\",\n options.fontLinkHeader,\n undefined,\n options.expireSeconds,\n cached.value.cacheControl,\n ),\n };\n }\n\n const result = await options.pageModule.getStaticProps({\n params: options.params,\n locale: options.i18n.locale,\n locales: options.i18n.locales,\n defaultLocale: options.i18n.defaultLocale,\n });\n\n if (result?.props) {\n pageProps = result.props;\n }\n\n if (result?.redirect) {\n return {\n kind: \"response\",\n response: new Response(null, {\n status: resolvePagesRedirectStatus(result.redirect),\n headers: { Location: options.sanitizeDestination(result.redirect.destination) },\n }),\n };\n }\n\n if (result?.notFound) {\n return {\n kind: \"response\",\n response: buildPagesDataNotFoundResponse(),\n };\n }\n\n if (typeof result?.revalidate === \"number\" && result.revalidate > 0) {\n isrRevalidateSeconds = result.revalidate;\n }\n }\n\n return {\n kind: \"render\",\n gsspRes,\n isrRevalidateSeconds,\n pageProps,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CachedPagesValue } from "../shims/cache.js";
|
|
1
2
|
import { ComponentType, ReactNode } from "react";
|
|
2
3
|
|
|
3
4
|
//#region src/server/pages-page-response.d.ts
|
|
@@ -29,19 +30,13 @@ type RenderPagesPageResponseOptions = {
|
|
|
29
30
|
getSSRHeadHTML?: (() => string) | undefined;
|
|
30
31
|
gsspRes: PagesGsspResponse | null;
|
|
31
32
|
isrCacheKey: (router: string, pathname: string) => string;
|
|
33
|
+
expireSeconds?: number;
|
|
32
34
|
isrRevalidateSeconds: number | null;
|
|
33
|
-
isrSet: (key: string, data:
|
|
34
|
-
kind: "PAGES";
|
|
35
|
-
html: string;
|
|
36
|
-
pageData: Record<string, unknown>;
|
|
37
|
-
headers: undefined;
|
|
38
|
-
status: undefined;
|
|
39
|
-
}, revalidateSeconds: number) => Promise<void>;
|
|
35
|
+
isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number, tags?: string[], expireSeconds?: number) => Promise<void>;
|
|
40
36
|
i18n: PagesI18nRenderContext;
|
|
41
37
|
pageProps: Record<string, unknown>;
|
|
42
38
|
params: Record<string, unknown>;
|
|
43
39
|
renderDocumentToString: (element: ReactNode) => Promise<string>;
|
|
44
|
-
renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;
|
|
45
40
|
renderToReadableStream: (element: ReactNode) => Promise<ReadableStream<Uint8Array>>;
|
|
46
41
|
resetSSRHead?: (() => void) | undefined;
|
|
47
42
|
routePattern: string;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
2
|
+
import { reportRequestError } from "./instrumentation.js";
|
|
1
3
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
2
4
|
import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr } from "./html.js";
|
|
5
|
+
import { buildRevalidateCacheControl } from "./cache-control.js";
|
|
6
|
+
import { readStreamAsText } from "../utils/text-stream.js";
|
|
3
7
|
import React from "react";
|
|
4
8
|
//#region src/server/pages-page-response.ts
|
|
5
9
|
function buildPagesFontHeadHtml(fontLinks, fontPreloads, fontStyles, scriptNonce) {
|
|
@@ -64,6 +68,30 @@ async function buildPagesCompositeStream(bodyStream, shellPrefix, shellSuffix) {
|
|
|
64
68
|
controller.close();
|
|
65
69
|
} });
|
|
66
70
|
}
|
|
71
|
+
async function reportPagesIsrCacheWriteError(error, cacheKey, routePattern) {
|
|
72
|
+
console.error(`[vinext] Pages ISR cache write failed for ${cacheKey}:`, error);
|
|
73
|
+
try {
|
|
74
|
+
await reportRequestError(error instanceof Error ? error : new Error(String(error)), {
|
|
75
|
+
path: cacheKey,
|
|
76
|
+
method: "GET",
|
|
77
|
+
headers: {}
|
|
78
|
+
}, {
|
|
79
|
+
routerKind: "Pages Router",
|
|
80
|
+
routePath: routePattern,
|
|
81
|
+
routeType: "render"
|
|
82
|
+
});
|
|
83
|
+
} catch {}
|
|
84
|
+
}
|
|
85
|
+
function schedulePagesIsrCacheWrite(options) {
|
|
86
|
+
const cacheWritePromise = readStreamAsText(options.stream).then((bodyHtml) => options.setCache(options.cacheKey, {
|
|
87
|
+
kind: "PAGES",
|
|
88
|
+
html: options.shellPrefix + bodyHtml + options.shellSuffix,
|
|
89
|
+
pageData: options.pageData,
|
|
90
|
+
headers: void 0,
|
|
91
|
+
status: void 0
|
|
92
|
+
}, options.revalidateSeconds, void 0, options.expireSeconds)).catch((error) => reportPagesIsrCacheWriteError(error, options.cacheKey, options.routePattern));
|
|
93
|
+
getRequestExecutionContext()?.waitUntil(cacheWritePromise);
|
|
94
|
+
}
|
|
67
95
|
function applyGsspHeaders(headers, gsspRes) {
|
|
68
96
|
if (!gsspRes) return 200;
|
|
69
97
|
const gsspHeaders = gsspRes.getHeaders();
|
|
@@ -108,34 +136,37 @@ async function renderPagesPageResponse(options) {
|
|
|
108
136
|
const markerIndex = shellHtml.indexOf(bodyMarker);
|
|
109
137
|
const shellPrefix = shellHtml.slice(0, markerIndex);
|
|
110
138
|
const shellSuffix = shellHtml.slice(markerIndex + 25);
|
|
111
|
-
|
|
139
|
+
let responseBodyStream = bodyStream;
|
|
112
140
|
if (!options.scriptNonce && options.isrRevalidateSeconds !== null && options.isrRevalidateSeconds > 0) {
|
|
113
|
-
const
|
|
114
|
-
|
|
141
|
+
const cacheBodyStreamPair = bodyStream.tee();
|
|
142
|
+
responseBodyStream = cacheBodyStreamPair[0];
|
|
143
|
+
const cacheBodyStream = cacheBodyStreamPair[1];
|
|
115
144
|
const isrPathname = options.routeUrl.split("?")[0];
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
html: fullHtml,
|
|
145
|
+
schedulePagesIsrCacheWrite({
|
|
146
|
+
cacheKey: options.isrCacheKey("pages", isrPathname),
|
|
147
|
+
expireSeconds: options.expireSeconds,
|
|
120
148
|
pageData: options.pageProps,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
149
|
+
revalidateSeconds: options.isrRevalidateSeconds,
|
|
150
|
+
routePattern: options.routePattern,
|
|
151
|
+
setCache: options.isrSet,
|
|
152
|
+
shellPrefix,
|
|
153
|
+
shellSuffix,
|
|
154
|
+
stream: cacheBodyStream
|
|
155
|
+
});
|
|
124
156
|
}
|
|
157
|
+
const compositeStream = await buildPagesCompositeStream(responseBodyStream, shellPrefix, shellSuffix);
|
|
125
158
|
const responseHeaders = new Headers({ "Content-Type": "text/html" });
|
|
126
159
|
const finalStatus = applyGsspHeaders(responseHeaders, options.gsspRes);
|
|
127
160
|
if (options.scriptNonce) responseHeaders.set("Cache-Control", "no-store, must-revalidate");
|
|
128
161
|
else if (options.isrRevalidateSeconds) {
|
|
129
|
-
responseHeaders.set("Cache-Control",
|
|
162
|
+
responseHeaders.set("Cache-Control", buildRevalidateCacheControl(options.isrRevalidateSeconds, options.expireSeconds));
|
|
130
163
|
responseHeaders.set("X-Vinext-Cache", "MISS");
|
|
131
164
|
}
|
|
132
165
|
if (options.fontLinkHeader) responseHeaders.set("Link", options.fontLinkHeader);
|
|
133
|
-
|
|
166
|
+
return Object.assign(new Response(compositeStream, {
|
|
134
167
|
status: finalStatus,
|
|
135
168
|
headers: responseHeaders
|
|
136
|
-
});
|
|
137
|
-
response.__vinextStreamedHtmlResponse = true;
|
|
138
|
-
return response;
|
|
169
|
+
}), { __vinextStreamedHtmlResponse: true });
|
|
139
170
|
}
|
|
140
171
|
//#endregion
|
|
141
172
|
export { buildPagesNextDataScript, renderPagesPageResponse };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages-page-response.js","names":[],"sources":["../../src/server/pages-page-response.ts"],"sourcesContent":["import React, { type ComponentType, type ReactNode } from \"react\";\nimport { withScriptNonce } from \"vinext/shims/script-nonce-context\";\nimport { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr } from \"./html.js\";\n\ntype PagesFontPreload = {\n href: string;\n type: string;\n};\n\nexport type PagesI18nRenderContext = {\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n domainLocales?: unknown;\n};\n\nexport type PagesGsspResponse = {\n statusCode: number;\n getHeaders(): Record<string, string | number | boolean | string[]>;\n};\n\ntype PagesStreamedHtmlResponse = {\n __vinextStreamedHtmlResponse?: boolean;\n} & Response;\n\ntype RenderPagesPageResponseOptions = {\n assetTags: string;\n buildId: string | null;\n clearSsrContext: () => void;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n DocumentComponent: ComponentType | null;\n flushPreloads?: (() => Promise<void> | void) | undefined;\n fontLinkHeader: string;\n fontPreloads: PagesFontPreload[];\n getFontLinks: () => string[];\n getFontStyles: () => string[];\n getSSRHeadHTML?: (() => string) | undefined;\n gsspRes: PagesGsspResponse | null;\n isrCacheKey: (router: string, pathname: string) => string;\n isrRevalidateSeconds: number | null;\n isrSet: (\n key: string,\n data: {\n kind: \"PAGES\";\n html: string;\n pageData: Record<string, unknown>;\n headers: undefined;\n status: undefined;\n },\n revalidateSeconds: number,\n ) => Promise<void>;\n i18n: PagesI18nRenderContext;\n pageProps: Record<string, unknown>;\n params: Record<string, unknown>;\n renderDocumentToString: (element: ReactNode) => Promise<string>;\n renderIsrPassToStringAsync: (element: ReactNode) => Promise<string>;\n renderToReadableStream: (element: ReactNode) => Promise<ReadableStream<Uint8Array>>;\n resetSSRHead?: (() => void) | undefined;\n routePattern: string;\n routeUrl: string;\n safeJsonStringify: (value: unknown) => string;\n scriptNonce?: string;\n};\n\nfunction buildPagesFontHeadHtml(\n fontLinks: string[],\n fontPreloads: PagesFontPreload[],\n fontStyles: string[],\n scriptNonce?: string,\n): string {\n let html = \"\";\n const nonceAttr = createNonceAttribute(scriptNonce);\n\n for (const link of fontLinks) {\n html += `<link rel=\"stylesheet\"${nonceAttr} href=\"${escapeHtmlAttr(link)}\" />\\n `;\n }\n\n for (const preload of fontPreloads) {\n html += `<link rel=\"preload\"${nonceAttr} href=\"${escapeHtmlAttr(preload.href)}\" as=\"font\" type=\"${escapeHtmlAttr(preload.type)}\" crossorigin />\\n `;\n }\n\n if (fontStyles.length > 0) {\n html += `<style data-vinext-fonts${nonceAttr}>${fontStyles.join(\"\\n\")}</style>\\n `;\n }\n\n return html;\n}\n\nexport function buildPagesNextDataScript(\n options: Pick<\n RenderPagesPageResponseOptions,\n | \"buildId\"\n | \"i18n\"\n | \"pageProps\"\n | \"params\"\n | \"routePattern\"\n | \"safeJsonStringify\"\n | \"scriptNonce\"\n >,\n): string {\n const nextDataPayload: Record<string, unknown> = {\n props: { pageProps: options.pageProps },\n page: options.routePattern,\n query: options.params,\n buildId: options.buildId,\n isFallback: false,\n };\n\n if (options.i18n.locales) {\n nextDataPayload.locale = options.i18n.locale;\n nextDataPayload.locales = options.i18n.locales;\n nextDataPayload.defaultLocale = options.i18n.defaultLocale;\n nextDataPayload.domainLocales = options.i18n.domainLocales;\n }\n\n const localeGlobals = options.i18n.locales\n ? `;window.__VINEXT_LOCALE__=${options.safeJsonStringify(options.i18n.locale)}` +\n `;window.__VINEXT_LOCALES__=${options.safeJsonStringify(options.i18n.locales)}` +\n `;window.__VINEXT_DEFAULT_LOCALE__=${options.safeJsonStringify(options.i18n.defaultLocale)}`\n : \"\";\n\n return createInlineScriptTag(\n `window.__NEXT_DATA__ = ${options.safeJsonStringify(nextDataPayload)}${localeGlobals}`,\n options.scriptNonce,\n );\n}\n\nasync function buildPagesShellHtml(\n bodyMarker: string,\n fontHeadHTML: string,\n nextDataScript: string,\n options: Pick<\n RenderPagesPageResponseOptions,\n \"assetTags\" | \"DocumentComponent\" | \"renderDocumentToString\"\n > & {\n ssrHeadHTML: string;\n },\n): Promise<string> {\n if (options.DocumentComponent) {\n let html = await options.renderDocumentToString(React.createElement(options.DocumentComponent));\n html = html.replace(\"__NEXT_MAIN__\", bodyMarker);\n if (options.ssrHeadHTML || options.assetTags || fontHeadHTML) {\n html = html.replace(\n \"</head>\",\n ` ${fontHeadHTML}${options.ssrHeadHTML}\\n ${options.assetTags}\\n</head>`,\n );\n }\n html = html.replace(\"<!-- __NEXT_SCRIPTS__ -->\", nextDataScript);\n if (!html.includes(\"__NEXT_DATA__\")) {\n html = html.replace(\"</body>\", ` ${nextDataScript}\\n</body>`);\n }\n return html;\n }\n\n return (\n \"<!DOCTYPE html>\\n<html>\\n<head>\\n\" +\n ' <meta charset=\"utf-8\" />\\n' +\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\\n' +\n ` ${fontHeadHTML}${options.ssrHeadHTML}\\n` +\n ` ${options.assetTags}\\n` +\n \"</head>\\n<body>\\n\" +\n ` <div id=\"__next\">${bodyMarker}</div>\\n` +\n ` ${nextDataScript}\\n` +\n \"</body>\\n</html>\"\n );\n}\n\nasync function buildPagesCompositeStream(\n bodyStream: ReadableStream<Uint8Array>,\n shellPrefix: string,\n shellSuffix: string,\n): Promise<ReadableStream<Uint8Array>> {\n const encoder = new TextEncoder();\n\n return new ReadableStream({\n async start(controller) {\n controller.enqueue(encoder.encode(shellPrefix));\n const reader = bodyStream.getReader();\n try {\n for (;;) {\n const chunk = await reader.read();\n if (chunk.done) {\n break;\n }\n controller.enqueue(chunk.value);\n }\n } finally {\n reader.releaseLock();\n }\n controller.enqueue(encoder.encode(shellSuffix));\n controller.close();\n },\n });\n}\n\nfunction applyGsspHeaders(headers: Headers, gsspRes: PagesGsspResponse | null): number {\n if (!gsspRes) {\n return 200;\n }\n\n const gsspHeaders = gsspRes.getHeaders();\n for (const key of Object.keys(gsspHeaders)) {\n const value = gsspHeaders[key];\n const lowerKey = key.toLowerCase();\n if (lowerKey === \"set-cookie\" && Array.isArray(value)) {\n for (const cookie of value) {\n headers.append(\"set-cookie\", String(cookie));\n }\n continue;\n }\n if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n continue;\n }\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n headers.set(key, String(value));\n }\n }\n headers.set(\"Content-Type\", \"text/html\");\n return gsspRes.statusCode;\n}\n\nexport async function renderPagesPageResponse(\n options: RenderPagesPageResponseOptions,\n): Promise<Response> {\n const pageElement = withScriptNonce(\n React.createElement(React.Fragment, null, options.createPageElement(options.pageProps)),\n options.scriptNonce,\n );\n\n options.resetSSRHead?.();\n await options.flushPreloads?.();\n\n const fontHeadHTML = buildPagesFontHeadHtml(\n options.getFontLinks(),\n options.fontPreloads,\n options.getFontStyles(),\n options.scriptNonce,\n );\n const nextDataScript = buildPagesNextDataScript({\n buildId: options.buildId,\n i18n: options.i18n,\n pageProps: options.pageProps,\n params: options.params,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n scriptNonce: options.scriptNonce,\n });\n const bodyMarker = \"<!--VINEXT_STREAM_BODY-->\";\n // Render the page FIRST so that <Head> and other SSR state collectors\n // (e.g. styled-jsx, useServerInsertedHTML) are populated before we read\n // them. This fixes a race condition where head styles were silently dropped\n // because they were collected before the page had finished rendering.\n // Mirrors Next.js fix: vercel/next.js@9853944\n const bodyStream = await options.renderToReadableStream(pageElement);\n\n const shellHtml = await buildPagesShellHtml(bodyMarker, fontHeadHTML, nextDataScript, {\n assetTags: options.assetTags,\n DocumentComponent: options.DocumentComponent,\n renderDocumentToString: options.renderDocumentToString,\n ssrHeadHTML: options.getSSRHeadHTML?.() ?? \"\",\n });\n\n options.clearSsrContext();\n\n const markerIndex = shellHtml.indexOf(bodyMarker);\n const shellPrefix = shellHtml.slice(0, markerIndex);\n const shellSuffix = shellHtml.slice(markerIndex + bodyMarker.length);\n const compositeStream = await buildPagesCompositeStream(bodyStream, shellPrefix, shellSuffix);\n\n if (\n // Keep nonce-bearing pages out of ISR writes: rewritePagesCachedHtml()\n // later matches the cached __NEXT_DATA__ block via a bare <script> marker.\n !options.scriptNonce &&\n options.isrRevalidateSeconds !== null &&\n options.isrRevalidateSeconds > 0\n ) {\n const isrElement = React.createElement(\n React.Fragment,\n null,\n options.createPageElement(options.pageProps),\n );\n const isrHtml = await options.renderIsrPassToStringAsync(isrElement);\n const fullHtml = shellPrefix + isrHtml + shellSuffix;\n const isrPathname = options.routeUrl.split(\"?\")[0];\n const cacheKey = options.isrCacheKey(\"pages\", isrPathname);\n await options.isrSet(\n cacheKey,\n {\n kind: \"PAGES\",\n html: fullHtml,\n pageData: options.pageProps,\n headers: undefined,\n status: undefined,\n },\n options.isrRevalidateSeconds,\n );\n }\n\n const responseHeaders = new Headers({ \"Content-Type\": \"text/html\" });\n const finalStatus = applyGsspHeaders(responseHeaders, options.gsspRes);\n\n if (options.scriptNonce) {\n responseHeaders.set(\"Cache-Control\", \"no-store, must-revalidate\");\n } else if (options.isrRevalidateSeconds) {\n responseHeaders.set(\n \"Cache-Control\",\n `s-maxage=${options.isrRevalidateSeconds}, stale-while-revalidate`,\n );\n responseHeaders.set(\"X-Vinext-Cache\", \"MISS\");\n }\n if (options.fontLinkHeader) {\n responseHeaders.set(\"Link\", options.fontLinkHeader);\n }\n\n const response = new Response(compositeStream, {\n status: finalStatus,\n headers: responseHeaders,\n }) as PagesStreamedHtmlResponse;\n // Mark the normal streamed HTML render so the Node prod server can strip\n // stale Content-Length only for this path, not for custom gSSP responses.\n response.__vinextStreamedHtmlResponse = true;\n return response;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pages-page-response.js","names":[],"sources":["../../src/server/pages-page-response.ts"],"sourcesContent":["import React, { type ComponentType, type ReactNode } from \"react\";\nimport type { CachedPagesValue } from \"vinext/shims/cache\";\nimport { withScriptNonce } from \"vinext/shims/script-nonce-context\";\nimport { getRequestExecutionContext } from \"vinext/shims/request-context\";\nimport { buildRevalidateCacheControl } from \"./cache-control.js\";\nimport { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr } from \"./html.js\";\nimport { reportRequestError } from \"./instrumentation.js\";\nimport { readStreamAsText } from \"../utils/text-stream.js\";\n\ntype PagesFontPreload = {\n href: string;\n type: string;\n};\n\nexport type PagesI18nRenderContext = {\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n domainLocales?: unknown;\n};\n\nexport type PagesGsspResponse = {\n statusCode: number;\n getHeaders(): Record<string, string | number | boolean | string[]>;\n};\n\ntype PagesStreamedHtmlResponse = {\n __vinextStreamedHtmlResponse?: boolean;\n} & Response;\n\ntype RenderPagesPageResponseOptions = {\n assetTags: string;\n buildId: string | null;\n clearSsrContext: () => void;\n createPageElement: (pageProps: Record<string, unknown>) => ReactNode;\n DocumentComponent: ComponentType | null;\n flushPreloads?: (() => Promise<void> | void) | undefined;\n fontLinkHeader: string;\n fontPreloads: PagesFontPreload[];\n getFontLinks: () => string[];\n getFontStyles: () => string[];\n getSSRHeadHTML?: (() => string) | undefined;\n gsspRes: PagesGsspResponse | null;\n isrCacheKey: (router: string, pathname: string) => string;\n expireSeconds?: number;\n isrRevalidateSeconds: number | null;\n isrSet: (\n key: string,\n data: CachedPagesValue,\n revalidateSeconds: number,\n tags?: string[],\n expireSeconds?: number,\n ) => Promise<void>;\n i18n: PagesI18nRenderContext;\n pageProps: Record<string, unknown>;\n params: Record<string, unknown>;\n renderDocumentToString: (element: ReactNode) => Promise<string>;\n renderToReadableStream: (element: ReactNode) => Promise<ReadableStream<Uint8Array>>;\n resetSSRHead?: (() => void) | undefined;\n routePattern: string;\n routeUrl: string;\n safeJsonStringify: (value: unknown) => string;\n scriptNonce?: string;\n};\n\nfunction buildPagesFontHeadHtml(\n fontLinks: string[],\n fontPreloads: PagesFontPreload[],\n fontStyles: string[],\n scriptNonce?: string,\n): string {\n let html = \"\";\n const nonceAttr = createNonceAttribute(scriptNonce);\n\n for (const link of fontLinks) {\n html += `<link rel=\"stylesheet\"${nonceAttr} href=\"${escapeHtmlAttr(link)}\" />\\n `;\n }\n\n for (const preload of fontPreloads) {\n html += `<link rel=\"preload\"${nonceAttr} href=\"${escapeHtmlAttr(preload.href)}\" as=\"font\" type=\"${escapeHtmlAttr(preload.type)}\" crossorigin />\\n `;\n }\n\n if (fontStyles.length > 0) {\n html += `<style data-vinext-fonts${nonceAttr}>${fontStyles.join(\"\\n\")}</style>\\n `;\n }\n\n return html;\n}\n\nexport function buildPagesNextDataScript(\n options: Pick<\n RenderPagesPageResponseOptions,\n | \"buildId\"\n | \"i18n\"\n | \"pageProps\"\n | \"params\"\n | \"routePattern\"\n | \"safeJsonStringify\"\n | \"scriptNonce\"\n >,\n): string {\n const nextDataPayload: Record<string, unknown> = {\n props: { pageProps: options.pageProps },\n page: options.routePattern,\n query: options.params,\n buildId: options.buildId,\n isFallback: false,\n };\n\n if (options.i18n.locales) {\n nextDataPayload.locale = options.i18n.locale;\n nextDataPayload.locales = options.i18n.locales;\n nextDataPayload.defaultLocale = options.i18n.defaultLocale;\n nextDataPayload.domainLocales = options.i18n.domainLocales;\n }\n\n const localeGlobals = options.i18n.locales\n ? `;window.__VINEXT_LOCALE__=${options.safeJsonStringify(options.i18n.locale)}` +\n `;window.__VINEXT_LOCALES__=${options.safeJsonStringify(options.i18n.locales)}` +\n `;window.__VINEXT_DEFAULT_LOCALE__=${options.safeJsonStringify(options.i18n.defaultLocale)}`\n : \"\";\n\n return createInlineScriptTag(\n `window.__NEXT_DATA__ = ${options.safeJsonStringify(nextDataPayload)}${localeGlobals}`,\n options.scriptNonce,\n );\n}\n\nasync function buildPagesShellHtml(\n bodyMarker: string,\n fontHeadHTML: string,\n nextDataScript: string,\n options: Pick<\n RenderPagesPageResponseOptions,\n \"assetTags\" | \"DocumentComponent\" | \"renderDocumentToString\"\n > & {\n ssrHeadHTML: string;\n },\n): Promise<string> {\n if (options.DocumentComponent) {\n let html = await options.renderDocumentToString(React.createElement(options.DocumentComponent));\n html = html.replace(\"__NEXT_MAIN__\", bodyMarker);\n if (options.ssrHeadHTML || options.assetTags || fontHeadHTML) {\n html = html.replace(\n \"</head>\",\n ` ${fontHeadHTML}${options.ssrHeadHTML}\\n ${options.assetTags}\\n</head>`,\n );\n }\n html = html.replace(\"<!-- __NEXT_SCRIPTS__ -->\", nextDataScript);\n if (!html.includes(\"__NEXT_DATA__\")) {\n html = html.replace(\"</body>\", ` ${nextDataScript}\\n</body>`);\n }\n return html;\n }\n\n return (\n \"<!DOCTYPE html>\\n<html>\\n<head>\\n\" +\n ' <meta charset=\"utf-8\" />\\n' +\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\\n' +\n ` ${fontHeadHTML}${options.ssrHeadHTML}\\n` +\n ` ${options.assetTags}\\n` +\n \"</head>\\n<body>\\n\" +\n ` <div id=\"__next\">${bodyMarker}</div>\\n` +\n ` ${nextDataScript}\\n` +\n \"</body>\\n</html>\"\n );\n}\n\nasync function buildPagesCompositeStream(\n bodyStream: ReadableStream<Uint8Array>,\n shellPrefix: string,\n shellSuffix: string,\n): Promise<ReadableStream<Uint8Array>> {\n const encoder = new TextEncoder();\n\n return new ReadableStream({\n async start(controller) {\n controller.enqueue(encoder.encode(shellPrefix));\n const reader = bodyStream.getReader();\n try {\n for (;;) {\n const chunk = await reader.read();\n if (chunk.done) {\n break;\n }\n controller.enqueue(chunk.value);\n }\n } finally {\n reader.releaseLock();\n }\n controller.enqueue(encoder.encode(shellSuffix));\n controller.close();\n },\n });\n}\n\nasync function reportPagesIsrCacheWriteError(\n error: unknown,\n cacheKey: string,\n routePattern: string,\n): Promise<void> {\n console.error(`[vinext] Pages ISR cache write failed for ${cacheKey}:`, error);\n try {\n await reportRequestError(\n error instanceof Error ? error : new Error(String(error)),\n { path: cacheKey, method: \"GET\", headers: {} },\n {\n routerKind: \"Pages Router\",\n routePath: routePattern,\n routeType: \"render\",\n },\n );\n } catch {\n // Cache-write failure reporting must never make the background task reject.\n }\n}\n\nfunction schedulePagesIsrCacheWrite(options: {\n cacheKey: string;\n expireSeconds?: number;\n pageData: Record<string, unknown>;\n revalidateSeconds: number;\n routePattern: string;\n shellPrefix: string;\n shellSuffix: string;\n stream: ReadableStream<Uint8Array>;\n setCache: RenderPagesPageResponseOptions[\"isrSet\"];\n}): void {\n const cacheWritePromise = readStreamAsText(options.stream)\n .then((bodyHtml) =>\n options.setCache(\n options.cacheKey,\n {\n kind: \"PAGES\",\n html: options.shellPrefix + bodyHtml + options.shellSuffix,\n pageData: options.pageData,\n headers: undefined,\n status: undefined,\n },\n options.revalidateSeconds,\n undefined,\n options.expireSeconds,\n ),\n )\n .catch((error: unknown) =>\n reportPagesIsrCacheWriteError(error, options.cacheKey, options.routePattern),\n );\n\n getRequestExecutionContext()?.waitUntil(cacheWritePromise);\n}\n\nfunction applyGsspHeaders(headers: Headers, gsspRes: PagesGsspResponse | null): number {\n if (!gsspRes) {\n return 200;\n }\n\n const gsspHeaders = gsspRes.getHeaders();\n for (const key of Object.keys(gsspHeaders)) {\n const value = gsspHeaders[key];\n const lowerKey = key.toLowerCase();\n if (lowerKey === \"set-cookie\" && Array.isArray(value)) {\n for (const cookie of value) {\n headers.append(\"set-cookie\", String(cookie));\n }\n continue;\n }\n if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n continue;\n }\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n headers.set(key, String(value));\n }\n }\n headers.set(\"Content-Type\", \"text/html\");\n return gsspRes.statusCode;\n}\n\nexport async function renderPagesPageResponse(\n options: RenderPagesPageResponseOptions,\n): Promise<Response> {\n const pageElement = withScriptNonce(\n React.createElement(React.Fragment, null, options.createPageElement(options.pageProps)),\n options.scriptNonce,\n );\n\n options.resetSSRHead?.();\n await options.flushPreloads?.();\n\n const fontHeadHTML = buildPagesFontHeadHtml(\n options.getFontLinks(),\n options.fontPreloads,\n options.getFontStyles(),\n options.scriptNonce,\n );\n const nextDataScript = buildPagesNextDataScript({\n buildId: options.buildId,\n i18n: options.i18n,\n pageProps: options.pageProps,\n params: options.params,\n routePattern: options.routePattern,\n safeJsonStringify: options.safeJsonStringify,\n scriptNonce: options.scriptNonce,\n });\n const bodyMarker = \"<!--VINEXT_STREAM_BODY-->\";\n // Render the page FIRST so that <Head> and other SSR state collectors\n // (e.g. styled-jsx, useServerInsertedHTML) are populated before we read\n // them. This fixes a race condition where head styles were silently dropped\n // because they were collected before the page had finished rendering.\n // Mirrors Next.js fix: vercel/next.js@9853944\n const bodyStream = await options.renderToReadableStream(pageElement);\n\n const shellHtml = await buildPagesShellHtml(bodyMarker, fontHeadHTML, nextDataScript, {\n assetTags: options.assetTags,\n DocumentComponent: options.DocumentComponent,\n renderDocumentToString: options.renderDocumentToString,\n ssrHeadHTML: options.getSSRHeadHTML?.() ?? \"\",\n });\n\n options.clearSsrContext();\n\n const markerIndex = shellHtml.indexOf(bodyMarker);\n const shellPrefix = shellHtml.slice(0, markerIndex);\n const shellSuffix = shellHtml.slice(markerIndex + bodyMarker.length);\n\n let responseBodyStream = bodyStream;\n if (\n // Keep nonce-bearing pages out of ISR writes: rewritePagesCachedHtml()\n // later matches the cached __NEXT_DATA__ block via a bare <script> marker.\n !options.scriptNonce &&\n options.isrRevalidateSeconds !== null &&\n options.isrRevalidateSeconds > 0\n ) {\n const cacheBodyStreamPair = bodyStream.tee();\n responseBodyStream = cacheBodyStreamPair[0];\n const cacheBodyStream = cacheBodyStreamPair[1];\n const isrPathname = options.routeUrl.split(\"?\")[0];\n const cacheKey = options.isrCacheKey(\"pages\", isrPathname);\n\n schedulePagesIsrCacheWrite({\n cacheKey,\n expireSeconds: options.expireSeconds,\n pageData: options.pageProps,\n revalidateSeconds: options.isrRevalidateSeconds,\n routePattern: options.routePattern,\n setCache: options.isrSet,\n shellPrefix,\n shellSuffix,\n stream: cacheBodyStream,\n });\n }\n\n const compositeStream = await buildPagesCompositeStream(\n responseBodyStream,\n shellPrefix,\n shellSuffix,\n );\n\n const responseHeaders = new Headers({ \"Content-Type\": \"text/html\" });\n const finalStatus = applyGsspHeaders(responseHeaders, options.gsspRes);\n\n if (options.scriptNonce) {\n responseHeaders.set(\"Cache-Control\", \"no-store, must-revalidate\");\n } else if (options.isrRevalidateSeconds) {\n responseHeaders.set(\n \"Cache-Control\",\n buildRevalidateCacheControl(options.isrRevalidateSeconds, options.expireSeconds),\n );\n responseHeaders.set(\"X-Vinext-Cache\", \"MISS\");\n }\n if (options.fontLinkHeader) {\n responseHeaders.set(\"Link\", options.fontLinkHeader);\n }\n\n const response: PagesStreamedHtmlResponse = Object.assign(\n new Response(compositeStream, {\n status: finalStatus,\n headers: responseHeaders,\n }),\n {\n __vinextStreamedHtmlResponse: true,\n },\n );\n // Mark the normal streamed HTML render so the Node prod server can strip\n // stale Content-Length only for this path, not for custom gSSP responses.\n return response;\n}\n"],"mappings":"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"}
|
|
@@ -8,6 +8,12 @@ type ProdServerOptions = {
|
|
|
8
8
|
host?: string; /** Path to the build output directory */
|
|
9
9
|
outDir?: string; /** Disable compression (default: false) */
|
|
10
10
|
noCompression?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Narrow startup context for callers that need a more precise log line.
|
|
13
|
+
* Omitted for normal `vinext start` so the existing production-server output
|
|
14
|
+
* remains stable.
|
|
15
|
+
*/
|
|
16
|
+
purpose?: "prerender";
|
|
11
17
|
};
|
|
12
18
|
/** Content types that benefit from compression. */
|
|
13
19
|
declare const COMPRESSIBLE_TYPES: Set<string>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
2
|
+
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
2
3
|
import { normalizePath } from "./normalize-path.js";
|
|
3
4
|
import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewrite, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
|
|
4
|
-
import {
|
|
5
|
-
import { applyConfigHeadersToHeaderRecord, isOpenRedirectShaped } from "./request-pipeline.js";
|
|
5
|
+
import { notFoundResponse } from "./http-error-responses.js";
|
|
6
|
+
import { applyConfigHeadersToHeaderRecord, filterInternalHeaders, isOpenRedirectShaped } from "./request-pipeline.js";
|
|
6
7
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
7
8
|
import { manifestFileWithBase } from "../utils/manifest-paths.js";
|
|
8
9
|
import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
|
|
@@ -168,6 +169,14 @@ function cancelResponseBody(response) {
|
|
|
168
169
|
function isVinextStreamedHtmlResponse(response) {
|
|
169
170
|
return response.__vinextStreamedHtmlResponse === true;
|
|
170
171
|
}
|
|
172
|
+
function logProdServerStarted(host, port, purpose) {
|
|
173
|
+
const url = `http://${host}:${port}`;
|
|
174
|
+
if (purpose === "prerender") {
|
|
175
|
+
console.log(`[vinext] Production server for prerendering running at ${url}`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
console.log(`[vinext] Production server running at ${url}`);
|
|
179
|
+
}
|
|
171
180
|
/**
|
|
172
181
|
* Merge middleware/config headers and an optional status override into a new
|
|
173
182
|
* Web Response while preserving the original body stream when allowed.
|
|
@@ -455,12 +464,13 @@ function nodeToWebRequest(req, urlOverride) {
|
|
|
455
464
|
const rawProto = trustProxy ? req.headers["x-forwarded-proto"]?.split(",")[0]?.trim() : void 0;
|
|
456
465
|
const origin = `${rawProto === "https" || rawProto === "http" ? rawProto : "http"}://${resolveHost(req, "localhost")}`;
|
|
457
466
|
const url = new URL(urlOverride ?? req.url ?? "/", origin);
|
|
458
|
-
const
|
|
467
|
+
const rawHeaders = new Headers();
|
|
459
468
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
460
469
|
if (value === void 0) continue;
|
|
461
|
-
if (Array.isArray(value)) for (const v of value)
|
|
462
|
-
else
|
|
470
|
+
if (Array.isArray(value)) for (const v of value) rawHeaders.append(key, v);
|
|
471
|
+
else rawHeaders.set(key, value);
|
|
463
472
|
}
|
|
473
|
+
const headers = filterInternalHeaders(rawHeaders);
|
|
464
474
|
const method = req.method ?? "GET";
|
|
465
475
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
466
476
|
const init = {
|
|
@@ -526,7 +536,7 @@ async function sendWebResponse(webResponse, req, res, compress) {
|
|
|
526
536
|
*/
|
|
527
537
|
async function startProdServer(options = {}) {
|
|
528
538
|
installSocketErrorBackstop();
|
|
529
|
-
const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), noCompression = false } = options;
|
|
539
|
+
const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), noCompression = false, purpose } = options;
|
|
530
540
|
const compress = !noCompression;
|
|
531
541
|
const resolvedOutDir = path.resolve(outDir);
|
|
532
542
|
const clientDir = path.join(resolvedOutDir, "client");
|
|
@@ -543,14 +553,16 @@ async function startProdServer(options = {}) {
|
|
|
543
553
|
host,
|
|
544
554
|
clientDir,
|
|
545
555
|
rscEntryPath,
|
|
546
|
-
compress
|
|
556
|
+
compress,
|
|
557
|
+
purpose
|
|
547
558
|
});
|
|
548
559
|
return startPagesRouterServer({
|
|
549
560
|
port,
|
|
550
561
|
host,
|
|
551
562
|
clientDir,
|
|
552
563
|
serverEntryPath,
|
|
553
|
-
compress
|
|
564
|
+
compress,
|
|
565
|
+
purpose
|
|
554
566
|
});
|
|
555
567
|
}
|
|
556
568
|
function createNodeExecutionContext() {
|
|
@@ -588,7 +600,7 @@ function resolveAppRouterHandler(entry) {
|
|
|
588
600
|
* 4. Stream the Web Response back (with optional compression)
|
|
589
601
|
*/
|
|
590
602
|
async function startAppRouterServer(options) {
|
|
591
|
-
const { port, host, clientDir, rscEntryPath, compress } = options;
|
|
603
|
+
const { port, host, clientDir, rscEntryPath, compress, purpose } = options;
|
|
592
604
|
let imageConfig;
|
|
593
605
|
const imageConfigPath = path.join(path.dirname(rscEntryPath), "image-config.json");
|
|
594
606
|
if (fs.existsSync(imageConfigPath)) try {
|
|
@@ -668,10 +680,7 @@ async function startAppRouterServer(options) {
|
|
|
668
680
|
const served = await tryServeStatic(req, res, clientDir, staticFilePath, compress, staticCache, staticResponseHeaders, response.status);
|
|
669
681
|
cancelResponseBody(response);
|
|
670
682
|
if (served) return;
|
|
671
|
-
await sendWebResponse(
|
|
672
|
-
status: 404,
|
|
673
|
-
headers: toWebHeaders(staticResponseHeaders)
|
|
674
|
-
}), req, res, compress);
|
|
683
|
+
await sendWebResponse(notFoundResponse({ headers: toWebHeaders(staticResponseHeaders) }), req, res, compress);
|
|
675
684
|
return;
|
|
676
685
|
}
|
|
677
686
|
await sendWebResponse(response, req, res, compress);
|
|
@@ -686,8 +695,7 @@ async function startAppRouterServer(options) {
|
|
|
686
695
|
await new Promise((resolve) => {
|
|
687
696
|
server.listen(port, host, () => {
|
|
688
697
|
const addr = server.address();
|
|
689
|
-
|
|
690
|
-
console.log(`[vinext] Production server running at http://${host}:${actualPort}`);
|
|
698
|
+
logProdServerStarted(host, typeof addr === "object" && addr ? addr.port : port, purpose);
|
|
691
699
|
resolve();
|
|
692
700
|
});
|
|
693
701
|
});
|
|
@@ -707,7 +715,7 @@ async function startAppRouterServer(options) {
|
|
|
707
715
|
* - vinextConfig — embedded next.config.js settings
|
|
708
716
|
*/
|
|
709
717
|
async function startPagesRouterServer(options) {
|
|
710
|
-
const { port, host, clientDir, serverEntryPath, compress } = options;
|
|
718
|
+
const { port, host, clientDir, serverEntryPath, compress, purpose } = options;
|
|
711
719
|
const serverMtime = fs.statSync(serverEntryPath).mtimeMs;
|
|
712
720
|
const serverEntry = await import(`${pathToFileURL(serverEntryPath).href}?t=${serverMtime}`);
|
|
713
721
|
const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig } = serverEntry;
|
|
@@ -828,7 +836,7 @@ async function startPagesRouterServer(options) {
|
|
|
828
836
|
return;
|
|
829
837
|
} else if (!trailingSlash && hasTrailing) {
|
|
830
838
|
const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
|
|
831
|
-
res.writeHead(308, { Location: basePath + pathname
|
|
839
|
+
res.writeHead(308, { Location: basePath + removeTrailingSlash(pathname) + qs });
|
|
832
840
|
res.end();
|
|
833
841
|
return;
|
|
834
842
|
}
|
|
@@ -836,10 +844,10 @@ async function startPagesRouterServer(options) {
|
|
|
836
844
|
const rawProtocol = trustProxy ? req.headers["x-forwarded-proto"]?.split(",")[0]?.trim() : void 0;
|
|
837
845
|
const protocol = rawProtocol === "https" || rawProtocol === "http" ? rawProtocol : "http";
|
|
838
846
|
const hostHeader = resolveHost(req, `${host}:${port}`);
|
|
839
|
-
const reqHeaders = Object.entries(req.headers).reduce((h, [k, v]) => {
|
|
847
|
+
const reqHeaders = filterInternalHeaders(Object.entries(req.headers).reduce((h, [k, v]) => {
|
|
840
848
|
if (v) h.set(k, Array.isArray(v) ? v.join(", ") : v);
|
|
841
849
|
return h;
|
|
842
|
-
}, new Headers());
|
|
850
|
+
}, new Headers()));
|
|
843
851
|
const method = req.method ?? "GET";
|
|
844
852
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
845
853
|
let webRequest = new Request(`${protocol}://${hostHeader}${url}`, {
|
|
@@ -994,8 +1002,7 @@ async function startPagesRouterServer(options) {
|
|
|
994
1002
|
await new Promise((resolve) => {
|
|
995
1003
|
server.listen(port, host, () => {
|
|
996
1004
|
const addr = server.address();
|
|
997
|
-
|
|
998
|
-
console.log(`[vinext] Production server running at http://${host}:${actualPort}`);
|
|
1005
|
+
logProdServerStarted(host, typeof addr === "object" && addr ? addr.port : port, purpose);
|
|
999
1006
|
resolve();
|
|
1000
1007
|
});
|
|
1001
1008
|
});
|