vinext 0.0.53 → 0.0.55
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 +1 -0
- package/dist/build/inline-css.d.ts +7 -0
- package/dist/build/inline-css.js +50 -0
- package/dist/build/inline-css.js.map +1 -0
- package/dist/build/prerender.js +2 -1
- package/dist/build/prerender.js.map +1 -1
- package/dist/check.js +19 -3
- package/dist/check.js.map +1 -1
- package/dist/client/navigation-runtime.d.ts +3 -1
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/navigation-runtime.js.map +1 -1
- package/dist/client/window-next.d.ts +7 -0
- package/dist/client/window-next.js.map +1 -1
- package/dist/config/next-config.d.ts +97 -2
- package/dist/config/next-config.js +155 -6
- package/dist/config/next-config.js.map +1 -1
- package/dist/config/tsconfig-paths.d.ts +12 -3
- package/dist/config/tsconfig-paths.js +55 -24
- package/dist/config/tsconfig-paths.js.map +1 -1
- package/dist/deploy.js +13 -0
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-browser-entry.d.ts +11 -1
- package/dist/entries/app-browser-entry.js +16 -6
- package/dist/entries/app-browser-entry.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +9 -1
- package/dist/entries/app-rsc-entry.js +30 -5
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-rsc-manifest.d.ts +21 -1
- package/dist/entries/app-rsc-manifest.js +28 -9
- package/dist/entries/app-rsc-manifest.js.map +1 -1
- package/dist/entries/pages-client-entry.d.ts +4 -1
- package/dist/entries/pages-client-entry.js +18 -2
- package/dist/entries/pages-client-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +123 -8
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.d.ts +1 -10
- package/dist/entries/runtime-entry-module.js +2 -12
- package/dist/entries/runtime-entry-module.js.map +1 -1
- package/dist/index.js +144 -44
- package/dist/index.js.map +1 -1
- package/dist/plugins/import-meta-url.d.ts +16 -0
- package/dist/plugins/import-meta-url.js +193 -0
- package/dist/plugins/import-meta-url.js.map +1 -0
- package/dist/plugins/remove-console.d.ts +16 -0
- package/dist/plugins/remove-console.js +176 -0
- package/dist/plugins/remove-console.js.map +1 -0
- package/dist/routing/app-route-graph.d.ts +24 -1
- package/dist/routing/app-route-graph.js +52 -4
- package/dist/routing/app-route-graph.js.map +1 -1
- package/dist/routing/app-router.d.ts +2 -2
- package/dist/routing/app-router.js +2 -2
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/file-matcher.d.ts +21 -1
- package/dist/routing/file-matcher.js +39 -1
- package/dist/routing/file-matcher.js.map +1 -1
- package/dist/routing/pages-router.d.ts +1 -1
- package/dist/routing/pages-router.js +10 -3
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/server/api-handler.js +1 -1
- package/dist/server/app-browser-action-result.d.ts +9 -16
- package/dist/server/app-browser-action-result.js +25 -14
- package/dist/server/app-browser-action-result.js.map +1 -1
- package/dist/server/app-browser-entry.js +195 -60
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-mpa-navigation.d.ts +16 -0
- package/dist/server/app-browser-mpa-navigation.js +36 -0
- package/dist/server/app-browser-mpa-navigation.js.map +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +2 -0
- package/dist/server/app-browser-navigation-controller.js +4 -0
- package/dist/server/app-browser-navigation-controller.js.map +1 -1
- package/dist/server/app-browser-popstate.d.ts +3 -1
- package/dist/server/app-browser-popstate.js +15 -1
- package/dist/server/app-browser-popstate.js.map +1 -1
- package/dist/server/app-browser-state.js +2 -1
- package/dist/server/app-browser-state.js.map +1 -1
- package/dist/server/app-elements-wire.d.ts +13 -4
- package/dist/server/app-elements-wire.js +10 -1
- package/dist/server/app-elements-wire.js.map +1 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-elements.js.map +1 -1
- package/dist/server/app-fallback-renderer.d.ts +15 -5
- package/dist/server/app-fallback-renderer.js +10 -4
- package/dist/server/app-fallback-renderer.js.map +1 -1
- package/dist/server/app-inline-css-client.d.ts +7 -0
- package/dist/server/app-inline-css-client.js +37 -0
- package/dist/server/app-inline-css-client.js.map +1 -0
- package/dist/server/app-layout-param-observation.d.ts +30 -0
- package/dist/server/app-layout-param-observation.js +130 -0
- package/dist/server/app-layout-param-observation.js.map +1 -0
- package/dist/server/app-page-boundary-render.js +2 -2
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-boundary.d.ts +21 -1
- package/dist/server/app-page-boundary.js +28 -3
- package/dist/server/app-page-boundary.js.map +1 -1
- package/dist/server/app-page-cache.d.ts +7 -3
- package/dist/server/app-page-cache.js +7 -7
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-dispatch.d.ts +10 -1
- package/dist/server/app-page-dispatch.js +126 -79
- package/dist/server/app-page-dispatch.js.map +1 -1
- package/dist/server/app-page-element-builder.js +12 -28
- package/dist/server/app-page-element-builder.js.map +1 -1
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +14 -1
- package/dist/server/app-page-params.js.map +1 -1
- package/dist/server/app-page-probe.d.ts +12 -1
- package/dist/server/app-page-probe.js +116 -1
- package/dist/server/app-page-probe.js.map +1 -1
- package/dist/server/app-page-render-identity.d.ts +22 -0
- package/dist/server/app-page-render-identity.js +42 -0
- package/dist/server/app-page-render-identity.js.map +1 -0
- package/dist/server/app-page-render.d.ts +8 -1
- package/dist/server/app-page-render.js +4 -1
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-request.d.ts +6 -3
- package/dist/server/app-page-request.js +5 -2
- package/dist/server/app-page-request.js.map +1 -1
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +15 -0
- package/dist/server/app-page-route-wiring.js +7 -5
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +11 -0
- package/dist/server/app-page-stream.js +1 -0
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-route-handler-response.js +37 -5
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +3 -2
- package/dist/server/app-rsc-cache-busting.js +9 -7
- package/dist/server/app-rsc-cache-busting.js.map +1 -1
- package/dist/server/app-rsc-handler.d.ts +14 -3
- package/dist/server/app-rsc-handler.js +56 -6
- package/dist/server/app-rsc-handler.js.map +1 -1
- package/dist/server/app-rsc-request-normalization.d.ts +2 -1
- package/dist/server/app-rsc-request-normalization.js +3 -2
- package/dist/server/app-rsc-request-normalization.js.map +1 -1
- package/dist/server/app-segment-config.d.ts +1 -1
- package/dist/server/app-segment-config.js +4 -1
- package/dist/server/app-segment-config.js.map +1 -1
- package/dist/server/app-server-action-execution.d.ts +26 -3
- package/dist/server/app-server-action-execution.js +240 -29
- package/dist/server/app-server-action-execution.js.map +1 -1
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +22 -7
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/app-ssr-error-meta.js +3 -3
- package/dist/server/app-ssr-error-meta.js.map +1 -1
- package/dist/server/app-ssr-stream.d.ts +2 -1
- package/dist/server/app-ssr-stream.js +176 -31
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/artifact-compatibility.d.ts +2 -1
- package/dist/server/artifact-compatibility.js +10 -1
- package/dist/server/artifact-compatibility.js.map +1 -1
- package/dist/server/client-reuse-manifest.d.ts +9 -4
- package/dist/server/client-reuse-manifest.js +2 -1
- package/dist/server/client-reuse-manifest.js.map +1 -1
- package/dist/server/client-trace-metadata.d.ts +31 -0
- package/dist/server/client-trace-metadata.js +83 -0
- package/dist/server/client-trace-metadata.js.map +1 -0
- package/dist/server/cookie-utils.d.ts +13 -0
- package/dist/server/cookie-utils.js +20 -0
- package/dist/server/cookie-utils.js.map +1 -0
- package/dist/server/dev-server.d.ts +8 -1
- package/dist/server/dev-server.js +83 -12
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/document-initial-head.d.ts +7 -0
- package/dist/server/document-initial-head.js +35 -0
- package/dist/server/document-initial-head.js.map +1 -0
- package/dist/server/html.d.ts +2 -1
- package/dist/server/html.js +6 -1
- package/dist/server/html.js.map +1 -1
- package/dist/server/isr-cache.d.ts +7 -5
- package/dist/server/isr-cache.js +17 -6
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/middleware-runtime.js +1 -2
- package/dist/server/middleware-runtime.js.map +1 -1
- package/dist/server/pages-document-initial-props.d.ts +89 -0
- package/dist/server/pages-document-initial-props.js +140 -0
- package/dist/server/pages-document-initial-props.js.map +1 -0
- package/dist/server/pages-node-compat.js +1 -1
- package/dist/server/pages-page-data.js +3 -0
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-method.d.ts +48 -0
- package/dist/server/pages-page-method.js +19 -0
- package/dist/server/pages-page-method.js.map +1 -0
- package/dist/server/pages-page-response.d.ts +20 -0
- package/dist/server/pages-page-response.js +37 -7
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/pages-serializable-props.d.ts +25 -0
- package/dist/server/pages-serializable-props.js +69 -0
- package/dist/server/pages-serializable-props.js.map +1 -0
- package/dist/server/prod-server.js +16 -6
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/server-action-not-found.js +3 -2
- package/dist/server/server-action-not-found.js.map +1 -1
- package/dist/server/skip-cache-proof.d.ts +23 -2
- package/dist/server/skip-cache-proof.js +81 -12
- package/dist/server/skip-cache-proof.js.map +1 -1
- package/dist/server/static-file-cache.js +2 -1
- package/dist/server/static-file-cache.js.map +1 -1
- package/dist/server/static-layout-client-reuse-proof.d.ts +16 -0
- package/dist/server/static-layout-client-reuse-proof.js +35 -0
- package/dist/server/static-layout-client-reuse-proof.js.map +1 -0
- package/dist/shims/app-router-scroll-state.d.ts +4 -2
- package/dist/shims/app-router-scroll-state.js +16 -3
- package/dist/shims/app-router-scroll-state.js.map +1 -1
- package/dist/shims/app-router-scroll.d.ts +16 -2
- package/dist/shims/app-router-scroll.js +18 -3
- package/dist/shims/app-router-scroll.js.map +1 -1
- package/dist/shims/cache.d.ts +27 -1
- package/dist/shims/cache.js +108 -6
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/document.d.ts +6 -0
- package/dist/shims/document.js +7 -8
- package/dist/shims/document.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error-boundary.js +27 -28
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/error.js +3 -0
- package/dist/shims/error.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +16 -5
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/hash-scroll.d.ts +4 -1
- package/dist/shims/hash-scroll.js +13 -1
- package/dist/shims/hash-scroll.js.map +1 -1
- package/dist/shims/head-state.d.ts +1 -0
- package/dist/shims/head-state.js +18 -3
- package/dist/shims/head-state.js.map +1 -1
- package/dist/shims/head.d.ts +35 -1
- package/dist/shims/head.js +113 -14
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +7 -0
- package/dist/shims/headers.js +9 -1
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/internal/app-route-detection.d.ts +37 -0
- package/dist/shims/internal/app-route-detection.js +69 -0
- package/dist/shims/internal/app-route-detection.js.map +1 -0
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +56 -0
- package/dist/shims/internal/pages-data-fetch-dedup.js +70 -0
- package/dist/shims/internal/pages-data-fetch-dedup.js.map +1 -0
- package/dist/shims/link.d.ts +18 -2
- package/dist/shims/link.js +98 -8
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +7 -6
- package/dist/shims/metadata.js +9 -5
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation.d.ts +40 -3
- package/dist/shims/navigation.js +124 -25
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/router.d.ts +5 -0
- package/dist/shims/router.js +51 -21
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/script.d.ts +11 -1
- package/dist/shims/script.js +75 -6
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/thenable-params.d.ts +5 -2
- package/dist/shims/thenable-params.js +25 -1
- package/dist/shims/thenable-params.js.map +1 -1
- package/dist/shims/unified-request-context.js +3 -0
- package/dist/shims/unified-request-context.js.map +1 -1
- package/dist/utils/client-build-manifest.d.ts +15 -0
- package/dist/utils/client-build-manifest.js +54 -0
- package/dist/utils/client-build-manifest.js.map +1 -0
- package/dist/utils/hash.js +1 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/lazy-chunks.d.ts +1 -1
- package/dist/utils/lazy-chunks.js.map +1 -1
- package/dist/utils/path.d.ts +13 -0
- package/dist/utils/path.js +16 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/vite-version.d.ts +11 -0
- package/dist/utils/vite-version.js +36 -0
- package/dist/utils/vite-version.js.map +1 -0
- package/package.json +2 -2
|
@@ -23,6 +23,12 @@ declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navContext: Na
|
|
|
23
23
|
};
|
|
24
24
|
formState?: ReactFormState | null;
|
|
25
25
|
basePath?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Allow-list of OpenTelemetry propagation keys (from
|
|
28
|
+
* `experimental.clientTraceMetadata`) to render as `<meta>` tags in the
|
|
29
|
+
* SSR head. Undefined or empty disables emission entirely.
|
|
30
|
+
*/
|
|
31
|
+
clientTraceMetadata?: readonly string[];
|
|
26
32
|
rootParams?: RootParams;
|
|
27
33
|
/** When true, wait for the full React tree (including Suspense boundaries)
|
|
28
34
|
* to resolve before returning the HTML stream. Used for static prerender
|
|
@@ -8,13 +8,14 @@ import { ServerInsertedHTMLContext, appRouterInstance, clearServerInsertedHTML,
|
|
|
8
8
|
import { runWithNavigationContext } from "../shims/navigation-state.js";
|
|
9
9
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
10
10
|
import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
|
|
11
|
+
import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
11
12
|
import { ElementsContext, Slot } from "../shims/slot.js";
|
|
12
13
|
import { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform } from "./app-ssr-stream.js";
|
|
13
|
-
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
14
|
-
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
15
14
|
import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
|
|
16
15
|
import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
|
|
17
16
|
import { deferUntilStreamConsumed } from "./app-page-stream.js";
|
|
17
|
+
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
18
|
+
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
18
19
|
import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
|
|
19
20
|
import { Fragment, createElement, use } from "react";
|
|
20
21
|
import { renderToReadableStream, renderToStaticMarkup } from "react-dom/server.edge";
|
|
@@ -78,15 +79,19 @@ function renderBeforeInteractiveInlineScripts(scripts) {
|
|
|
78
79
|
}
|
|
79
80
|
return html;
|
|
80
81
|
}
|
|
81
|
-
function renderFontHtml(fontData, nonce) {
|
|
82
|
+
function renderFontHtml(fontData, nonce, options = {}) {
|
|
82
83
|
if (!fontData) return "";
|
|
83
84
|
let fontHTML = "";
|
|
84
85
|
const nonceAttr = createNonceAttribute(nonce);
|
|
86
|
+
const includeStyles = options.includeStyles ?? true;
|
|
85
87
|
for (const url of fontData.links ?? []) fontHTML += `<link rel="stylesheet"${nonceAttr} href="${escapeHtmlAttr(url)}" />\n`;
|
|
86
88
|
for (const preload of fontData.preloads ?? []) fontHTML += `<link rel="preload"${nonceAttr} href="${escapeHtmlAttr(preload.href)}" as="font" type="${escapeHtmlAttr(preload.type)}" crossorigin />\n`;
|
|
87
|
-
if (fontData.styles && fontData.styles.length > 0) fontHTML += `<style data-vinext-fonts${nonceAttr}>${fontData.styles.join("\n")}</style>\n`;
|
|
89
|
+
if (includeStyles && fontData.styles && fontData.styles.length > 0) fontHTML += `<style data-vinext-fonts${nonceAttr}>${fontData.styles.join("\n")}</style>\n`;
|
|
88
90
|
return fontHTML;
|
|
89
91
|
}
|
|
92
|
+
function hasInlineCssManifest(manifest) {
|
|
93
|
+
return manifest !== void 0 && Object.keys(manifest).length > 0;
|
|
94
|
+
}
|
|
90
95
|
/**
|
|
91
96
|
* Extract the bootstrap module URL from the `import("...")` string that
|
|
92
97
|
* `import.meta.viteRsc.loadBootstrapScriptContent("index")` returns.
|
|
@@ -168,17 +173,27 @@ async function handleSsr(rscStream, navContext, fontData, options) {
|
|
|
168
173
|
}
|
|
169
174
|
});
|
|
170
175
|
if (options?.waitForAllReady === true) await htmlStream.allReady;
|
|
171
|
-
const
|
|
176
|
+
const inlineCssManifest = globalThis.__VINEXT_INLINE_CSS__;
|
|
177
|
+
const fontStyles = fontData?.styles ?? [];
|
|
178
|
+
const mergeFontStylesIntoInlineCss = fontStyles.length > 0 && hasInlineCssManifest(inlineCssManifest);
|
|
179
|
+
const inlineCssFontStyles = mergeFontStylesIntoInlineCss ? fontStyles.join("\n") : "";
|
|
180
|
+
const inlineCssFontStyleFallbackHTML = mergeFontStylesIntoInlineCss ? renderFontHtml({ styles: fontStyles }, options?.scriptNonce) : "";
|
|
181
|
+
const fontHTML = renderFontHtml(fontData, options?.scriptNonce, { includeStyles: !mergeFontStylesIntoInlineCss });
|
|
182
|
+
let traceMetaHTML = null;
|
|
183
|
+
const getTraceMetaHTML = () => {
|
|
184
|
+
if (traceMetaHTML === null) traceMetaHTML = getClientTraceMetadataHTML(options?.clientTraceMetadata);
|
|
185
|
+
return traceMetaHTML;
|
|
186
|
+
};
|
|
172
187
|
let didInjectHeadHTML = false;
|
|
173
188
|
const getInsertedHTML = () => {
|
|
174
189
|
const insertedHTML = renderInsertedHtml(renderServerInsertedHTML());
|
|
175
190
|
const errorMetaHTML = errorMetaRenderer.flush();
|
|
176
191
|
if (didInjectHeadHTML) return insertedHTML + errorMetaHTML;
|
|
177
192
|
didInjectHeadHTML = true;
|
|
178
|
-
return buildHeadInjectionHtml(navContext, bootstrapModuleUrl, options?.formState ?? null, insertedHTML + errorMetaHTML, fontHTML, options?.scriptNonce);
|
|
193
|
+
return buildHeadInjectionHtml(navContext, bootstrapModuleUrl, options?.formState ?? null, insertedHTML + errorMetaHTML + getTraceMetaHTML(), fontHTML, options?.scriptNonce);
|
|
179
194
|
};
|
|
180
195
|
const getBeforeInteractiveHeadHTML = () => renderBeforeInteractiveInlineScripts(beforeInteractiveInlineScripts);
|
|
181
|
-
return deferUntilStreamConsumed(htmlStream.pipeThrough(createTickBufferedTransform(rscEmbed, getInsertedHTML, getBeforeInteractiveHeadHTML)), cleanup);
|
|
196
|
+
return deferUntilStreamConsumed(htmlStream.pipeThrough(createTickBufferedTransform(rscEmbed, getInsertedHTML, getBeforeInteractiveHeadHTML, inlineCssManifest, inlineCssFontStyles, inlineCssFontStyleFallbackHTML, options?.scriptNonce)), cleanup);
|
|
182
197
|
} catch (error) {
|
|
183
198
|
cleanup();
|
|
184
199
|
throw error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-ssr-entry.js","names":["createReactElement"],"sources":["../../src/server/app-ssr-entry.ts"],"sourcesContent":["/// <reference types=\"@vitejs/plugin-rsc/types\" />\n\nimport \"./server-globals.js\";\nimport type { ReactNode } from \"react\";\nimport type { ReactFormState } from \"react-dom/client\";\nimport { Fragment, createElement as createReactElement, use } from \"react\";\nimport { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { renderToReadableStream, renderToStaticMarkup } from \"react-dom/server.edge\";\nimport clientReferences from \"virtual:vite-rsc/client-references\";\nimport type { NavigationContext } from \"vinext/shims/navigation\";\nimport {\n ServerInsertedHTMLContext,\n appRouterInstance,\n clearServerInsertedHTML,\n renderServerInsertedHTML,\n setNavigationContext,\n useServerInsertedHTML,\n} from \"vinext/shims/navigation\";\nimport { runWithNavigationContext } from \"vinext/shims/navigation-state\";\nimport { runWithRootParamsScope, type RootParams } from \"vinext/shims/root-params\";\nimport { isOpenRedirectShaped } from \"./request-pipeline.js\";\nimport { notFoundResponse } from \"./http-error-responses.js\";\nimport { withScriptNonce } from \"vinext/shims/script-nonce-context\";\nimport {\n BeforeInteractiveContext,\n type BeforeInteractiveInlineScript,\n} from \"vinext/shims/before-interactive-context\";\nimport {\n createInlineScriptTag,\n createNonceAttribute,\n escapeHtmlAttr,\n safeJsonStringify,\n} from \"./html.js\";\nimport {\n createNavigationRuntimeRscMetadataScript,\n createRscEmbedTransform,\n createTickBufferedTransform,\n} from \"./app-ssr-stream.js\";\nimport { deferUntilStreamConsumed } from \"./app-page-stream.js\";\nimport { createSsrErrorMetaRenderer } from \"./app-ssr-error-meta.js\";\nimport { AppElementsWire, type AppWireElements } from \"./app-elements.js\";\nimport { ElementsContext, Slot } from \"vinext/shims/slot\";\nimport { AppRouterContext } from \"vinext/shims/internal/app-router-context\";\nimport { createClientReferencePreloader } from \"./app-client-reference-preloader.js\";\nimport { RSC_FORM_STATE_GLOBAL } from \"./app-browser-hydration.js\";\n\nexport type FontPreload = {\n href: string;\n type: string;\n};\n\nexport type FontData = {\n links?: string[];\n styles?: string[];\n preloads?: FontPreload[];\n};\n\nconst clientReferencePreloader = createClientReferencePreloader({\n getReferences() {\n return clientReferences;\n },\n getClientRequire() {\n return globalThis.__vite_rsc_client_require__;\n },\n onPreloadError(id, error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] failed to preload client ref:\", id, error);\n }\n },\n});\n\nfunction ssrErrorDigest(input: string): string {\n let hash = 5381;\n for (let i = input.length - 1; i >= 0; i--) {\n hash = (hash * 33) ^ input.charCodeAt(i);\n }\n return (hash >>> 0).toString();\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === \"string\") return error;\n return Object.prototype.toString.call(error);\n}\n\nfunction renderInsertedHtml(insertedElements: readonly unknown[]): string {\n let insertedHTML = \"\";\n\n for (const element of insertedElements) {\n try {\n insertedHTML += renderToStaticMarkup(\n createReactElement(Fragment, null, element as ReactNode),\n );\n } catch {\n // Ignore individual callback failures so the rest of the page can render.\n }\n }\n\n return insertedHTML;\n}\n\n/**\n * Render captured `<Script strategy=\"beforeInteractive\">` inline scripts to\n * HTML, ready to splice immediately after `<head ...>` opens. Each entry has\n * already had its inline content escaped via `escapeInlineContent(..., \"script\")`\n * inside the Script shim, so this function only quotes the attributes that\n * actually go on the tag (id, nonce, plus the residual passthroughs).\n *\n * Keeping this function colocated with the rest of the head-injection\n * helpers makes it obvious where the boundary is: anything passed through\n * here is being concatenated directly into HTML; treat the inputs\n * accordingly.\n */\n// Conservative subset of the HTML attribute-name grammar. Must start with a\n// letter and contain only letters, digits, underscores, hyphens, or dots —\n// enough to round-trip data-* and standard attributes (`async`, `defer`,\n// `type`, `crossorigin`, etc.) without ever splicing a `\"`/`>`/whitespace\n// into the unquoted *name* position where escaping wouldn't help.\nconst VALID_ATTR_NAME = /^[a-zA-Z][\\w.-]*$/;\n\nfunction renderBeforeInteractiveInlineScripts(\n scripts: readonly BeforeInteractiveInlineScript[],\n): string {\n if (scripts.length === 0) return \"\";\n let html = \"\";\n for (const script of scripts) {\n let attrs = \"\";\n if (script.id) {\n attrs += ` id=\"${escapeHtmlAttr(script.id)}\"`;\n }\n attrs += createNonceAttribute(script.nonce);\n if (script.attributes) {\n for (const [key, value] of Object.entries(script.attributes)) {\n // Attribute *values* go through escapeHtmlAttr below. The *name*\n // can't be escaped — a malformed key would break the tag — so we\n // gate at the boundary instead of trying to neutralise it.\n if (!VALID_ATTR_NAME.test(key)) continue;\n if (value === true) {\n attrs += ` ${key}`;\n } else if (typeof value === \"string\") {\n attrs += ` ${key}=\"${escapeHtmlAttr(value)}\"`;\n }\n }\n }\n html += `<script${attrs}>${script.innerHTML}</script>`;\n }\n return html;\n}\n\nfunction renderFontHtml(fontData?: FontData, nonce?: string): string {\n if (!fontData) return \"\";\n\n let fontHTML = \"\";\n const nonceAttr = createNonceAttribute(nonce);\n\n for (const url of fontData.links ?? []) {\n fontHTML += `<link rel=\"stylesheet\"${nonceAttr} href=\"${escapeHtmlAttr(url)}\" />\\n`;\n }\n\n for (const preload of fontData.preloads ?? []) {\n fontHTML += `<link rel=\"preload\"${nonceAttr} href=\"${escapeHtmlAttr(preload.href)}\" as=\"font\" type=\"${escapeHtmlAttr(preload.type)}\" crossorigin />\\n`;\n }\n\n if (fontData.styles && fontData.styles.length > 0) {\n fontHTML += `<style data-vinext-fonts${nonceAttr}>${fontData.styles.join(\"\\n\")}</style>\\n`;\n }\n\n return fontHTML;\n}\n\n/**\n * Extract the bootstrap module URL from the `import(\"...\")` string that\n * `import.meta.viteRsc.loadBootstrapScriptContent(\"index\")` returns.\n *\n * The plugin-rsc helper returns the bootstrap as an inline call so we can\n * inject it via `bootstrapScriptContent`. We instead pass the URL to\n * React's `bootstrapModules` option so a real\n * `<script type=\"module\" src=\"…\">` tag ends up in the streamed HTML —\n * this exposes the URL to anything that reads `script.attribs.src` (e.g.\n * the Next.js asset-prefix fixture test). The same URL also feeds the\n * `<link rel=\"modulepreload\">` we emit ahead of the bootstrap.\n *\n * Returns `undefined` when the helper produced no URL (older plugin-rsc\n * versions, or a custom client entry that disables bootstrap content).\n */\nfunction extractBootstrapModuleUrl(bootstrapScriptContent?: string): string | undefined {\n if (!bootstrapScriptContent) return undefined;\n // Accept either quote style — plugin-rsc currently emits double quotes\n // (`import(\"…\")`) but a future version could switch to single quotes,\n // and there's no public contract documenting which is used.\n const match = bootstrapScriptContent.match(/import\\([\"']([^\"']+)[\"']\\)/);\n return match?.[1] ?? undefined;\n}\n\nfunction buildModulePreloadHtml(bootstrapModuleUrl?: string, nonce?: string): string {\n if (!bootstrapModuleUrl) return \"\";\n return `<link rel=\"modulepreload\"${createNonceAttribute(nonce)} href=\"${escapeHtmlAttr(bootstrapModuleUrl)}\" />\\n`;\n}\n\nfunction buildHeadInjectionHtml(\n navContext: NavigationContext | null,\n bootstrapModuleUrl: string | undefined,\n formState: ReactFormState | null,\n insertedHTML: string,\n fontHTML: string,\n scriptNonce?: string,\n): string {\n const navPayload = {\n pathname: navContext?.pathname ?? \"/\",\n searchParams: navContext?.searchParams ? [...navContext.searchParams.entries()] : [],\n };\n const rscMetadataScript = createInlineScriptTag(\n createNavigationRuntimeRscMetadataScript(navContext?.params ?? {}, navPayload),\n scriptNonce,\n );\n const formStateScript =\n formState === null\n ? \"\"\n : createInlineScriptTag(\n \"self[\" + safeJsonStringify(RSC_FORM_STATE_GLOBAL) + \"]=\" + safeJsonStringify(formState),\n scriptNonce,\n );\n\n return (\n rscMetadataScript +\n formStateScript +\n buildModulePreloadHtml(bootstrapModuleUrl, scriptNonce) +\n insertedHTML +\n fontHTML\n );\n}\n\nexport async function handleSsr(\n rscStream: ReadableStream<Uint8Array>,\n navContext: NavigationContext | null,\n fontData?: FontData,\n options?: {\n scriptNonce?: string;\n /** Pre-split side stream for embed+capture fusion. When provided,\n * rscStream is fed directly to createFromReadableStream (no internal tee).\n * The embed transform accumulates raw bytes. */\n sideStream?: ReadableStream<Uint8Array>;\n /** Out-parameter: filled with accumulated raw RSC bytes when sideStream is consumed. */\n capturedRscDataRef?: { value: Promise<ArrayBuffer> | null };\n formState?: ReactFormState | null;\n basePath?: string;\n rootParams?: RootParams;\n /** When true, wait for the full React tree (including Suspense boundaries)\n * to resolve before returning the HTML stream. Used for static prerender\n * and ISR cache writes to avoid caching fallback content. */\n waitForAllReady?: boolean;\n },\n): Promise<ReadableStream<Uint8Array>> {\n return runWithNavigationContext(async () => {\n await clientReferencePreloader.preload();\n\n if (navContext) {\n setNavigationContext(navContext);\n }\n\n clearServerInsertedHTML();\n\n const cleanup = (): void => {\n setNavigationContext(null);\n clearServerInsertedHTML();\n };\n\n const rootParams = options?.rootParams ?? {};\n return runWithRootParamsScope(rootParams, async () => {\n try {\n // Fused tee path (#981): caller pre-split the stream. No internal tee needed.\n // sideStream carries both the embed transform and raw byte accumulation.\n // rscStream is used directly for createFromReadableStream (SSR).\n let ssrStream: ReadableStream<Uint8Array>;\n let rscEmbed;\n\n if (options?.sideStream) {\n ssrStream = rscStream;\n rscEmbed = createRscEmbedTransform(options.sideStream, options?.scriptNonce);\n if (options.capturedRscDataRef) {\n options.capturedRscDataRef.value = rscEmbed.getRawBuffer();\n }\n } else {\n const [s1, s2] = rscStream.tee();\n ssrStream = s1;\n rscEmbed = createRscEmbedTransform(s2, options?.scriptNonce);\n }\n\n let flightRoot: PromiseLike<AppWireElements> | null = null;\n\n function VinextFlightRoot(): ReactNode {\n if (!flightRoot) {\n flightRoot = createFromReadableStream<AppWireElements>(ssrStream);\n }\n const wireElements = use(flightRoot);\n const elements = AppElementsWire.decode(wireElements);\n const metadata = AppElementsWire.readMetadata(elements);\n return createReactElement(\n ElementsContext.Provider,\n { value: elements },\n createReactElement(Slot, { id: metadata.routeId }),\n );\n }\n\n const flightRootElement = createReactElement(VinextFlightRoot);\n const root = AppRouterContext\n ? createReactElement(\n AppRouterContext.Provider,\n { value: appRouterInstance },\n flightRootElement,\n )\n : flightRootElement;\n const ssrTree = ServerInsertedHTMLContext\n ? createReactElement(\n ServerInsertedHTMLContext.Provider,\n { value: useServerInsertedHTML },\n root,\n )\n : root;\n\n // Capture inline `<Script strategy=\"beforeInteractive\">` content so the\n // SSR stream transform can emit it immediately after `<head ...>`\n // opens — ahead of every React-emitted resource hint. The Script shim\n // pushes here when it sees an inline beforeInteractive Script and\n // returns `null` from its render so React does not also serialize the\n // tag where the user wrote it (where Fizz would push it *after* the\n // hoisted stylesheets/modulepreloads). See\n // packages/vinext/src/shims/script.tsx for the capture side.\n const beforeInteractiveInlineScripts: BeforeInteractiveInlineScript[] = [];\n const registerBeforeInteractiveInlineScript = (\n script: BeforeInteractiveInlineScript,\n ): void => {\n beforeInteractiveInlineScripts.push(script);\n };\n const treeWithBeforeInteractive = createReactElement(\n BeforeInteractiveContext.Provider,\n { value: registerBeforeInteractiveInlineScript },\n ssrTree,\n );\n const ssrRoot = withScriptNonce(treeWithBeforeInteractive, options?.scriptNonce);\n\n // plugin-rsc returns the bootstrap as `import(\"<url>\")` so callers can\n // inject it via `bootstrapScriptContent`. We hand the URL to React's\n // `bootstrapModules` option instead so the streamed HTML contains a\n // real `<script type=\"module\" src=\"<url>\">` tag — exposing the URL\n // to anything that inspects `script.attribs.src` (e.g. the Next.js\n // asset-prefix fixture test \"bundles should return 200 on served\n // assetPrefix\"). Mirrors Next.js's app-render path which passes\n // `bootstrapScripts: [{ src }]` for the same reason; we use\n // `bootstrapModules` because vinext's chunks are native ES modules\n // (Vite output) so a `type=\"module\"` tag is the correct loader.\n //\n // In dev, `<url>` is a Vite dev URL like\n // `/@id/__x00__virtual:vinext-app-browser-entry`; the browser fetches\n // it as a module from the dev server. In prod it's the hashed bundle\n // URL (e.g. `/_next/static/index-abc123.js`, optionally prefixed by\n // `assetPrefix`). Both are valid `<script type=\"module\" src=…>` targets.\n const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent(\n \"index\",\n );\n const bootstrapModuleUrl = extractBootstrapModuleUrl(bootstrapScriptContent);\n const errorMetaRenderer = createSsrErrorMetaRenderer({\n basePath: options?.basePath,\n });\n\n const htmlStream = await renderToReadableStream(ssrRoot, {\n // `bootstrapScriptContent` was previously how vinext injected the\n // dynamic-import call. `bootstrapModules` performs the same work\n // natively (and exposes the URL in the DOM), so passing both would\n // load the bootstrap module twice.\n //\n // CSP implications of using `bootstrapModules` instead of inline\n // `bootstrapScriptContent`:\n // - Apps no longer need `script-src 'unsafe-inline'` to load the\n // bootstrap (improvement — inline imports required `'unsafe-inline'`).\n // - Apps that restrict script sources need `'self'` for the\n // common case, or the CDN origin when `assetPrefix` is an\n // absolute URL like `https://cdn.example.com`.\n // - React still applies `nonce` to the emitted\n // `<script type=\"module\" src=…>` tag, so nonce-based CSP\n // (`script-src 'nonce-…' 'strict-dynamic'`) keeps working.\n bootstrapModules: bootstrapModuleUrl ? [bootstrapModuleUrl] : undefined,\n formState: options?.formState ?? null,\n nonce: options?.scriptNonce,\n onError(error) {\n errorMetaRenderer.capture(error);\n\n if (error && typeof error === \"object\" && \"digest\" in error) {\n return String(error.digest);\n }\n\n if (process.env.NODE_ENV === \"production\" && error) {\n const message = getErrorMessage(error);\n const stack = error instanceof Error ? (error.stack ?? \"\") : \"\";\n return ssrErrorDigest(message + stack);\n }\n\n return undefined;\n },\n });\n\n // When producing static output (prerender / ISR cache writes), wait for\n // the full React tree to resolve before emitting bytes. This prevents\n // Suspense fallback content from being serialized to the cache.\n // Matches Next.js waitForAllReady forkpoint in renderToNodeFizzStream.\n if (options?.waitForAllReady === true) {\n await htmlStream.allReady;\n }\n\n const fontHTML = renderFontHtml(fontData, options?.scriptNonce);\n let didInjectHeadHTML = false;\n const getInsertedHTML = (): string => {\n const insertedHTML = renderInsertedHtml(renderServerInsertedHTML());\n const errorMetaHTML = errorMetaRenderer.flush();\n if (didInjectHeadHTML) return insertedHTML + errorMetaHTML;\n\n didInjectHeadHTML = true;\n return buildHeadInjectionHtml(\n navContext,\n bootstrapModuleUrl,\n options?.formState ?? null,\n insertedHTML + errorMetaHTML,\n fontHTML,\n options?.scriptNonce,\n );\n };\n\n // The transform calls this once when it splices after `<head ...>`.\n // By that point React Fizz has rendered the layout's `<head>` children\n // (which is where the Script shim registers), so the captured array is\n // populated. We deliberately return a snapshot — `flushBuffered` will\n // not re-invoke us, and any beforeInteractive Script that renders\n // later (inside a Suspense boundary further down the tree) falls back\n // to its inline location, matching the documented guarantee that\n // ordering applies to scripts rendered in the initial shell.\n const getBeforeInteractiveHeadHTML = (): string =>\n renderBeforeInteractiveInlineScripts(beforeInteractiveInlineScripts);\n\n return deferUntilStreamConsumed(\n htmlStream.pipeThrough(\n createTickBufferedTransform(rscEmbed, getInsertedHTML, getBeforeInteractiveHeadHTML),\n ),\n cleanup,\n );\n } catch (error) {\n cleanup();\n throw error;\n }\n });\n }) as Promise<ReadableStream<Uint8Array>>;\n}\n\nexport default {\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n // Block protocol-relative URL open redirects (including percent-encoded\n // variants like /%5Cevil.com/). See request-pipeline.ts for details.\n if (isOpenRedirectShaped(url.pathname)) {\n return notFoundResponse();\n }\n\n const rscModule = await import.meta.viteRsc.loadModule<{\n default(request: Request): Promise<Response | string | null | undefined>;\n }>(\"rsc\", \"index\");\n const result = await rscModule.default(request);\n\n if (result instanceof Response) {\n return result;\n }\n\n if (result == null) {\n return notFoundResponse();\n }\n\n return new Response(String(result), { status: 200 });\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-ssr-entry.js","names":["createReactElement"],"sources":["../../src/server/app-ssr-entry.ts"],"sourcesContent":["/// <reference types=\"@vitejs/plugin-rsc/types\" />\n\nimport \"./server-globals.js\";\nimport type { ReactNode } from \"react\";\nimport type { ReactFormState } from \"react-dom/client\";\nimport { Fragment, createElement as createReactElement, use } from \"react\";\nimport { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { renderToReadableStream, renderToStaticMarkup } from \"react-dom/server.edge\";\nimport clientReferences from \"virtual:vite-rsc/client-references\";\nimport type { NavigationContext } from \"vinext/shims/navigation\";\nimport {\n ServerInsertedHTMLContext,\n appRouterInstance,\n clearServerInsertedHTML,\n renderServerInsertedHTML,\n setNavigationContext,\n useServerInsertedHTML,\n} from \"vinext/shims/navigation\";\nimport { runWithNavigationContext } from \"vinext/shims/navigation-state\";\nimport { runWithRootParamsScope, type RootParams } from \"vinext/shims/root-params\";\nimport { isOpenRedirectShaped } from \"./request-pipeline.js\";\nimport { notFoundResponse } from \"./http-error-responses.js\";\nimport { withScriptNonce } from \"vinext/shims/script-nonce-context\";\nimport {\n BeforeInteractiveContext,\n type BeforeInteractiveInlineScript,\n} from \"vinext/shims/before-interactive-context\";\nimport {\n createInlineScriptTag,\n createNonceAttribute,\n escapeHtmlAttr,\n safeJsonStringify,\n} from \"./html.js\";\nimport {\n createNavigationRuntimeRscMetadataScript,\n createRscEmbedTransform,\n createTickBufferedTransform,\n} from \"./app-ssr-stream.js\";\nimport { deferUntilStreamConsumed } from \"./app-page-stream.js\";\nimport { createSsrErrorMetaRenderer } from \"./app-ssr-error-meta.js\";\nimport { getClientTraceMetadataHTML } from \"./client-trace-metadata.js\";\nimport { AppElementsWire, type AppWireElements } from \"./app-elements.js\";\nimport { ElementsContext, Slot } from \"vinext/shims/slot\";\nimport { AppRouterContext } from \"vinext/shims/internal/app-router-context\";\nimport { createClientReferencePreloader } from \"./app-client-reference-preloader.js\";\nimport { RSC_FORM_STATE_GLOBAL } from \"./app-browser-hydration.js\";\n\nexport type FontPreload = {\n href: string;\n type: string;\n};\n\nexport type FontData = {\n links?: string[];\n styles?: string[];\n preloads?: FontPreload[];\n};\n\nconst clientReferencePreloader = createClientReferencePreloader({\n getReferences() {\n return clientReferences;\n },\n getClientRequire() {\n return globalThis.__vite_rsc_client_require__;\n },\n onPreloadError(id, error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] failed to preload client ref:\", id, error);\n }\n },\n});\n\nfunction ssrErrorDigest(input: string): string {\n let hash = 5381;\n for (let i = input.length - 1; i >= 0; i--) {\n hash = (hash * 33) ^ input.charCodeAt(i);\n }\n return (hash >>> 0).toString();\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === \"string\") return error;\n return Object.prototype.toString.call(error);\n}\n\nfunction renderInsertedHtml(insertedElements: readonly unknown[]): string {\n let insertedHTML = \"\";\n\n for (const element of insertedElements) {\n try {\n insertedHTML += renderToStaticMarkup(\n createReactElement(Fragment, null, element as ReactNode),\n );\n } catch {\n // Ignore individual callback failures so the rest of the page can render.\n }\n }\n\n return insertedHTML;\n}\n\n/**\n * Render captured `<Script strategy=\"beforeInteractive\">` inline scripts to\n * HTML, ready to splice immediately after `<head ...>` opens. Each entry has\n * already had its inline content escaped via `escapeInlineContent(..., \"script\")`\n * inside the Script shim, so this function only quotes the attributes that\n * actually go on the tag (id, nonce, plus the residual passthroughs).\n *\n * Keeping this function colocated with the rest of the head-injection\n * helpers makes it obvious where the boundary is: anything passed through\n * here is being concatenated directly into HTML; treat the inputs\n * accordingly.\n */\n// Conservative subset of the HTML attribute-name grammar. Must start with a\n// letter and contain only letters, digits, underscores, hyphens, or dots —\n// enough to round-trip data-* and standard attributes (`async`, `defer`,\n// `type`, `crossorigin`, etc.) without ever splicing a `\"`/`>`/whitespace\n// into the unquoted *name* position where escaping wouldn't help.\nconst VALID_ATTR_NAME = /^[a-zA-Z][\\w.-]*$/;\n\nfunction renderBeforeInteractiveInlineScripts(\n scripts: readonly BeforeInteractiveInlineScript[],\n): string {\n if (scripts.length === 0) return \"\";\n let html = \"\";\n for (const script of scripts) {\n let attrs = \"\";\n if (script.id) {\n attrs += ` id=\"${escapeHtmlAttr(script.id)}\"`;\n }\n attrs += createNonceAttribute(script.nonce);\n if (script.attributes) {\n for (const [key, value] of Object.entries(script.attributes)) {\n // Attribute *values* go through escapeHtmlAttr below. The *name*\n // can't be escaped — a malformed key would break the tag — so we\n // gate at the boundary instead of trying to neutralise it.\n if (!VALID_ATTR_NAME.test(key)) continue;\n if (value === true) {\n attrs += ` ${key}`;\n } else if (typeof value === \"string\") {\n attrs += ` ${key}=\"${escapeHtmlAttr(value)}\"`;\n }\n }\n }\n html += `<script${attrs}>${script.innerHTML}</script>`;\n }\n return html;\n}\n\nfunction renderFontHtml(\n fontData?: FontData,\n nonce?: string,\n options: { includeStyles?: boolean } = {},\n): string {\n if (!fontData) return \"\";\n\n let fontHTML = \"\";\n const nonceAttr = createNonceAttribute(nonce);\n const includeStyles = options.includeStyles ?? true;\n\n for (const url of fontData.links ?? []) {\n fontHTML += `<link rel=\"stylesheet\"${nonceAttr} href=\"${escapeHtmlAttr(url)}\" />\\n`;\n }\n\n for (const preload of fontData.preloads ?? []) {\n fontHTML += `<link rel=\"preload\"${nonceAttr} href=\"${escapeHtmlAttr(preload.href)}\" as=\"font\" type=\"${escapeHtmlAttr(preload.type)}\" crossorigin />\\n`;\n }\n\n if (includeStyles && fontData.styles && fontData.styles.length > 0) {\n fontHTML += `<style data-vinext-fonts${nonceAttr}>${fontData.styles.join(\"\\n\")}</style>\\n`;\n }\n\n return fontHTML;\n}\n\nfunction hasInlineCssManifest(manifest: Record<string, string> | undefined): boolean {\n return manifest !== undefined && Object.keys(manifest).length > 0;\n}\n\n/**\n * Extract the bootstrap module URL from the `import(\"...\")` string that\n * `import.meta.viteRsc.loadBootstrapScriptContent(\"index\")` returns.\n *\n * The plugin-rsc helper returns the bootstrap as an inline call so we can\n * inject it via `bootstrapScriptContent`. We instead pass the URL to\n * React's `bootstrapModules` option so a real\n * `<script type=\"module\" src=\"…\">` tag ends up in the streamed HTML —\n * this exposes the URL to anything that reads `script.attribs.src` (e.g.\n * the Next.js asset-prefix fixture test). The same URL also feeds the\n * `<link rel=\"modulepreload\">` we emit ahead of the bootstrap.\n *\n * Returns `undefined` when the helper produced no URL (older plugin-rsc\n * versions, or a custom client entry that disables bootstrap content).\n */\nfunction extractBootstrapModuleUrl(bootstrapScriptContent?: string): string | undefined {\n if (!bootstrapScriptContent) return undefined;\n // Accept either quote style — plugin-rsc currently emits double quotes\n // (`import(\"…\")`) but a future version could switch to single quotes,\n // and there's no public contract documenting which is used.\n const match = bootstrapScriptContent.match(/import\\([\"']([^\"']+)[\"']\\)/);\n return match?.[1] ?? undefined;\n}\n\nfunction buildModulePreloadHtml(bootstrapModuleUrl?: string, nonce?: string): string {\n if (!bootstrapModuleUrl) return \"\";\n return `<link rel=\"modulepreload\"${createNonceAttribute(nonce)} href=\"${escapeHtmlAttr(bootstrapModuleUrl)}\" />\\n`;\n}\n\nfunction buildHeadInjectionHtml(\n navContext: NavigationContext | null,\n bootstrapModuleUrl: string | undefined,\n formState: ReactFormState | null,\n insertedHTML: string,\n fontHTML: string,\n scriptNonce?: string,\n): string {\n const navPayload = {\n pathname: navContext?.pathname ?? \"/\",\n searchParams: navContext?.searchParams ? [...navContext.searchParams.entries()] : [],\n };\n const rscMetadataScript = createInlineScriptTag(\n createNavigationRuntimeRscMetadataScript(navContext?.params ?? {}, navPayload),\n scriptNonce,\n );\n const formStateScript =\n formState === null\n ? \"\"\n : createInlineScriptTag(\n \"self[\" + safeJsonStringify(RSC_FORM_STATE_GLOBAL) + \"]=\" + safeJsonStringify(formState),\n scriptNonce,\n );\n\n return (\n rscMetadataScript +\n formStateScript +\n buildModulePreloadHtml(bootstrapModuleUrl, scriptNonce) +\n insertedHTML +\n fontHTML\n );\n}\n\nexport async function handleSsr(\n rscStream: ReadableStream<Uint8Array>,\n navContext: NavigationContext | null,\n fontData?: FontData,\n options?: {\n scriptNonce?: string;\n /** Pre-split side stream for embed+capture fusion. When provided,\n * rscStream is fed directly to createFromReadableStream (no internal tee).\n * The embed transform accumulates raw bytes. */\n sideStream?: ReadableStream<Uint8Array>;\n /** Out-parameter: filled with accumulated raw RSC bytes when sideStream is consumed. */\n capturedRscDataRef?: { value: Promise<ArrayBuffer> | null };\n formState?: ReactFormState | null;\n basePath?: string;\n /**\n * Allow-list of OpenTelemetry propagation keys (from\n * `experimental.clientTraceMetadata`) to render as `<meta>` tags in the\n * SSR head. Undefined or empty disables emission entirely.\n */\n clientTraceMetadata?: readonly string[];\n rootParams?: RootParams;\n /** When true, wait for the full React tree (including Suspense boundaries)\n * to resolve before returning the HTML stream. Used for static prerender\n * and ISR cache writes to avoid caching fallback content. */\n waitForAllReady?: boolean;\n },\n): Promise<ReadableStream<Uint8Array>> {\n return runWithNavigationContext(async () => {\n await clientReferencePreloader.preload();\n\n if (navContext) {\n setNavigationContext(navContext);\n }\n\n clearServerInsertedHTML();\n\n const cleanup = (): void => {\n setNavigationContext(null);\n clearServerInsertedHTML();\n };\n\n const rootParams = options?.rootParams ?? {};\n return runWithRootParamsScope(rootParams, async () => {\n try {\n // Fused tee path (#981): caller pre-split the stream. No internal tee needed.\n // sideStream carries both the embed transform and raw byte accumulation.\n // rscStream is used directly for createFromReadableStream (SSR).\n let ssrStream: ReadableStream<Uint8Array>;\n let rscEmbed;\n\n if (options?.sideStream) {\n ssrStream = rscStream;\n rscEmbed = createRscEmbedTransform(options.sideStream, options?.scriptNonce);\n if (options.capturedRscDataRef) {\n options.capturedRscDataRef.value = rscEmbed.getRawBuffer();\n }\n } else {\n const [s1, s2] = rscStream.tee();\n ssrStream = s1;\n rscEmbed = createRscEmbedTransform(s2, options?.scriptNonce);\n }\n\n let flightRoot: PromiseLike<AppWireElements> | null = null;\n\n function VinextFlightRoot(): ReactNode {\n if (!flightRoot) {\n flightRoot = createFromReadableStream<AppWireElements>(ssrStream);\n }\n const wireElements = use(flightRoot);\n const elements = AppElementsWire.decode(wireElements);\n const metadata = AppElementsWire.readMetadata(elements);\n return createReactElement(\n ElementsContext.Provider,\n { value: elements },\n createReactElement(Slot, { id: metadata.routeId }),\n );\n }\n\n const flightRootElement = createReactElement(VinextFlightRoot);\n const root = AppRouterContext\n ? createReactElement(\n AppRouterContext.Provider,\n { value: appRouterInstance },\n flightRootElement,\n )\n : flightRootElement;\n const ssrTree = ServerInsertedHTMLContext\n ? createReactElement(\n ServerInsertedHTMLContext.Provider,\n { value: useServerInsertedHTML },\n root,\n )\n : root;\n\n // Capture inline `<Script strategy=\"beforeInteractive\">` content so the\n // SSR stream transform can emit it immediately after `<head ...>`\n // opens — ahead of every React-emitted resource hint. The Script shim\n // pushes here when it sees an inline beforeInteractive Script and\n // returns `null` from its render so React does not also serialize the\n // tag where the user wrote it (where Fizz would push it *after* the\n // hoisted stylesheets/modulepreloads). See\n // packages/vinext/src/shims/script.tsx for the capture side.\n const beforeInteractiveInlineScripts: BeforeInteractiveInlineScript[] = [];\n const registerBeforeInteractiveInlineScript = (\n script: BeforeInteractiveInlineScript,\n ): void => {\n beforeInteractiveInlineScripts.push(script);\n };\n const treeWithBeforeInteractive = createReactElement(\n BeforeInteractiveContext.Provider,\n { value: registerBeforeInteractiveInlineScript },\n ssrTree,\n );\n const ssrRoot = withScriptNonce(treeWithBeforeInteractive, options?.scriptNonce);\n\n // plugin-rsc returns the bootstrap as `import(\"<url>\")` so callers can\n // inject it via `bootstrapScriptContent`. We hand the URL to React's\n // `bootstrapModules` option instead so the streamed HTML contains a\n // real `<script type=\"module\" src=\"<url>\">` tag — exposing the URL\n // to anything that inspects `script.attribs.src` (e.g. the Next.js\n // asset-prefix fixture test \"bundles should return 200 on served\n // assetPrefix\"). Mirrors Next.js's app-render path which passes\n // `bootstrapScripts: [{ src }]` for the same reason; we use\n // `bootstrapModules` because vinext's chunks are native ES modules\n // (Vite output) so a `type=\"module\"` tag is the correct loader.\n //\n // In dev, `<url>` is a Vite dev URL like\n // `/@id/__x00__virtual:vinext-app-browser-entry`; the browser fetches\n // it as a module from the dev server. In prod it's the hashed bundle\n // URL (e.g. `/_next/static/index-abc123.js`, optionally prefixed by\n // `assetPrefix`). Both are valid `<script type=\"module\" src=…>` targets.\n const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent(\n \"index\",\n );\n const bootstrapModuleUrl = extractBootstrapModuleUrl(bootstrapScriptContent);\n const errorMetaRenderer = createSsrErrorMetaRenderer({\n basePath: options?.basePath,\n });\n\n const htmlStream = await renderToReadableStream(ssrRoot, {\n // `bootstrapScriptContent` was previously how vinext injected the\n // dynamic-import call. `bootstrapModules` performs the same work\n // natively (and exposes the URL in the DOM), so passing both would\n // load the bootstrap module twice.\n //\n // CSP implications of using `bootstrapModules` instead of inline\n // `bootstrapScriptContent`:\n // - Apps no longer need `script-src 'unsafe-inline'` to load the\n // bootstrap (improvement — inline imports required `'unsafe-inline'`).\n // - Apps that restrict script sources need `'self'` for the\n // common case, or the CDN origin when `assetPrefix` is an\n // absolute URL like `https://cdn.example.com`.\n // - React still applies `nonce` to the emitted\n // `<script type=\"module\" src=…>` tag, so nonce-based CSP\n // (`script-src 'nonce-…' 'strict-dynamic'`) keeps working.\n bootstrapModules: bootstrapModuleUrl ? [bootstrapModuleUrl] : undefined,\n formState: options?.formState ?? null,\n nonce: options?.scriptNonce,\n onError(error) {\n errorMetaRenderer.capture(error);\n\n if (error && typeof error === \"object\" && \"digest\" in error) {\n return String(error.digest);\n }\n\n if (process.env.NODE_ENV === \"production\" && error) {\n const message = getErrorMessage(error);\n const stack = error instanceof Error ? (error.stack ?? \"\") : \"\";\n return ssrErrorDigest(message + stack);\n }\n\n return undefined;\n },\n });\n\n // When producing static output (prerender / ISR cache writes), wait for\n // the full React tree to resolve before emitting bytes. This prevents\n // Suspense fallback content from being serialized to the cache.\n // Matches Next.js waitForAllReady forkpoint in renderToNodeFizzStream.\n if (options?.waitForAllReady === true) {\n await htmlStream.allReady;\n }\n\n // Populated before any SSR request runs: at prod-server startup\n // (prod-server.ts) or via build-time bundle injection (index.ts). Left\n // undefined in dev, which naturally disables inline CSS there.\n const inlineCssManifest = globalThis.__VINEXT_INLINE_CSS__;\n const fontStyles = fontData?.styles ?? [];\n const mergeFontStylesIntoInlineCss =\n fontStyles.length > 0 && hasInlineCssManifest(inlineCssManifest);\n const inlineCssFontStyles = mergeFontStylesIntoInlineCss ? fontStyles.join(\"\\n\") : \"\";\n const inlineCssFontStyleFallbackHTML = mergeFontStylesIntoInlineCss\n ? renderFontHtml({ styles: fontStyles }, options?.scriptNonce)\n : \"\";\n const fontHTML = renderFontHtml(fontData, options?.scriptNonce, {\n includeStyles: !mergeFontStylesIntoInlineCss,\n });\n // Trace meta tags only need to land in the document head once.\n // Read the active OTel context lazily so the value reflects the\n // span that was active when the SSR shell rendered. When\n // clientTraceMetadata is unset (the common case) this is empty.\n let traceMetaHTML: string | null = null;\n const getTraceMetaHTML = (): string => {\n if (traceMetaHTML === null) {\n traceMetaHTML = getClientTraceMetadataHTML(options?.clientTraceMetadata);\n }\n return traceMetaHTML;\n };\n let didInjectHeadHTML = false;\n const getInsertedHTML = (): string => {\n const insertedHTML = renderInsertedHtml(renderServerInsertedHTML());\n const errorMetaHTML = errorMetaRenderer.flush();\n if (didInjectHeadHTML) return insertedHTML + errorMetaHTML;\n\n didInjectHeadHTML = true;\n return buildHeadInjectionHtml(\n navContext,\n bootstrapModuleUrl,\n options?.formState ?? null,\n insertedHTML + errorMetaHTML + getTraceMetaHTML(),\n fontHTML,\n options?.scriptNonce,\n );\n };\n\n // The transform calls this once when it splices after `<head ...>`.\n // By that point React Fizz has rendered the layout's `<head>` children\n // (which is where the Script shim registers), so the captured array is\n // populated. We deliberately return a snapshot — `flushBuffered` will\n // not re-invoke us, and any beforeInteractive Script that renders\n // later (inside a Suspense boundary further down the tree) falls back\n // to its inline location, matching the documented guarantee that\n // ordering applies to scripts rendered in the initial shell.\n const getBeforeInteractiveHeadHTML = (): string =>\n renderBeforeInteractiveInlineScripts(beforeInteractiveInlineScripts);\n\n return deferUntilStreamConsumed(\n htmlStream.pipeThrough(\n createTickBufferedTransform(\n rscEmbed,\n getInsertedHTML,\n getBeforeInteractiveHeadHTML,\n inlineCssManifest,\n inlineCssFontStyles,\n inlineCssFontStyleFallbackHTML,\n options?.scriptNonce,\n ),\n ),\n cleanup,\n );\n } catch (error) {\n cleanup();\n throw error;\n }\n });\n }) as Promise<ReadableStream<Uint8Array>>;\n}\n\nexport default {\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n // Block protocol-relative URL open redirects (including percent-encoded\n // variants like /%5Cevil.com/). See request-pipeline.ts for details.\n if (isOpenRedirectShaped(url.pathname)) {\n return notFoundResponse();\n }\n\n const rscModule = await import.meta.viteRsc.loadModule<{\n default(request: Request): Promise<Response | string | null | undefined>;\n }>(\"rsc\", \"index\");\n const result = await rscModule.default(request);\n\n if (result instanceof Response) {\n return result;\n }\n\n if (result == null) {\n return notFoundResponse();\n }\n\n return new Response(String(result), { status: 200 });\n },\n};\n"],"mappings":"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"}
|
|
@@ -14,15 +14,15 @@ function renderSsrErrorMetaTag(error, options) {
|
|
|
14
14
|
const digest = getNextErrorDigest(error);
|
|
15
15
|
if (!digest) return "";
|
|
16
16
|
if (parseNextHttpErrorDigest(digest)) {
|
|
17
|
-
let html = "<meta name=\"robots\" content=\"noindex\"
|
|
18
|
-
if ((options.nodeEnv ?? process.env.NODE_ENV) === "development") html += "<meta name=\"next-error\" content=\"not-found\"
|
|
17
|
+
let html = "<meta name=\"robots\" content=\"noindex\"/>";
|
|
18
|
+
if ((options.nodeEnv ?? process.env.NODE_ENV) === "development") html += "<meta name=\"next-error\" content=\"not-found\"/>";
|
|
19
19
|
return html;
|
|
20
20
|
}
|
|
21
21
|
const redirect = parseNextRedirectDigest(digest);
|
|
22
22
|
if (!redirect) return "";
|
|
23
23
|
const delay = redirect.status === PERMANENT_REDIRECT_STATUS ? 0 : 1;
|
|
24
24
|
const location = prefixRedirectLocation(redirect.url, options.basePath);
|
|
25
|
-
return "<meta id=\"__next-page-redirect\" http-equiv=\"refresh\" content=\"" + delay + ";url=" + escapeHtmlAttr(location) + "\"
|
|
25
|
+
return "<meta id=\"__next-page-redirect\" http-equiv=\"refresh\" content=\"" + delay + ";url=" + escapeHtmlAttr(location) + "\"/>";
|
|
26
26
|
}
|
|
27
27
|
function renderSsrErrorMetaTags(errors, options = {}) {
|
|
28
28
|
let html = "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-ssr-error-meta.js","names":[],"sources":["../../src/server/app-ssr-error-meta.ts"],"sourcesContent":["import { addBasePathToPathname } from \"../utils/base-path.js\";\nimport { escapeHtmlAttr } from \"./html.js\";\nimport {\n getNextErrorDigest,\n parseNextHttpErrorDigest,\n parseNextRedirectDigest,\n} from \"./next-error-digest.js\";\n\ntype SsrErrorMetaRenderOptions = {\n basePath?: string;\n nodeEnv?: string;\n};\n\ntype SsrErrorMetaRenderer = {\n capture: (error: unknown) => void;\n flush: () => string;\n};\n\nconst PERMANENT_REDIRECT_STATUS = 308;\n\nfunction prefixRedirectLocation(location: string, basePath?: string): string {\n if (!basePath || !location.startsWith(\"/\")) {\n return location;\n }\n\n const hashIndex = location.indexOf(\"#\");\n const queryIndex = location.indexOf(\"?\");\n const pathnameEnd =\n queryIndex === -1\n ? hashIndex === -1\n ? location.length\n : hashIndex\n : hashIndex === -1\n ? queryIndex\n : Math.min(queryIndex, hashIndex);\n const pathname = location.slice(0, pathnameEnd);\n\n return addBasePathToPathname(pathname, basePath) + location.slice(pathnameEnd);\n}\n\nfunction renderSsrErrorMetaTag(error: unknown, options: SsrErrorMetaRenderOptions): string {\n const digest = getNextErrorDigest(error);\n if (!digest) return \"\";\n\n const httpError = parseNextHttpErrorDigest(digest);\n if (httpError) {\n let html = '<meta name=\"robots\" content=\"noindex\"
|
|
1
|
+
{"version":3,"file":"app-ssr-error-meta.js","names":[],"sources":["../../src/server/app-ssr-error-meta.ts"],"sourcesContent":["import { addBasePathToPathname } from \"../utils/base-path.js\";\nimport { escapeHtmlAttr } from \"./html.js\";\nimport {\n getNextErrorDigest,\n parseNextHttpErrorDigest,\n parseNextRedirectDigest,\n} from \"./next-error-digest.js\";\n\ntype SsrErrorMetaRenderOptions = {\n basePath?: string;\n nodeEnv?: string;\n};\n\ntype SsrErrorMetaRenderer = {\n capture: (error: unknown) => void;\n flush: () => string;\n};\n\nconst PERMANENT_REDIRECT_STATUS = 308;\n\nfunction prefixRedirectLocation(location: string, basePath?: string): string {\n if (!basePath || !location.startsWith(\"/\")) {\n return location;\n }\n\n const hashIndex = location.indexOf(\"#\");\n const queryIndex = location.indexOf(\"?\");\n const pathnameEnd =\n queryIndex === -1\n ? hashIndex === -1\n ? location.length\n : hashIndex\n : hashIndex === -1\n ? queryIndex\n : Math.min(queryIndex, hashIndex);\n const pathname = location.slice(0, pathnameEnd);\n\n return addBasePathToPathname(pathname, basePath) + location.slice(pathnameEnd);\n}\n\nfunction renderSsrErrorMetaTag(error: unknown, options: SsrErrorMetaRenderOptions): string {\n const digest = getNextErrorDigest(error);\n if (!digest) return \"\";\n\n const httpError = parseNextHttpErrorDigest(digest);\n if (httpError) {\n // Output format matches Next.js's `make-get-server-inserted-html.tsx`,\n // which serializes these meta tags via React's HTML renderer. React's\n // void-element output uses no space before `/>`, and Next.js tests assert\n // on that exact substring (e.g. `'<meta name=\"robots\" content=\"noindex\"/>'`).\n // https://github.com/vercel/next.js/blob/canary/packages/next/src/server/app-render/make-get-server-inserted-html.tsx\n let html = '<meta name=\"robots\" content=\"noindex\"/>';\n if ((options.nodeEnv ?? process.env.NODE_ENV) === \"development\") {\n html += '<meta name=\"next-error\" content=\"not-found\"/>';\n }\n return html;\n }\n\n const redirect = parseNextRedirectDigest(digest);\n if (!redirect) return \"\";\n\n const delay = redirect.status === PERMANENT_REDIRECT_STATUS ? 0 : 1;\n const location = prefixRedirectLocation(redirect.url, options.basePath);\n return (\n '<meta id=\"__next-page-redirect\" http-equiv=\"refresh\" content=\"' +\n delay +\n \";url=\" +\n escapeHtmlAttr(location) +\n '\"/>'\n );\n}\n\nexport function renderSsrErrorMetaTags(\n errors: readonly unknown[],\n options: SsrErrorMetaRenderOptions = {},\n): string {\n let html = \"\";\n\n for (const error of errors) {\n html += renderSsrErrorMetaTag(error, options);\n }\n\n return html;\n}\n\nexport function createSsrErrorMetaRenderer(\n options: SsrErrorMetaRenderOptions = {},\n): SsrErrorMetaRenderer {\n const capturedErrors: unknown[] = [];\n let flushedUntil = 0;\n\n return {\n capture(error) {\n capturedErrors.push(error);\n },\n flush() {\n if (flushedUntil >= capturedErrors.length) return \"\";\n\n const html = renderSsrErrorMetaTags(capturedErrors.slice(flushedUntil), options);\n flushedUntil = capturedErrors.length;\n return html;\n },\n };\n}\n"],"mappings":";;;;AAkBA,MAAM,4BAA4B;AAElC,SAAS,uBAAuB,UAAkB,UAA2B;CAC3E,IAAI,CAAC,YAAY,CAAC,SAAS,WAAW,IAAI,EACxC,OAAO;CAGT,MAAM,YAAY,SAAS,QAAQ,IAAI;CACvC,MAAM,aAAa,SAAS,QAAQ,IAAI;CACxC,MAAM,cACJ,eAAe,KACX,cAAc,KACZ,SAAS,SACT,YACF,cAAc,KACZ,aACA,KAAK,IAAI,YAAY,UAAU;CAGvC,OAAO,sBAFU,SAAS,MAAM,GAAG,YAEE,EAAE,SAAS,GAAG,SAAS,MAAM,YAAY;;AAGhF,SAAS,sBAAsB,OAAgB,SAA4C;CACzF,MAAM,SAAS,mBAAmB,MAAM;CACxC,IAAI,CAAC,QAAQ,OAAO;CAGpB,IADkB,yBAAyB,OAC9B,EAAE;EAMb,IAAI,OAAO;EACX,KAAK,QAAQ,WAAW,QAAQ,IAAI,cAAc,eAChD,QAAQ;EAEV,OAAO;;CAGT,MAAM,WAAW,wBAAwB,OAAO;CAChD,IAAI,CAAC,UAAU,OAAO;CAEtB,MAAM,QAAQ,SAAS,WAAW,4BAA4B,IAAI;CAClE,MAAM,WAAW,uBAAuB,SAAS,KAAK,QAAQ,SAAS;CACvE,OACE,wEACA,QACA,UACA,eAAe,SAAS,GACxB;;AAIJ,SAAgB,uBACd,QACA,UAAqC,EAAE,EAC/B;CACR,IAAI,OAAO;CAEX,KAAK,MAAM,SAAS,QAClB,QAAQ,sBAAsB,OAAO,QAAQ;CAG/C,OAAO;;AAGT,SAAgB,2BACd,UAAqC,EAAE,EACjB;CACtB,MAAM,iBAA4B,EAAE;CACpC,IAAI,eAAe;CAEnB,OAAO;EACL,QAAQ,OAAO;GACb,eAAe,KAAK,MAAM;;EAE5B,QAAQ;GACN,IAAI,gBAAgB,eAAe,QAAQ,OAAO;GAElD,MAAM,OAAO,uBAAuB,eAAe,MAAM,aAAa,EAAE,QAAQ;GAChF,eAAe,eAAe;GAC9B,OAAO;;EAEV"}
|
|
@@ -5,6 +5,7 @@ type RscEmbedTransform = {
|
|
|
5
5
|
getRawBuffer(): Promise<ArrayBuffer>;
|
|
6
6
|
};
|
|
7
7
|
type HtmlInsertion = string | (() => string);
|
|
8
|
+
type InlineCssManifest = Record<string, string>;
|
|
8
9
|
declare function navigationRuntimeRscBootstrapExpression(): string;
|
|
9
10
|
declare function createNavigationRuntimeRscMetadataScript(params: Record<string, string | string[]>, nav: {
|
|
10
11
|
pathname: string;
|
|
@@ -54,7 +55,7 @@ declare function fixPreloadAs(html: string): string;
|
|
|
54
55
|
* to no-op and let the user-rendered Script (in its source-order
|
|
55
56
|
* position) ship as-is.
|
|
56
57
|
*/
|
|
57
|
-
declare function createTickBufferedTransform(rscEmbed: RscEmbedTransform, injectHTML?: HtmlInsertion, injectAfterHeadOpenHTML?: HtmlInsertion): TransformStream<Uint8Array, Uint8Array>;
|
|
58
|
+
declare function createTickBufferedTransform(rscEmbed: RscEmbedTransform, injectHTML?: HtmlInsertion, injectAfterHeadOpenHTML?: HtmlInsertion, inlineCssManifest?: InlineCssManifest, inlineCssPrependCss?: string, inlineCssPrependFallbackHTML?: string, inlineCssScriptNonce?: string): TransformStream<Uint8Array, Uint8Array>;
|
|
58
59
|
//#endregion
|
|
59
60
|
export { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, fixFlightHints, fixPreloadAs, navigationRuntimeRscBootstrapExpression };
|
|
60
61
|
//# sourceMappingURL=app-ssr-stream.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "../client/navigation-runtime.js";
|
|
2
|
-
import { createInlineScriptTag, safeJsonStringify } from "./html.js";
|
|
2
|
+
import { createInlineScriptTag, escapeHtmlAttr, htmlTokenListContains, safeJsonStringify } from "./html.js";
|
|
3
3
|
import { bytesToBase64, concatUint8Arrays } from "./app-rsc-embedded-chunks.js";
|
|
4
4
|
//#region src/server/app-ssr-stream.ts
|
|
5
5
|
const NAVIGATION_RUNTIME_REFERENCE = `self[Symbol.for(${safeJsonStringify(NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION)})]`;
|
|
@@ -86,6 +86,130 @@ function createRscEmbedTransform(embedStream, scriptNonce) {
|
|
|
86
86
|
function fixPreloadAs(html) {
|
|
87
87
|
return html.replace(/<link(?=[^>]*\srel="preload")[^>]*>/g, (tag) => tag.replace(" as=\"stylesheet\"", " as=\"style\""));
|
|
88
88
|
}
|
|
89
|
+
const LINK_TAG_RE = /<link\b[^>]*>/gi;
|
|
90
|
+
const HTML_REWRITE_EXCLUDED_REGION_RE = /<!--[\s\S]*?-->|<(script|style|textarea|title)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
|
|
91
|
+
const HTML_REWRITE_EXCLUDED_REGION_START_RE = /<!--|<(script|style|textarea|title)\b[^>]*>/gi;
|
|
92
|
+
function getHtmlAttribute(tag, name) {
|
|
93
|
+
const attrRe = /\s([^\s"'=<>`]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
94
|
+
let match;
|
|
95
|
+
while ((match = attrRe.exec(tag)) !== null) {
|
|
96
|
+
if (match[1]?.toLowerCase() !== name.toLowerCase()) continue;
|
|
97
|
+
return match[2] ?? match[3] ?? match[4] ?? "";
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
function htmlAttributeHasToken(tag, name, token) {
|
|
102
|
+
return htmlTokenListContains(getHtmlAttribute(tag, name), token);
|
|
103
|
+
}
|
|
104
|
+
function getInlineCss(manifest, href) {
|
|
105
|
+
if (Object.prototype.hasOwnProperty.call(manifest, href)) return manifest[href] ?? "";
|
|
106
|
+
try {
|
|
107
|
+
const pathname = new URL(href).pathname;
|
|
108
|
+
if (Object.prototype.hasOwnProperty.call(manifest, pathname)) return manifest[pathname] ?? "";
|
|
109
|
+
} catch {}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const TRAILING_LINK_OPEN_RE = /<link/gi;
|
|
113
|
+
function splitTrailingIncompleteLinkTag(html) {
|
|
114
|
+
TRAILING_LINK_OPEN_RE.lastIndex = 0;
|
|
115
|
+
let lastIndex = -1;
|
|
116
|
+
let match;
|
|
117
|
+
while ((match = TRAILING_LINK_OPEN_RE.exec(html)) !== null) lastIndex = match.index;
|
|
118
|
+
if (lastIndex === -1) return {
|
|
119
|
+
complete: html,
|
|
120
|
+
trailing: ""
|
|
121
|
+
};
|
|
122
|
+
if (html.indexOf(">", lastIndex) !== -1) return {
|
|
123
|
+
complete: html,
|
|
124
|
+
trailing: ""
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
complete: html.slice(0, lastIndex),
|
|
128
|
+
trailing: html.slice(lastIndex)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function findTrailingOpenHtmlRewriteExcludedRegionStart(html) {
|
|
132
|
+
let match;
|
|
133
|
+
HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex = 0;
|
|
134
|
+
while ((match = HTML_REWRITE_EXCLUDED_REGION_START_RE.exec(html)) !== null) {
|
|
135
|
+
const start = match.index;
|
|
136
|
+
if (match[0] === "<!--") {
|
|
137
|
+
const close = html.indexOf("-->", HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex);
|
|
138
|
+
if (close === -1) return start;
|
|
139
|
+
HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex = close + 3;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const tagName = match[1]?.toLowerCase();
|
|
143
|
+
if (!tagName) continue;
|
|
144
|
+
const close = new RegExp(`</${tagName}\\s*>`, "i").exec(html.slice(HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex));
|
|
145
|
+
if (!close) return start;
|
|
146
|
+
HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex += close.index + close[0].length;
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function splitTrailingInlineCssRewriteBoundary(html) {
|
|
151
|
+
const linkSplit = splitTrailingIncompleteLinkTag(html);
|
|
152
|
+
const incompleteLinkStart = linkSplit.trailing ? linkSplit.complete.length : null;
|
|
153
|
+
const openRegionStart = findTrailingOpenHtmlRewriteExcludedRegionStart(html);
|
|
154
|
+
const trailingStart = incompleteLinkStart === null ? openRegionStart : openRegionStart === null ? incompleteLinkStart : Math.min(incompleteLinkStart, openRegionStart);
|
|
155
|
+
if (trailingStart === null) return {
|
|
156
|
+
complete: html,
|
|
157
|
+
trailing: ""
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
complete: html.slice(0, trailingStart),
|
|
161
|
+
trailing: html.slice(trailingStart)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function escapeStyleText(css) {
|
|
165
|
+
return css.replace(/<\/style/gi, "<\\/style");
|
|
166
|
+
}
|
|
167
|
+
const CSS_PREPEND_UNSAFE_PREAMBLE_RE = /^\uFEFF?(?:\s|\/\*[\s\S]*?\*\/)*@(charset|import|layer|namespace)\b/i;
|
|
168
|
+
function canPrependCss(css) {
|
|
169
|
+
return !CSS_PREPEND_UNSAFE_PREAMBLE_RE.test(css);
|
|
170
|
+
}
|
|
171
|
+
function replaceLinkTags(html, replaceLinkTag) {
|
|
172
|
+
LINK_TAG_RE.lastIndex = 0;
|
|
173
|
+
return html.replace(LINK_TAG_RE, replaceLinkTag);
|
|
174
|
+
}
|
|
175
|
+
function replaceLinkTagsOutsideRawText(html, replaceLinkTag) {
|
|
176
|
+
let rewritten = "";
|
|
177
|
+
let cursor = 0;
|
|
178
|
+
let match;
|
|
179
|
+
HTML_REWRITE_EXCLUDED_REGION_RE.lastIndex = 0;
|
|
180
|
+
while ((match = HTML_REWRITE_EXCLUDED_REGION_RE.exec(html)) !== null) {
|
|
181
|
+
rewritten += replaceLinkTags(html.slice(cursor, match.index), replaceLinkTag);
|
|
182
|
+
rewritten += match[0];
|
|
183
|
+
cursor = match.index + match[0].length;
|
|
184
|
+
}
|
|
185
|
+
const tail = html.slice(cursor);
|
|
186
|
+
const openRegionStart = findTrailingOpenHtmlRewriteExcludedRegionStart(tail);
|
|
187
|
+
if (openRegionStart === null) return rewritten + replaceLinkTags(tail, replaceLinkTag);
|
|
188
|
+
return rewritten + replaceLinkTags(tail.slice(0, openRegionStart), replaceLinkTag) + tail.slice(openRegionStart);
|
|
189
|
+
}
|
|
190
|
+
function rewriteInlineCssStylesheetLinks(html, inlineCssManifest, prependCss, ssrScriptNonce) {
|
|
191
|
+
if (!inlineCssManifest || Object.keys(inlineCssManifest).length === 0) return {
|
|
192
|
+
html,
|
|
193
|
+
consumedPrependCss: false
|
|
194
|
+
};
|
|
195
|
+
let consumedPrependCss = false;
|
|
196
|
+
return {
|
|
197
|
+
html: replaceLinkTagsOutsideRawText(html, (tag) => {
|
|
198
|
+
if (!htmlAttributeHasToken(tag, "rel", "stylesheet")) return tag;
|
|
199
|
+
const href = getHtmlAttribute(tag, "href");
|
|
200
|
+
const precedence = getHtmlAttribute(tag, "data-precedence") ?? getHtmlAttribute(tag, "precedence");
|
|
201
|
+
if (!href || !precedence) return tag;
|
|
202
|
+
const css = getInlineCss(inlineCssManifest, href);
|
|
203
|
+
if (css === null) return tag;
|
|
204
|
+
const effectiveNonce = getHtmlAttribute(tag, "nonce") ?? ssrScriptNonce;
|
|
205
|
+
const nonceAttr = effectiveNonce ? ` nonce="${escapeHtmlAttr(effectiveNonce)}"` : "";
|
|
206
|
+
const cssPrefix = !consumedPrependCss && prependCss.length > 0 && canPrependCss(css) ? `${prependCss}\n` : "";
|
|
207
|
+
consumedPrependCss ||= cssPrefix.length > 0;
|
|
208
|
+
return `<style data-vinext-inline-css${nonceAttr} data-precedence="${escapeHtmlAttr(precedence)}" data-href="${escapeHtmlAttr(href)}">${escapeStyleText(cssPrefix + css)}</style>`;
|
|
209
|
+
}),
|
|
210
|
+
consumedPrependCss
|
|
211
|
+
};
|
|
212
|
+
}
|
|
89
213
|
/**
|
|
90
214
|
* Match the `<head ...>` opening tag in a chunk. Matches both bare `<head>`
|
|
91
215
|
* and `<head class="foo">` shapes. Used to splice HTML immediately after the
|
|
@@ -120,18 +244,25 @@ const HEAD_OPEN_RE = /<head\b[^>]*>/;
|
|
|
120
244
|
* to no-op and let the user-rendered Script (in its source-order
|
|
121
245
|
* position) ship as-is.
|
|
122
246
|
*/
|
|
123
|
-
function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadOpenHTML = "") {
|
|
247
|
+
function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadOpenHTML = "", inlineCssManifest, inlineCssPrependCss = "", inlineCssPrependFallbackHTML = "", inlineCssScriptNonce) {
|
|
124
248
|
const decoder = new TextDecoder();
|
|
125
249
|
const encoder = new TextEncoder();
|
|
126
250
|
const insertsPerFlush = typeof injectHTML === "function";
|
|
127
251
|
let injected = false;
|
|
128
252
|
let preHeadInjected = false;
|
|
129
253
|
let buffered = [];
|
|
254
|
+
let pendingHtml = "";
|
|
130
255
|
let timeoutId = null;
|
|
256
|
+
const hasInlineCssManifest = inlineCssManifest !== void 0 && Object.keys(inlineCssManifest).length > 0;
|
|
131
257
|
const readInsertion = () => typeof injectHTML === "function" ? injectHTML() : injectHTML;
|
|
132
258
|
const readPreHeadInsertion = () => typeof injectAfterHeadOpenHTML === "function" ? injectAfterHeadOpenHTML() : injectAfterHeadOpenHTML;
|
|
259
|
+
const readInlineCssPrependFallback = () => {
|
|
260
|
+
if (!inlineCssPrependCss || !inlineCssPrependFallbackHTML) return "";
|
|
261
|
+
inlineCssPrependCss = "";
|
|
262
|
+
return inlineCssPrependFallbackHTML;
|
|
263
|
+
};
|
|
133
264
|
const emitInsertion = (controller) => {
|
|
134
|
-
const insertion = readInsertion();
|
|
265
|
+
const insertion = readInlineCssPrependFallback() + readInsertion();
|
|
135
266
|
if (insertion) controller.enqueue(encoder.encode(insertion));
|
|
136
267
|
};
|
|
137
268
|
/**
|
|
@@ -140,11 +271,11 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadO
|
|
|
140
271
|
* rewritten chunk and a flag indicating whether the splice happened, so the
|
|
141
272
|
* caller can mark `preHeadInjected` and stop scanning further chunks.
|
|
142
273
|
*
|
|
143
|
-
* NOTE: This is called only when `<head ...>` lies fully inside
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
274
|
+
* NOTE: This is called only when `<head ...>` lies fully inside the current
|
|
275
|
+
* tick-buffered batch. We deliberately avoid retaining arbitrary output until
|
|
276
|
+
* a future chunk completes `<head ...>`, which would delay TTFB and complicate
|
|
277
|
+
* the existing `</head>` injection path. In practice React Fizz emits the
|
|
278
|
+
* opening shell as a single batch.
|
|
148
279
|
*/
|
|
149
280
|
const spliceAfterHeadOpen = (chunk) => {
|
|
150
281
|
if (preHeadInjected) return {
|
|
@@ -167,35 +298,47 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadO
|
|
|
167
298
|
spliced: true
|
|
168
299
|
};
|
|
169
300
|
};
|
|
170
|
-
const flushBuffered = (controller) => {
|
|
171
|
-
if (buffered.length === 0) return;
|
|
301
|
+
const flushBuffered = (controller, final = false) => {
|
|
302
|
+
if (buffered.length === 0 && !pendingHtml) return;
|
|
303
|
+
const rawHtml = pendingHtml + buffered.join("");
|
|
304
|
+
buffered = [];
|
|
305
|
+
pendingHtml = "";
|
|
306
|
+
const split = final || !hasInlineCssManifest ? {
|
|
307
|
+
complete: rawHtml,
|
|
308
|
+
trailing: ""
|
|
309
|
+
} : splitTrailingInlineCssRewriteBoundary(rawHtml);
|
|
310
|
+
if (split.trailing) pendingHtml = split.trailing;
|
|
311
|
+
if (!split.complete) return;
|
|
172
312
|
if (injected && insertsPerFlush) emitInsertion(controller);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
313
|
+
const preparedHtml = fixPreloadAs(split.complete);
|
|
314
|
+
const inlineCssResult = hasInlineCssManifest ? rewriteInlineCssStylesheetLinks(preparedHtml, inlineCssManifest, inlineCssPrependCss, inlineCssScriptNonce) : {
|
|
315
|
+
html: preparedHtml,
|
|
316
|
+
consumedPrependCss: false
|
|
317
|
+
};
|
|
318
|
+
if (inlineCssResult.consumedPrependCss) inlineCssPrependCss = "";
|
|
319
|
+
let working = inlineCssResult.html;
|
|
320
|
+
if (!preHeadInjected) {
|
|
321
|
+
const result = spliceAfterHeadOpen(working);
|
|
322
|
+
if (result.spliced) {
|
|
323
|
+
working = result.chunk;
|
|
324
|
+
preHeadInjected = true;
|
|
181
325
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
326
|
+
}
|
|
327
|
+
if (!injected) {
|
|
328
|
+
const headEnd = working.indexOf("</head>");
|
|
329
|
+
if (headEnd !== -1) {
|
|
330
|
+
const before = working.slice(0, headEnd);
|
|
331
|
+
const after = working.slice(headEnd);
|
|
332
|
+
controller.enqueue(encoder.encode(before + readInlineCssPrependFallback() + readInsertion() + after));
|
|
333
|
+
injected = true;
|
|
334
|
+
return;
|
|
191
335
|
}
|
|
192
|
-
controller.enqueue(encoder.encode(working));
|
|
193
336
|
}
|
|
194
|
-
|
|
337
|
+
controller.enqueue(encoder.encode(working));
|
|
195
338
|
};
|
|
196
339
|
return new TransformStream({
|
|
197
340
|
transform(chunk, controller) {
|
|
198
|
-
buffered.push(
|
|
341
|
+
buffered.push(decoder.decode(chunk, { stream: true }));
|
|
199
342
|
if (timeoutId !== null) return;
|
|
200
343
|
timeoutId = setTimeout(() => {
|
|
201
344
|
try {
|
|
@@ -211,7 +354,9 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadO
|
|
|
211
354
|
clearTimeout(timeoutId);
|
|
212
355
|
timeoutId = null;
|
|
213
356
|
}
|
|
214
|
-
|
|
357
|
+
const remainder = decoder.decode();
|
|
358
|
+
if (remainder) buffered.push(remainder);
|
|
359
|
+
flushBuffered(controller, true);
|
|
215
360
|
if (!injected) {
|
|
216
361
|
emitInsertion(controller);
|
|
217
362
|
injected = true;
|