vinext 0.0.53 → 0.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/README.md +1 -0
  2. package/dist/build/inline-css.d.ts +7 -0
  3. package/dist/build/inline-css.js +50 -0
  4. package/dist/build/inline-css.js.map +1 -0
  5. package/dist/build/prerender.js +2 -1
  6. package/dist/build/prerender.js.map +1 -1
  7. package/dist/check.js +19 -3
  8. package/dist/check.js.map +1 -1
  9. package/dist/client/navigation-runtime.d.ts +3 -1
  10. package/dist/client/navigation-runtime.js +1 -1
  11. package/dist/client/navigation-runtime.js.map +1 -1
  12. package/dist/client/window-next.d.ts +7 -0
  13. package/dist/client/window-next.js.map +1 -1
  14. package/dist/config/next-config.d.ts +97 -2
  15. package/dist/config/next-config.js +155 -6
  16. package/dist/config/next-config.js.map +1 -1
  17. package/dist/config/tsconfig-paths.d.ts +12 -3
  18. package/dist/config/tsconfig-paths.js +55 -24
  19. package/dist/config/tsconfig-paths.js.map +1 -1
  20. package/dist/deploy.js +13 -0
  21. package/dist/deploy.js.map +1 -1
  22. package/dist/entries/app-browser-entry.d.ts +11 -1
  23. package/dist/entries/app-browser-entry.js +16 -6
  24. package/dist/entries/app-browser-entry.js.map +1 -1
  25. package/dist/entries/app-rsc-entry.d.ts +9 -1
  26. package/dist/entries/app-rsc-entry.js +30 -5
  27. package/dist/entries/app-rsc-entry.js.map +1 -1
  28. package/dist/entries/app-rsc-manifest.d.ts +21 -1
  29. package/dist/entries/app-rsc-manifest.js +28 -9
  30. package/dist/entries/app-rsc-manifest.js.map +1 -1
  31. package/dist/entries/pages-client-entry.d.ts +4 -1
  32. package/dist/entries/pages-client-entry.js +18 -2
  33. package/dist/entries/pages-client-entry.js.map +1 -1
  34. package/dist/entries/pages-server-entry.js +123 -8
  35. package/dist/entries/pages-server-entry.js.map +1 -1
  36. package/dist/entries/runtime-entry-module.d.ts +1 -10
  37. package/dist/entries/runtime-entry-module.js +2 -12
  38. package/dist/entries/runtime-entry-module.js.map +1 -1
  39. package/dist/index.js +144 -44
  40. package/dist/index.js.map +1 -1
  41. package/dist/plugins/import-meta-url.d.ts +16 -0
  42. package/dist/plugins/import-meta-url.js +193 -0
  43. package/dist/plugins/import-meta-url.js.map +1 -0
  44. package/dist/plugins/remove-console.d.ts +16 -0
  45. package/dist/plugins/remove-console.js +176 -0
  46. package/dist/plugins/remove-console.js.map +1 -0
  47. package/dist/routing/app-route-graph.d.ts +24 -1
  48. package/dist/routing/app-route-graph.js +52 -4
  49. package/dist/routing/app-route-graph.js.map +1 -1
  50. package/dist/routing/app-router.d.ts +2 -2
  51. package/dist/routing/app-router.js +2 -2
  52. package/dist/routing/app-router.js.map +1 -1
  53. package/dist/routing/file-matcher.d.ts +21 -1
  54. package/dist/routing/file-matcher.js +39 -1
  55. package/dist/routing/file-matcher.js.map +1 -1
  56. package/dist/routing/pages-router.d.ts +1 -1
  57. package/dist/routing/pages-router.js +10 -3
  58. package/dist/routing/pages-router.js.map +1 -1
  59. package/dist/server/api-handler.js +1 -1
  60. package/dist/server/app-browser-action-result.d.ts +9 -16
  61. package/dist/server/app-browser-action-result.js +25 -14
  62. package/dist/server/app-browser-action-result.js.map +1 -1
  63. package/dist/server/app-browser-entry.js +195 -60
  64. package/dist/server/app-browser-entry.js.map +1 -1
  65. package/dist/server/app-browser-mpa-navigation.d.ts +16 -0
  66. package/dist/server/app-browser-mpa-navigation.js +36 -0
  67. package/dist/server/app-browser-mpa-navigation.js.map +1 -0
  68. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  69. package/dist/server/app-browser-navigation-controller.js +4 -0
  70. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  71. package/dist/server/app-browser-popstate.d.ts +3 -1
  72. package/dist/server/app-browser-popstate.js +15 -1
  73. package/dist/server/app-browser-popstate.js.map +1 -1
  74. package/dist/server/app-browser-state.js +2 -1
  75. package/dist/server/app-browser-state.js.map +1 -1
  76. package/dist/server/app-elements-wire.d.ts +13 -4
  77. package/dist/server/app-elements-wire.js +10 -1
  78. package/dist/server/app-elements-wire.js.map +1 -1
  79. package/dist/server/app-elements.d.ts +2 -2
  80. package/dist/server/app-elements.js +2 -2
  81. package/dist/server/app-elements.js.map +1 -1
  82. package/dist/server/app-fallback-renderer.d.ts +15 -5
  83. package/dist/server/app-fallback-renderer.js +10 -4
  84. package/dist/server/app-fallback-renderer.js.map +1 -1
  85. package/dist/server/app-inline-css-client.d.ts +7 -0
  86. package/dist/server/app-inline-css-client.js +37 -0
  87. package/dist/server/app-inline-css-client.js.map +1 -0
  88. package/dist/server/app-layout-param-observation.d.ts +30 -0
  89. package/dist/server/app-layout-param-observation.js +130 -0
  90. package/dist/server/app-layout-param-observation.js.map +1 -0
  91. package/dist/server/app-page-boundary-render.js +2 -2
  92. package/dist/server/app-page-boundary-render.js.map +1 -1
  93. package/dist/server/app-page-boundary.d.ts +21 -1
  94. package/dist/server/app-page-boundary.js +28 -3
  95. package/dist/server/app-page-boundary.js.map +1 -1
  96. package/dist/server/app-page-cache.d.ts +7 -3
  97. package/dist/server/app-page-cache.js +7 -7
  98. package/dist/server/app-page-cache.js.map +1 -1
  99. package/dist/server/app-page-dispatch.d.ts +10 -1
  100. package/dist/server/app-page-dispatch.js +126 -79
  101. package/dist/server/app-page-dispatch.js.map +1 -1
  102. package/dist/server/app-page-element-builder.js +12 -28
  103. package/dist/server/app-page-element-builder.js.map +1 -1
  104. package/dist/server/app-page-params.d.ts +2 -1
  105. package/dist/server/app-page-params.js +14 -1
  106. package/dist/server/app-page-params.js.map +1 -1
  107. package/dist/server/app-page-probe.d.ts +12 -1
  108. package/dist/server/app-page-probe.js +116 -1
  109. package/dist/server/app-page-probe.js.map +1 -1
  110. package/dist/server/app-page-render-identity.d.ts +22 -0
  111. package/dist/server/app-page-render-identity.js +42 -0
  112. package/dist/server/app-page-render-identity.js.map +1 -0
  113. package/dist/server/app-page-render.d.ts +8 -1
  114. package/dist/server/app-page-render.js +4 -1
  115. package/dist/server/app-page-render.js.map +1 -1
  116. package/dist/server/app-page-request.d.ts +6 -3
  117. package/dist/server/app-page-request.js +5 -2
  118. package/dist/server/app-page-request.js.map +1 -1
  119. package/dist/server/app-page-response.js +2 -2
  120. package/dist/server/app-page-response.js.map +1 -1
  121. package/dist/server/app-page-route-wiring.d.ts +15 -0
  122. package/dist/server/app-page-route-wiring.js +7 -5
  123. package/dist/server/app-page-route-wiring.js.map +1 -1
  124. package/dist/server/app-page-stream.d.ts +11 -0
  125. package/dist/server/app-page-stream.js +1 -0
  126. package/dist/server/app-page-stream.js.map +1 -1
  127. package/dist/server/app-route-handler-response.js +37 -5
  128. package/dist/server/app-route-handler-response.js.map +1 -1
  129. package/dist/server/app-rsc-cache-busting.d.ts +3 -2
  130. package/dist/server/app-rsc-cache-busting.js +9 -7
  131. package/dist/server/app-rsc-cache-busting.js.map +1 -1
  132. package/dist/server/app-rsc-handler.d.ts +14 -3
  133. package/dist/server/app-rsc-handler.js +56 -6
  134. package/dist/server/app-rsc-handler.js.map +1 -1
  135. package/dist/server/app-rsc-request-normalization.d.ts +2 -1
  136. package/dist/server/app-rsc-request-normalization.js +3 -2
  137. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  138. package/dist/server/app-segment-config.d.ts +1 -1
  139. package/dist/server/app-segment-config.js +4 -1
  140. package/dist/server/app-segment-config.js.map +1 -1
  141. package/dist/server/app-server-action-execution.d.ts +26 -3
  142. package/dist/server/app-server-action-execution.js +240 -29
  143. package/dist/server/app-server-action-execution.js.map +1 -1
  144. package/dist/server/app-ssr-entry.d.ts +6 -0
  145. package/dist/server/app-ssr-entry.js +22 -7
  146. package/dist/server/app-ssr-entry.js.map +1 -1
  147. package/dist/server/app-ssr-error-meta.js +3 -3
  148. package/dist/server/app-ssr-error-meta.js.map +1 -1
  149. package/dist/server/app-ssr-stream.d.ts +2 -1
  150. package/dist/server/app-ssr-stream.js +176 -31
  151. package/dist/server/app-ssr-stream.js.map +1 -1
  152. package/dist/server/artifact-compatibility.d.ts +2 -1
  153. package/dist/server/artifact-compatibility.js +10 -1
  154. package/dist/server/artifact-compatibility.js.map +1 -1
  155. package/dist/server/client-reuse-manifest.d.ts +9 -4
  156. package/dist/server/client-reuse-manifest.js +2 -1
  157. package/dist/server/client-reuse-manifest.js.map +1 -1
  158. package/dist/server/client-trace-metadata.d.ts +31 -0
  159. package/dist/server/client-trace-metadata.js +83 -0
  160. package/dist/server/client-trace-metadata.js.map +1 -0
  161. package/dist/server/cookie-utils.d.ts +13 -0
  162. package/dist/server/cookie-utils.js +20 -0
  163. package/dist/server/cookie-utils.js.map +1 -0
  164. package/dist/server/dev-server.d.ts +8 -1
  165. package/dist/server/dev-server.js +83 -12
  166. package/dist/server/dev-server.js.map +1 -1
  167. package/dist/server/document-initial-head.d.ts +7 -0
  168. package/dist/server/document-initial-head.js +35 -0
  169. package/dist/server/document-initial-head.js.map +1 -0
  170. package/dist/server/html.d.ts +2 -1
  171. package/dist/server/html.js +6 -1
  172. package/dist/server/html.js.map +1 -1
  173. package/dist/server/isr-cache.d.ts +7 -5
  174. package/dist/server/isr-cache.js +17 -6
  175. package/dist/server/isr-cache.js.map +1 -1
  176. package/dist/server/middleware-runtime.js +1 -2
  177. package/dist/server/middleware-runtime.js.map +1 -1
  178. package/dist/server/pages-document-initial-props.d.ts +89 -0
  179. package/dist/server/pages-document-initial-props.js +140 -0
  180. package/dist/server/pages-document-initial-props.js.map +1 -0
  181. package/dist/server/pages-node-compat.js +1 -1
  182. package/dist/server/pages-page-data.js +3 -0
  183. package/dist/server/pages-page-data.js.map +1 -1
  184. package/dist/server/pages-page-method.d.ts +48 -0
  185. package/dist/server/pages-page-method.js +19 -0
  186. package/dist/server/pages-page-method.js.map +1 -0
  187. package/dist/server/pages-page-response.d.ts +20 -0
  188. package/dist/server/pages-page-response.js +37 -7
  189. package/dist/server/pages-page-response.js.map +1 -1
  190. package/dist/server/pages-serializable-props.d.ts +25 -0
  191. package/dist/server/pages-serializable-props.js +69 -0
  192. package/dist/server/pages-serializable-props.js.map +1 -0
  193. package/dist/server/prod-server.js +16 -6
  194. package/dist/server/prod-server.js.map +1 -1
  195. package/dist/server/server-action-not-found.js +3 -2
  196. package/dist/server/server-action-not-found.js.map +1 -1
  197. package/dist/server/skip-cache-proof.d.ts +23 -2
  198. package/dist/server/skip-cache-proof.js +81 -12
  199. package/dist/server/skip-cache-proof.js.map +1 -1
  200. package/dist/server/static-file-cache.js +2 -1
  201. package/dist/server/static-file-cache.js.map +1 -1
  202. package/dist/server/static-layout-client-reuse-proof.d.ts +16 -0
  203. package/dist/server/static-layout-client-reuse-proof.js +35 -0
  204. package/dist/server/static-layout-client-reuse-proof.js.map +1 -0
  205. package/dist/shims/app-router-scroll-state.d.ts +4 -2
  206. package/dist/shims/app-router-scroll-state.js +16 -3
  207. package/dist/shims/app-router-scroll-state.js.map +1 -1
  208. package/dist/shims/app-router-scroll.d.ts +16 -2
  209. package/dist/shims/app-router-scroll.js +18 -3
  210. package/dist/shims/app-router-scroll.js.map +1 -1
  211. package/dist/shims/cache.d.ts +27 -1
  212. package/dist/shims/cache.js +108 -6
  213. package/dist/shims/cache.js.map +1 -1
  214. package/dist/shims/document.d.ts +6 -0
  215. package/dist/shims/document.js +7 -8
  216. package/dist/shims/document.js.map +1 -1
  217. package/dist/shims/error-boundary.d.ts +4 -4
  218. package/dist/shims/error-boundary.js +27 -28
  219. package/dist/shims/error-boundary.js.map +1 -1
  220. package/dist/shims/error.js +3 -0
  221. package/dist/shims/error.js.map +1 -1
  222. package/dist/shims/fetch-cache.d.ts +3 -1
  223. package/dist/shims/fetch-cache.js +16 -5
  224. package/dist/shims/fetch-cache.js.map +1 -1
  225. package/dist/shims/hash-scroll.d.ts +4 -1
  226. package/dist/shims/hash-scroll.js +13 -1
  227. package/dist/shims/hash-scroll.js.map +1 -1
  228. package/dist/shims/head-state.d.ts +1 -0
  229. package/dist/shims/head-state.js +18 -3
  230. package/dist/shims/head-state.js.map +1 -1
  231. package/dist/shims/head.d.ts +35 -1
  232. package/dist/shims/head.js +113 -14
  233. package/dist/shims/head.js.map +1 -1
  234. package/dist/shims/headers.d.ts +7 -0
  235. package/dist/shims/headers.js +9 -1
  236. package/dist/shims/headers.js.map +1 -1
  237. package/dist/shims/internal/app-route-detection.d.ts +37 -0
  238. package/dist/shims/internal/app-route-detection.js +69 -0
  239. package/dist/shims/internal/app-route-detection.js.map +1 -0
  240. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +56 -0
  241. package/dist/shims/internal/pages-data-fetch-dedup.js +70 -0
  242. package/dist/shims/internal/pages-data-fetch-dedup.js.map +1 -0
  243. package/dist/shims/link.d.ts +18 -2
  244. package/dist/shims/link.js +98 -8
  245. package/dist/shims/link.js.map +1 -1
  246. package/dist/shims/metadata.d.ts +7 -6
  247. package/dist/shims/metadata.js +9 -5
  248. package/dist/shims/metadata.js.map +1 -1
  249. package/dist/shims/navigation.d.ts +40 -3
  250. package/dist/shims/navigation.js +124 -25
  251. package/dist/shims/navigation.js.map +1 -1
  252. package/dist/shims/router.d.ts +5 -0
  253. package/dist/shims/router.js +51 -21
  254. package/dist/shims/router.js.map +1 -1
  255. package/dist/shims/script.d.ts +11 -1
  256. package/dist/shims/script.js +75 -6
  257. package/dist/shims/script.js.map +1 -1
  258. package/dist/shims/thenable-params.d.ts +5 -2
  259. package/dist/shims/thenable-params.js +25 -1
  260. package/dist/shims/thenable-params.js.map +1 -1
  261. package/dist/shims/unified-request-context.js +3 -0
  262. package/dist/shims/unified-request-context.js.map +1 -1
  263. package/dist/utils/client-build-manifest.d.ts +15 -0
  264. package/dist/utils/client-build-manifest.js +54 -0
  265. package/dist/utils/client-build-manifest.js.map +1 -0
  266. package/dist/utils/hash.js +1 -1
  267. package/dist/utils/hash.js.map +1 -1
  268. package/dist/utils/lazy-chunks.d.ts +1 -1
  269. package/dist/utils/lazy-chunks.js.map +1 -1
  270. package/dist/utils/path.d.ts +13 -0
  271. package/dist/utils/path.js +16 -0
  272. package/dist/utils/path.js.map +1 -0
  273. package/dist/utils/vite-version.d.ts +11 -0
  274. package/dist/utils/vite-version.js +36 -0
  275. package/dist/utils/vite-version.js.map +1 -0
  276. package/package.json +2 -2
