vinext 0.0.40 → 0.0.42
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 -2
- package/dist/build/client-build-config.d.ts +119 -0
- package/dist/build/client-build-config.js +149 -0
- package/dist/build/client-build-config.js.map +1 -0
- package/dist/build/layout-classification-types.d.ts +62 -0
- package/dist/build/layout-classification-types.js +1 -0
- package/dist/build/layout-classification.d.ts +60 -0
- package/dist/build/layout-classification.js +98 -0
- package/dist/build/layout-classification.js.map +1 -0
- package/dist/build/report.d.ts +15 -1
- package/dist/build/report.js +50 -1
- package/dist/build/report.js.map +1 -1
- package/dist/build/route-classification-manifest.d.ts +53 -0
- package/dist/build/route-classification-manifest.js +145 -0
- package/dist/build/route-classification-manifest.js.map +1 -0
- package/dist/build/run-prerender.js +1 -1
- package/dist/build/ssr-manifest.d.ts +19 -0
- package/dist/build/ssr-manifest.js +71 -0
- package/dist/build/ssr-manifest.js.map +1 -0
- package/dist/check.js +4 -4
- package/dist/check.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/client/entry.js +1 -1
- package/dist/config/config-matchers.js +1 -0
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +341 -114
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +205 -199
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +1 -169
- package/dist/index.js +113 -432
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.js +2 -2
- package/dist/init.js.map +1 -1
- package/dist/plugins/fonts.d.ts +49 -1
- package/dist/plugins/fonts.js +97 -3
- package/dist/plugins/fonts.js.map +1 -1
- package/dist/plugins/postcss.d.ts +27 -0
- package/dist/plugins/postcss.js +94 -0
- package/dist/plugins/postcss.js.map +1 -0
- package/dist/plugins/strip-server-exports.d.ts +14 -0
- package/dist/plugins/strip-server-exports.js +73 -0
- package/dist/plugins/strip-server-exports.js.map +1 -0
- package/dist/routing/app-router.d.ts +6 -4
- package/dist/routing/app-router.js +21 -22
- package/dist/routing/app-router.js.map +1 -1
- package/dist/server/app-browser-entry.js +235 -97
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-error.d.ts +8 -0
- package/dist/server/app-browser-error.js +9 -0
- package/dist/server/app-browser-error.js.map +1 -0
- package/dist/server/app-browser-state.d.ts +93 -0
- package/dist/server/app-browser-state.js +132 -0
- package/dist/server/app-browser-state.js.map +1 -0
- package/dist/server/app-elements.d.ts +92 -0
- package/dist/server/app-elements.js +122 -0
- package/dist/server/app-elements.js.map +1 -0
- package/dist/server/app-page-boundary-render.d.ts +3 -1
- package/dist/server/app-page-boundary-render.js +41 -1
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-cache.d.ts +6 -3
- package/dist/server/app-page-cache.js +14 -8
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-execution.d.ts +36 -3
- package/dist/server/app-page-execution.js +50 -10
- package/dist/server/app-page-execution.js.map +1 -1
- package/dist/server/app-page-probe.d.ts +10 -4
- package/dist/server/app-page-probe.js +24 -15
- package/dist/server/app-page-probe.js.map +1 -1
- package/dist/server/app-page-render.d.ts +8 -4
- package/dist/server/app-page-render.js +15 -4
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-request.d.ts +52 -4
- package/dist/server/app-page-request.js +86 -16
- package/dist/server/app-page-request.js.map +1 -1
- package/dist/server/app-page-response.d.ts +4 -11
- package/dist/server/app-page-response.js +7 -19
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +22 -8
- package/dist/server/app-page-route-wiring.js +219 -81
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +4 -1
- package/dist/server/app-page-stream.js +2 -1
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-render-dependency.d.ts +13 -0
- package/dist/server/app-render-dependency.js +35 -0
- package/dist/server/app-render-dependency.js.map +1 -0
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-execution.js.map +1 -1
- package/dist/server/app-route-handler-response.js +2 -1
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-route-handler-runtime.d.ts +1 -0
- package/dist/server/app-route-handler-runtime.js +26 -1
- package/dist/server/app-route-handler-runtime.js.map +1 -1
- package/dist/server/app-ssr-entry.d.ts +3 -1
- package/dist/server/app-ssr-entry.js +23 -19
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/app-ssr-stream.d.ts +1 -1
- package/dist/server/app-ssr-stream.js +4 -4
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/csp.d.ts +12 -0
- package/dist/server/csp.js +46 -0
- package/dist/server/csp.js.map +1 -0
- package/dist/server/dev-server.js +22 -18
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/html.d.ts +4 -1
- package/dist/server/html.js +11 -1
- package/dist/server/html.js.map +1 -1
- package/dist/server/middleware-response-headers.d.ts +12 -0
- package/dist/server/middleware-response-headers.js +23 -0
- package/dist/server/middleware-response-headers.js.map +1 -0
- package/dist/server/middleware.js +1 -5
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/pages-page-data.d.ts +1 -0
- package/dist/server/pages-page-data.js +2 -2
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-response.d.ts +2 -1
- package/dist/server/pages-page-response.js +16 -14
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/prod-server.d.ts +3 -3
- package/dist/server/prod-server.js +5 -4
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-pipeline.d.ts +15 -1
- package/dist/server/request-pipeline.js +88 -5
- package/dist/server/request-pipeline.js.map +1 -1
- package/dist/shims/cache-runtime.d.ts +1 -0
- package/dist/shims/cache-runtime.js +0 -5
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +1 -0
- package/dist/shims/cache.js +1 -8
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/client-hook-error.d.ts +14 -0
- package/dist/shims/client-hook-error.js +19 -0
- package/dist/shims/client-hook-error.js.map +1 -0
- package/dist/shims/constants.d.ts +3 -3
- package/dist/shims/constants.js +3 -3
- package/dist/shims/constants.js.map +1 -1
- package/dist/shims/document.d.ts +6 -6
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error-boundary.js +1 -1
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/form.d.ts +3 -3
- package/dist/shims/head-state.d.ts +1 -0
- package/dist/shims/head-state.js +0 -5
- package/dist/shims/head-state.js.map +1 -1
- package/dist/shims/headers.d.ts +11 -0
- package/dist/shims/headers.js +13 -10
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/i18n-state.d.ts +1 -0
- package/dist/shims/i18n-state.js +0 -4
- package/dist/shims/i18n-state.js.map +1 -1
- package/dist/shims/internal/app-router-context.d.ts +6 -6
- package/dist/shims/internal/router-context.d.ts +2 -2
- package/dist/shims/layout-segment-context.d.ts +2 -2
- package/dist/shims/link.js +19 -11
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +3 -3
- package/dist/shims/navigation-state.d.ts +2 -0
- package/dist/shims/navigation-state.js +0 -13
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +55 -8
- package/dist/shims/navigation.js +97 -23
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/navigation.react-server.d.ts +14 -0
- package/dist/shims/navigation.react-server.js +29 -0
- package/dist/shims/navigation.react-server.js.map +1 -0
- package/dist/shims/request-context.d.ts +1 -0
- package/dist/shims/request-context.js +0 -9
- package/dist/shims/request-context.js.map +1 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js +0 -5
- package/dist/shims/router-state.js.map +1 -1
- package/dist/shims/script-nonce-context.d.ts +12 -0
- package/dist/shims/script-nonce-context.js +17 -0
- package/dist/shims/script-nonce-context.js.map +1 -0
- package/dist/shims/script.js +41 -10
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/server.js +6 -1
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/slot.d.ts +11 -7
- package/dist/shims/slot.js +28 -19
- package/dist/shims/slot.js.map +1 -1
- package/dist/shims/unified-request-context.d.ts +2 -0
- package/dist/shims/unified-request-context.js +0 -14
- package/dist/shims/unified-request-context.js.map +1 -1
- package/dist/shims/url-safety.js +25 -4
- package/dist/shims/url-safety.js.map +1 -1
- package/dist/utils/mdx-scan.d.ts +10 -0
- package/dist/utils/mdx-scan.js +36 -0
- package/dist/utils/mdx-scan.js.map +1 -0
- package/dist/utils/public-routes.d.ts +5 -0
- package/dist/utils/public-routes.js +50 -0
- package/dist/utils/public-routes.js.map +1 -0
- package/package.json +9 -9
- package/dist/plugins/fix-use-server-closure-collision.d.ts +0 -29
- package/dist/plugins/fix-use-server-closure-collision.js +0 -204
- package/dist/plugins/fix-use-server-closure-collision.js.map +0 -1
|
@@ -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 type { ReactNode } from \"react\";\nimport { Fragment, createElement as createReactElement } from \"react\";\nimport { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { renderToReadableStream, renderToStaticMarkup } from \"react-dom/server.edge\";\nimport * as clientReferences from \"virtual:vite-rsc/client-references\";\nimport type { NavigationContext } from \"../shims/navigation.js\";\nimport {\n ServerInsertedHTMLContext,\n clearServerInsertedHTML,\n flushServerInsertedHTML,\n setNavigationContext,\n useServerInsertedHTML,\n} from \"../shims/navigation.js\";\nimport { runWithNavigationContext } from \"../shims/navigation-state.js\";\nimport { safeJsonStringify } from \"./html.js\";\nimport { createRscEmbedTransform, createTickBufferedTransform } from \"./app-ssr-stream.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\ntype ClientRequire = (id: string) => Promise<unknown>;\n\nlet clientRefsPreloaded = false;\n\nfunction getClientReferenceRequire(): ClientRequire | undefined {\n return (\n globalThis as typeof globalThis & {\n __vite_rsc_client_require__?: ClientRequire;\n }\n ).__vite_rsc_client_require__;\n}\n\nasync function preloadClientReferences(): Promise<void> {\n if (clientRefsPreloaded) return;\n\n const refs = (clientReferences as { default?: Record<string, unknown> }).default;\n const clientRequire = getClientReferenceRequire();\n if (!refs || !clientRequire) return;\n\n await Promise.all(\n Object.keys(refs).map((id) =>\n clientRequire(id).catch((error) => {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] failed to preload client ref:\", id, error);\n }\n }),\n ),\n );\n\n clientRefsPreloaded = true;\n}\n\nfunction escapeHtmlAttr(value: string): string {\n return value.replace(/&/g, \"&\").replace(/\"/g, \""\");\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\nfunction renderFontHtml(fontData?: FontData): string {\n if (!fontData) return \"\";\n\n let fontHTML = \"\";\n\n for (const url of fontData.links ?? []) {\n fontHTML += `<link rel=\"stylesheet\" href=\"${escapeHtmlAttr(url)}\" />\\n`;\n }\n\n for (const preload of fontData.preloads ?? []) {\n fontHTML += `<link rel=\"preload\" 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>${fontData.styles.join(\"\\n\")}</style>\\n`;\n }\n\n return fontHTML;\n}\n\nfunction extractModulePreloadHtml(bootstrapScriptContent?: string): string {\n if (!bootstrapScriptContent) return \"\";\n\n const match = bootstrapScriptContent.match(/import\\(\"([^\"]+)\"\\)/);\n if (!match?.[1]) return \"\";\n\n return `<link rel=\"modulepreload\" href=\"${escapeHtmlAttr(match[1])}\" />\\n`;\n}\n\nfunction buildHeadInjectionHtml(\n navContext: NavigationContext | null,\n bootstrapScriptContent: string | undefined,\n insertedHTML: string,\n fontHTML: string,\n): string {\n const paramsScript =\n \"<script>self.__VINEXT_RSC_PARAMS__=\" +\n safeJsonStringify(navContext?.params ?? {}) +\n \"</script>\";\n const navPayload = {\n pathname: navContext?.pathname ?? \"/\",\n searchParams: navContext?.searchParams ? [...navContext.searchParams.entries()] : [],\n };\n const navScript =\n \"<script>self.__VINEXT_RSC_NAV__=\" + safeJsonStringify(navPayload) + \"</script>\";\n\n return (\n paramsScript +\n navScript +\n extractModulePreloadHtml(bootstrapScriptContent) +\n insertedHTML +\n fontHTML\n );\n}\n\nexport async function handleSsr(\n rscStream: ReadableStream<Uint8Array>,\n navContext: NavigationContext | null,\n fontData?: FontData,\n): Promise<ReadableStream<Uint8Array>> {\n return runWithNavigationContext(async () => {\n await preloadClientReferences();\n\n if (navContext) {\n setNavigationContext(navContext);\n }\n\n clearServerInsertedHTML();\n\n try {\n const [ssrStream, embedStream] = rscStream.tee();\n const rscEmbed = createRscEmbedTransform(embedStream);\n\n let flightRoot: Promise<unknown> | null = null;\n\n function VinextFlightRoot(): ReactNode {\n if (!flightRoot) {\n flightRoot = createFromReadableStream(ssrStream);\n }\n return flightRoot as unknown as ReactNode;\n }\n\n const root = createReactElement(VinextFlightRoot);\n const ssrRoot = ServerInsertedHTMLContext\n ? createReactElement(\n ServerInsertedHTMLContext.Provider,\n { value: useServerInsertedHTML },\n root,\n )\n : root;\n\n const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent(\"index\");\n\n const htmlStream = await renderToReadableStream(ssrRoot, {\n bootstrapScriptContent,\n onError(error) {\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 const insertedHTML = renderInsertedHtml(flushServerInsertedHTML());\n const fontHTML = renderFontHtml(fontData);\n const injectHTML = buildHeadInjectionHtml(\n navContext,\n bootstrapScriptContent,\n insertedHTML,\n fontHTML,\n );\n\n return htmlStream.pipeThrough(createTickBufferedTransform(rscEmbed, injectHTML));\n } finally {\n setNavigationContext(null);\n clearServerInsertedHTML();\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 if (url.pathname.startsWith(\"//\")) {\n return new Response(\"404 Not Found\", { status: 404 });\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 new Response(\"Not Found\", { status: 404 });\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 type { ReactNode } from \"react\";\nimport { Fragment, createElement as createReactElement, use } from \"react\";\nimport { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { renderToReadableStream, renderToStaticMarkup } from \"react-dom/server.edge\";\nimport * as clientReferences from \"virtual:vite-rsc/client-references\";\nimport type { NavigationContext } from \"../shims/navigation.js\";\nimport {\n ServerInsertedHTMLContext,\n clearServerInsertedHTML,\n flushServerInsertedHTML,\n setNavigationContext,\n useServerInsertedHTML,\n} from \"../shims/navigation.js\";\nimport { runWithNavigationContext } from \"../shims/navigation-state.js\";\nimport { withScriptNonce } from \"../shims/script-nonce-context.js\";\nimport {\n createInlineScriptTag,\n createNonceAttribute,\n escapeHtmlAttr,\n safeJsonStringify,\n} from \"./html.js\";\nimport { createRscEmbedTransform, createTickBufferedTransform } from \"./app-ssr-stream.js\";\nimport {\n normalizeAppElements,\n readAppElementsMetadata,\n type AppWireElements,\n} from \"./app-elements.js\";\nimport { ElementsContext, Slot } from \"../shims/slot.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\ntype ClientRequire = (id: string) => Promise<unknown>;\n\nlet clientRefsPreloaded = false;\n\nfunction getClientReferenceRequire(): ClientRequire | undefined {\n return (\n globalThis as typeof globalThis & {\n __vite_rsc_client_require__?: ClientRequire;\n }\n ).__vite_rsc_client_require__;\n}\n\nasync function preloadClientReferences(): Promise<void> {\n if (clientRefsPreloaded) return;\n\n const refs = (clientReferences as { default?: Record<string, unknown> }).default;\n const clientRequire = getClientReferenceRequire();\n if (!refs || !clientRequire) return;\n\n await Promise.all(\n Object.keys(refs).map((id) =>\n clientRequire(id).catch((error) => {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] failed to preload client ref:\", id, error);\n }\n }),\n ),\n );\n\n clientRefsPreloaded = true;\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\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\nfunction extractModulePreloadHtml(bootstrapScriptContent?: string, nonce?: string): string {\n if (!bootstrapScriptContent) return \"\";\n\n const match = bootstrapScriptContent.match(/import\\(\"([^\"]+)\"\\)/);\n if (!match?.[1]) return \"\";\n\n return `<link rel=\"modulepreload\"${createNonceAttribute(nonce)} href=\"${escapeHtmlAttr(match[1])}\" />\\n`;\n}\n\nfunction buildHeadInjectionHtml(\n navContext: NavigationContext | null,\n bootstrapScriptContent: string | undefined,\n insertedHTML: string,\n fontHTML: string,\n scriptNonce?: string,\n): string {\n const paramsScript = createInlineScriptTag(\n \"self.__VINEXT_RSC_PARAMS__=\" + safeJsonStringify(navContext?.params ?? {}),\n scriptNonce,\n );\n const navPayload = {\n pathname: navContext?.pathname ?? \"/\",\n searchParams: navContext?.searchParams ? [...navContext.searchParams.entries()] : [],\n };\n const navScript = createInlineScriptTag(\n \"self.__VINEXT_RSC_NAV__=\" + safeJsonStringify(navPayload),\n scriptNonce,\n );\n\n return (\n paramsScript +\n navScript +\n extractModulePreloadHtml(bootstrapScriptContent, 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?: { scriptNonce?: string },\n): Promise<ReadableStream<Uint8Array>> {\n return runWithNavigationContext(async () => {\n await preloadClientReferences();\n\n if (navContext) {\n setNavigationContext(navContext);\n }\n\n clearServerInsertedHTML();\n\n try {\n const [ssrStream, embedStream] = rscStream.tee();\n const rscEmbed = createRscEmbedTransform(embedStream, options?.scriptNonce);\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 = normalizeAppElements(wireElements);\n const metadata = readAppElementsMetadata(elements);\n return createReactElement(\n ElementsContext.Provider,\n { value: elements },\n createReactElement(Slot, { id: metadata.routeId }),\n );\n }\n\n const root = createReactElement(VinextFlightRoot);\n const ssrTree = ServerInsertedHTMLContext\n ? createReactElement(\n ServerInsertedHTMLContext.Provider,\n { value: useServerInsertedHTML },\n root,\n )\n : root;\n const ssrRoot = withScriptNonce(ssrTree, options?.scriptNonce);\n\n const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent(\"index\");\n\n const htmlStream = await renderToReadableStream(ssrRoot, {\n bootstrapScriptContent,\n nonce: options?.scriptNonce,\n onError(error) {\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 const insertedHTML = renderInsertedHtml(flushServerInsertedHTML());\n const fontHTML = renderFontHtml(fontData, options?.scriptNonce);\n const injectHTML = buildHeadInjectionHtml(\n navContext,\n bootstrapScriptContent,\n insertedHTML,\n fontHTML,\n options?.scriptNonce,\n );\n\n return htmlStream.pipeThrough(createTickBufferedTransform(rscEmbed, injectHTML));\n } finally {\n setNavigationContext(null);\n clearServerInsertedHTML();\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 if (url.pathname.startsWith(\"//\")) {\n return new Response(\"404 Not Found\", { status: 404 });\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 new Response(\"Not Found\", { status: 404 });\n }\n\n return new Response(String(result), { status: 200 });\n },\n};\n"],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ declare function fixFlightHints(text: string): string;
|
|
|
13
13
|
* Create a helper that progressively embeds RSC chunks as inline <script> tags.
|
|
14
14
|
* The browser entry turns the embedded text chunks back into Uint8Array data.
|
|
15
15
|
*/
|
|
16
|
-
declare function createRscEmbedTransform(embedStream: ReadableStream<Uint8Array
|
|
16
|
+
declare function createRscEmbedTransform(embedStream: ReadableStream<Uint8Array>, scriptNonce?: string): RscEmbedTransform;
|
|
17
17
|
/**
|
|
18
18
|
* Fix invalid preload "as" values in server-rendered HTML.
|
|
19
19
|
* React Fizz emits <link rel="preload" as="stylesheet"> for CSS, but the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { safeJsonStringify } from "./html.js";
|
|
1
|
+
import { createInlineScriptTag, safeJsonStringify } from "./html.js";
|
|
2
2
|
//#region src/server/app-ssr-stream.ts
|
|
3
3
|
/**
|
|
4
4
|
* Fix invalid preload "as" values in RSC Flight hint lines before they reach
|
|
@@ -12,7 +12,7 @@ function fixFlightHints(text) {
|
|
|
12
12
|
* Create a helper that progressively embeds RSC chunks as inline <script> tags.
|
|
13
13
|
* The browser entry turns the embedded text chunks back into Uint8Array data.
|
|
14
14
|
*/
|
|
15
|
-
function createRscEmbedTransform(embedStream) {
|
|
15
|
+
function createRscEmbedTransform(embedStream, scriptNonce) {
|
|
16
16
|
const reader = embedStream.getReader();
|
|
17
17
|
const decoder = new TextDecoder();
|
|
18
18
|
let pendingChunks = [];
|
|
@@ -40,13 +40,13 @@ function createRscEmbedTransform(embedStream) {
|
|
|
40
40
|
const chunks = pendingChunks;
|
|
41
41
|
pendingChunks = [];
|
|
42
42
|
let scripts = "";
|
|
43
|
-
for (const chunk of chunks) scripts += "
|
|
43
|
+
for (const chunk of chunks) scripts += createInlineScriptTag("self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(" + safeJsonStringify(chunk) + ")", scriptNonce);
|
|
44
44
|
return scripts;
|
|
45
45
|
},
|
|
46
46
|
async finalize() {
|
|
47
47
|
await pumpPromise;
|
|
48
48
|
let scripts = this.flush();
|
|
49
|
-
scripts += "
|
|
49
|
+
scripts += createInlineScriptTag("self.__VINEXT_RSC_DONE__=true", scriptNonce);
|
|
50
50
|
return scripts;
|
|
51
51
|
}
|
|
52
52
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-ssr-stream.js","names":[],"sources":["../../src/server/app-ssr-stream.ts"],"sourcesContent":["import { safeJsonStringify } from \"./html.js\";\n\nexport type RscEmbedTransform = {\n flush(): string;\n finalize(): Promise<string>;\n};\n\n/**\n * Fix invalid preload \"as\" values in RSC Flight hint lines before they reach\n * the client. React Flight emits HL hints with as=\"stylesheet\" for CSS, but\n * the HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixFlightHints(text: string): string {\n return text.replace(/(\\d*:HL\\[.*?),\"stylesheet\"(\\]|,)/g, '$1,\"style\"$2');\n}\n\n/**\n * Create a helper that progressively embeds RSC chunks as inline <script> tags.\n * The browser entry turns the embedded text chunks back into Uint8Array data.\n */\nexport function createRscEmbedTransform(\n embedStream: ReadableStream<Uint8Array>,\n): RscEmbedTransform {\n const reader = embedStream.getReader();\n const decoder = new TextDecoder();\n let pendingChunks: string[] = [];\n let reading = false;\n\n async function pumpReader(): Promise<void> {\n if (reading) return;\n reading = true;\n try {\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n const text = decoder.decode(result.value, { stream: true });\n // The RSC entry already fixes HL hints at the source. Keep this second\n // pass as defense in depth for any embed stream that bypasses that\n // wrapper; the rewrite is idempotent, so double-application is safe.\n pendingChunks.push(fixFlightHints(text));\n }\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] RSC embed stream read error:\", error);\n }\n } finally {\n reading = false;\n }\n }\n\n const pumpPromise = pumpReader();\n\n return {\n flush(): string {\n if (pendingChunks.length === 0) return \"\";\n\n const chunks = pendingChunks;\n pendingChunks = [];\n\n let scripts = \"\";\n for (const chunk of chunks) {\n scripts
|
|
1
|
+
{"version":3,"file":"app-ssr-stream.js","names":[],"sources":["../../src/server/app-ssr-stream.ts"],"sourcesContent":["import { createInlineScriptTag, safeJsonStringify } from \"./html.js\";\n\nexport type RscEmbedTransform = {\n flush(): string;\n finalize(): Promise<string>;\n};\n\n/**\n * Fix invalid preload \"as\" values in RSC Flight hint lines before they reach\n * the client. React Flight emits HL hints with as=\"stylesheet\" for CSS, but\n * the HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixFlightHints(text: string): string {\n return text.replace(/(\\d*:HL\\[.*?),\"stylesheet\"(\\]|,)/g, '$1,\"style\"$2');\n}\n\n/**\n * Create a helper that progressively embeds RSC chunks as inline <script> tags.\n * The browser entry turns the embedded text chunks back into Uint8Array data.\n */\nexport function createRscEmbedTransform(\n embedStream: ReadableStream<Uint8Array>,\n scriptNonce?: string,\n): RscEmbedTransform {\n const reader = embedStream.getReader();\n const decoder = new TextDecoder();\n let pendingChunks: string[] = [];\n let reading = false;\n\n async function pumpReader(): Promise<void> {\n if (reading) return;\n reading = true;\n try {\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n const text = decoder.decode(result.value, { stream: true });\n // The RSC entry already fixes HL hints at the source. Keep this second\n // pass as defense in depth for any embed stream that bypasses that\n // wrapper; the rewrite is idempotent, so double-application is safe.\n pendingChunks.push(fixFlightHints(text));\n }\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] RSC embed stream read error:\", error);\n }\n } finally {\n reading = false;\n }\n }\n\n const pumpPromise = pumpReader();\n\n return {\n flush(): string {\n if (pendingChunks.length === 0) return \"\";\n\n const chunks = pendingChunks;\n pendingChunks = [];\n\n let scripts = \"\";\n for (const chunk of chunks) {\n scripts += createInlineScriptTag(\n \"self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(\" +\n safeJsonStringify(chunk) +\n \")\",\n scriptNonce,\n );\n }\n return scripts;\n },\n\n async finalize(): Promise<string> {\n await pumpPromise;\n let scripts = this.flush();\n scripts += createInlineScriptTag(\"self.__VINEXT_RSC_DONE__=true\", scriptNonce);\n return scripts;\n },\n };\n}\n\n/**\n * Fix invalid preload \"as\" values in server-rendered HTML.\n * React Fizz emits <link rel=\"preload\" as=\"stylesheet\"> for CSS, but the\n * HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixPreloadAs(html: string): string {\n return html.replace(/<link(?=[^>]*\\srel=\"preload\")[^>]*>/g, (tag) =>\n tag.replace(' as=\"stylesheet\"', ' as=\"style\"'),\n );\n}\n\n/**\n * Create the tick-buffered HTML transform that injects RSC scripts between\n * React Fizz flush cycles without corrupting split HTML chunks.\n */\nexport function createTickBufferedTransform(\n rscEmbed: RscEmbedTransform,\n injectHTML = \"\",\n): TransformStream<Uint8Array, Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let injected = false;\n let buffered: string[] = [];\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const flushBuffered = (controller: TransformStreamDefaultController<Uint8Array>): void => {\n for (const chunk of buffered) {\n if (!injected) {\n const headEnd = chunk.indexOf(\"</head>\");\n if (headEnd !== -1) {\n const before = chunk.slice(0, headEnd);\n const after = chunk.slice(headEnd);\n controller.enqueue(encoder.encode(before + injectHTML + after));\n injected = true;\n continue;\n }\n }\n controller.enqueue(encoder.encode(chunk));\n }\n buffered = [];\n };\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));\n\n if (timeoutId !== null) return;\n\n timeoutId = setTimeout(() => {\n try {\n flushBuffered(controller);\n\n const rscScripts = rscEmbed.flush();\n if (rscScripts) {\n controller.enqueue(encoder.encode(rscScripts));\n }\n } catch {\n // Stream was cancelled between when the timeout was registered and\n // when it fired (e.g. client disconnected, health-check cancelled\n // the response body). Ignore — the stream is already closed.\n }\n\n timeoutId = null;\n }, 0);\n },\n\n async flush(controller) {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n flushBuffered(controller);\n\n if (!injected && injectHTML) {\n controller.enqueue(encoder.encode(injectHTML));\n }\n\n const finalScripts = await rscEmbed.finalize();\n if (finalScripts) {\n controller.enqueue(encoder.encode(finalScripts));\n }\n },\n });\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,eAAe,MAAsB;AACnD,QAAO,KAAK,QAAQ,qCAAqC,iBAAe;;;;;;AAO1E,SAAgB,wBACd,aACA,aACmB;CACnB,MAAM,SAAS,YAAY,WAAW;CACtC,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,gBAA0B,EAAE;CAChC,IAAI,UAAU;CAEd,eAAe,aAA4B;AACzC,MAAI,QAAS;AACb,YAAU;AACV,MAAI;AACF,UAAO,MAAM;IACX,MAAM,SAAS,MAAM,OAAO,MAAM;AAClC,QAAI,OAAO,KAAM;IACjB,MAAM,OAAO,QAAQ,OAAO,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;AAI3D,kBAAc,KAAK,eAAe,KAAK,CAAC;;WAEnC,OAAO;AACd,OAAI,QAAQ,IAAI,aAAa,aAC3B,SAAQ,KAAK,yCAAyC,MAAM;YAEtD;AACR,aAAU;;;CAId,MAAM,cAAc,YAAY;AAEhC,QAAO;EACL,QAAgB;AACd,OAAI,cAAc,WAAW,EAAG,QAAO;GAEvC,MAAM,SAAS;AACf,mBAAgB,EAAE;GAElB,IAAI,UAAU;AACd,QAAK,MAAM,SAAS,OAClB,YAAW,sBACT,+FACE,kBAAkB,MAAM,GACxB,KACF,YACD;AAEH,UAAO;;EAGT,MAAM,WAA4B;AAChC,SAAM;GACN,IAAI,UAAU,KAAK,OAAO;AAC1B,cAAW,sBAAsB,iCAAiC,YAAY;AAC9E,UAAO;;EAEV;;;;;;;AAQH,SAAgB,aAAa,MAAsB;AACjD,QAAO,KAAK,QAAQ,yCAAyC,QAC3D,IAAI,QAAQ,sBAAoB,gBAAc,CAC/C;;;;;;AAOH,SAAgB,4BACd,UACA,aAAa,IAC4B;CACzC,MAAM,UAAU,IAAI,aAAa;CACjC,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,WAAW;CACf,IAAI,WAAqB,EAAE;CAC3B,IAAI,YAAkD;CAEtD,MAAM,iBAAiB,eAAmE;AACxF,OAAK,MAAM,SAAS,UAAU;AAC5B,OAAI,CAAC,UAAU;IACb,MAAM,UAAU,MAAM,QAAQ,UAAU;AACxC,QAAI,YAAY,IAAI;KAClB,MAAM,SAAS,MAAM,MAAM,GAAG,QAAQ;KACtC,MAAM,QAAQ,MAAM,MAAM,QAAQ;AAClC,gBAAW,QAAQ,QAAQ,OAAO,SAAS,aAAa,MAAM,CAAC;AAC/D,gBAAW;AACX;;;AAGJ,cAAW,QAAQ,QAAQ,OAAO,MAAM,CAAC;;AAE3C,aAAW,EAAE;;AAGf,QAAO,IAAI,gBAAwC;EACjD,UAAU,OAAO,YAAY;AAC3B,YAAS,KAAK,aAAa,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC;AAEpE,OAAI,cAAc,KAAM;AAExB,eAAY,iBAAiB;AAC3B,QAAI;AACF,mBAAc,WAAW;KAEzB,MAAM,aAAa,SAAS,OAAO;AACnC,SAAI,WACF,YAAW,QAAQ,QAAQ,OAAO,WAAW,CAAC;YAE1C;AAMR,gBAAY;MACX,EAAE;;EAGP,MAAM,MAAM,YAAY;AACtB,OAAI,cAAc,MAAM;AACtB,iBAAa,UAAU;AACvB,gBAAY;;AAGd,iBAAc,WAAW;AAEzB,OAAI,CAAC,YAAY,WACf,YAAW,QAAQ,QAAQ,OAAO,WAAW,CAAC;GAGhD,MAAM,eAAe,MAAM,SAAS,UAAU;AAC9C,OAAI,aACF,YAAW,QAAQ,QAAQ,OAAO,aAAa,CAAC;;EAGrD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IncomingHttpHeaders, OutgoingHttpHeaders } from "node:http";
|
|
2
|
+
|
|
3
|
+
//#region src/server/csp.d.ts
|
|
4
|
+
type NodeHeaders = IncomingHttpHeaders | OutgoingHttpHeaders;
|
|
5
|
+
declare function getScriptNonceFromHeader(cspHeaderValue: string): string | undefined;
|
|
6
|
+
declare function getScriptNonceFromHeaders(headers: Headers | null | undefined): string | undefined;
|
|
7
|
+
declare function getScriptNonceFromNodeHeaders(headers: NodeHeaders | null | undefined): string | undefined;
|
|
8
|
+
declare function getScriptNonceFromNodeHeaderSources(...headersList: readonly (NodeHeaders | null | undefined)[]): string | undefined;
|
|
9
|
+
declare function getScriptNonceFromHeaderSources(...headersList: readonly (Headers | null | undefined)[]): string | undefined;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { getScriptNonceFromHeader, getScriptNonceFromHeaderSources, getScriptNonceFromHeaders, getScriptNonceFromNodeHeaderSources, getScriptNonceFromNodeHeaders };
|
|
12
|
+
//# sourceMappingURL=csp.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/server/csp.ts
|
|
2
|
+
const ESCAPE_REGEX = /[&><\u2028\u2029]/;
|
|
3
|
+
function matchesDirectiveName(directive, name) {
|
|
4
|
+
return directive === name || directive.startsWith(`${name} `);
|
|
5
|
+
}
|
|
6
|
+
function getNodeHeaderValue(headers, key) {
|
|
7
|
+
const value = headers?.[key];
|
|
8
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
9
|
+
if (value == null) return;
|
|
10
|
+
return String(value);
|
|
11
|
+
}
|
|
12
|
+
function getScriptNonceFromHeader(cspHeaderValue) {
|
|
13
|
+
const directives = cspHeaderValue.split(";").map((directive) => directive.trim());
|
|
14
|
+
const directive = directives.find((value) => matchesDirectiveName(value, "script-src")) ?? directives.find((value) => matchesDirectiveName(value, "default-src"));
|
|
15
|
+
if (!directive) return;
|
|
16
|
+
const nonce = directive.split(" ").slice(1).map((source) => source.trim()).find((source) => source.startsWith("'nonce-") && source.length > 8 && source.endsWith("'"))?.slice(7, -1);
|
|
17
|
+
if (!nonce) return;
|
|
18
|
+
if (ESCAPE_REGEX.test(nonce)) throw new Error("Nonce value from Content-Security-Policy contained HTML escape characters.\nLearn more: https://nextjs.org/docs/messages/nonce-contained-invalid-characters");
|
|
19
|
+
return nonce;
|
|
20
|
+
}
|
|
21
|
+
function getScriptNonceFromHeaders(headers) {
|
|
22
|
+
const csp = headers?.get("content-security-policy") ?? headers?.get("content-security-policy-report-only");
|
|
23
|
+
if (!csp) return;
|
|
24
|
+
return getScriptNonceFromHeader(csp);
|
|
25
|
+
}
|
|
26
|
+
function getScriptNonceFromNodeHeaders(headers) {
|
|
27
|
+
const csp = getNodeHeaderValue(headers, "content-security-policy") ?? getNodeHeaderValue(headers, "content-security-policy-report-only");
|
|
28
|
+
if (!csp) return;
|
|
29
|
+
return getScriptNonceFromHeader(csp);
|
|
30
|
+
}
|
|
31
|
+
function getScriptNonceFromNodeHeaderSources(...headersList) {
|
|
32
|
+
for (const headers of headersList) {
|
|
33
|
+
const nonce = getScriptNonceFromNodeHeaders(headers);
|
|
34
|
+
if (nonce) return nonce;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function getScriptNonceFromHeaderSources(...headersList) {
|
|
38
|
+
for (const headers of headersList) {
|
|
39
|
+
const nonce = getScriptNonceFromHeaders(headers);
|
|
40
|
+
if (nonce) return nonce;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { getScriptNonceFromHeader, getScriptNonceFromHeaderSources, getScriptNonceFromHeaders, getScriptNonceFromNodeHeaderSources, getScriptNonceFromNodeHeaders };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=csp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp.js","names":[],"sources":["../../src/server/csp.ts"],"sourcesContent":["import type { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nconst ESCAPE_REGEX = /[&><\\u2028\\u2029]/;\ntype NodeHeaders = IncomingHttpHeaders | OutgoingHttpHeaders;\n\nfunction matchesDirectiveName(directive: string, name: string): boolean {\n return directive === name || directive.startsWith(`${name} `);\n}\n\nfunction getNodeHeaderValue(\n headers: NodeHeaders | null | undefined,\n key: \"content-security-policy\" | \"content-security-policy-report-only\",\n): string | undefined {\n const value = headers?.[key];\n if (Array.isArray(value)) {\n return value.join(\", \");\n }\n if (value == null) {\n return undefined;\n }\n return String(value);\n}\n\nexport function getScriptNonceFromHeader(cspHeaderValue: string): string | undefined {\n const directives = cspHeaderValue.split(\";\").map((directive) => directive.trim());\n\n const directive =\n directives.find((value) => matchesDirectiveName(value, \"script-src\")) ??\n directives.find((value) => matchesDirectiveName(value, \"default-src\"));\n\n if (!directive) {\n return undefined;\n }\n\n const nonce = directive\n .split(\" \")\n .slice(1)\n .map((source) => source.trim())\n .find((source) => source.startsWith(\"'nonce-\") && source.length > 8 && source.endsWith(\"'\"))\n ?.slice(7, -1);\n\n if (!nonce) {\n return undefined;\n }\n\n if (ESCAPE_REGEX.test(nonce)) {\n throw new Error(\n \"Nonce value from Content-Security-Policy contained HTML escape characters.\\nLearn more: https://nextjs.org/docs/messages/nonce-contained-invalid-characters\",\n );\n }\n\n return nonce;\n}\n\nexport function getScriptNonceFromHeaders(headers: Headers | null | undefined): string | undefined {\n const csp =\n headers?.get(\"content-security-policy\") ?? headers?.get(\"content-security-policy-report-only\");\n\n if (!csp) {\n return undefined;\n }\n\n return getScriptNonceFromHeader(csp);\n}\n\nexport function getScriptNonceFromNodeHeaders(\n headers: NodeHeaders | null | undefined,\n): string | undefined {\n const csp =\n getNodeHeaderValue(headers, \"content-security-policy\") ??\n getNodeHeaderValue(headers, \"content-security-policy-report-only\");\n\n if (!csp) {\n return undefined;\n }\n\n return getScriptNonceFromHeader(csp);\n}\n\nexport function getScriptNonceFromNodeHeaderSources(\n ...headersList: readonly (NodeHeaders | null | undefined)[]\n): string | undefined {\n for (const headers of headersList) {\n const nonce = getScriptNonceFromNodeHeaders(headers);\n if (nonce) {\n return nonce;\n }\n }\n\n return undefined;\n}\n\nexport function getScriptNonceFromHeaderSources(\n ...headersList: readonly (Headers | null | undefined)[]\n): string | undefined {\n for (const headers of headersList) {\n const nonce = getScriptNonceFromHeaders(headers);\n if (nonce) {\n return nonce;\n }\n }\n\n return undefined;\n}\n"],"mappings":";AAEA,MAAM,eAAe;AAGrB,SAAS,qBAAqB,WAAmB,MAAuB;AACtE,QAAO,cAAc,QAAQ,UAAU,WAAW,GAAG,KAAK,GAAG;;AAG/D,SAAS,mBACP,SACA,KACoB;CACpB,MAAM,QAAQ,UAAU;AACxB,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,KAAK,KAAK;AAEzB,KAAI,SAAS,KACX;AAEF,QAAO,OAAO,MAAM;;AAGtB,SAAgB,yBAAyB,gBAA4C;CACnF,MAAM,aAAa,eAAe,MAAM,IAAI,CAAC,KAAK,cAAc,UAAU,MAAM,CAAC;CAEjF,MAAM,YACJ,WAAW,MAAM,UAAU,qBAAqB,OAAO,aAAa,CAAC,IACrE,WAAW,MAAM,UAAU,qBAAqB,OAAO,cAAc,CAAC;AAExE,KAAI,CAAC,UACH;CAGF,MAAM,QAAQ,UACX,MAAM,IAAI,CACV,MAAM,EAAE,CACR,KAAK,WAAW,OAAO,MAAM,CAAC,CAC9B,MAAM,WAAW,OAAO,WAAW,UAAU,IAAI,OAAO,SAAS,KAAK,OAAO,SAAS,IAAI,CAAC,EAC1F,MAAM,GAAG,GAAG;AAEhB,KAAI,CAAC,MACH;AAGF,KAAI,aAAa,KAAK,MAAM,CAC1B,OAAM,IAAI,MACR,8JACD;AAGH,QAAO;;AAGT,SAAgB,0BAA0B,SAAyD;CACjG,MAAM,MACJ,SAAS,IAAI,0BAA0B,IAAI,SAAS,IAAI,sCAAsC;AAEhG,KAAI,CAAC,IACH;AAGF,QAAO,yBAAyB,IAAI;;AAGtC,SAAgB,8BACd,SACoB;CACpB,MAAM,MACJ,mBAAmB,SAAS,0BAA0B,IACtD,mBAAmB,SAAS,sCAAsC;AAEpE,KAAI,CAAC,IACH;AAGF,QAAO,yBAAyB,IAAI;;AAGtC,SAAgB,oCACd,GAAG,aACiB;AACpB,MAAK,MAAM,WAAW,aAAa;EACjC,MAAM,QAAQ,8BAA8B,QAAQ;AACpD,MAAI,MACF,QAAO;;;AAOb,SAAgB,gCACd,GAAG,aACiB;AACpB,MAAK,MAAM,WAAW,aAAa;EACjC,MAAM,QAAQ,0BAA0B,QAAQ;AAChD,MAAI,MACF,QAAO"}
|
|
@@ -11,7 +11,9 @@ import { parseQueryString } from "../utils/query.js";
|
|
|
11
11
|
import "../shims/router-state.js";
|
|
12
12
|
import { runWithHeadState } from "../shims/head-state.js";
|
|
13
13
|
import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
|
|
14
|
-
import {
|
|
14
|
+
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
15
|
+
import { createInlineScriptTag, createNonceAttribute, safeJsonStringify } from "./html.js";
|
|
16
|
+
import { getScriptNonceFromNodeHeaderSources } from "./csp.js";
|
|
15
17
|
import { logRequest, now } from "./request-log.js";
|
|
16
18
|
import { detectLocaleFromAcceptLanguage, extractLocaleFromUrl as extractLocaleFromUrl$1, parseCookieLocaleFromHeader, resolvePagesI18nRequest } from "./pages-i18n.js";
|
|
17
19
|
import fs from "node:fs";
|
|
@@ -268,6 +270,8 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
268
270
|
else gsspExtraHeaders[key] = String(val);
|
|
269
271
|
}
|
|
270
272
|
}
|
|
273
|
+
const responseHeaders = typeof res.getHeaders === "function" ? res.getHeaders() : void 0;
|
|
274
|
+
const scriptNonce = getScriptNonceFromNodeHeaderSources(req.headers, responseHeaders);
|
|
271
275
|
let earlyFontLinkHeader = "";
|
|
272
276
|
try {
|
|
273
277
|
const earlyPreloads = [];
|
|
@@ -280,21 +284,20 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
280
284
|
if (typeof pageModule.getStaticProps === "function") {
|
|
281
285
|
const cacheKey = isrCacheKey("pages", url.split("?")[0], process.env.__VINEXT_BUILD_ID);
|
|
282
286
|
const cached = await isrGet(cacheKey);
|
|
283
|
-
if (cached && !cached.isStale && cached.value.value?.kind === "PAGES") {
|
|
287
|
+
if (cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !scriptNonce) {
|
|
284
288
|
const cachedHtml = cached.value.value.html;
|
|
285
289
|
const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
|
|
286
|
-
const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
|
|
287
290
|
const hitHeaders = {
|
|
288
291
|
"Content-Type": "text/html",
|
|
289
292
|
"X-Vinext-Cache": "HIT",
|
|
290
|
-
"Cache-Control": `s-maxage=${
|
|
293
|
+
"Cache-Control": `s-maxage=${getRevalidateDuration(cacheKey) ?? 60}, stale-while-revalidate`
|
|
291
294
|
};
|
|
292
295
|
if (earlyFontLinkHeader) hitHeaders["Link"] = earlyFontLinkHeader;
|
|
293
296
|
res.writeHead(200, hitHeaders);
|
|
294
297
|
res.end(transformedHtml);
|
|
295
298
|
return;
|
|
296
299
|
}
|
|
297
|
-
if (cached && cached.isStale && cached.value.value?.kind === "PAGES") {
|
|
300
|
+
if (cached && cached.isStale && cached.value.value?.kind === "PAGES" && !scriptNonce) {
|
|
298
301
|
const cachedHtml = cached.value.value.html;
|
|
299
302
|
const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
|
|
300
303
|
triggerBackgroundRegeneration(cacheKey, async () => {
|
|
@@ -343,7 +346,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
343
346
|
pageProps: freshProps
|
|
344
347
|
}) : React.createElement(pageModule.default, freshProps);
|
|
345
348
|
if (routerShim.wrapWithRouterContext) el = routerShim.wrapWithRouterContext(el);
|
|
346
|
-
const freshBody = await renderIsrPassToStringAsync(el);
|
|
349
|
+
const freshBody = await renderIsrPassToStringAsync(withScriptNonce(el, scriptNonce));
|
|
347
350
|
const viteRoot = server.config?.root;
|
|
348
351
|
const regenPageUrl = viteRoot ? "/" + path.relative(viteRoot, route.filePath) : route.filePath;
|
|
349
352
|
const regenAppUrl = RegenApp ? viteRoot ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app")) : path.join(pagesDir, "_app") : null;
|
|
@@ -367,11 +370,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
367
370
|
}
|
|
368
371
|
});
|
|
369
372
|
});
|
|
370
|
-
const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
|
|
371
373
|
const staleHeaders = {
|
|
372
374
|
"Content-Type": "text/html",
|
|
373
375
|
"X-Vinext-Cache": "STALE",
|
|
374
|
-
"Cache-Control": `s-maxage=${
|
|
376
|
+
"Cache-Control": `s-maxage=${getRevalidateDuration(cacheKey) ?? 60}, stale-while-revalidate`
|
|
375
377
|
};
|
|
376
378
|
if (earlyFontLinkHeader) staleHeaders["Link"] = earlyFontLinkHeader;
|
|
377
379
|
res.writeHead(200, staleHeaders);
|
|
@@ -419,6 +421,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
419
421
|
if (typeof headShim.resetSSRHead === "function") headShim.resetSSRHead();
|
|
420
422
|
const dynamicShim = await importModule(runner, "next/dynamic");
|
|
421
423
|
if (typeof dynamicShim.flushPreloads === "function") await dynamicShim.flushPreloads();
|
|
424
|
+
const nonceAttr = createNonceAttribute(scriptNonce);
|
|
422
425
|
let fontHeadHTML = "";
|
|
423
426
|
const allFontStyles = [];
|
|
424
427
|
const allFontPreloads = [];
|
|
@@ -428,7 +431,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
428
431
|
const fontUrls = fontGoogle.getSSRFontLinks();
|
|
429
432
|
for (const fontUrl of fontUrls) {
|
|
430
433
|
const safeFontUrl = fontUrl.replace(/&/g, "&").replace(/"/g, """);
|
|
431
|
-
fontHeadHTML += `<link rel="stylesheet" href="${safeFontUrl}" />\n `;
|
|
434
|
+
fontHeadHTML += `<link rel="stylesheet"${nonceAttr} href="${safeFontUrl}" />\n `;
|
|
432
435
|
}
|
|
433
436
|
}
|
|
434
437
|
if (typeof fontGoogle.getSSRFontStyles === "function") allFontStyles.push(...fontGoogle.getSSRFontStyles());
|
|
@@ -442,14 +445,14 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
442
445
|
for (const { href, type } of allFontPreloads) {
|
|
443
446
|
const safeHref = href.replace(/&/g, "&").replace(/"/g, """);
|
|
444
447
|
const safeType = type.replace(/&/g, "&").replace(/"/g, """);
|
|
445
|
-
fontHeadHTML += `<link rel="preload" href="${safeHref}" as="font" type="${safeType}" crossorigin />\n `;
|
|
448
|
+
fontHeadHTML += `<link rel="preload"${nonceAttr} href="${safeHref}" as="font" type="${safeType}" crossorigin />\n `;
|
|
446
449
|
}
|
|
447
|
-
if (allFontStyles.length > 0) fontHeadHTML += `<style data-vinext-fonts>${allFontStyles.join("\n")}</style>\n `;
|
|
450
|
+
if (allFontStyles.length > 0) fontHeadHTML += `<style data-vinext-fonts${nonceAttr}>${allFontStyles.join("\n")}</style>\n `;
|
|
448
451
|
const viteRoot = server.config.root;
|
|
449
452
|
const pageModuleUrl = "/" + path.relative(viteRoot, route.filePath);
|
|
450
453
|
const appModuleUrl = AppComponent ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app")) : null;
|
|
451
454
|
const hydrationScript = `
|
|
452
|
-
<script type="module">
|
|
455
|
+
<script type="module"${nonceAttr}>
|
|
453
456
|
import "vinext/instrumentation-client";
|
|
454
457
|
import React from "react";
|
|
455
458
|
import { hydrateRoot } from "react-dom/client";
|
|
@@ -477,7 +480,7 @@ async function hydrate() {
|
|
|
477
480
|
}
|
|
478
481
|
hydrate();
|
|
479
482
|
<\/script>`;
|
|
480
|
-
const nextDataScript =
|
|
483
|
+
const nextDataScript = createInlineScriptTag(`window.__NEXT_DATA__ = ${safeJsonStringify({
|
|
481
484
|
props: { pageProps },
|
|
482
485
|
page: patternToNextFormat(route.pattern),
|
|
483
486
|
query: params,
|
|
@@ -491,7 +494,7 @@ hydrate();
|
|
|
491
494
|
pageModuleUrl,
|
|
492
495
|
appModuleUrl
|
|
493
496
|
}
|
|
494
|
-
})}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}
|
|
497
|
+
})}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}`, scriptNonce);
|
|
495
498
|
const docPath = path.join(pagesDir, "_document");
|
|
496
499
|
let DocumentComponent = null;
|
|
497
500
|
if (findFileWithExtensions(docPath, matcher)) try {
|
|
@@ -499,12 +502,13 @@ hydrate();
|
|
|
499
502
|
} catch {}
|
|
500
503
|
const allScripts = `${nextDataScript}\n ${hydrationScript}`;
|
|
501
504
|
const extraHeaders = { ...gsspExtraHeaders };
|
|
502
|
-
if (isrRevalidateSeconds)
|
|
505
|
+
if (isrRevalidateSeconds) if (scriptNonce) extraHeaders["Cache-Control"] = "no-store, must-revalidate";
|
|
506
|
+
else {
|
|
503
507
|
extraHeaders["Cache-Control"] = `s-maxage=${isrRevalidateSeconds}, stale-while-revalidate`;
|
|
504
508
|
extraHeaders["X-Vinext-Cache"] = "MISS";
|
|
505
509
|
}
|
|
506
510
|
if (allFontPreloads.length > 0) extraHeaders["Link"] = allFontPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
|
|
507
|
-
await streamPageToResponse(res, element, {
|
|
511
|
+
await streamPageToResponse(res, withScriptNonce(element, scriptNonce), {
|
|
508
512
|
url,
|
|
509
513
|
server,
|
|
510
514
|
fontHeadHTML,
|
|
@@ -516,13 +520,13 @@ hydrate();
|
|
|
516
520
|
});
|
|
517
521
|
_renderEnd = now();
|
|
518
522
|
if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext(null);
|
|
519
|
-
if (isrRevalidateSeconds !== null && isrRevalidateSeconds > 0) {
|
|
523
|
+
if (!scriptNonce && isrRevalidateSeconds !== null && isrRevalidateSeconds > 0) {
|
|
520
524
|
let isrElement = AppComponent ? createElement(AppComponent, {
|
|
521
525
|
Component: pageModule.default,
|
|
522
526
|
pageProps
|
|
523
527
|
}) : createElement(pageModule.default, pageProps);
|
|
524
528
|
if (wrapWithRouterContext) isrElement = wrapWithRouterContext(isrElement);
|
|
525
|
-
const isrHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${await renderIsrPassToStringAsync(isrElement)}</div>${allScripts}</body></html>`;
|
|
529
|
+
const isrHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${await renderIsrPassToStringAsync(withScriptNonce(isrElement, scriptNonce))}</div>${allScripts}</body></html>`;
|
|
526
530
|
const cacheKey = isrCacheKey("pages", url.split("?")[0], process.env.__VINEXT_BUILD_ID);
|
|
527
531
|
await isrSet(cacheKey, buildPagesCacheValue(isrHtml, pageProps), isrRevalidateSeconds);
|
|
528
532
|
setRevalidateDuration(cacheKey, isrRevalidateSeconds);
|