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.
Files changed (201) hide show
  1. package/README.md +1 -2
  2. package/dist/build/client-build-config.d.ts +119 -0
  3. package/dist/build/client-build-config.js +149 -0
  4. package/dist/build/client-build-config.js.map +1 -0
  5. package/dist/build/layout-classification-types.d.ts +62 -0
  6. package/dist/build/layout-classification-types.js +1 -0
  7. package/dist/build/layout-classification.d.ts +60 -0
  8. package/dist/build/layout-classification.js +98 -0
  9. package/dist/build/layout-classification.js.map +1 -0
  10. package/dist/build/report.d.ts +15 -1
  11. package/dist/build/report.js +50 -1
  12. package/dist/build/report.js.map +1 -1
  13. package/dist/build/route-classification-manifest.d.ts +53 -0
  14. package/dist/build/route-classification-manifest.js +145 -0
  15. package/dist/build/route-classification-manifest.js.map +1 -0
  16. package/dist/build/run-prerender.js +1 -1
  17. package/dist/build/ssr-manifest.d.ts +19 -0
  18. package/dist/build/ssr-manifest.js +71 -0
  19. package/dist/build/ssr-manifest.js.map +1 -0
  20. package/dist/check.js +4 -4
  21. package/dist/check.js.map +1 -1
  22. package/dist/cli.js +1 -1
  23. package/dist/cli.js.map +1 -1
  24. package/dist/client/entry.js +1 -1
  25. package/dist/config/config-matchers.js +1 -0
  26. package/dist/config/config-matchers.js.map +1 -1
  27. package/dist/entries/app-rsc-entry.js +341 -114
  28. package/dist/entries/app-rsc-entry.js.map +1 -1
  29. package/dist/entries/pages-server-entry.js +205 -199
  30. package/dist/entries/pages-server-entry.js.map +1 -1
  31. package/dist/index.d.ts +1 -169
  32. package/dist/index.js +113 -432
  33. package/dist/index.js.map +1 -1
  34. package/dist/init.d.ts +1 -1
  35. package/dist/init.js +2 -2
  36. package/dist/init.js.map +1 -1
  37. package/dist/plugins/fonts.d.ts +49 -1
  38. package/dist/plugins/fonts.js +97 -3
  39. package/dist/plugins/fonts.js.map +1 -1
  40. package/dist/plugins/postcss.d.ts +27 -0
  41. package/dist/plugins/postcss.js +94 -0
  42. package/dist/plugins/postcss.js.map +1 -0
  43. package/dist/plugins/strip-server-exports.d.ts +14 -0
  44. package/dist/plugins/strip-server-exports.js +73 -0
  45. package/dist/plugins/strip-server-exports.js.map +1 -0
  46. package/dist/routing/app-router.d.ts +6 -4
  47. package/dist/routing/app-router.js +21 -22
  48. package/dist/routing/app-router.js.map +1 -1
  49. package/dist/server/app-browser-entry.js +235 -97
  50. package/dist/server/app-browser-entry.js.map +1 -1
  51. package/dist/server/app-browser-error.d.ts +8 -0
  52. package/dist/server/app-browser-error.js +9 -0
  53. package/dist/server/app-browser-error.js.map +1 -0
  54. package/dist/server/app-browser-state.d.ts +93 -0
  55. package/dist/server/app-browser-state.js +132 -0
  56. package/dist/server/app-browser-state.js.map +1 -0
  57. package/dist/server/app-elements.d.ts +92 -0
  58. package/dist/server/app-elements.js +122 -0
  59. package/dist/server/app-elements.js.map +1 -0
  60. package/dist/server/app-page-boundary-render.d.ts +3 -1
  61. package/dist/server/app-page-boundary-render.js +41 -1
  62. package/dist/server/app-page-boundary-render.js.map +1 -1
  63. package/dist/server/app-page-cache.d.ts +6 -3
  64. package/dist/server/app-page-cache.js +14 -8
  65. package/dist/server/app-page-cache.js.map +1 -1
  66. package/dist/server/app-page-execution.d.ts +36 -3
  67. package/dist/server/app-page-execution.js +50 -10
  68. package/dist/server/app-page-execution.js.map +1 -1
  69. package/dist/server/app-page-probe.d.ts +10 -4
  70. package/dist/server/app-page-probe.js +24 -15
  71. package/dist/server/app-page-probe.js.map +1 -1
  72. package/dist/server/app-page-render.d.ts +8 -4
  73. package/dist/server/app-page-render.js +15 -4
  74. package/dist/server/app-page-render.js.map +1 -1
  75. package/dist/server/app-page-request.d.ts +52 -4
  76. package/dist/server/app-page-request.js +86 -16
  77. package/dist/server/app-page-request.js.map +1 -1
  78. package/dist/server/app-page-response.d.ts +4 -11
  79. package/dist/server/app-page-response.js +7 -19
  80. package/dist/server/app-page-response.js.map +1 -1
  81. package/dist/server/app-page-route-wiring.d.ts +22 -8
  82. package/dist/server/app-page-route-wiring.js +219 -81
  83. package/dist/server/app-page-route-wiring.js.map +1 -1
  84. package/dist/server/app-page-stream.d.ts +4 -1
  85. package/dist/server/app-page-stream.js +2 -1
  86. package/dist/server/app-page-stream.js.map +1 -1
  87. package/dist/server/app-render-dependency.d.ts +13 -0
  88. package/dist/server/app-render-dependency.js +35 -0
  89. package/dist/server/app-render-dependency.js.map +1 -0
  90. package/dist/server/app-route-handler-execution.d.ts +1 -0
  91. package/dist/server/app-route-handler-execution.js +1 -0
  92. package/dist/server/app-route-handler-execution.js.map +1 -1
  93. package/dist/server/app-route-handler-response.js +2 -1
  94. package/dist/server/app-route-handler-response.js.map +1 -1
  95. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  96. package/dist/server/app-route-handler-runtime.js +26 -1
  97. package/dist/server/app-route-handler-runtime.js.map +1 -1
  98. package/dist/server/app-ssr-entry.d.ts +3 -1
  99. package/dist/server/app-ssr-entry.js +23 -19
  100. package/dist/server/app-ssr-entry.js.map +1 -1
  101. package/dist/server/app-ssr-stream.d.ts +1 -1
  102. package/dist/server/app-ssr-stream.js +4 -4
  103. package/dist/server/app-ssr-stream.js.map +1 -1
  104. package/dist/server/csp.d.ts +12 -0
  105. package/dist/server/csp.js +46 -0
  106. package/dist/server/csp.js.map +1 -0
  107. package/dist/server/dev-server.js +22 -18
  108. package/dist/server/dev-server.js.map +1 -1
  109. package/dist/server/html.d.ts +4 -1
  110. package/dist/server/html.js +11 -1
  111. package/dist/server/html.js.map +1 -1
  112. package/dist/server/middleware-response-headers.d.ts +12 -0
  113. package/dist/server/middleware-response-headers.js +23 -0
  114. package/dist/server/middleware-response-headers.js.map +1 -0
  115. package/dist/server/middleware.js +1 -5
  116. package/dist/server/middleware.js.map +1 -1
  117. package/dist/server/pages-page-data.d.ts +1 -0
  118. package/dist/server/pages-page-data.js +2 -2
  119. package/dist/server/pages-page-data.js.map +1 -1
  120. package/dist/server/pages-page-response.d.ts +2 -1
  121. package/dist/server/pages-page-response.js +16 -14
  122. package/dist/server/pages-page-response.js.map +1 -1
  123. package/dist/server/prod-server.d.ts +3 -3
  124. package/dist/server/prod-server.js +5 -4
  125. package/dist/server/prod-server.js.map +1 -1
  126. package/dist/server/request-pipeline.d.ts +15 -1
  127. package/dist/server/request-pipeline.js +88 -5
  128. package/dist/server/request-pipeline.js.map +1 -1
  129. package/dist/shims/cache-runtime.d.ts +1 -0
  130. package/dist/shims/cache-runtime.js +0 -5
  131. package/dist/shims/cache-runtime.js.map +1 -1
  132. package/dist/shims/cache.d.ts +1 -0
  133. package/dist/shims/cache.js +1 -8
  134. package/dist/shims/cache.js.map +1 -1
  135. package/dist/shims/client-hook-error.d.ts +14 -0
  136. package/dist/shims/client-hook-error.js +19 -0
  137. package/dist/shims/client-hook-error.js.map +1 -0
  138. package/dist/shims/constants.d.ts +3 -3
  139. package/dist/shims/constants.js +3 -3
  140. package/dist/shims/constants.js.map +1 -1
  141. package/dist/shims/document.d.ts +6 -6
  142. package/dist/shims/error-boundary.d.ts +4 -4
  143. package/dist/shims/error-boundary.js +1 -1
  144. package/dist/shims/error-boundary.js.map +1 -1
  145. package/dist/shims/form.d.ts +3 -3
  146. package/dist/shims/head-state.d.ts +1 -0
  147. package/dist/shims/head-state.js +0 -5
  148. package/dist/shims/head-state.js.map +1 -1
  149. package/dist/shims/headers.d.ts +11 -0
  150. package/dist/shims/headers.js +13 -10
  151. package/dist/shims/headers.js.map +1 -1
  152. package/dist/shims/i18n-state.d.ts +1 -0
  153. package/dist/shims/i18n-state.js +0 -4
  154. package/dist/shims/i18n-state.js.map +1 -1
  155. package/dist/shims/internal/app-router-context.d.ts +6 -6
  156. package/dist/shims/internal/router-context.d.ts +2 -2
  157. package/dist/shims/layout-segment-context.d.ts +2 -2
  158. package/dist/shims/link.js +19 -11
  159. package/dist/shims/link.js.map +1 -1
  160. package/dist/shims/metadata.d.ts +3 -3
  161. package/dist/shims/navigation-state.d.ts +2 -0
  162. package/dist/shims/navigation-state.js +0 -13
  163. package/dist/shims/navigation-state.js.map +1 -1
  164. package/dist/shims/navigation.d.ts +55 -8
  165. package/dist/shims/navigation.js +97 -23
  166. package/dist/shims/navigation.js.map +1 -1
  167. package/dist/shims/navigation.react-server.d.ts +14 -0
  168. package/dist/shims/navigation.react-server.js +29 -0
  169. package/dist/shims/navigation.react-server.js.map +1 -0
  170. package/dist/shims/request-context.d.ts +1 -0
  171. package/dist/shims/request-context.js +0 -9
  172. package/dist/shims/request-context.js.map +1 -1
  173. package/dist/shims/request-state-types.d.ts +1 -1
  174. package/dist/shims/router-state.d.ts +1 -0
  175. package/dist/shims/router-state.js +0 -5
  176. package/dist/shims/router-state.js.map +1 -1
  177. package/dist/shims/script-nonce-context.d.ts +12 -0
  178. package/dist/shims/script-nonce-context.js +17 -0
  179. package/dist/shims/script-nonce-context.js.map +1 -0
  180. package/dist/shims/script.js +41 -10
  181. package/dist/shims/script.js.map +1 -1
  182. package/dist/shims/server.js +6 -1
  183. package/dist/shims/server.js.map +1 -1
  184. package/dist/shims/slot.d.ts +11 -7
  185. package/dist/shims/slot.js +28 -19
  186. package/dist/shims/slot.js.map +1 -1
  187. package/dist/shims/unified-request-context.d.ts +2 -0
  188. package/dist/shims/unified-request-context.js +0 -14
  189. package/dist/shims/unified-request-context.js.map +1 -1
  190. package/dist/shims/url-safety.js +25 -4
  191. package/dist/shims/url-safety.js.map +1 -1
  192. package/dist/utils/mdx-scan.d.ts +10 -0
  193. package/dist/utils/mdx-scan.js +36 -0
  194. package/dist/utils/mdx-scan.js.map +1 -0
  195. package/dist/utils/public-routes.d.ts +5 -0
  196. package/dist/utils/public-routes.js +50 -0
  197. package/dist/utils/public-routes.js.map +1 -0
  198. package/package.json +9 -9
  199. package/dist/plugins/fix-use-server-closure-collision.d.ts +0 -29
  200. package/dist/plugins/fix-use-server-closure-collision.js +0 -204
  201. 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, \"&amp;\").replace(/\"/g, \"&quot;\");\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>): RscEmbedTransform;
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 += "<script>self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(" + safeJsonStringify(chunk) + ")<\/script>";
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 += "<script>self.__VINEXT_RSC_DONE__=true<\/script>";
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 +=\n \"<script>self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(\" +\n safeJsonStringify(chunk) +\n \")</script>\";\n }\n return scripts;\n },\n\n async finalize(): Promise<string> {\n await pumpPromise;\n let scripts = this.flush();\n scripts += \"<script>self.__VINEXT_RSC_DONE__=true</script>\";\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,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,YACE,uGACA,kBAAkB,MAAM,GACxB;AAEJ,UAAO;;EAGT,MAAM,WAA4B;AAChC,SAAM;GACN,IAAI,UAAU,KAAK,OAAO;AAC1B,cAAW;AACX,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"}
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 { safeJsonStringify } from "./html.js";
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=${revalidateSecs}, stale-while-revalidate`
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=${revalidateSecs}, stale-while-revalidate`
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, "&amp;").replace(/"/g, "&quot;");
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, "&amp;").replace(/"/g, "&quot;");
444
447
  const safeType = type.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
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 = `<script>window.__NEXT_DATA__ = ${safeJsonStringify({
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)}` : ""}<\/script>`;
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);