@@ -33,6 +33,46 @@ function resolveScriptNonce(explicitNonce, contextualNonce) {
33
33
  if (typeof window === "undefined") return;
34
34
  return getClientAutoNonce();
35
35
  }
36
+ /**
37
+ * Insert `<link rel="stylesheet">` tags into `document.head` for each entry
38
+ * in `stylesheets`. Used by the imperative client-side load path
39
+ * (`handleClientScriptLoad`) when `ReactDOM.preinit` is not available
40
+ * (e.g. pre-Float React or hosts that strip it). Mirrors Next.js's
41
+ * `insertStylesheets` Pages-Router fallback at
42
+ * `.nextjs-ref/packages/next/src/client/script.tsx:48-59`.
43
+ *
44
+ * The `ReactDOM.preinit` path is preferred where available — it dedupes
45
+ * across mounts and respects React Float's hoisting order. This DOM
46
+ * fallback is best-effort: no dedupe, no ordering guarantee.
47
+ */
48
+ function insertClientStylesheets(stylesheets) {
49
+ if (!stylesheets || stylesheets.length === 0) return;
50
+ if (typeof document === "undefined") return;
51
+ if (typeof ReactDOM.preinit === "function") {
52
+ for (const href of stylesheets) ReactDOM.preinit(href, { as: "style" });
53
+ return;
54
+ }
55
+ const head = document.head;
56
+ if (!head) return;
57
+ for (const href of stylesheets) {
58
+ const link = document.createElement("link");
59
+ link.rel = "stylesheet";
60
+ link.type = "text/css";
61
+ link.href = href;
62
+ head.appendChild(link);
63
+ }
64
+ }
65
+ /**
66
+ * Emit `<link rel="stylesheet">` tags during SSR for each entry in
67
+ * `stylesheets` via `ReactDOM.preinit`. React Float hoists these into
68
+ * `<head>` in the streamed HTML. Mirrors the App-Router branch of
69
+ * Next.js's Script component at `.nextjs-ref/packages/next/src/client/script.tsx:309-313`.
70
+ */
71
+ function preinitStylesheetsForSSR(stylesheets) {
72
+ if (!stylesheets || stylesheets.length === 0) return;
73
+ if (typeof ReactDOM.preinit !== "function") return;
74
+ for (const href of stylesheets) ReactDOM.preinit(href, { as: "style" });
75
+ }
36
76
  function buildBeforeInteractiveScriptProps(options) {
37
77
  const scriptProps = { ...options.rest };
38
78
  if (options.src) scriptProps.src = options.src;
@@ -62,11 +102,30 @@ function extractBeforeInteractiveInlineContent(children, dangerouslySetInnerHTML
62
102
  return null;
63
103
  }
64
104
  /**
105
+ * Map of React DOM prop names to their HTML attribute equivalents. Mirrors
106
+ * Next.js's `set-attributes-from-props.ts`:
107
+ * .nextjs-ref/packages/next/src/client/set-attributes-from-props.ts
108
+ * HTML parses attribute names case-insensitively, so without this translation
109
+ * `className="foo"` round-trips as `classname="foo"` and CSS selectors on
110
+ * `.foo` never match. Same hazard for `htmlFor`/`for`, `httpEquiv`/`http-equiv`,
111
+ * `acceptCharset`/`accept-charset`.
112
+ */
113
+ const REACT_TO_HTML_ATTR = {
114
+ acceptCharset: "accept-charset",
115
+ className: "class",
116
+ htmlFor: "for",
117
+ httpEquiv: "http-equiv"
118
+ };
119
+ /**
65
120
  * Convert the residual `<Script>` props into a plain string-attributes record
66
121
  * for emission inside a hoisted `<script>` tag. Drops React-only props
67
122
  * (event handlers, children, etc.) and reserved keys already handled by the
68
123
  * pre-head-injection emitter (id, nonce). Skips `undefined`/`null` so they
69
124
  * round-trip as "attribute absent" rather than `attr="undefined"`.
125
+ *
126
+ * React DOM prop names (className, htmlFor, etc.) are translated to their
127
+ * HTML attribute names so the output parses correctly — see comment on
128
+ * `REACT_TO_HTML_ATTR`.
70
129
  */
71
130
  function collectBeforeInteractiveAttributes(rest) {
72
131
  const RESERVED = new Set([
@@ -78,18 +137,20 @@ function collectBeforeInteractiveAttributes(rest) {
78
137
  "dangerouslySetInnerHTML",
79
138
  "onLoad",
80
139
  "onReady",
81
- "onError"
140
+ "onError",
141
+ "stylesheets"
82
142
  ]);
83
143
  const out = {};
84
144
  for (const [key, value] of Object.entries(rest)) {
85
145
  if (RESERVED.has(key)) continue;
86
146
  if (value === void 0 || value === null || value === false) continue;
147
+ const attrName = REACT_TO_HTML_ATTR[key] ?? key;
87
148
  if (typeof value === "boolean") {
88
- out[key] = true;
149
+ out[attrName] = true;
89
150
  continue;
90
151
  }
91
152
  if (typeof value === "string" || typeof value === "number") {
92
- out[key] = String(value);
153
+ out[attrName] = String(value);
93
154
  continue;
94
155
  }
95
156
  }
@@ -127,8 +188,9 @@ function setScriptAttributes(el, rest) {
127
188
  }
128
189
  }
129
190
  function loadClientScript(props, options) {
130
- const { src, id, onLoad, onReady, onError, strategy = "afterInteractive", children, dangerouslySetInnerHTML, ...rest } = props;
191
+ const { src, id, onLoad, onReady, onError, strategy = "afterInteractive", children, dangerouslySetInnerHTML, stylesheets, ...rest } = props;
131
192
  if (typeof window === "undefined") return;
193
+ insertClientStylesheets(stylesheets);
132
194
  const key = id ?? src ?? "";
133
195
  if (key && loadedScripts.has(key)) {
134
196
  if (options.fireReadyWhenAlreadyLoaded) onReady?.();
@@ -195,7 +257,7 @@ function initScriptLoader(scripts) {
195
257
  for (const script of scripts) handleClientScriptLoad(script);
196
258
  }
197
259
  function Script(props) {
198
- const { src, id, strategy = "afterInteractive", onLoad, onReady, onError, children, dangerouslySetInnerHTML, ...rest } = props;
260
+ const { src, id, strategy = "afterInteractive", onLoad, onReady, onError, children, dangerouslySetInnerHTML, stylesheets, ...rest } = props;
199
261
  const hasMounted = useRef(false);
200
262
  const key = id ?? src ?? "";
201
263
  const contextualNonce = useScriptNonce();
@@ -204,8 +266,12 @@ function Script(props) {
204
266
  useEffect(() => {
205
267
  if (hasMounted.current) return;
206
268
  hasMounted.current = true;
207
- if (strategy === "beforeInteractive") return;
269
+ if (strategy === "beforeInteractive") {
270
+ insertClientStylesheets(stylesheets);
271
+ return;
272
+ }
208
273
  if (key && loadedScripts.has(key)) {
274
+ insertClientStylesheets(stylesheets);
209
275
  onReady?.();
210
276
  return;
211
277
  }
@@ -223,6 +289,7 @@ function Script(props) {
223
289
  onError,
224
290
  children,
225
291
  dangerouslySetInnerHTML,
292
+ stylesheets,
226
293
  ...rest
227
294
  }, {
228
295
  resolvedNonce,
@@ -245,11 +312,13 @@ function Script(props) {
245
312
  onError,
246
313
  children,
247
314
  dangerouslySetInnerHTML,
315
+ stylesheets,
248
316
  key,
249
317
  resolvedNonce,
250
318
  rest
251
319
  ]);
252
320
  if (typeof window === "undefined") {
321
+ preinitStylesheetsForSSR(stylesheets);
253
322
  if (src && typeof ReactDOM.preload === "function" && (strategy === "afterInteractive" || strategy === "beforeInteractive")) {
254
323
  const integrity = typeof rest.integrity === "string" ? rest.integrity : void 0;
255
324
  const preloadOptions = {
@@ -1 +1 @@
1
- {"version":3,"file":"script.js","names":[],"sources":["../../src/shims/script.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * next/script shim\n *\n * Provides the <Script> component for loading third-party scripts with\n * configurable loading strategies.\n *\n * Strategies:\n * - \"beforeInteractive\": rendered as a <script> tag in SSR output\n * - \"afterInteractive\" (default): loaded client-side after hydration\n * - \"lazyOnload\": deferred until window.load + requestIdleCallback\n * - \"worker\": sets type=\"text/partytown\" (requires Partytown setup)\n */\nimport React, { useEffect, useRef } from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { hasAppNavigationRuntimeBootstrap } from \"../client/navigation-runtime.js\";\nimport { escapeInlineContent } from \"./head.js\";\nimport { useScriptNonce } from \"./script-nonce-context.js\";\nimport {\n useBeforeInteractiveRegister,\n type BeforeInteractiveInlineScript,\n} from \"./before-interactive-context.js\";\n\nexport type ScriptProps = {\n /** Script source URL */\n src?: string;\n /** Loading strategy. Default: \"afterInteractive\" */\n strategy?: \"beforeInteractive\" | \"afterInteractive\" | \"lazyOnload\" | \"worker\";\n /** Unique identifier for the script */\n id?: string;\n /** Called when the script has loaded */\n onLoad?: (e: Event) => void;\n /** Called when the script is ready (after load, and on every re-render if already loaded) */\n onReady?: () => void;\n /** Called on script load error */\n onError?: (e: Event) => void;\n /** Inline script content */\n children?: React.ReactNode;\n /** Dangerous inner HTML */\n dangerouslySetInnerHTML?: { __html: string };\n /** Script type attribute */\n type?: string;\n /** Async attribute */\n async?: boolean;\n /** Defer attribute */\n defer?: boolean;\n /** Crossorigin attribute */\n crossOrigin?: string;\n /** Nonce for CSP */\n nonce?: string;\n /** Integrity hash */\n integrity?: string;\n /** Additional attributes */\n [key: string]: unknown;\n};\n\n// Track scripts that have already been loaded, plus remote scripts currently\n// loading, to avoid duplicate DOM insertion when same-src components mount\n// before the first load event fires.\nconst loadedScripts = new Set<string>();\nconst loadingScripts = new Map<string, Promise<Event>>();\n\nfunction getClientAutoNonce(): string | undefined {\n if (typeof document === \"undefined\") return undefined;\n\n const existingNonceElement = document.querySelector(\"[nonce]\");\n if (!existingNonceElement) return undefined;\n\n // `HTMLElement` is not defined in some SSR/edge runtimes that polyfill\n // `document` but stop short of the full DOM surface. Guarding the\n // constructor before `instanceof` keeps SSR from crashing in those hosts;\n // when the constructor *is* present we still prefer the typed `.nonce`\n // property because browsers strip the `nonce` attribute from serialised\n // HTML for CSP reasons.\n if (typeof HTMLElement !== \"undefined\" && existingNonceElement instanceof HTMLElement) {\n return existingNonceElement.nonce || existingNonceElement.getAttribute(\"nonce\") || undefined;\n }\n\n return existingNonceElement.getAttribute(\"nonce\") || undefined;\n}\n\nfunction resolveScriptNonce(explicitNonce: unknown, contextualNonce?: string): string | undefined {\n if (typeof explicitNonce === \"string\" && explicitNonce.length > 0) {\n return explicitNonce;\n }\n\n if (typeof contextualNonce === \"string\" && contextualNonce.length > 0) {\n return contextualNonce;\n }\n\n if (typeof window === \"undefined\") {\n return undefined;\n }\n\n return getClientAutoNonce();\n}\n\nfunction buildBeforeInteractiveScriptProps(options: {\n src?: string;\n id?: string;\n rest: Record<string, unknown>;\n resolvedNonce?: string;\n dangerouslySetInnerHTML?: { __html: string };\n}): Record<string, unknown> {\n const scriptProps: Record<string, unknown> = { ...options.rest };\n if (options.src) scriptProps.src = options.src;\n if (options.id) scriptProps.id = options.id;\n if (options.resolvedNonce) {\n scriptProps.nonce = options.resolvedNonce;\n }\n if (options.dangerouslySetInnerHTML) {\n scriptProps.dangerouslySetInnerHTML = {\n __html: escapeInlineContent(options.dangerouslySetInnerHTML.__html, \"script\"),\n };\n }\n return scriptProps;\n}\n\n/**\n * Extract the inline script content for a `beforeInteractive` Script element\n * with no `src`. Returns `null` when the element has neither a string-shaped\n * `children` value nor a valid `dangerouslySetInnerHTML.__html` payload — in\n * that case the caller should fall through to React's regular rendering path.\n *\n * The returned string is the raw author-supplied JavaScript content. Callers\n * are responsible for passing it through `escapeInlineContent(..., \"script\")`\n * before emitting it inside a `<script>` tag (we keep that escape adjacent\n * to the emit point so the rule is obvious at the boundary).\n */\nfunction extractBeforeInteractiveInlineContent(\n children: React.ReactNode,\n dangerouslySetInnerHTML?: { __html: string },\n): string | null {\n if (\n dangerouslySetInnerHTML &&\n typeof dangerouslySetInnerHTML.__html === \"string\" &&\n dangerouslySetInnerHTML.__html.length > 0\n ) {\n return dangerouslySetInnerHTML.__html;\n }\n if (typeof children === \"string\" && children.length > 0) {\n return children;\n }\n if (Array.isArray(children) && children.every((c) => typeof c === \"string\")) {\n const joined = (children as string[]).join(\"\");\n return joined.length > 0 ? joined : null;\n }\n return null;\n}\n\n/**\n * Convert the residual `<Script>` props into a plain string-attributes record\n * for emission inside a hoisted `<script>` tag. Drops React-only props\n * (event handlers, children, etc.) and reserved keys already handled by the\n * pre-head-injection emitter (id, nonce). Skips `undefined`/`null` so they\n * round-trip as \"attribute absent\" rather than `attr=\"undefined\"`.\n */\nfunction collectBeforeInteractiveAttributes(\n rest: Record<string, unknown>,\n): Record<string, string | boolean> {\n const RESERVED = new Set([\n \"id\",\n \"nonce\",\n \"src\",\n \"children\",\n \"strategy\",\n \"dangerouslySetInnerHTML\",\n \"onLoad\",\n \"onReady\",\n \"onError\",\n ]);\n const out: Record<string, string | boolean> = {};\n for (const [key, value] of Object.entries(rest)) {\n if (RESERVED.has(key)) continue;\n if (value === undefined || value === null || value === false) continue;\n if (typeof value === \"boolean\") {\n out[key] = true;\n continue;\n }\n if (typeof value === \"string\" || typeof value === \"number\") {\n out[key] = String(value);\n continue;\n }\n // Skip anything else (functions, objects) — they cannot serialise into an\n // HTML attribute and only the developer-controlled string/boolean shape\n // is expected for native `<script>` attributes here.\n }\n return out;\n}\n\nfunction setBooleanScriptAttribute(el: HTMLScriptElement, attr: string, value: unknown): boolean {\n const enabled = value !== false && value !== \"false\" && Boolean(value);\n\n switch (attr) {\n case \"async\":\n el.async = enabled;\n break;\n case \"defer\":\n el.defer = enabled;\n break;\n case \"noModule\":\n case \"nomodule\":\n el.noModule = enabled;\n break;\n default:\n return false;\n }\n\n if (!enabled) {\n // Dynamic script elements start in the browser's force-async state.\n // Setting and removing the attribute mirrors Next.js and clears that state.\n el.setAttribute(attr, \"\");\n el.removeAttribute(attr);\n }\n\n return true;\n}\n\nfunction setScriptAttributes(el: HTMLScriptElement, rest: Record<string, unknown>): void {\n for (const [attr, value] of Object.entries(rest)) {\n if (attr === \"dangerouslySetInnerHTML\") continue;\n if (value === undefined) continue;\n if (setBooleanScriptAttribute(el, attr, value)) continue;\n if (attr === \"className\" && typeof value === \"string\") {\n el.setAttribute(\"class\", value);\n } else if (typeof value === \"string\") {\n el.setAttribute(attr, value);\n } else if (typeof value === \"boolean\" && value) {\n el.setAttribute(attr, \"\");\n }\n }\n}\n\nfunction loadClientScript(\n props: ScriptProps,\n options: {\n resolvedNonce?: string;\n fireReadyWhenAlreadyLoaded: boolean;\n },\n): void {\n const {\n src,\n id,\n onLoad,\n onReady,\n onError,\n strategy = \"afterInteractive\",\n children,\n dangerouslySetInnerHTML,\n ...rest\n } = props;\n if (typeof window === \"undefined\") return;\n\n const key = id ?? src ?? \"\";\n if (key && loadedScripts.has(key)) {\n if (options.fireReadyWhenAlreadyLoaded) {\n onReady?.();\n }\n return;\n }\n\n if (src) {\n const existingLoad = loadingScripts.get(src);\n if (existingLoad) {\n void existingLoad.then(\n (event) => {\n if (key) loadedScripts.add(key);\n onLoad?.(event);\n onReady?.();\n },\n (event) => onError?.(event),\n );\n return;\n }\n }\n\n const el = document.createElement(\"script\");\n if (src) el.src = src;\n if (id) el.id = id;\n\n setScriptAttributes(el, rest);\n if (options.resolvedNonce && !el.getAttribute(\"nonce\")) {\n el.setAttribute(\"nonce\", options.resolvedNonce);\n }\n\n if (strategy === \"worker\") {\n el.setAttribute(\"type\", \"text/partytown\");\n }\n\n const markLoaded = () => {\n if (key) loadedScripts.add(key);\n onReady?.();\n };\n\n if (dangerouslySetInnerHTML?.__html) {\n // Intentional: mirrors the Next.js <Script> API where dangerouslySetInnerHTML\n // is developer-supplied inline script content (not user input). The prop name\n // itself signals developer awareness of the XSS risk, consistent with React's\n // design. User-supplied data must never flow into this prop.\n el.innerHTML = dangerouslySetInnerHTML.__html;\n markLoaded();\n } else if (children && typeof children === \"string\") {\n el.textContent = children;\n markLoaded();\n } else if (src) {\n const loadPromise = new Promise<Event>((resolve, reject) => {\n el.addEventListener(\"load\", (event) => {\n resolve(event);\n if (key) loadedScripts.add(key);\n onLoad?.(event);\n onReady?.();\n });\n el.addEventListener(\"error\", (event) => {\n reject(event);\n onError?.(event);\n });\n });\n loadPromise.catch(() => undefined).finally(() => loadingScripts.delete(src));\n loadingScripts.set(src, loadPromise);\n }\n\n document.body.appendChild(el);\n}\n\n/**\n * Load a script imperatively (outside of React).\n */\nexport function handleClientScriptLoad(props: ScriptProps): void {\n loadClientScript(props, {\n resolvedNonce: resolveScriptNonce(props.nonce),\n fireReadyWhenAlreadyLoaded: false,\n });\n}\n\n/**\n * Initialize multiple scripts at once (called during app bootstrap).\n */\nexport function initScriptLoader(scripts: ScriptProps[]): void {\n for (const script of scripts) {\n handleClientScriptLoad(script);\n }\n}\n\nfunction Script(props: ScriptProps): React.ReactElement | null {\n const {\n src,\n id,\n strategy = \"afterInteractive\",\n onLoad,\n onReady,\n onError,\n children,\n dangerouslySetInnerHTML,\n ...rest\n } = props;\n\n const hasMounted = useRef(false);\n const key = id ?? src ?? \"\";\n const contextualNonce = useScriptNonce();\n const resolvedNonce = resolveScriptNonce(rest.nonce, contextualNonce);\n // Available only during SSR — the provider lives in app-ssr-entry.ts. When\n // missing (Pages Router SSR, raw renderToString, client render) we keep the\n // inline `<script>` element in source order.\n const registerBeforeInteractive = useBeforeInteractiveRegister();\n\n // Client path: load scripts via useEffect based on strategy.\n // useEffect never runs during SSR, so it's safe to call unconditionally.\n useEffect(() => {\n if (hasMounted.current) return;\n hasMounted.current = true;\n\n if (strategy === \"beforeInteractive\") {\n return;\n }\n\n // Already loaded — just fire onReady\n if (key && loadedScripts.has(key)) {\n onReady?.();\n return;\n }\n\n const load = () => {\n if (key && loadedScripts.has(key)) {\n onReady?.();\n return;\n }\n\n loadClientScript(\n {\n src,\n id,\n strategy,\n onLoad,\n onReady,\n onError,\n children,\n dangerouslySetInnerHTML,\n ...rest,\n },\n { resolvedNonce, fireReadyWhenAlreadyLoaded: true },\n );\n };\n\n if (strategy === \"lazyOnload\") {\n // Wait for window load, then use idle callback\n if (document.readyState === \"complete\") {\n if (typeof requestIdleCallback === \"function\") {\n requestIdleCallback(load);\n } else {\n setTimeout(load, 1);\n }\n } else {\n window.addEventListener(\"load\", () => {\n if (typeof requestIdleCallback === \"function\") {\n requestIdleCallback(load);\n } else {\n setTimeout(load, 1);\n }\n });\n }\n } else {\n // \"afterInteractive\" (default), \"beforeInteractive\" (client re-mount), \"worker\"\n load();\n }\n }, [\n src,\n id,\n strategy,\n onLoad,\n onReady,\n onError,\n children,\n dangerouslySetInnerHTML,\n key,\n resolvedNonce,\n rest,\n ]);\n\n // SSR path: only \"beforeInteractive\" renders a <script> tag server-side\n if (typeof window === \"undefined\") {\n // React Float preload — emits <link rel=\"preload\" as=\"script\" /> in <head>\n // so the script is fetched while HTML streams. Mirrors Next.js's App Router\n // behavior at .nextjs-ref/packages/next/src/client/script.tsx:298-376:\n // - afterInteractive with src: preload only (no <script> tag in SSR)\n // - beforeInteractive with src: preload + <script> tag\n // - inline scripts (no src): no preload\n // Calling ReactDOM.preload during SSR is safe in both routers; React only\n // hoists the link when it has a real <head> to hoist into.\n if (\n src &&\n typeof ReactDOM.preload === \"function\" &&\n (strategy === \"afterInteractive\" || strategy === \"beforeInteractive\")\n ) {\n const integrity = typeof rest.integrity === \"string\" ? rest.integrity : undefined;\n const crossOrigin =\n rest.crossOrigin === \"anonymous\" || rest.crossOrigin === \"use-credentials\"\n ? rest.crossOrigin\n : undefined;\n const preloadOptions: ReactDOM.PreloadOptions = {\n as: \"script\",\n crossOrigin,\n };\n if (resolvedNonce !== undefined) {\n preloadOptions.nonce = resolvedNonce;\n }\n if (integrity !== undefined) {\n preloadOptions.integrity = integrity;\n }\n ReactDOM.preload(src, preloadOptions);\n }\n\n if (strategy === \"beforeInteractive\") {\n // Inline beforeInteractive scripts (no src) need to run BEFORE any\n // stylesheets, modulepreload links, or other resource hints React Float\n // hoists into <head>. React Fizz emits user-rendered head children\n // AFTER the hoisted resources, so leaving the script in source order\n // breaks the no-flash dark-mode pattern. We instead capture the inline\n // content through BeforeInteractiveContext and the SSR pipeline emits\n // it immediately after `<head>` opens — guaranteeing it precedes every\n // React-emitted hint in the streamed HTML.\n const inlineContent = src\n ? null\n : extractBeforeInteractiveInlineContent(children, dangerouslySetInnerHTML);\n if (inlineContent !== null && registerBeforeInteractive) {\n const inline: BeforeInteractiveInlineScript = {\n id,\n // Escape `</script>` sequences exactly as the inline render path does\n // (see buildBeforeInteractiveScriptProps); keep the escape colocated\n // with the emit boundary so it never gets accidentally skipped.\n innerHTML: escapeInlineContent(inlineContent, \"script\"),\n nonce: resolvedNonce,\n attributes: collectBeforeInteractiveAttributes(rest),\n };\n registerBeforeInteractive(inline);\n return null;\n }\n\n return React.createElement(\n \"script\",\n buildBeforeInteractiveScriptProps({\n src,\n id,\n rest,\n resolvedNonce,\n dangerouslySetInnerHTML,\n }),\n children,\n );\n }\n // Other strategies don't render during SSR\n return null;\n }\n\n if (strategy === \"beforeInteractive\") {\n // On the client, only suppress the `<script>` render for inline\n // beforeInteractive Scripts in App Router pages. The pre-head splice\n // in app-ssr-entry/app-ssr-stream already put the tag in the DOM, so\n // rendering it again would either duplicate the script (for Scripts\n // outside `<head>`) or cause a hydration mismatch (positions differ).\n //\n // For Pages Router and any other SSR path that didn't run through\n // app-ssr-entry, the server rendered the `<script>` inline in source\n // order, so the client must match. We detect \"App Router\" via the\n // navigation runtime that the App Router bootstrap installs before\n // calling hydrateRoot — it is the most reliable runtime signal we\n // can read from inside a `\"use client\"` shim.\n //\n // External-`src` beforeInteractive scripts always keep rendering\n // inline. They are not captured by the pre-head splice and must mount\n // through React so their `src` attribute is fetched on the client.\n const inlineContent = src\n ? null\n : extractBeforeInteractiveInlineContent(children, dangerouslySetInnerHTML);\n if (inlineContent !== null && hasAppNavigationRuntimeBootstrap()) {\n return null;\n }\n\n return React.createElement(\n \"script\",\n buildBeforeInteractiveScriptProps({\n src,\n id,\n rest,\n resolvedNonce,\n dangerouslySetInnerHTML,\n }),\n children,\n );\n }\n\n // The component itself renders nothing — scripts are injected imperatively\n return null;\n}\n\nexport default Script;\n"],"mappings":"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"}
1
+ {"version":3,"file":"script.js","names":[],"sources":["../../src/shims/script.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * next/script shim\n *\n * Provides the <Script> component for loading third-party scripts with\n * configurable loading strategies.\n *\n * Strategies:\n * - \"beforeInteractive\": rendered as a <script> tag in SSR output\n * - \"afterInteractive\" (default): loaded client-side after hydration\n * - \"lazyOnload\": deferred until window.load + requestIdleCallback\n * - \"worker\": sets type=\"text/partytown\" (requires Partytown setup)\n */\nimport React, { useEffect, useRef } from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { hasAppNavigationRuntimeBootstrap } from \"../client/navigation-runtime.js\";\nimport { escapeInlineContent } from \"./head.js\";\nimport { useScriptNonce } from \"./script-nonce-context.js\";\nimport {\n useBeforeInteractiveRegister,\n type BeforeInteractiveInlineScript,\n} from \"./before-interactive-context.js\";\n\nexport type ScriptProps = {\n /** Script source URL */\n src?: string;\n /** Loading strategy. Default: \"afterInteractive\" */\n strategy?: \"beforeInteractive\" | \"afterInteractive\" | \"lazyOnload\" | \"worker\";\n /** Unique identifier for the script */\n id?: string;\n /** Called when the script has loaded */\n onLoad?: (e: Event) => void;\n /** Called when the script is ready (after load, and on every re-render if already loaded) */\n onReady?: () => void;\n /** Called on script load error */\n onError?: (e: Event) => void;\n /** Inline script content */\n children?: React.ReactNode;\n /** Dangerous inner HTML */\n dangerouslySetInnerHTML?: { __html: string };\n /** Script type attribute */\n type?: string;\n /** Async attribute */\n async?: boolean;\n /** Defer attribute */\n defer?: boolean;\n /** Crossorigin attribute */\n crossOrigin?: string;\n /** Nonce for CSP */\n nonce?: string;\n /** Integrity hash */\n integrity?: string;\n /**\n * Associated stylesheets to load alongside the script. Emitted as\n * `<link rel=\"stylesheet\" href=\"...\">` on SSR (via `ReactDOM.preinit`)\n * and inserted into `<head>` on the client load path.\n *\n * Mirrors Next.js App Router behaviour at\n * `.nextjs-ref/packages/next/src/client/script.tsx` (`insertStylesheets`\n * and the `appDir` block).\n */\n stylesheets?: string[];\n /** Additional attributes */\n [key: string]: unknown;\n};\n\n// Track scripts that have already been loaded, plus remote scripts currently\n// loading, to avoid duplicate DOM insertion when same-src components mount\n// before the first load event fires.\nconst loadedScripts = new Set<string>();\nconst loadingScripts = new Map<string, Promise<Event>>();\n\nfunction getClientAutoNonce(): string | undefined {\n if (typeof document === \"undefined\") return undefined;\n\n const existingNonceElement = document.querySelector(\"[nonce]\");\n if (!existingNonceElement) return undefined;\n\n // `HTMLElement` is not defined in some SSR/edge runtimes that polyfill\n // `document` but stop short of the full DOM surface. Guarding the\n // constructor before `instanceof` keeps SSR from crashing in those hosts;\n // when the constructor *is* present we still prefer the typed `.nonce`\n // property because browsers strip the `nonce` attribute from serialised\n // HTML for CSP reasons.\n if (typeof HTMLElement !== \"undefined\" && existingNonceElement instanceof HTMLElement) {\n return existingNonceElement.nonce || existingNonceElement.getAttribute(\"nonce\") || undefined;\n }\n\n return existingNonceElement.getAttribute(\"nonce\") || undefined;\n}\n\nfunction resolveScriptNonce(explicitNonce: unknown, contextualNonce?: string): string | undefined {\n if (typeof explicitNonce === \"string\" && explicitNonce.length > 0) {\n return explicitNonce;\n }\n\n if (typeof contextualNonce === \"string\" && contextualNonce.length > 0) {\n return contextualNonce;\n }\n\n if (typeof window === \"undefined\") {\n return undefined;\n }\n\n return getClientAutoNonce();\n}\n\n/**\n * Insert `<link rel=\"stylesheet\">` tags into `document.head` for each entry\n * in `stylesheets`. Used by the imperative client-side load path\n * (`handleClientScriptLoad`) when `ReactDOM.preinit` is not available\n * (e.g. pre-Float React or hosts that strip it). Mirrors Next.js's\n * `insertStylesheets` Pages-Router fallback at\n * `.nextjs-ref/packages/next/src/client/script.tsx:48-59`.\n *\n * The `ReactDOM.preinit` path is preferred where available — it dedupes\n * across mounts and respects React Float's hoisting order. This DOM\n * fallback is best-effort: no dedupe, no ordering guarantee.\n */\nfunction insertClientStylesheets(stylesheets: string[] | undefined): void {\n if (!stylesheets || stylesheets.length === 0) return;\n if (typeof document === \"undefined\") return;\n\n // Prefer ReactDOM.preinit when available — it dedupes via React Float\n // and matches Next.js's app-router behaviour.\n if (typeof ReactDOM.preinit === \"function\") {\n for (const href of stylesheets) {\n ReactDOM.preinit(href, { as: \"style\" });\n }\n return;\n }\n\n const head = document.head;\n if (!head) return;\n for (const href of stylesheets) {\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.type = \"text/css\";\n link.href = href;\n head.appendChild(link);\n }\n}\n\n/**\n * Emit `<link rel=\"stylesheet\">` tags during SSR for each entry in\n * `stylesheets` via `ReactDOM.preinit`. React Float hoists these into\n * `<head>` in the streamed HTML. Mirrors the App-Router branch of\n * Next.js's Script component at `.nextjs-ref/packages/next/src/client/script.tsx:309-313`.\n */\nfunction preinitStylesheetsForSSR(stylesheets: string[] | undefined): void {\n if (!stylesheets || stylesheets.length === 0) return;\n if (typeof ReactDOM.preinit !== \"function\") return;\n for (const href of stylesheets) {\n ReactDOM.preinit(href, { as: \"style\" });\n }\n}\n\nfunction buildBeforeInteractiveScriptProps(options: {\n src?: string;\n id?: string;\n rest: Record<string, unknown>;\n resolvedNonce?: string;\n dangerouslySetInnerHTML?: { __html: string };\n}): Record<string, unknown> {\n const scriptProps: Record<string, unknown> = { ...options.rest };\n if (options.src) scriptProps.src = options.src;\n if (options.id) scriptProps.id = options.id;\n if (options.resolvedNonce) {\n scriptProps.nonce = options.resolvedNonce;\n }\n if (options.dangerouslySetInnerHTML) {\n scriptProps.dangerouslySetInnerHTML = {\n __html: escapeInlineContent(options.dangerouslySetInnerHTML.__html, \"script\"),\n };\n }\n return scriptProps;\n}\n\n/**\n * Extract the inline script content for a `beforeInteractive` Script element\n * with no `src`. Returns `null` when the element has neither a string-shaped\n * `children` value nor a valid `dangerouslySetInnerHTML.__html` payload — in\n * that case the caller should fall through to React's regular rendering path.\n *\n * The returned string is the raw author-supplied JavaScript content. Callers\n * are responsible for passing it through `escapeInlineContent(..., \"script\")`\n * before emitting it inside a `<script>` tag (we keep that escape adjacent\n * to the emit point so the rule is obvious at the boundary).\n */\nfunction extractBeforeInteractiveInlineContent(\n children: React.ReactNode,\n dangerouslySetInnerHTML?: { __html: string },\n): string | null {\n if (\n dangerouslySetInnerHTML &&\n typeof dangerouslySetInnerHTML.__html === \"string\" &&\n dangerouslySetInnerHTML.__html.length > 0\n ) {\n return dangerouslySetInnerHTML.__html;\n }\n if (typeof children === \"string\" && children.length > 0) {\n return children;\n }\n if (Array.isArray(children) && children.every((c) => typeof c === \"string\")) {\n const joined = (children as string[]).join(\"\");\n return joined.length > 0 ? joined : null;\n }\n return null;\n}\n\n/**\n * Map of React DOM prop names to their HTML attribute equivalents. Mirrors\n * Next.js's `set-attributes-from-props.ts`:\n * .nextjs-ref/packages/next/src/client/set-attributes-from-props.ts\n * HTML parses attribute names case-insensitively, so without this translation\n * `className=\"foo\"` round-trips as `classname=\"foo\"` and CSS selectors on\n * `.foo` never match. Same hazard for `htmlFor`/`for`, `httpEquiv`/`http-equiv`,\n * `acceptCharset`/`accept-charset`.\n */\nconst REACT_TO_HTML_ATTR: Record<string, string> = {\n acceptCharset: \"accept-charset\",\n className: \"class\",\n htmlFor: \"for\",\n httpEquiv: \"http-equiv\",\n};\n\n/**\n * Convert the residual `<Script>` props into a plain string-attributes record\n * for emission inside a hoisted `<script>` tag. Drops React-only props\n * (event handlers, children, etc.) and reserved keys already handled by the\n * pre-head-injection emitter (id, nonce). Skips `undefined`/`null` so they\n * round-trip as \"attribute absent\" rather than `attr=\"undefined\"`.\n *\n * React DOM prop names (className, htmlFor, etc.) are translated to their\n * HTML attribute names so the output parses correctly — see comment on\n * `REACT_TO_HTML_ATTR`.\n */\nfunction collectBeforeInteractiveAttributes(\n rest: Record<string, unknown>,\n): Record<string, string | boolean> {\n const RESERVED = new Set([\n \"id\",\n \"nonce\",\n \"src\",\n \"children\",\n \"strategy\",\n \"dangerouslySetInnerHTML\",\n \"onLoad\",\n \"onReady\",\n \"onError\",\n \"stylesheets\",\n ]);\n const out: Record<string, string | boolean> = {};\n for (const [key, value] of Object.entries(rest)) {\n if (RESERVED.has(key)) continue;\n if (value === undefined || value === null || value === false) continue;\n const attrName = REACT_TO_HTML_ATTR[key] ?? key;\n if (typeof value === \"boolean\") {\n out[attrName] = true;\n continue;\n }\n if (typeof value === \"string\" || typeof value === \"number\") {\n out[attrName] = String(value);\n continue;\n }\n // Skip anything else (functions, objects) — they cannot serialise into an\n // HTML attribute and only the developer-controlled string/boolean shape\n // is expected for native `<script>` attributes here.\n }\n return out;\n}\n\nfunction setBooleanScriptAttribute(el: HTMLScriptElement, attr: string, value: unknown): boolean {\n const enabled = value !== false && value !== \"false\" && Boolean(value);\n\n switch (attr) {\n case \"async\":\n el.async = enabled;\n break;\n case \"defer\":\n el.defer = enabled;\n break;\n case \"noModule\":\n case \"nomodule\":\n el.noModule = enabled;\n break;\n default:\n return false;\n }\n\n if (!enabled) {\n // Dynamic script elements start in the browser's force-async state.\n // Setting and removing the attribute mirrors Next.js and clears that state.\n el.setAttribute(attr, \"\");\n el.removeAttribute(attr);\n }\n\n return true;\n}\n\nfunction setScriptAttributes(el: HTMLScriptElement, rest: Record<string, unknown>): void {\n for (const [attr, value] of Object.entries(rest)) {\n if (attr === \"dangerouslySetInnerHTML\") continue;\n if (value === undefined) continue;\n if (setBooleanScriptAttribute(el, attr, value)) continue;\n if (attr === \"className\" && typeof value === \"string\") {\n el.setAttribute(\"class\", value);\n } else if (typeof value === \"string\") {\n el.setAttribute(attr, value);\n } else if (typeof value === \"boolean\" && value) {\n el.setAttribute(attr, \"\");\n }\n }\n}\n\nfunction loadClientScript(\n props: ScriptProps,\n options: {\n resolvedNonce?: string;\n fireReadyWhenAlreadyLoaded: boolean;\n },\n): void {\n const {\n src,\n id,\n onLoad,\n onReady,\n onError,\n strategy = \"afterInteractive\",\n children,\n dangerouslySetInnerHTML,\n stylesheets,\n ...rest\n } = props;\n if (typeof window === \"undefined\") return;\n\n // Insert associated stylesheets into <head> regardless of whether the\n // script was already loaded — the script's onReady handlers may already\n // assume the stylesheet is present. `insertClientStylesheets` dedupes\n // via ReactDOM.preinit where available.\n insertClientStylesheets(stylesheets);\n\n const key = id ?? src ?? \"\";\n if (key && loadedScripts.has(key)) {\n if (options.fireReadyWhenAlreadyLoaded) {\n onReady?.();\n }\n return;\n }\n\n if (src) {\n const existingLoad = loadingScripts.get(src);\n if (existingLoad) {\n void existingLoad.then(\n (event) => {\n if (key) loadedScripts.add(key);\n onLoad?.(event);\n onReady?.();\n },\n (event) => onError?.(event),\n );\n return;\n }\n }\n\n const el = document.createElement(\"script\");\n if (src) el.src = src;\n if (id) el.id = id;\n\n setScriptAttributes(el, rest);\n if (options.resolvedNonce && !el.getAttribute(\"nonce\")) {\n el.setAttribute(\"nonce\", options.resolvedNonce);\n }\n\n if (strategy === \"worker\") {\n el.setAttribute(\"type\", \"text/partytown\");\n }\n\n const markLoaded = () => {\n if (key) loadedScripts.add(key);\n onReady?.();\n };\n\n if (dangerouslySetInnerHTML?.__html) {\n // Intentional: mirrors the Next.js <Script> API where dangerouslySetInnerHTML\n // is developer-supplied inline script content (not user input). The prop name\n // itself signals developer awareness of the XSS risk, consistent with React's\n // design. User-supplied data must never flow into this prop.\n el.innerHTML = dangerouslySetInnerHTML.__html;\n markLoaded();\n } else if (children && typeof children === \"string\") {\n el.textContent = children;\n markLoaded();\n } else if (src) {\n const loadPromise = new Promise<Event>((resolve, reject) => {\n el.addEventListener(\"load\", (event) => {\n resolve(event);\n if (key) loadedScripts.add(key);\n onLoad?.(event);\n onReady?.();\n });\n el.addEventListener(\"error\", (event) => {\n reject(event);\n onError?.(event);\n });\n });\n loadPromise.catch(() => undefined).finally(() => loadingScripts.delete(src));\n loadingScripts.set(src, loadPromise);\n }\n\n document.body.appendChild(el);\n}\n\n/**\n * Load a script imperatively (outside of React).\n */\nexport function handleClientScriptLoad(props: ScriptProps): void {\n loadClientScript(props, {\n resolvedNonce: resolveScriptNonce(props.nonce),\n fireReadyWhenAlreadyLoaded: false,\n });\n}\n\n/**\n * Initialize multiple scripts at once (called during app bootstrap).\n */\nexport function initScriptLoader(scripts: ScriptProps[]): void {\n for (const script of scripts) {\n handleClientScriptLoad(script);\n }\n}\n\nfunction Script(props: ScriptProps): React.ReactElement | null {\n const {\n src,\n id,\n strategy = \"afterInteractive\",\n onLoad,\n onReady,\n onError,\n children,\n dangerouslySetInnerHTML,\n stylesheets,\n ...rest\n } = props;\n\n const hasMounted = useRef(false);\n const key = id ?? src ?? \"\";\n const contextualNonce = useScriptNonce();\n const resolvedNonce = resolveScriptNonce(rest.nonce, contextualNonce);\n // Available only during SSR — the provider lives in app-ssr-entry.ts. When\n // missing (Pages Router SSR, raw renderToString, client render) we keep the\n // inline `<script>` element in source order.\n const registerBeforeInteractive = useBeforeInteractiveRegister();\n\n // Client path: load scripts via useEffect based on strategy.\n // useEffect never runs during SSR, so it's safe to call unconditionally.\n useEffect(() => {\n if (hasMounted.current) return;\n hasMounted.current = true;\n\n if (strategy === \"beforeInteractive\") {\n // The script itself is loaded by Next.js's bootstrap before hydration,\n // but the associated stylesheets still need to land in <head> on the\n // client. ReactDOM.preinit (called inside insertClientStylesheets)\n // dedupes against any SSR-emitted <link rel=\"stylesheet\">, so this is\n // safe even when the server already hoisted them via React Float.\n insertClientStylesheets(stylesheets);\n return;\n }\n\n // Already loaded — just fire onReady\n if (key && loadedScripts.has(key)) {\n // Stylesheets must still be inserted on subsequent mounts of the same\n // script. loadClientScript handles this for the fresh-load path; the\n // already-loaded shortcut needs it explicitly.\n insertClientStylesheets(stylesheets);\n onReady?.();\n return;\n }\n\n const load = () => {\n if (key && loadedScripts.has(key)) {\n onReady?.();\n return;\n }\n\n loadClientScript(\n {\n src,\n id,\n strategy,\n onLoad,\n onReady,\n onError,\n children,\n dangerouslySetInnerHTML,\n stylesheets,\n ...rest,\n },\n { resolvedNonce, fireReadyWhenAlreadyLoaded: true },\n );\n };\n\n if (strategy === \"lazyOnload\") {\n // Wait for window load, then use idle callback\n if (document.readyState === \"complete\") {\n if (typeof requestIdleCallback === \"function\") {\n requestIdleCallback(load);\n } else {\n setTimeout(load, 1);\n }\n } else {\n window.addEventListener(\"load\", () => {\n if (typeof requestIdleCallback === \"function\") {\n requestIdleCallback(load);\n } else {\n setTimeout(load, 1);\n }\n });\n }\n } else {\n // \"afterInteractive\" (default), \"beforeInteractive\" (client re-mount), \"worker\"\n load();\n }\n }, [\n src,\n id,\n strategy,\n onLoad,\n onReady,\n onError,\n children,\n dangerouslySetInnerHTML,\n stylesheets,\n key,\n resolvedNonce,\n rest,\n ]);\n\n // SSR path: only \"beforeInteractive\" renders a <script> tag server-side\n if (typeof window === \"undefined\") {\n // Emit associated stylesheets as <link rel=\"stylesheet\"> via React Float.\n // ReactDOM.preinit dedupes across mounts and hoists the link into <head>\n // regardless of strategy — matches Next.js's app-router branch at\n // `.nextjs-ref/packages/next/src/client/script.tsx:309-313`.\n preinitStylesheetsForSSR(stylesheets);\n\n // React Float preload — emits <link rel=\"preload\" as=\"script\" /> in <head>\n // so the script is fetched while HTML streams. Mirrors Next.js's App Router\n // behavior at .nextjs-ref/packages/next/src/client/script.tsx:298-376:\n // - afterInteractive with src: preload only (no <script> tag in SSR)\n // - beforeInteractive with src: preload + <script> tag\n // - inline scripts (no src): no preload\n // Calling ReactDOM.preload during SSR is safe in both routers; React only\n // hoists the link when it has a real <head> to hoist into.\n if (\n src &&\n typeof ReactDOM.preload === \"function\" &&\n (strategy === \"afterInteractive\" || strategy === \"beforeInteractive\")\n ) {\n const integrity = typeof rest.integrity === \"string\" ? rest.integrity : undefined;\n const crossOrigin =\n rest.crossOrigin === \"anonymous\" || rest.crossOrigin === \"use-credentials\"\n ? rest.crossOrigin\n : undefined;\n const preloadOptions: ReactDOM.PreloadOptions = {\n as: \"script\",\n crossOrigin,\n };\n if (resolvedNonce !== undefined) {\n preloadOptions.nonce = resolvedNonce;\n }\n if (integrity !== undefined) {\n preloadOptions.integrity = integrity;\n }\n ReactDOM.preload(src, preloadOptions);\n }\n\n if (strategy === \"beforeInteractive\") {\n // Inline beforeInteractive scripts (no src) need to run BEFORE any\n // stylesheets, modulepreload links, or other resource hints React Float\n // hoists into <head>. React Fizz emits user-rendered head children\n // AFTER the hoisted resources, so leaving the script in source order\n // breaks the no-flash dark-mode pattern. We instead capture the inline\n // content through BeforeInteractiveContext and the SSR pipeline emits\n // it immediately after `<head>` opens — guaranteeing it precedes every\n // React-emitted hint in the streamed HTML.\n const inlineContent = src\n ? null\n : extractBeforeInteractiveInlineContent(children, dangerouslySetInnerHTML);\n if (inlineContent !== null && registerBeforeInteractive) {\n const inline: BeforeInteractiveInlineScript = {\n id,\n // Escape `</script>` sequences exactly as the inline render path does\n // (see buildBeforeInteractiveScriptProps); keep the escape colocated\n // with the emit boundary so it never gets accidentally skipped.\n innerHTML: escapeInlineContent(inlineContent, \"script\"),\n nonce: resolvedNonce,\n attributes: collectBeforeInteractiveAttributes(rest),\n };\n registerBeforeInteractive(inline);\n return null;\n }\n\n return React.createElement(\n \"script\",\n buildBeforeInteractiveScriptProps({\n src,\n id,\n rest,\n resolvedNonce,\n dangerouslySetInnerHTML,\n }),\n children,\n );\n }\n // Other strategies don't render during SSR\n return null;\n }\n\n if (strategy === \"beforeInteractive\") {\n // On the client, only suppress the `<script>` render for inline\n // beforeInteractive Scripts in App Router pages. The pre-head splice\n // in app-ssr-entry/app-ssr-stream already put the tag in the DOM, so\n // rendering it again would either duplicate the script (for Scripts\n // outside `<head>`) or cause a hydration mismatch (positions differ).\n //\n // For Pages Router and any other SSR path that didn't run through\n // app-ssr-entry, the server rendered the `<script>` inline in source\n // order, so the client must match. We detect \"App Router\" via the\n // navigation runtime that the App Router bootstrap installs before\n // calling hydrateRoot — it is the most reliable runtime signal we\n // can read from inside a `\"use client\"` shim.\n //\n // External-`src` beforeInteractive scripts always keep rendering\n // inline. They are not captured by the pre-head splice and must mount\n // through React so their `src` attribute is fetched on the client.\n const inlineContent = src\n ? null\n : extractBeforeInteractiveInlineContent(children, dangerouslySetInnerHTML);\n if (inlineContent !== null && hasAppNavigationRuntimeBootstrap()) {\n return null;\n }\n\n return React.createElement(\n \"script\",\n buildBeforeInteractiveScriptProps({\n src,\n id,\n rest,\n resolvedNonce,\n dangerouslySetInnerHTML,\n }),\n children,\n );\n }\n\n // The component itself renders nothing — scripts are injected imperatively\n return null;\n}\n\nexport default Script;\n"],"mappings":"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"}
@@ -2,7 +2,10 @@
2
2
  declare const WELL_KNOWN_PROPERTIES: readonly ["hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toString", "valueOf", "toLocaleString", "then", "catch", "finally", "status", "value", "error", "displayName", "_debugInfo", "toJSON", "$$typeof", "__esModule", "@@iterator"];
3
3
  type WellKnownProperty = (typeof WELL_KNOWN_PROPERTIES)[number];
4
4
  type ThenableParams<T extends Record<string, unknown>> = Promise<T> & Omit<T, WellKnownProperty>;
5
- declare function makeThenableParams<T extends Record<string, unknown>>(obj: T): ThenableParams<T>;
5
+ type ThenableParamsObserver = Readonly<{
6
+ observeParamAccess: (keys: readonly string[]) => void;
7
+ }>;
8
+ declare function makeThenableParams<T extends Record<string, unknown>>(obj: T, observer?: ThenableParamsObserver): ThenableParams<T>;
6
9
  //#endregion
7
- export { ThenableParams, makeThenableParams };
10
+ export { ThenableParams, ThenableParamsObserver, makeThenableParams };
8
11
  //# sourceMappingURL=thenable-params.d.ts.map
@@ -25,16 +25,38 @@ const wellKnownProperties = new Set([
25
25
  function isWellKnownProperty(prop) {
26
26
  return wellKnownProperties.has(prop);
27
27
  }
28
- function makeThenableParams(obj) {
28
+ function observeParamKeys(observer, keys) {
29
+ if (observer) observer.observeParamAccess(keys);
30
+ }
31
+ function observeAllParamKeys(observer, plain) {
32
+ observeParamKeys(observer, Object.keys(plain));
33
+ }
34
+ function observeReadableParamKeys(observer, plain) {
35
+ observeParamKeys(observer, Object.keys(plain).filter((key) => !isWellKnownProperty(key)));
36
+ }
37
+ function isPromiseContinuation(prop) {
38
+ return prop === "then" || prop === "catch" || prop === "finally";
39
+ }
40
+ function makeThenableParams(obj, observer) {
29
41
  const plain = { ...obj };
30
42
  const promise = Promise.resolve(plain);
31
43
  return new Proxy(promise, {
32
44
  get(target, prop, receiver) {
45
+ if (isPromiseContinuation(prop)) {
46
+ const value = Reflect.get(target, prop, receiver);
47
+ if (typeof value !== "function") return value;
48
+ return (...args) => {
49
+ observeAllParamKeys(observer, plain);
50
+ return Reflect.apply(value, target, args);
51
+ };
52
+ }
53
+ if (typeof prop === "string" && !isWellKnownProperty(prop)) observeParamKeys(observer, [prop]);
33
54
  if (!isWellKnownProperty(prop) && hasParamProperty(plain, prop)) return Reflect.get(plain, prop);
34
55
  const value = Reflect.get(target, prop, receiver);
35
56
  return typeof value === "function" ? value.bind(target) : value;
36
57
  },
37
58
  getOwnPropertyDescriptor(target, prop) {
59
+ if (typeof prop === "string" && !isWellKnownProperty(prop)) observeParamKeys(observer, [prop]);
38
60
  if (!isWellKnownProperty(prop) && hasParamProperty(plain, prop)) return {
39
61
  configurable: true,
40
62
  enumerable: true,
@@ -44,9 +66,11 @@ function makeThenableParams(obj) {
44
66
  return Reflect.getOwnPropertyDescriptor(target, prop);
45
67
  },
46
68
  has(target, prop) {
69
+ if (typeof prop === "string" && !isWellKnownProperty(prop)) observeParamKeys(observer, [prop]);
47
70
  return Reflect.has(target, prop) || !isWellKnownProperty(prop) && hasParamProperty(plain, prop);
48
71
  },
49
72
  ownKeys() {
73
+ observeReadableParamKeys(observer, plain);
50
74
  return Reflect.ownKeys(plain).filter((prop) => !isWellKnownProperty(prop));
51
75
  }
52
76
  });
@@ -1 +1 @@
1
- {"version":3,"file":"thenable-params.js","names":[],"sources":["../../src/shims/thenable-params.ts"],"sourcesContent":["function hasParamProperty<T extends Record<string, unknown>>(obj: T, prop: PropertyKey): boolean {\n return Object.prototype.hasOwnProperty.call(obj, prop);\n}\n\n// Properties that cannot be shadowed by param names because they need to\n// remain the true underlying value for Promises / React to work correctly.\n//\n// Next.js comments out `value` and `error` in reflect-utils.ts because they\n// use `Promise.resolve(underlyingParams)` directly in production, so React\n// mutations on the promise object are never shadowed. vinext uses a Proxy\n// that intercepts sync reads through a separate `plain` object, which means\n// a param named `value` or `error` would shadow React's `.status`/`.value`\n// attachments that React adds to resolved promises for `use()` caching.\n// https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/utils/reflect-utils.ts\nconst WELL_KNOWN_PROPERTIES = [\n // Object prototype\n \"hasOwnProperty\",\n \"isPrototypeOf\",\n \"propertyIsEnumerable\",\n \"toString\",\n \"valueOf\",\n \"toLocaleString\",\n\n // Promise prototype\n \"then\",\n \"catch\",\n \"finally\",\n\n // React Promise extension (status is explicitly reserved by Next.js;\n // value/error are reserved here because our Proxy-based approach creates\n // a shadowing risk that native Promise does not have)\n \"status\",\n \"value\",\n \"error\",\n\n // React introspection\n \"displayName\",\n \"_debugInfo\",\n\n // Common tested properties\n \"toJSON\",\n \"$$typeof\",\n \"__esModule\",\n\n // Tested by flight when checking for iterables\n \"@@iterator\",\n] as const;\n\n// The type-level set of well-known properties is derived directly from the\n// runtime array above, so they can never drift out of sync. These properties\n// are omitted from the synchronous intersection because the Proxy returns\n// Promise/React internals for them, not the param value. After awaiting, the\n// resolved object contains the actual param values for all keys.\ntype WellKnownProperty = (typeof WELL_KNOWN_PROPERTIES)[number];\n\nconst wellKnownProperties = new Set<PropertyKey>(WELL_KNOWN_PROPERTIES);\n\nfunction isWellKnownProperty(prop: PropertyKey): boolean {\n return wellKnownProperties.has(prop);\n}\n\nexport type ThenableParams<T extends Record<string, unknown>> = Promise<T> &\n Omit<T, WellKnownProperty>;\n\nexport function makeThenableParams<T extends Record<string, unknown>>(obj: T): ThenableParams<T> {\n const plain = { ...obj };\n const promise = Promise.resolve(plain);\n\n // The Proxy implements both Promise and plain-object behaviour so that\n // `await params` and `params.id` both work. TypeScript's Proxy type\n // cannot express this intersection precisely — the cast is isolated to\n // the boundary so the handler above stays fully type-checked.\n return new Proxy(promise, {\n get(target, prop, receiver) {\n if (!isWellKnownProperty(prop) && hasParamProperty(plain, prop)) {\n return Reflect.get(plain, prop);\n }\n\n const value = Reflect.get(target, prop, receiver);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n getOwnPropertyDescriptor(target, prop) {\n if (!isWellKnownProperty(prop) && hasParamProperty(plain, prop)) {\n return {\n configurable: true,\n enumerable: true,\n value: Reflect.get(plain, prop),\n writable: true,\n };\n }\n\n return Reflect.getOwnPropertyDescriptor(target, prop);\n },\n has(target, prop) {\n return (\n Reflect.has(target, prop) || (!isWellKnownProperty(prop) && hasParamProperty(plain, prop))\n );\n },\n ownKeys() {\n return Reflect.ownKeys(plain).filter((prop) => !isWellKnownProperty(prop));\n },\n }) as unknown as ThenableParams<T>;\n}\n"],"mappings":";AAAA,SAAS,iBAAoD,KAAQ,MAA4B;CAC/F,OAAO,OAAO,UAAU,eAAe,KAAK,KAAK,KAAK;;AAsDxD,MAAM,sBAAsB,IAAI,IAAiB;CAvC/C;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAKA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CAGA;CAUoE,CAAC;AAEvE,SAAS,oBAAoB,MAA4B;CACvD,OAAO,oBAAoB,IAAI,KAAK;;AAMtC,SAAgB,mBAAsD,KAA2B;CAC/F,MAAM,QAAQ,EAAE,GAAG,KAAK;CACxB,MAAM,UAAU,QAAQ,QAAQ,MAAM;CAMtC,OAAO,IAAI,MAAM,SAAS;EACxB,IAAI,QAAQ,MAAM,UAAU;GAC1B,IAAI,CAAC,oBAAoB,KAAK,IAAI,iBAAiB,OAAO,KAAK,EAC7D,OAAO,QAAQ,IAAI,OAAO,KAAK;GAGjC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,SAAS;GACjD,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,OAAO,GAAG;;EAE5D,yBAAyB,QAAQ,MAAM;GACrC,IAAI,CAAC,oBAAoB,KAAK,IAAI,iBAAiB,OAAO,KAAK,EAC7D,OAAO;IACL,cAAc;IACd,YAAY;IACZ,OAAO,QAAQ,IAAI,OAAO,KAAK;IAC/B,UAAU;IACX;GAGH,OAAO,QAAQ,yBAAyB,QAAQ,KAAK;;EAEvD,IAAI,QAAQ,MAAM;GAChB,OACE,QAAQ,IAAI,QAAQ,KAAK,IAAK,CAAC,oBAAoB,KAAK,IAAI,iBAAiB,OAAO,KAAK;;EAG7F,UAAU;GACR,OAAO,QAAQ,QAAQ,MAAM,CAAC,QAAQ,SAAS,CAAC,oBAAoB,KAAK,CAAC;;EAE7E,CAAC"}
1
+ {"version":3,"file":"thenable-params.js","names":[],"sources":["../../src/shims/thenable-params.ts"],"sourcesContent":["function hasParamProperty<T extends Record<string, unknown>>(obj: T, prop: PropertyKey): boolean {\n return Object.prototype.hasOwnProperty.call(obj, prop);\n}\n\n// Properties that cannot be shadowed by param names because they need to\n// remain the true underlying value for Promises / React to work correctly.\n//\n// Next.js comments out `value` and `error` in reflect-utils.ts because they\n// use `Promise.resolve(underlyingParams)` directly in production, so React\n// mutations on the promise object are never shadowed. vinext uses a Proxy\n// that intercepts sync reads through a separate `plain` object, which means\n// a param named `value` or `error` would shadow React's `.status`/`.value`\n// attachments that React adds to resolved promises for `use()` caching.\n// https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/utils/reflect-utils.ts\nconst WELL_KNOWN_PROPERTIES = [\n // Object prototype\n \"hasOwnProperty\",\n \"isPrototypeOf\",\n \"propertyIsEnumerable\",\n \"toString\",\n \"valueOf\",\n \"toLocaleString\",\n\n // Promise prototype\n \"then\",\n \"catch\",\n \"finally\",\n\n // React Promise extension (status is explicitly reserved by Next.js;\n // value/error are reserved here because our Proxy-based approach creates\n // a shadowing risk that native Promise does not have)\n \"status\",\n \"value\",\n \"error\",\n\n // React introspection\n \"displayName\",\n \"_debugInfo\",\n\n // Common tested properties\n \"toJSON\",\n \"$$typeof\",\n \"__esModule\",\n\n // Tested by flight when checking for iterables\n \"@@iterator\",\n] as const;\n\n// The type-level set of well-known properties is derived directly from the\n// runtime array above, so they can never drift out of sync. These properties\n// are omitted from the synchronous intersection because the Proxy returns\n// Promise/React internals for them, not the param value. After awaiting, the\n// resolved object contains the actual param values for all keys.\ntype WellKnownProperty = (typeof WELL_KNOWN_PROPERTIES)[number];\n\nconst wellKnownProperties = new Set<PropertyKey>(WELL_KNOWN_PROPERTIES);\n\nfunction isWellKnownProperty(prop: PropertyKey): boolean {\n return wellKnownProperties.has(prop);\n}\n\nexport type ThenableParams<T extends Record<string, unknown>> = Promise<T> &\n Omit<T, WellKnownProperty>;\n\nexport type ThenableParamsObserver = Readonly<{\n observeParamAccess: (keys: readonly string[]) => void;\n}>;\n\nfunction observeParamKeys(\n observer: ThenableParamsObserver | undefined,\n keys: readonly string[],\n): void {\n if (observer) {\n observer.observeParamAccess(keys);\n }\n}\n\nfunction observeAllParamKeys<T extends Record<string, unknown>>(\n observer: ThenableParamsObserver | undefined,\n plain: T,\n): void {\n observeParamKeys(observer, Object.keys(plain));\n}\n\nfunction observeReadableParamKeys<T extends Record<string, unknown>>(\n observer: ThenableParamsObserver | undefined,\n plain: T,\n): void {\n const keys = Object.keys(plain).filter((key) => !isWellKnownProperty(key));\n observeParamKeys(observer, keys);\n}\n\nfunction isPromiseContinuation(prop: PropertyKey): boolean {\n return prop === \"then\" || prop === \"catch\" || prop === \"finally\";\n}\n\nexport function makeThenableParams<T extends Record<string, unknown>>(\n obj: T,\n observer?: ThenableParamsObserver,\n): ThenableParams<T> {\n const plain = { ...obj };\n const promise = Promise.resolve(plain);\n\n // The Proxy implements both Promise and plain-object behaviour so that\n // `await params` and `params.id` both work. TypeScript's Proxy type\n // cannot express this intersection precisely — the cast is isolated to\n // the boundary so the handler above stays fully type-checked.\n return new Proxy(promise, {\n get(target, prop, receiver) {\n if (isPromiseContinuation(prop)) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== \"function\") return value;\n return (...args: unknown[]) => {\n observeAllParamKeys(observer, plain);\n return Reflect.apply(value, target, args);\n };\n }\n\n if (typeof prop === \"string\" && !isWellKnownProperty(prop)) {\n observeParamKeys(observer, [prop]);\n }\n\n if (!isWellKnownProperty(prop) && hasParamProperty(plain, prop)) {\n return Reflect.get(plain, prop);\n }\n\n const value = Reflect.get(target, prop, receiver);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n getOwnPropertyDescriptor(target, prop) {\n if (typeof prop === \"string\" && !isWellKnownProperty(prop)) {\n observeParamKeys(observer, [prop]);\n }\n\n if (!isWellKnownProperty(prop) && hasParamProperty(plain, prop)) {\n return {\n configurable: true,\n enumerable: true,\n value: Reflect.get(plain, prop),\n writable: true,\n };\n }\n\n return Reflect.getOwnPropertyDescriptor(target, prop);\n },\n has(target, prop) {\n if (typeof prop === \"string\" && !isWellKnownProperty(prop)) {\n observeParamKeys(observer, [prop]);\n }\n\n return (\n Reflect.has(target, prop) || (!isWellKnownProperty(prop) && hasParamProperty(plain, prop))\n );\n },\n ownKeys() {\n observeReadableParamKeys(observer, plain);\n return Reflect.ownKeys(plain).filter((prop) => !isWellKnownProperty(prop));\n },\n }) as unknown as ThenableParams<T>;\n}\n"],"mappings":";AAAA,SAAS,iBAAoD,KAAQ,MAA4B;CAC/F,OAAO,OAAO,UAAU,eAAe,KAAK,KAAK,KAAK;;AAsDxD,MAAM,sBAAsB,IAAI,IAAiB;CAvC/C;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAKA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CAGA;CAUoE,CAAC;AAEvE,SAAS,oBAAoB,MAA4B;CACvD,OAAO,oBAAoB,IAAI,KAAK;;AAUtC,SAAS,iBACP,UACA,MACM;CACN,IAAI,UACF,SAAS,mBAAmB,KAAK;;AAIrC,SAAS,oBACP,UACA,OACM;CACN,iBAAiB,UAAU,OAAO,KAAK,MAAM,CAAC;;AAGhD,SAAS,yBACP,UACA,OACM;CAEN,iBAAiB,UADJ,OAAO,KAAK,MAAM,CAAC,QAAQ,QAAQ,CAAC,oBAAoB,IAAI,CAC1C,CAAC;;AAGlC,SAAS,sBAAsB,MAA4B;CACzD,OAAO,SAAS,UAAU,SAAS,WAAW,SAAS;;AAGzD,SAAgB,mBACd,KACA,UACmB;CACnB,MAAM,QAAQ,EAAE,GAAG,KAAK;CACxB,MAAM,UAAU,QAAQ,QAAQ,MAAM;CAMtC,OAAO,IAAI,MAAM,SAAS;EACxB,IAAI,QAAQ,MAAM,UAAU;GAC1B,IAAI,sBAAsB,KAAK,EAAE;IAC/B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,SAAS;IACjD,IAAI,OAAO,UAAU,YAAY,OAAO;IACxC,QAAQ,GAAG,SAAoB;KAC7B,oBAAoB,UAAU,MAAM;KACpC,OAAO,QAAQ,MAAM,OAAO,QAAQ,KAAK;;;GAI7C,IAAI,OAAO,SAAS,YAAY,CAAC,oBAAoB,KAAK,EACxD,iBAAiB,UAAU,CAAC,KAAK,CAAC;GAGpC,IAAI,CAAC,oBAAoB,KAAK,IAAI,iBAAiB,OAAO,KAAK,EAC7D,OAAO,QAAQ,IAAI,OAAO,KAAK;GAGjC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,SAAS;GACjD,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,OAAO,GAAG;;EAE5D,yBAAyB,QAAQ,MAAM;GACrC,IAAI,OAAO,SAAS,YAAY,CAAC,oBAAoB,KAAK,EACxD,iBAAiB,UAAU,CAAC,KAAK,CAAC;GAGpC,IAAI,CAAC,oBAAoB,KAAK,IAAI,iBAAiB,OAAO,KAAK,EAC7D,OAAO;IACL,cAAc;IACd,YAAY;IACZ,OAAO,QAAQ,IAAI,OAAO,KAAK;IAC/B,UAAU;IACX;GAGH,OAAO,QAAQ,yBAAyB,QAAQ,KAAK;;EAEvD,IAAI,QAAQ,MAAM;GAChB,IAAI,OAAO,SAAS,YAAY,CAAC,oBAAoB,KAAK,EACxD,iBAAiB,UAAU,CAAC,KAAK,CAAC;GAGpC,OACE,QAAQ,IAAI,QAAQ,KAAK,IAAK,CAAC,oBAAoB,KAAK,IAAI,iBAAiB,OAAO,KAAK;;EAG7F,UAAU;GACR,yBAAyB,UAAU,MAAM;GACzC,OAAO,QAAQ,QAAQ,MAAM,CAAC,QAAQ,SAAS,CAAC,oBAAoB,KAAK,CAAC;;EAE7E,CAAC"}
@@ -26,8 +26,10 @@ function createRequestContext(opts) {
26
26
  serverContext: null,
27
27
  serverInsertedHTMLCallbacks: [],
28
28
  requestScopedCacheLife: null,
29
+ unstableCacheObservations: /* @__PURE__ */ new Map(),
29
30
  unstableCacheRevalidation: "foreground",
30
31
  _privateCache: null,
32
+ cacheableFetchUrls: /* @__PURE__ */ new Set(),
31
33
  currentRequestTags: [],
32
34
  currentFetchSoftTags: [],
33
35
  currentFetchCacheMode: null,
@@ -38,6 +40,7 @@ function createRequestContext(opts) {
38
40
  requestCache: /* @__PURE__ */ new WeakMap(),
39
41
  ssrContext: null,
40
42
  ssrHeadChildren: [],
43
+ documentInitialHead: [],
41
44
  rootParams: null,
42
45
  ...opts
43
46
  };
@@ -1 +1 @@
1
- {"version":3,"file":"unified-request-context.js","names":[],"sources":["../../src/shims/unified-request-context.ts"],"sourcesContent":["/**\n * Unified per-request context backed by a single AsyncLocalStorage.\n *\n * Consolidates the 5–6 nested ALS scopes that previously wrapped every\n * App Router request (headers, navigation, cache-state, private-cache,\n * fetch-cache, execution-context) into one flat store.\n *\n * Each shim module checks `isInsideUnifiedScope()` and reads its sub-fields\n * from the unified store, falling back to its own standalone ALS when\n * outside (SSR environment, Pages Router, tests).\n */\n\nimport type { AsyncLocalStorage } from \"node:async_hooks\";\nimport { getOrCreateAls } from \"./internal/als-registry.js\";\nimport type {\n CacheState,\n ExecutionContextLike,\n FetchCacheState,\n HeadState,\n I18nState,\n NavigationState,\n PrivateCacheState,\n RouterState,\n RootParamsState,\n VinextHeadersShimState,\n} from \"./request-state-types.js\";\n\n// ---------------------------------------------------------------------------\n// Unified context shape\n// ---------------------------------------------------------------------------\n\n/**\n * Flat union of all per-request state previously spread across\n * VinextHeadersShimState, NavigationState, CacheState, PrivateCacheState,\n * FetchCacheState, and ExecutionContextLike.\n *\n * Each field group is documented with its source shim module.\n */\nexport type UnifiedRequestContext = {\n // ── request-context.ts ─────────────────────────────────────────────\n /** Cloudflare Workers ExecutionContext, or null on Node.js dev. */\n executionContext: ExecutionContextLike | null;\n\n // ── cache-for-request.ts ──────────────────────────────────────────\n /** Per-request cache for cacheForRequest(). Keyed by factory function reference. */\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n requestCache: WeakMap<(...args: any[]) => any, unknown>;\n} & VinextHeadersShimState &\n I18nState &\n NavigationState &\n CacheState &\n PrivateCacheState &\n FetchCacheState &\n RouterState &\n HeadState &\n RootParamsState;\n\n// ---------------------------------------------------------------------------\n// ALS setup — stored on globalThis via Symbol.for so all Vite environments\n// (RSC/SSR/client) share the same instance.\n// ---------------------------------------------------------------------------\n\nconst _REQUEST_CONTEXT_ALS_KEY = Symbol.for(\"vinext.requestContext.als\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = getOrCreateAls<UnifiedRequestContext>(\"vinext.unifiedRequestContext.als\");\n\nfunction _getInheritedExecutionContext(): ExecutionContextLike | null {\n const unifiedStore = _als.getStore();\n if (unifiedStore) return unifiedStore.executionContext;\n\n const executionContextAls = _g[_REQUEST_CONTEXT_ALS_KEY] as\n | AsyncLocalStorage<ExecutionContextLike | null>\n | undefined;\n return executionContextAls?.getStore() ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a fresh `UnifiedRequestContext` with defaults for all fields.\n * Pass partial overrides for the fields you need to pre-populate.\n */\nexport function createRequestContext(opts?: Partial<UnifiedRequestContext>): UnifiedRequestContext {\n return {\n headersContext: null,\n actionRevalidationKind: 0,\n dynamicUsageDetected: false,\n renderRequestApiUsage: new Set(),\n invalidDynamicUsageError: null,\n pendingSetCookies: [],\n draftModeCookieHeader: null,\n phase: \"render\",\n i18nContext: null,\n serverContext: null,\n serverInsertedHTMLCallbacks: [],\n requestScopedCacheLife: null,\n unstableCacheRevalidation: \"foreground\",\n _privateCache: null,\n currentRequestTags: [],\n currentFetchSoftTags: [],\n currentFetchCacheMode: null,\n dynamicFetchUrls: new Set<string>(),\n isFetchDedupeActive: false,\n currentFetchDedupeEntries: new Map(),\n executionContext: _getInheritedExecutionContext(), // inherits from standalone ALS if present\n requestCache: new WeakMap(),\n ssrContext: null,\n ssrHeadChildren: [],\n rootParams: null,\n ...opts,\n };\n}\n\n/**\n * Run `fn` within a unified request context scope.\n * All shim modules will read/write their state from `ctx` for the\n * duration of the call, including async continuations.\n */\nexport function runWithRequestContext<T>(\n ctx: UnifiedRequestContext,\n fn: () => Promise<T>,\n): Promise<T>;\nexport function runWithRequestContext<T>(\n ctx: UnifiedRequestContext,\n fn: () => T | Promise<T>,\n): T | Promise<T>;\nexport function runWithRequestContext<T>(\n ctx: UnifiedRequestContext,\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n return _als.run(ctx, fn);\n}\n\n/**\n * Run `fn` in a nested unified scope derived from the current request context.\n * Used by legacy runWith* wrappers to reset or override one sub-state while\n * preserving proper async isolation for continuations created inside `fn`.\n * The child scope is a shallow clone of the parent store, so untouched fields\n * keep sharing their existing references while overridden slices can be reset.\n *\n * @internal\n */\nexport function runWithUnifiedStateMutation<T>(\n mutate: (ctx: UnifiedRequestContext) => void,\n fn: () => Promise<T>,\n): Promise<T>;\nexport function runWithUnifiedStateMutation<T>(\n mutate: (ctx: UnifiedRequestContext) => void,\n fn: () => T | Promise<T>,\n): T | Promise<T>;\nexport function runWithUnifiedStateMutation<T>(\n mutate: (ctx: UnifiedRequestContext) => void,\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n const parentCtx = _als.getStore();\n if (!parentCtx) return fn();\n\n const childCtx = { ...parentCtx };\n // NOTE: This is a shallow clone. Array fields (pendingSetCookies,\n // serverInsertedHTMLCallbacks, currentRequestTags, ssrHeadChildren), Set\n // fields (renderRequestApiUsage, dynamicFetchUrls), the _privateCache Map,\n // requestCache WeakMap, and object fields (headersContext,\n // i18nContext, serverContext, ssrContext, executionContext,\n // requestScopedCacheLife) still share references with the parent until\n // replaced. requestCache is intentionally shared — nested scopes within\n // the same request should see the same cached values. The mutate\n // callback must replace those reference-typed slices (for example\n // `ctx.currentRequestTags = []` or `ctx.renderRequestApiUsage = new Set()`)\n // rather than mutating them in-place (for\n // example `ctx.currentRequestTags.push(...)`) or the parent scope will\n // observe those changes too. Keep this enumeration in sync with\n // UnifiedRequestContext: when adding a new reference-typed field, add it\n // here too and verify callers still follow the replace-not-mutate rule.\n mutate(childCtx);\n return _als.run(childCtx, fn);\n}\n\n/**\n * Get the current unified request context.\n * Returns the ALS store when inside a `runWithRequestContext()` scope,\n * or a fresh detached context otherwise. Unlike the legacy per-shim fallback\n * singletons, this detached value is ephemeral — mutations do not persist\n * across calls. This is intentional to prevent state leakage outside request\n * scopes.\n *\n * Only direct callers observe this detached fallback. Shim `_getState()`\n * helpers should continue to gate on `isInsideUnifiedScope()` and fall back\n * to their standalone ALS/fallback singletons outside the unified scope.\n * If called inside a standalone `runWithExecutionContext()` scope, the\n * detached context still reflects that inherited `executionContext`.\n */\nexport function getRequestContext(): UnifiedRequestContext {\n return _als.getStore() ?? createRequestContext();\n}\n\n/**\n * Check whether the current execution is inside a `runWithRequestContext()` scope.\n * Shim modules use this to decide whether to read from the unified store\n * or fall back to their own standalone ALS.\n */\nexport function isInsideUnifiedScope(): boolean {\n return _als.getStore() != null;\n}\n"],"mappings":";;AA8DA,MAAM,2BAA2B,OAAO,IAAI,4BAA4B;AACxE,MAAM,KAAK;AACX,MAAM,OAAO,eAAsC,mCAAmC;AAEtF,SAAS,gCAA6D;CACpE,MAAM,eAAe,KAAK,UAAU;CACpC,IAAI,cAAc,OAAO,aAAa;CAKtC,OAH4B,GAAG,2BAGH,UAAU,IAAI;;;;;;AAW5C,SAAgB,qBAAqB,MAA8D;CACjG,OAAO;EACL,gBAAgB;EAChB,wBAAwB;EACxB,sBAAsB;EACtB,uCAAuB,IAAI,KAAK;EAChC,0BAA0B;EAC1B,mBAAmB,EAAE;EACrB,uBAAuB;EACvB,OAAO;EACP,aAAa;EACb,eAAe;EACf,6BAA6B,EAAE;EAC/B,wBAAwB;EACxB,2BAA2B;EAC3B,eAAe;EACf,oBAAoB,EAAE;EACtB,sBAAsB,EAAE;EACxB,uBAAuB;EACvB,kCAAkB,IAAI,KAAa;EACnC,qBAAqB;EACrB,2CAA2B,IAAI,KAAK;EACpC,kBAAkB,+BAA+B;EACjD,8BAAc,IAAI,SAAS;EAC3B,YAAY;EACZ,iBAAiB,EAAE;EACnB,YAAY;EACZ,GAAG;EACJ;;AAgBH,SAAgB,sBACd,KACA,IACgB;CAChB,OAAO,KAAK,IAAI,KAAK,GAAG;;AAoB1B,SAAgB,4BACd,QACA,IACgB;CAChB,MAAM,YAAY,KAAK,UAAU;CACjC,IAAI,CAAC,WAAW,OAAO,IAAI;CAE3B,MAAM,WAAW,EAAE,GAAG,WAAW;CAgBjC,OAAO,SAAS;CAChB,OAAO,KAAK,IAAI,UAAU,GAAG;;;;;;;;;;;;;;;;AAiB/B,SAAgB,oBAA2C;CACzD,OAAO,KAAK,UAAU,IAAI,sBAAsB;;;;;;;AAQlD,SAAgB,uBAAgC;CAC9C,OAAO,KAAK,UAAU,IAAI"}
1
+ {"version":3,"file":"unified-request-context.js","names":[],"sources":["../../src/shims/unified-request-context.ts"],"sourcesContent":["/**\n * Unified per-request context backed by a single AsyncLocalStorage.\n *\n * Consolidates the 5–6 nested ALS scopes that previously wrapped every\n * App Router request (headers, navigation, cache-state, private-cache,\n * fetch-cache, execution-context) into one flat store.\n *\n * Each shim module checks `isInsideUnifiedScope()` and reads its sub-fields\n * from the unified store, falling back to its own standalone ALS when\n * outside (SSR environment, Pages Router, tests).\n */\n\nimport type { AsyncLocalStorage } from \"node:async_hooks\";\nimport { getOrCreateAls } from \"./internal/als-registry.js\";\nimport type {\n CacheState,\n ExecutionContextLike,\n FetchCacheState,\n HeadState,\n I18nState,\n NavigationState,\n PrivateCacheState,\n RouterState,\n RootParamsState,\n VinextHeadersShimState,\n} from \"./request-state-types.js\";\n\n// ---------------------------------------------------------------------------\n// Unified context shape\n// ---------------------------------------------------------------------------\n\n/**\n * Flat union of all per-request state previously spread across\n * VinextHeadersShimState, NavigationState, CacheState, PrivateCacheState,\n * FetchCacheState, and ExecutionContextLike.\n *\n * Each field group is documented with its source shim module.\n */\nexport type UnifiedRequestContext = {\n // ── request-context.ts ─────────────────────────────────────────────\n /** Cloudflare Workers ExecutionContext, or null on Node.js dev. */\n executionContext: ExecutionContextLike | null;\n\n // ── cache-for-request.ts ──────────────────────────────────────────\n /** Per-request cache for cacheForRequest(). Keyed by factory function reference. */\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n requestCache: WeakMap<(...args: any[]) => any, unknown>;\n} & VinextHeadersShimState &\n I18nState &\n NavigationState &\n CacheState &\n PrivateCacheState &\n FetchCacheState &\n RouterState &\n HeadState &\n RootParamsState;\n\n// ---------------------------------------------------------------------------\n// ALS setup — stored on globalThis via Symbol.for so all Vite environments\n// (RSC/SSR/client) share the same instance.\n// ---------------------------------------------------------------------------\n\nconst _REQUEST_CONTEXT_ALS_KEY = Symbol.for(\"vinext.requestContext.als\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = getOrCreateAls<UnifiedRequestContext>(\"vinext.unifiedRequestContext.als\");\n\nfunction _getInheritedExecutionContext(): ExecutionContextLike | null {\n const unifiedStore = _als.getStore();\n if (unifiedStore) return unifiedStore.executionContext;\n\n const executionContextAls = _g[_REQUEST_CONTEXT_ALS_KEY] as\n | AsyncLocalStorage<ExecutionContextLike | null>\n | undefined;\n return executionContextAls?.getStore() ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a fresh `UnifiedRequestContext` with defaults for all fields.\n * Pass partial overrides for the fields you need to pre-populate.\n */\nexport function createRequestContext(opts?: Partial<UnifiedRequestContext>): UnifiedRequestContext {\n return {\n headersContext: null,\n actionRevalidationKind: 0,\n dynamicUsageDetected: false,\n renderRequestApiUsage: new Set(),\n invalidDynamicUsageError: null,\n pendingSetCookies: [],\n draftModeCookieHeader: null,\n phase: \"render\",\n i18nContext: null,\n serverContext: null,\n serverInsertedHTMLCallbacks: [],\n requestScopedCacheLife: null,\n unstableCacheObservations: new Map(),\n unstableCacheRevalidation: \"foreground\",\n _privateCache: null,\n cacheableFetchUrls: new Set<string>(),\n currentRequestTags: [],\n currentFetchSoftTags: [],\n currentFetchCacheMode: null,\n dynamicFetchUrls: new Set<string>(),\n isFetchDedupeActive: false,\n currentFetchDedupeEntries: new Map(),\n executionContext: _getInheritedExecutionContext(), // inherits from standalone ALS if present\n requestCache: new WeakMap(),\n ssrContext: null,\n ssrHeadChildren: [],\n documentInitialHead: [],\n rootParams: null,\n ...opts,\n };\n}\n\n/**\n * Run `fn` within a unified request context scope.\n * All shim modules will read/write their state from `ctx` for the\n * duration of the call, including async continuations.\n */\nexport function runWithRequestContext<T>(\n ctx: UnifiedRequestContext,\n fn: () => Promise<T>,\n): Promise<T>;\nexport function runWithRequestContext<T>(\n ctx: UnifiedRequestContext,\n fn: () => T | Promise<T>,\n): T | Promise<T>;\nexport function runWithRequestContext<T>(\n ctx: UnifiedRequestContext,\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n return _als.run(ctx, fn);\n}\n\n/**\n * Run `fn` in a nested unified scope derived from the current request context.\n * Used by legacy runWith* wrappers to reset or override one sub-state while\n * preserving proper async isolation for continuations created inside `fn`.\n * The child scope is a shallow clone of the parent store, so untouched fields\n * keep sharing their existing references while overridden slices can be reset.\n *\n * @internal\n */\nexport function runWithUnifiedStateMutation<T>(\n mutate: (ctx: UnifiedRequestContext) => void,\n fn: () => Promise<T>,\n): Promise<T>;\nexport function runWithUnifiedStateMutation<T>(\n mutate: (ctx: UnifiedRequestContext) => void,\n fn: () => T | Promise<T>,\n): T | Promise<T>;\nexport function runWithUnifiedStateMutation<T>(\n mutate: (ctx: UnifiedRequestContext) => void,\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n const parentCtx = _als.getStore();\n if (!parentCtx) return fn();\n\n const childCtx = { ...parentCtx };\n // NOTE: This is a shallow clone. Array fields (pendingSetCookies,\n // serverInsertedHTMLCallbacks, currentRequestTags, ssrHeadChildren), Set\n // fields (renderRequestApiUsage, cacheableFetchUrls, dynamicFetchUrls),\n // Map fields (unstableCacheObservations, _privateCache),\n // requestCache WeakMap, and object fields (headersContext,\n // i18nContext, serverContext, ssrContext, executionContext,\n // requestScopedCacheLife) still share references with the parent until\n // replaced. requestCache is intentionally shared — nested scopes within\n // the same request should see the same cached values. The mutate\n // callback must replace those reference-typed slices (for example\n // `ctx.currentRequestTags = []` or `ctx.renderRequestApiUsage = new Set()`)\n // rather than mutating them in-place (for\n // example `ctx.currentRequestTags.push(...)`) or the parent scope will\n // observe those changes too. Keep this enumeration in sync with\n // UnifiedRequestContext: when adding a new reference-typed field, add it\n // here too and verify callers still follow the replace-not-mutate rule.\n mutate(childCtx);\n return _als.run(childCtx, fn);\n}\n\n/**\n * Get the current unified request context.\n * Returns the ALS store when inside a `runWithRequestContext()` scope,\n * or a fresh detached context otherwise. Unlike the legacy per-shim fallback\n * singletons, this detached value is ephemeral — mutations do not persist\n * across calls. This is intentional to prevent state leakage outside request\n * scopes.\n *\n * Only direct callers observe this detached fallback. Shim `_getState()`\n * helpers should continue to gate on `isInsideUnifiedScope()` and fall back\n * to their standalone ALS/fallback singletons outside the unified scope.\n * If called inside a standalone `runWithExecutionContext()` scope, the\n * detached context still reflects that inherited `executionContext`.\n */\nexport function getRequestContext(): UnifiedRequestContext {\n return _als.getStore() ?? createRequestContext();\n}\n\n/**\n * Check whether the current execution is inside a `runWithRequestContext()` scope.\n * Shim modules use this to decide whether to read from the unified store\n * or fall back to their own standalone ALS.\n */\nexport function isInsideUnifiedScope(): boolean {\n return _als.getStore() != null;\n}\n"],"mappings":";;AA8DA,MAAM,2BAA2B,OAAO,IAAI,4BAA4B;AACxE,MAAM,KAAK;AACX,MAAM,OAAO,eAAsC,mCAAmC;AAEtF,SAAS,gCAA6D;CACpE,MAAM,eAAe,KAAK,UAAU;CACpC,IAAI,cAAc,OAAO,aAAa;CAKtC,OAH4B,GAAG,2BAGH,UAAU,IAAI;;;;;;AAW5C,SAAgB,qBAAqB,MAA8D;CACjG,OAAO;EACL,gBAAgB;EAChB,wBAAwB;EACxB,sBAAsB;EACtB,uCAAuB,IAAI,KAAK;EAChC,0BAA0B;EAC1B,mBAAmB,EAAE;EACrB,uBAAuB;EACvB,OAAO;EACP,aAAa;EACb,eAAe;EACf,6BAA6B,EAAE;EAC/B,wBAAwB;EACxB,2CAA2B,IAAI,KAAK;EACpC,2BAA2B;EAC3B,eAAe;EACf,oCAAoB,IAAI,KAAa;EACrC,oBAAoB,EAAE;EACtB,sBAAsB,EAAE;EACxB,uBAAuB;EACvB,kCAAkB,IAAI,KAAa;EACnC,qBAAqB;EACrB,2CAA2B,IAAI,KAAK;EACpC,kBAAkB,+BAA+B;EACjD,8BAAc,IAAI,SAAS;EAC3B,YAAY;EACZ,iBAAiB,EAAE;EACnB,qBAAqB,EAAE;EACvB,YAAY;EACZ,GAAG;EACJ;;AAgBH,SAAgB,sBACd,KACA,IACgB;CAChB,OAAO,KAAK,IAAI,KAAK,GAAG;;AAoB1B,SAAgB,4BACd,QACA,IACgB;CAChB,MAAM,YAAY,KAAK,UAAU;CACjC,IAAI,CAAC,WAAW,OAAO,IAAI;CAE3B,MAAM,WAAW,EAAE,GAAG,WAAW;CAiBjC,OAAO,SAAS;CAChB,OAAO,KAAK,IAAI,UAAU,GAAG;;;;;;;;;;;;;;;;AAiB/B,SAAgB,oBAA2C;CACzD,OAAO,KAAK,UAAU,IAAI,sBAAsB;;;;;;;AAQlD,SAAgB,uBAAgC;CAC9C,OAAO,KAAK,UAAU,IAAI"}
@@ -0,0 +1,15 @@
1
+ import { BuildManifestChunk } from "./lazy-chunks.js";
2
+
3
+ //#region src/utils/client-build-manifest.d.ts
4
+ type ClientBuildManifest = Record<string, BuildManifestChunk>;
5
+ declare function readClientBuildManifest(manifestPath: string): ClientBuildManifest | undefined;
6
+ declare function findClientEntryFileFromManifest(buildManifest: ClientBuildManifest, assetBase: string): string | undefined;
7
+ declare function findClientEntryFile(options: {
8
+ buildManifest?: ClientBuildManifest;
9
+ clientDir: string;
10
+ assetsSubdir: string;
11
+ assetBase: string;
12
+ }): string | undefined;
13
+ //#endregion
14
+ export { findClientEntryFile, findClientEntryFileFromManifest, readClientBuildManifest };
15
+ //# sourceMappingURL=client-build-manifest.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { isUnknownRecord } from "./record.js";
2
+ import { manifestFileWithBase } from "./manifest-paths.js";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ //#region src/utils/client-build-manifest.ts
6
+ const CLIENT_ENTRY_MARKERS = ["vinext-client-entry", "vinext-app-browser-entry"];
7
+ function readClientBuildManifest(manifestPath) {
8
+ if (!fs.existsSync(manifestPath)) return void 0;
9
+ try {
10
+ const value = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
11
+ if (!isUnknownRecord(value)) return void 0;
12
+ const manifest = {};
13
+ for (const [key, entry] of Object.entries(value)) {
14
+ if (!isUnknownRecord(entry) || typeof entry.file !== "string") continue;
15
+ const imports = readStringArray(entry.imports);
16
+ const dynamicImports = readStringArray(entry.dynamicImports);
17
+ const css = readStringArray(entry.css);
18
+ const assets = readStringArray(entry.assets);
19
+ manifest[key] = {
20
+ file: entry.file,
21
+ ...entry.isEntry === true ? { isEntry: true } : {},
22
+ ...entry.isDynamicEntry === true ? { isDynamicEntry: true } : {},
23
+ ...imports ? { imports } : {},
24
+ ...dynamicImports ? { dynamicImports } : {},
25
+ ...css ? { css } : {},
26
+ ...assets ? { assets } : {}
27
+ };
28
+ }
29
+ return manifest;
30
+ } catch {
31
+ return;
32
+ }
33
+ }
34
+ function findClientEntryFileFromManifest(buildManifest, assetBase) {
35
+ const entries = Object.values(buildManifest).filter((entry) => entry.isEntry && entry.file);
36
+ const chosen = entries.find((entry) => CLIENT_ENTRY_MARKERS.some((marker) => entry.file.includes(marker))) ?? entries[0];
37
+ return chosen ? manifestFileWithBase(chosen.file, assetBase) : void 0;
38
+ }
39
+ function findClientEntryFileInAssetsDir(options) {
40
+ const assetsDir = path.join(options.clientDir, options.assetsSubdir);
41
+ if (!fs.existsSync(assetsDir)) return void 0;
42
+ const entry = fs.readdirSync(assetsDir).find((file) => CLIENT_ENTRY_MARKERS.some((marker) => file.includes(marker)) && file.endsWith(".js"));
43
+ return entry ? manifestFileWithBase(`${options.assetsSubdir}/${entry}`, options.assetBase) : void 0;
44
+ }
45
+ function findClientEntryFile(options) {
46
+ return (options.buildManifest ? findClientEntryFileFromManifest(options.buildManifest, options.assetBase) : void 0) ?? findClientEntryFileInAssetsDir(options);
47
+ }
48
+ function readStringArray(value) {
49
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
50
+ }
51
+ //#endregion
52
+ export { findClientEntryFile, findClientEntryFileFromManifest, readClientBuildManifest };
53
+
54
+ //# sourceMappingURL=client-build-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-build-manifest.js","names":[],"sources":["../../src/utils/client-build-manifest.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { manifestFileWithBase } from \"./manifest-paths.js\";\nimport type { BuildManifestChunk } from \"./lazy-chunks.js\";\nimport { isUnknownRecord } from \"./record.js\";\n\ntype ClientBuildManifest = Record<string, BuildManifestChunk>;\n\nconst CLIENT_ENTRY_MARKERS = [\"vinext-client-entry\", \"vinext-app-browser-entry\"] as const;\n\nexport function readClientBuildManifest(manifestPath: string): ClientBuildManifest | undefined {\n if (!fs.existsSync(manifestPath)) return undefined;\n\n try {\n const value: unknown = JSON.parse(fs.readFileSync(manifestPath, \"utf-8\"));\n if (!isUnknownRecord(value)) return undefined;\n\n const manifest: ClientBuildManifest = {};\n for (const [key, entry] of Object.entries(value)) {\n if (!isUnknownRecord(entry) || typeof entry.file !== \"string\") continue;\n\n const imports = readStringArray(entry.imports);\n const dynamicImports = readStringArray(entry.dynamicImports);\n const css = readStringArray(entry.css);\n const assets = readStringArray(entry.assets);\n manifest[key] = {\n file: entry.file,\n ...(entry.isEntry === true ? { isEntry: true } : {}),\n ...(entry.isDynamicEntry === true ? { isDynamicEntry: true } : {}),\n ...(imports ? { imports } : {}),\n ...(dynamicImports ? { dynamicImports } : {}),\n ...(css ? { css } : {}),\n ...(assets ? { assets } : {}),\n };\n }\n\n return manifest;\n } catch {\n return undefined;\n }\n}\n\nexport function findClientEntryFileFromManifest(\n buildManifest: ClientBuildManifest,\n assetBase: string,\n): string | undefined {\n const entries = Object.values(buildManifest).filter((entry) => entry.isEntry && entry.file);\n // A client build can emit more than one `isEntry` chunk (e.g. the client\n // entry plus instrumentation or middleware entries), and the manifest's\n // iteration order is not guaranteed to surface the client entry first.\n // Prefer the chunk whose file carries a known client-entry marker — matching\n // the precise on-disk scan in findClientEntryFileInAssetsDir — and only fall\n // back to the first entry when nothing is marked.\n const markedEntry = entries.find((entry) =>\n CLIENT_ENTRY_MARKERS.some((marker) => entry.file.includes(marker)),\n );\n const chosen = markedEntry ?? entries[0];\n\n return chosen ? manifestFileWithBase(chosen.file, assetBase) : undefined;\n}\n\nfunction findClientEntryFileInAssetsDir(options: {\n clientDir: string;\n assetsSubdir: string;\n assetBase: string;\n}): string | undefined {\n const assetsDir = path.join(options.clientDir, options.assetsSubdir);\n if (!fs.existsSync(assetsDir)) return undefined;\n\n const entry = fs\n .readdirSync(assetsDir)\n .find(\n (file) =>\n CLIENT_ENTRY_MARKERS.some((marker) => file.includes(marker)) && file.endsWith(\".js\"),\n );\n\n return entry\n ? manifestFileWithBase(`${options.assetsSubdir}/${entry}`, options.assetBase)\n : undefined;\n}\n\nexport function findClientEntryFile(options: {\n buildManifest?: ClientBuildManifest;\n clientDir: string;\n assetsSubdir: string;\n assetBase: string;\n}): string | undefined {\n return (\n (options.buildManifest\n ? findClientEntryFileFromManifest(options.buildManifest, options.assetBase)\n : undefined) ?? findClientEntryFileInAssetsDir(options)\n );\n}\n\nfunction readStringArray(value: unknown): string[] | undefined {\n return Array.isArray(value) && value.every((item): item is string => typeof item === \"string\")\n ? value\n : undefined;\n}\n"],"mappings":";;;;;AAQA,MAAM,uBAAuB,CAAC,uBAAuB,2BAA2B;AAEhF,SAAgB,wBAAwB,cAAuD;CAC7F,IAAI,CAAC,GAAG,WAAW,aAAa,EAAE,OAAO,KAAA;CAEzC,IAAI;EACF,MAAM,QAAiB,KAAK,MAAM,GAAG,aAAa,cAAc,QAAQ,CAAC;EACzE,IAAI,CAAC,gBAAgB,MAAM,EAAE,OAAO,KAAA;EAEpC,MAAM,WAAgC,EAAE;EACxC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;GAChD,IAAI,CAAC,gBAAgB,MAAM,IAAI,OAAO,MAAM,SAAS,UAAU;GAE/D,MAAM,UAAU,gBAAgB,MAAM,QAAQ;GAC9C,MAAM,iBAAiB,gBAAgB,MAAM,eAAe;GAC5D,MAAM,MAAM,gBAAgB,MAAM,IAAI;GACtC,MAAM,SAAS,gBAAgB,MAAM,OAAO;GAC5C,SAAS,OAAO;IACd,MAAM,MAAM;IACZ,GAAI,MAAM,YAAY,OAAO,EAAE,SAAS,MAAM,GAAG,EAAE;IACnD,GAAI,MAAM,mBAAmB,OAAO,EAAE,gBAAgB,MAAM,GAAG,EAAE;IACjE,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;IAC9B,GAAI,iBAAiB,EAAE,gBAAgB,GAAG,EAAE;IAC5C,GAAI,MAAM,EAAE,KAAK,GAAG,EAAE;IACtB,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;IAC7B;;EAGH,OAAO;SACD;EACN;;;AAIJ,SAAgB,gCACd,eACA,WACoB;CACpB,MAAM,UAAU,OAAO,OAAO,cAAc,CAAC,QAAQ,UAAU,MAAM,WAAW,MAAM,KAAK;CAU3F,MAAM,SAHc,QAAQ,MAAM,UAChC,qBAAqB,MAAM,WAAW,MAAM,KAAK,SAAS,OAAO,CAAC,CAE1C,IAAI,QAAQ;CAEtC,OAAO,SAAS,qBAAqB,OAAO,MAAM,UAAU,GAAG,KAAA;;AAGjE,SAAS,+BAA+B,SAIjB;CACrB,MAAM,YAAY,KAAK,KAAK,QAAQ,WAAW,QAAQ,aAAa;CACpE,IAAI,CAAC,GAAG,WAAW,UAAU,EAAE,OAAO,KAAA;CAEtC,MAAM,QAAQ,GACX,YAAY,UAAU,CACtB,MACE,SACC,qBAAqB,MAAM,WAAW,KAAK,SAAS,OAAO,CAAC,IAAI,KAAK,SAAS,MAAM,CACvF;CAEH,OAAO,QACH,qBAAqB,GAAG,QAAQ,aAAa,GAAG,SAAS,QAAQ,UAAU,GAC3E,KAAA;;AAGN,SAAgB,oBAAoB,SAKb;CACrB,QACG,QAAQ,gBACL,gCAAgC,QAAQ,eAAe,QAAQ,UAAU,GACzE,KAAA,MAAc,+BAA+B,QAAQ;;AAI7D,SAAS,gBAAgB,OAAsC;CAC7D,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM,OAAO,SAAyB,OAAO,SAAS,SAAS,GAC1F,QACA,KAAA"}
@@ -14,7 +14,7 @@ function fnv1a64(input) {
14
14
  h2 ^= input.charCodeAt(i);
15
15
  h2 = h2 * 16777619 >>> 0;
16
16
  }
17
- return h1.toString(36) + h2.toString(36);
17
+ return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
18
18
  }
19
19
  //#endregion
20
20
  export { fnv1a64 };
@@ -1 +1 @@
1
- {"version":3,"file":"hash.js","names":[],"sources":["../../src/utils/hash.ts"],"sourcesContent":["/**\n * FNV-1a hash producing a 64-bit result (two 32-bit rounds with different seeds).\n * Used for deterministic key generation where collisions must be rare.\n */\nexport function fnv1a64(input: string): string {\n // First 32-bit round with standard FNV offset basis\n let h1 = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h1 ^= input.charCodeAt(i);\n h1 = (h1 * 0x01000193) >>> 0;\n }\n // Second 32-bit round with different seed\n let h2 = 0x050c5d1f;\n for (let i = 0; i < input.length; i++) {\n h2 ^= input.charCodeAt(i);\n h2 = (h2 * 0x01000193) >>> 0;\n }\n return h1.toString(36) + h2.toString(36);\n}\n"],"mappings":";;;;;AAIA,SAAgB,QAAQ,OAAuB;CAE7C,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,MAAM,WAAW,EAAE;EACzB,KAAM,KAAK,aAAgB;;CAG7B,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,MAAM,WAAW,EAAE;EACzB,KAAM,KAAK,aAAgB;;CAE7B,OAAO,GAAG,SAAS,GAAG,GAAG,GAAG,SAAS,GAAG"}
1
+ {"version":3,"file":"hash.js","names":[],"sources":["../../src/utils/hash.ts"],"sourcesContent":["/**\n * FNV-1a hash producing a 64-bit result (two 32-bit rounds with different seeds).\n * Used for deterministic key generation where collisions must be rare.\n */\nexport function fnv1a64(input: string): string {\n // First 32-bit round with standard FNV offset basis\n let h1 = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h1 ^= input.charCodeAt(i);\n h1 = (h1 * 0x01000193) >>> 0;\n }\n // Second 32-bit round with different seed\n let h2 = 0x050c5d1f;\n for (let i = 0; i < input.length; i++) {\n h2 ^= input.charCodeAt(i);\n h2 = (h2 * 0x01000193) >>> 0;\n }\n return h1.toString(16).padStart(8, \"0\") + h2.toString(16).padStart(8, \"0\");\n}\n"],"mappings":";;;;;AAIA,SAAgB,QAAQ,OAAuB;CAE7C,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,MAAM,WAAW,EAAE;EACzB,KAAM,KAAK,aAAgB;;CAG7B,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,MAAM,WAAW,EAAE;EACzB,KAAM,KAAK,aAAgB;;CAE7B,OAAO,GAAG,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,GAAG,GAAG,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI"}
@@ -30,5 +30,5 @@ type BuildManifestChunk = {
30
30
  */
31
31
  declare function computeLazyChunks(buildManifest: Record<string, BuildManifestChunk>): string[];
32
32
  //#endregion
33
- export { computeLazyChunks };
33
+ export { BuildManifestChunk, computeLazyChunks };
34
34
  //# sourceMappingURL=lazy-chunks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-chunks.js","names":[],"sources":["../../src/utils/lazy-chunks.ts"],"sourcesContent":["/**\n * Build-manifest chunk metadata used to compute lazy chunks.\n */\ntype BuildManifestChunk = {\n file: string;\n isEntry?: boolean;\n isDynamicEntry?: boolean;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n};\n\n/**\n * Compute the set of chunk filenames that are ONLY reachable through dynamic\n * imports (i.e. behind React.lazy(), next/dynamic, or manual import()).\n *\n * These chunks should NOT be modulepreloaded in the HTML — they will be\n * fetched on demand when the dynamic import executes.\n *\n * Algorithm: Starting from all entry chunks in the build manifest, walk the\n * static `imports` tree (breadth-first). Any chunk file NOT reached by this\n * walk is only reachable through `dynamicImports` and is therefore \"lazy\".\n *\n * @param buildManifest - Vite's build manifest (manifest.json), which is a\n * Record<string, ManifestChunk> where each chunk has `file`, `imports`,\n * `dynamicImports`, `isEntry`, and `isDynamicEntry` fields.\n * @returns Array of chunk filenames (e.g. \"_next/static/mermaid-NOHMQCX5.js\") that\n * should be excluded from modulepreload hints.\n */\nexport function computeLazyChunks(buildManifest: Record<string, BuildManifestChunk>): string[] {\n // Collect all chunk files that are statically reachable from entries\n const eagerFiles = new Set<string>();\n const visited = new Set<string>();\n const queue: string[] = [];\n\n // Start BFS from all entry chunks\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.isEntry) {\n queue.push(key);\n }\n }\n\n while (queue.length > 0) {\n const key = queue.shift();\n if (!key || visited.has(key)) continue;\n visited.add(key);\n\n const chunk = buildManifest[key];\n if (!chunk) continue;\n\n // Mark this chunk's file as eager\n eagerFiles.add(chunk.file);\n\n // Also mark its CSS as eager (CSS should always be preloaded to avoid FOUC)\n if (chunk.css) {\n for (const cssFile of chunk.css) {\n eagerFiles.add(cssFile);\n }\n }\n\n // Follow only static imports — NOT dynamicImports\n if (chunk.imports) {\n for (const imp of chunk.imports) {\n if (!visited.has(imp)) {\n queue.push(imp);\n }\n }\n }\n }\n\n // Any JS file in the manifest that's NOT in eagerFiles is a lazy chunk\n const lazyChunks: string[] = [];\n const allFiles = new Set<string>();\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.file && !allFiles.has(chunk.file)) {\n allFiles.add(chunk.file);\n if (!eagerFiles.has(chunk.file) && chunk.file.endsWith(\".js\")) {\n lazyChunks.push(chunk.file);\n }\n }\n }\n\n return lazyChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8BA,SAAgB,kBAAkB,eAA6D;CAE7F,MAAM,6BAAa,IAAI,KAAa;CACpC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,QAAkB,EAAE;CAG1B,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAE1C,IADc,cAAc,KAClB,SACR,MAAM,KAAK,IAAI;CAInB,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,OAAO;EACzB,IAAI,CAAC,OAAO,QAAQ,IAAI,IAAI,EAAE;EAC9B,QAAQ,IAAI,IAAI;EAEhB,MAAM,QAAQ,cAAc;EAC5B,IAAI,CAAC,OAAO;EAGZ,WAAW,IAAI,MAAM,KAAK;EAG1B,IAAI,MAAM,KACR,KAAK,MAAM,WAAW,MAAM,KAC1B,WAAW,IAAI,QAAQ;EAK3B,IAAI,MAAM;QACH,MAAM,OAAO,MAAM,SACtB,IAAI,CAAC,QAAQ,IAAI,IAAI,EACnB,MAAM,KAAK,IAAI;;;CAOvB,MAAM,aAAuB,EAAE;CAC/B,MAAM,2BAAW,IAAI,KAAa;CAClC,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAAE;EAC5C,MAAM,QAAQ,cAAc;EAC5B,IAAI,MAAM,QAAQ,CAAC,SAAS,IAAI,MAAM,KAAK,EAAE;GAC3C,SAAS,IAAI,MAAM,KAAK;GACxB,IAAI,CAAC,WAAW,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,EAC3D,WAAW,KAAK,MAAM,KAAK;;;CAKjC,OAAO"}
1
+ {"version":3,"file":"lazy-chunks.js","names":[],"sources":["../../src/utils/lazy-chunks.ts"],"sourcesContent":["/**\n * Build-manifest chunk metadata used to compute lazy chunks.\n */\nexport type BuildManifestChunk = {\n file: string;\n isEntry?: boolean;\n isDynamicEntry?: boolean;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n};\n\n/**\n * Compute the set of chunk filenames that are ONLY reachable through dynamic\n * imports (i.e. behind React.lazy(), next/dynamic, or manual import()).\n *\n * These chunks should NOT be modulepreloaded in the HTML — they will be\n * fetched on demand when the dynamic import executes.\n *\n * Algorithm: Starting from all entry chunks in the build manifest, walk the\n * static `imports` tree (breadth-first). Any chunk file NOT reached by this\n * walk is only reachable through `dynamicImports` and is therefore \"lazy\".\n *\n * @param buildManifest - Vite's build manifest (manifest.json), which is a\n * Record<string, ManifestChunk> where each chunk has `file`, `imports`,\n * `dynamicImports`, `isEntry`, and `isDynamicEntry` fields.\n * @returns Array of chunk filenames (e.g. \"_next/static/mermaid-NOHMQCX5.js\") that\n * should be excluded from modulepreload hints.\n */\nexport function computeLazyChunks(buildManifest: Record<string, BuildManifestChunk>): string[] {\n // Collect all chunk files that are statically reachable from entries\n const eagerFiles = new Set<string>();\n const visited = new Set<string>();\n const queue: string[] = [];\n\n // Start BFS from all entry chunks\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.isEntry) {\n queue.push(key);\n }\n }\n\n while (queue.length > 0) {\n const key = queue.shift();\n if (!key || visited.has(key)) continue;\n visited.add(key);\n\n const chunk = buildManifest[key];\n if (!chunk) continue;\n\n // Mark this chunk's file as eager\n eagerFiles.add(chunk.file);\n\n // Also mark its CSS as eager (CSS should always be preloaded to avoid FOUC)\n if (chunk.css) {\n for (const cssFile of chunk.css) {\n eagerFiles.add(cssFile);\n }\n }\n\n // Follow only static imports — NOT dynamicImports\n if (chunk.imports) {\n for (const imp of chunk.imports) {\n if (!visited.has(imp)) {\n queue.push(imp);\n }\n }\n }\n }\n\n // Any JS file in the manifest that's NOT in eagerFiles is a lazy chunk\n const lazyChunks: string[] = [];\n const allFiles = new Set<string>();\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.file && !allFiles.has(chunk.file)) {\n allFiles.add(chunk.file);\n if (!eagerFiles.has(chunk.file) && chunk.file.endsWith(\".js\")) {\n lazyChunks.push(chunk.file);\n }\n }\n }\n\n return lazyChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8BA,SAAgB,kBAAkB,eAA6D;CAE7F,MAAM,6BAAa,IAAI,KAAa;CACpC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,QAAkB,EAAE;CAG1B,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAE1C,IADc,cAAc,KAClB,SACR,MAAM,KAAK,IAAI;CAInB,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,OAAO;EACzB,IAAI,CAAC,OAAO,QAAQ,IAAI,IAAI,EAAE;EAC9B,QAAQ,IAAI,IAAI;EAEhB,MAAM,QAAQ,cAAc;EAC5B,IAAI,CAAC,OAAO;EAGZ,WAAW,IAAI,MAAM,KAAK;EAG1B,IAAI,MAAM,KACR,KAAK,MAAM,WAAW,MAAM,KAC1B,WAAW,IAAI,QAAQ;EAK3B,IAAI,MAAM;QACH,MAAM,OAAO,MAAM,SACtB,IAAI,CAAC,QAAQ,IAAI,IAAI,EACnB,MAAM,KAAK,IAAI;;;CAOvB,MAAM,aAAuB,EAAE;CAC/B,MAAM,2BAAW,IAAI,KAAa;CAClC,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAAE;EAC5C,MAAM,QAAQ,cAAc;EAC5B,IAAI,MAAM,QAAQ,CAAC,SAAS,IAAI,MAAM,KAAK,EAAE;GAC3C,SAAS,IAAI,MAAM,KAAK;GACxB,IAAI,CAAC,WAAW,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,EAC3D,WAAW,KAAK,MAAM,KAAK;;;CAKjC,OAAO"}
@@ -0,0 +1,13 @@
1
+ //#region src/utils/path.d.ts
2
+ /**
3
+ * Convert Windows-style backslash path separators to forward slashes.
4
+ *
5
+ * Generated entry modules embed absolute filesystem paths inside `import`
6
+ * statements. On Windows the OS-native paths use `\` which is invalid in JS
7
+ * module specifiers, so every entry generator normalizes paths through this
8
+ * helper before stringifying them into the emitted code.
9
+ */
10
+ declare function normalizePathSeparators(p: string): string;
11
+ //#endregion
12
+ export { normalizePathSeparators };
13
+ //# sourceMappingURL=path.d.ts.map