vinext 0.0.49 → 0.0.50

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 (390) hide show
  1. package/dist/build/client-build-config.js.map +1 -1
  2. package/dist/build/google-fonts/build-url.js.map +1 -1
  3. package/dist/build/google-fonts/get-axes.js.map +1 -1
  4. package/dist/build/google-fonts/sort-variants.js.map +1 -1
  5. package/dist/build/google-fonts/validate.js.map +1 -1
  6. package/dist/build/layout-classification.js.map +1 -1
  7. package/dist/build/nitro-route-rules.js.map +1 -1
  8. package/dist/build/precompress.js.map +1 -1
  9. package/dist/build/prerender.d.ts +17 -1
  10. package/dist/build/prerender.js +77 -16
  11. package/dist/build/prerender.js.map +1 -1
  12. package/dist/build/report.js.map +1 -1
  13. package/dist/build/route-classification-injector.js.map +1 -1
  14. package/dist/build/route-classification-manifest.js.map +1 -1
  15. package/dist/build/run-prerender.js.map +1 -1
  16. package/dist/build/server-manifest.js.map +1 -1
  17. package/dist/build/ssr-manifest.js.map +1 -1
  18. package/dist/build/standalone.js.map +1 -1
  19. package/dist/build/static-export.js.map +1 -1
  20. package/dist/check.js +1 -1
  21. package/dist/check.js.map +1 -1
  22. package/dist/cli-args.js.map +1 -1
  23. package/dist/cli.js +8 -4
  24. package/dist/cli.js.map +1 -1
  25. package/dist/client/instrumentation-client-state.js.map +1 -1
  26. package/dist/client/validate-module-path.js.map +1 -1
  27. package/dist/client/vinext-next-data.d.ts +5 -1
  28. package/dist/client/window-next.d.ts +149 -0
  29. package/dist/client/window-next.js +48 -0
  30. package/dist/client/window-next.js.map +1 -0
  31. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  32. package/dist/cloudflare/tpr.js +2 -1
  33. package/dist/cloudflare/tpr.js.map +1 -1
  34. package/dist/config/config-matchers.d.ts +3 -1
  35. package/dist/config/config-matchers.js +5 -4
  36. package/dist/config/config-matchers.js.map +1 -1
  37. package/dist/config/dotenv.js.map +1 -1
  38. package/dist/config/next-config.d.ts +6 -3
  39. package/dist/config/next-config.js +13 -2
  40. package/dist/config/next-config.js.map +1 -1
  41. package/dist/deploy.js +13 -5
  42. package/dist/deploy.js.map +1 -1
  43. package/dist/entries/app-browser-entry.d.ts +3 -1
  44. package/dist/entries/app-browser-entry.js +11 -2
  45. package/dist/entries/app-browser-entry.js.map +1 -1
  46. package/dist/entries/app-rsc-entry.js +11 -0
  47. package/dist/entries/app-rsc-entry.js.map +1 -1
  48. package/dist/entries/app-rsc-manifest.js +4 -0
  49. package/dist/entries/app-rsc-manifest.js.map +1 -1
  50. package/dist/entries/app-ssr-entry.js.map +1 -1
  51. package/dist/entries/pages-client-entry.js.map +1 -1
  52. package/dist/entries/pages-entry-helpers.js.map +1 -1
  53. package/dist/entries/pages-server-entry.js +15 -0
  54. package/dist/entries/pages-server-entry.js.map +1 -1
  55. package/dist/entries/runtime-entry-module.js.map +1 -1
  56. package/dist/index.js +76 -18
  57. package/dist/index.js.map +1 -1
  58. package/dist/init.js.map +1 -1
  59. package/dist/plugins/async-hooks-stub.js.map +1 -1
  60. package/dist/plugins/client-reference-dedup.js.map +1 -1
  61. package/dist/plugins/fonts.js.map +1 -1
  62. package/dist/plugins/instrumentation-client.js.map +1 -1
  63. package/dist/plugins/og-assets.js.map +1 -1
  64. package/dist/plugins/optimize-imports.js.map +1 -1
  65. package/dist/plugins/postcss.js.map +1 -1
  66. package/dist/plugins/rsc-client-reference-loaders.d.ts +7 -0
  67. package/dist/plugins/rsc-client-reference-loaders.js +48 -0
  68. package/dist/plugins/rsc-client-reference-loaders.js.map +1 -0
  69. package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
  70. package/dist/plugins/server-externals-manifest.js.map +1 -1
  71. package/dist/plugins/strip-server-exports.js.map +1 -1
  72. package/dist/routing/app-route-graph.d.ts +48 -5
  73. package/dist/routing/app-route-graph.js +159 -15
  74. package/dist/routing/app-route-graph.js.map +1 -1
  75. package/dist/routing/app-router.js.map +1 -1
  76. package/dist/routing/file-matcher.js.map +1 -1
  77. package/dist/routing/pages-router.js.map +1 -1
  78. package/dist/routing/route-matching.js.map +1 -1
  79. package/dist/routing/route-pattern.js.map +1 -1
  80. package/dist/routing/route-trie.js.map +1 -1
  81. package/dist/routing/route-validation.js.map +1 -1
  82. package/dist/routing/utils.js.map +1 -1
  83. package/dist/server/api-handler.js.map +1 -1
  84. package/dist/server/app-browser-action-result.d.ts +19 -0
  85. package/dist/server/app-browser-action-result.js +18 -0
  86. package/dist/server/app-browser-action-result.js.map +1 -0
  87. package/dist/server/app-browser-entry.js +91 -48
  88. package/dist/server/app-browser-entry.js.map +1 -1
  89. package/dist/server/app-browser-error.js.map +1 -1
  90. package/dist/server/app-browser-hydration.d.ts +19 -0
  91. package/dist/server/app-browser-hydration.js +22 -0
  92. package/dist/server/app-browser-hydration.js.map +1 -0
  93. package/dist/server/app-browser-navigation-controller.d.ts +6 -3
  94. package/dist/server/app-browser-navigation-controller.js +67 -19
  95. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  96. package/dist/server/app-browser-state.d.ts +17 -17
  97. package/dist/server/app-browser-state.js +122 -36
  98. package/dist/server/app-browser-state.js.map +1 -1
  99. package/dist/server/app-browser-stream.d.ts +4 -0
  100. package/dist/server/app-browser-stream.js +24 -2
  101. package/dist/server/app-browser-stream.js.map +1 -1
  102. package/dist/server/app-browser-visible-commit.d.ts +6 -1
  103. package/dist/server/app-browser-visible-commit.js +34 -19
  104. package/dist/server/app-browser-visible-commit.js.map +1 -1
  105. package/dist/server/app-client-reference-preloader.js.map +1 -1
  106. package/dist/server/app-elements-wire.d.ts +6 -1
  107. package/dist/server/app-elements-wire.js +17 -1
  108. package/dist/server/app-elements-wire.js.map +1 -1
  109. package/dist/server/app-elements.d.ts +2 -2
  110. package/dist/server/app-elements.js +2 -2
  111. package/dist/server/app-elements.js.map +1 -1
  112. package/dist/server/app-fallback-renderer.js.map +1 -1
  113. package/dist/server/app-hook-warning-suppression.js.map +1 -1
  114. package/dist/server/app-middleware.d.ts +1 -1
  115. package/dist/server/app-middleware.js +4 -9
  116. package/dist/server/app-middleware.js.map +1 -1
  117. package/dist/server/app-mounted-slots-header.js.map +1 -1
  118. package/dist/server/app-page-boundary-render.d.ts +1 -0
  119. package/dist/server/app-page-boundary-render.js +14 -13
  120. package/dist/server/app-page-boundary-render.js.map +1 -1
  121. package/dist/server/app-page-boundary.d.ts +1 -0
  122. package/dist/server/app-page-boundary.js +7 -5
  123. package/dist/server/app-page-boundary.js.map +1 -1
  124. package/dist/server/app-page-cache.d.ts +10 -3
  125. package/dist/server/app-page-cache.js +42 -23
  126. package/dist/server/app-page-cache.js.map +1 -1
  127. package/dist/server/app-page-dispatch.d.ts +6 -1
  128. package/dist/server/app-page-dispatch.js +21 -7
  129. package/dist/server/app-page-dispatch.js.map +1 -1
  130. package/dist/server/app-page-element-builder.d.ts +3 -1
  131. package/dist/server/app-page-element-builder.js +6 -2
  132. package/dist/server/app-page-element-builder.js.map +1 -1
  133. package/dist/server/app-page-execution.js.map +1 -1
  134. package/dist/server/app-page-head.js +4 -0
  135. package/dist/server/app-page-head.js.map +1 -1
  136. package/dist/server/app-page-method.js.map +1 -1
  137. package/dist/server/app-page-params.js.map +1 -1
  138. package/dist/server/app-page-probe.js.map +1 -1
  139. package/dist/server/app-page-render.d.ts +7 -1
  140. package/dist/server/app-page-render.js +11 -4
  141. package/dist/server/app-page-render.js.map +1 -1
  142. package/dist/server/app-page-request.js +2 -1
  143. package/dist/server/app-page-request.js.map +1 -1
  144. package/dist/server/app-page-response.d.ts +2 -0
  145. package/dist/server/app-page-response.js +15 -5
  146. package/dist/server/app-page-response.js.map +1 -1
  147. package/dist/server/app-page-route-wiring.d.ts +6 -2
  148. package/dist/server/app-page-route-wiring.js +50 -49
  149. package/dist/server/app-page-route-wiring.js.map +1 -1
  150. package/dist/server/app-page-segment-state.d.ts +10 -0
  151. package/dist/server/app-page-segment-state.js +87 -0
  152. package/dist/server/app-page-segment-state.js.map +1 -0
  153. package/dist/server/app-page-stream.d.ts +7 -2
  154. package/dist/server/app-page-stream.js +3 -1
  155. package/dist/server/app-page-stream.js.map +1 -1
  156. package/dist/server/app-post-middleware-context.js.map +1 -1
  157. package/dist/server/app-prerender-endpoints.js.map +1 -1
  158. package/dist/server/app-prerender-static-params.js.map +1 -1
  159. package/dist/server/app-render-dependency.js.map +1 -1
  160. package/dist/server/app-request-context.js.map +1 -1
  161. package/dist/server/app-route-handler-cache.js.map +1 -1
  162. package/dist/server/app-route-handler-dispatch.js +3 -1
  163. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  164. package/dist/server/app-route-handler-execution.js.map +1 -1
  165. package/dist/server/app-route-handler-policy.js +1 -0
  166. package/dist/server/app-route-handler-policy.js.map +1 -1
  167. package/dist/server/app-route-handler-response.js +4 -3
  168. package/dist/server/app-route-handler-response.js.map +1 -1
  169. package/dist/server/app-route-handler-runtime.js.map +1 -1
  170. package/dist/server/app-router-entry.js +6 -2
  171. package/dist/server/app-router-entry.js.map +1 -1
  172. package/dist/server/app-rsc-cache-busting.d.ts +5 -2
  173. package/dist/server/app-rsc-cache-busting.js +40 -19
  174. package/dist/server/app-rsc-cache-busting.js.map +1 -1
  175. package/dist/server/app-rsc-error-handler.js.map +1 -1
  176. package/dist/server/app-rsc-errors.js.map +1 -1
  177. package/dist/server/app-rsc-handler.d.ts +10 -1
  178. package/dist/server/app-rsc-handler.js +51 -17
  179. package/dist/server/app-rsc-handler.js.map +1 -1
  180. package/dist/server/app-rsc-render-mode.d.ts +11 -0
  181. package/dist/server/app-rsc-render-mode.js +21 -0
  182. package/dist/server/app-rsc-render-mode.js.map +1 -0
  183. package/dist/server/app-rsc-request-normalization.d.ts +4 -1
  184. package/dist/server/app-rsc-request-normalization.js +7 -2
  185. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  186. package/dist/server/app-rsc-response-finalizer.d.ts +2 -1
  187. package/dist/server/app-rsc-response-finalizer.js +6 -1
  188. package/dist/server/app-rsc-response-finalizer.js.map +1 -1
  189. package/dist/server/app-rsc-route-matching.js.map +1 -1
  190. package/dist/server/app-segment-config.js.map +1 -1
  191. package/dist/server/app-server-action-execution.d.ts +16 -2
  192. package/dist/server/app-server-action-execution.js +79 -23
  193. package/dist/server/app-server-action-execution.js.map +1 -1
  194. package/dist/server/app-ssr-entry.d.ts +6 -0
  195. package/dist/server/app-ssr-entry.js +10 -4
  196. package/dist/server/app-ssr-entry.js.map +1 -1
  197. package/dist/server/app-ssr-stream.js.map +1 -1
  198. package/dist/server/app-static-generation.js.map +1 -1
  199. package/dist/server/artifact-compatibility.js.map +1 -1
  200. package/dist/server/cache-control.js +1 -0
  201. package/dist/server/cache-control.js.map +1 -1
  202. package/dist/server/cache-proof.js.map +1 -1
  203. package/dist/server/csp.js.map +1 -1
  204. package/dist/server/dev-error-overlay-store.js.map +1 -1
  205. package/dist/server/dev-error-overlay.js +5 -0
  206. package/dist/server/dev-error-overlay.js.map +1 -1
  207. package/dist/server/dev-module-runner.js.map +1 -1
  208. package/dist/server/dev-origin-check.js.map +1 -1
  209. package/dist/server/dev-route-files.js.map +1 -1
  210. package/dist/server/dev-server.js +8 -5
  211. package/dist/server/dev-server.js.map +1 -1
  212. package/dist/server/file-based-metadata.js.map +1 -1
  213. package/dist/server/headers.d.ts +79 -0
  214. package/dist/server/headers.js +101 -0
  215. package/dist/server/headers.js.map +1 -0
  216. package/dist/server/html.js.map +1 -1
  217. package/dist/server/http-error-responses.js.map +1 -1
  218. package/dist/server/image-optimization.d.ts +11 -1
  219. package/dist/server/image-optimization.js.map +1 -1
  220. package/dist/server/implicit-tags.js +2 -1
  221. package/dist/server/implicit-tags.js.map +1 -1
  222. package/dist/server/instrumentation-runtime.js.map +1 -1
  223. package/dist/server/instrumentation.js.map +1 -1
  224. package/dist/server/isr-cache.d.ts +10 -1
  225. package/dist/server/isr-cache.js +12 -3
  226. package/dist/server/isr-cache.js.map +1 -1
  227. package/dist/server/metadata-route-build-data.js.map +1 -1
  228. package/dist/server/metadata-route-response.js.map +1 -1
  229. package/dist/server/metadata-routes.js.map +1 -1
  230. package/dist/server/middleware-matcher.js.map +1 -1
  231. package/dist/server/middleware-request-headers.d.ts +4 -1
  232. package/dist/server/middleware-request-headers.js +15 -8
  233. package/dist/server/middleware-request-headers.js.map +1 -1
  234. package/dist/server/middleware-response-headers.d.ts +2 -1
  235. package/dist/server/middleware-response-headers.js +1 -1
  236. package/dist/server/middleware-response-headers.js.map +1 -1
  237. package/dist/server/middleware-runtime.d.ts +1 -0
  238. package/dist/server/middleware-runtime.js +6 -3
  239. package/dist/server/middleware-runtime.js.map +1 -1
  240. package/dist/server/middleware.js.map +1 -1
  241. package/dist/server/navigation-planner.d.ts +119 -0
  242. package/dist/server/navigation-planner.js +171 -0
  243. package/dist/server/navigation-planner.js.map +1 -0
  244. package/dist/server/navigation-trace.d.ts +12 -2
  245. package/dist/server/navigation-trace.js +13 -1
  246. package/dist/server/navigation-trace.js.map +1 -1
  247. package/dist/server/next-error-digest.d.ts +3 -2
  248. package/dist/server/next-error-digest.js +4 -2
  249. package/dist/server/next-error-digest.js.map +1 -1
  250. package/dist/server/normalize-path.js.map +1 -1
  251. package/dist/server/pages-api-route.js.map +1 -1
  252. package/dist/server/pages-i18n.js.map +1 -1
  253. package/dist/server/pages-media-type.js.map +1 -1
  254. package/dist/server/pages-node-compat.js.map +1 -1
  255. package/dist/server/pages-page-data.js +5 -2
  256. package/dist/server/pages-page-data.js.map +1 -1
  257. package/dist/server/pages-page-response.js +3 -2
  258. package/dist/server/pages-page-response.js.map +1 -1
  259. package/dist/server/prerender-work-unit-setup.js +1 -1
  260. package/dist/server/prerender-work-unit-setup.js.map +1 -1
  261. package/dist/server/prod-server.js +35 -13
  262. package/dist/server/prod-server.js.map +1 -1
  263. package/dist/server/request-log.js.map +1 -1
  264. package/dist/server/request-pipeline.d.ts +1 -13
  265. package/dist/server/request-pipeline.js +3 -25
  266. package/dist/server/request-pipeline.js.map +1 -1
  267. package/dist/server/rsc-stream-hints.js.map +1 -1
  268. package/dist/server/seed-cache.js.map +1 -1
  269. package/dist/server/server-action-not-found.js +3 -3
  270. package/dist/server/server-action-not-found.js.map +1 -1
  271. package/dist/server/socket-error-backstop.js.map +1 -1
  272. package/dist/server/static-file-cache.js.map +1 -1
  273. package/dist/server/worker-utils.d.ts +0 -7
  274. package/dist/server/worker-utils.js +3 -2
  275. package/dist/server/worker-utils.js.map +1 -1
  276. package/dist/shims/amp.js.map +1 -1
  277. package/dist/shims/app.d.ts +37 -4
  278. package/dist/shims/app.js +50 -1
  279. package/dist/shims/app.js.map +1 -0
  280. package/dist/shims/cache-for-request.js.map +1 -1
  281. package/dist/shims/cache-runtime.js +20 -8
  282. package/dist/shims/cache-runtime.js.map +1 -1
  283. package/dist/shims/cache.d.ts +15 -3
  284. package/dist/shims/cache.js +99 -15
  285. package/dist/shims/cache.js.map +1 -1
  286. package/dist/shims/client-hook-error.js.map +1 -1
  287. package/dist/shims/compat-router.js.map +1 -1
  288. package/dist/shims/config.js.map +1 -1
  289. package/dist/shims/constants.js.map +1 -1
  290. package/dist/shims/document.js.map +1 -1
  291. package/dist/shims/dynamic.d.ts +18 -10
  292. package/dist/shims/dynamic.js +107 -51
  293. package/dist/shims/dynamic.js.map +1 -1
  294. package/dist/shims/error-boundary.d.ts +35 -6
  295. package/dist/shims/error-boundary.js +118 -33
  296. package/dist/shims/error-boundary.js.map +1 -1
  297. package/dist/shims/error.js.map +1 -1
  298. package/dist/shims/fetch-cache.d.ts +22 -1
  299. package/dist/shims/fetch-cache.js +124 -13
  300. package/dist/shims/fetch-cache.js.map +1 -1
  301. package/dist/shims/font-google-base.js.map +1 -1
  302. package/dist/shims/font-local.js.map +1 -1
  303. package/dist/shims/form.js +3 -1
  304. package/dist/shims/form.js.map +1 -1
  305. package/dist/shims/head-state.js.map +1 -1
  306. package/dist/shims/head.d.ts +3 -1
  307. package/dist/shims/head.js +28 -16
  308. package/dist/shims/head.js.map +1 -1
  309. package/dist/shims/headers.d.ts +4 -2
  310. package/dist/shims/headers.js +24 -7
  311. package/dist/shims/headers.js.map +1 -1
  312. package/dist/shims/i18n-context.js.map +1 -1
  313. package/dist/shims/i18n-state.js.map +1 -1
  314. package/dist/shims/image-config.d.ts +14 -1
  315. package/dist/shims/image-config.js +24 -1
  316. package/dist/shims/image-config.js.map +1 -1
  317. package/dist/shims/image.js +15 -2
  318. package/dist/shims/image.js.map +1 -1
  319. package/dist/shims/internal/als-registry.js.map +1 -1
  320. package/dist/shims/internal/app-router-context.d.ts +1 -0
  321. package/dist/shims/internal/app-router-context.js.map +1 -1
  322. package/dist/shims/internal/cookie-serialize.js.map +1 -1
  323. package/dist/shims/internal/make-hanging-promise.d.ts +1 -1
  324. package/dist/shims/internal/make-hanging-promise.js +1 -1
  325. package/dist/shims/internal/make-hanging-promise.js.map +1 -1
  326. package/dist/shims/internal/parse-cookie-header.js.map +1 -1
  327. package/dist/shims/internal/utils.js.map +1 -1
  328. package/dist/shims/internal/work-unit-async-storage.js +2 -2
  329. package/dist/shims/internal/work-unit-async-storage.js.map +1 -1
  330. package/dist/shims/layout-segment-context.js.map +1 -1
  331. package/dist/shims/legacy-image.js.map +1 -1
  332. package/dist/shims/link-prefetch.d.ts +34 -0
  333. package/dist/shims/link-prefetch.js +40 -0
  334. package/dist/shims/link-prefetch.js.map +1 -0
  335. package/dist/shims/link.d.ts +27 -4
  336. package/dist/shims/link.js +91 -27
  337. package/dist/shims/link.js.map +1 -1
  338. package/dist/shims/metadata.js.map +1 -1
  339. package/dist/shims/navigation-state.js.map +1 -1
  340. package/dist/shims/navigation.d.ts +22 -1
  341. package/dist/shims/navigation.js +30 -15
  342. package/dist/shims/navigation.js.map +1 -1
  343. package/dist/shims/navigation.react-server.js.map +1 -1
  344. package/dist/shims/offline.js.map +1 -1
  345. package/dist/shims/readonly-url-search-params.js.map +1 -1
  346. package/dist/shims/request-context.js.map +1 -1
  347. package/dist/shims/root-params.js.map +1 -1
  348. package/dist/shims/router-state.js.map +1 -1
  349. package/dist/shims/router.d.ts +38 -2
  350. package/dist/shims/router.js +45 -17
  351. package/dist/shims/router.js.map +1 -1
  352. package/dist/shims/script-nonce-context.js.map +1 -1
  353. package/dist/shims/script.js.map +1 -1
  354. package/dist/shims/server.js +10 -14
  355. package/dist/shims/server.js.map +1 -1
  356. package/dist/shims/slot.d.ts +6 -1
  357. package/dist/shims/slot.js +20 -7
  358. package/dist/shims/slot.js.map +1 -1
  359. package/dist/shims/thenable-params.js.map +1 -1
  360. package/dist/shims/unified-request-context.js +3 -0
  361. package/dist/shims/unified-request-context.js.map +1 -1
  362. package/dist/shims/url-safety.js.map +1 -1
  363. package/dist/shims/url-utils.d.ts +2 -1
  364. package/dist/shims/url-utils.js +10 -1
  365. package/dist/shims/url-utils.js.map +1 -1
  366. package/dist/shims/use-merged-ref.js.map +1 -1
  367. package/dist/shims/web-vitals.d.ts +4 -21
  368. package/dist/shims/web-vitals.js +19 -6
  369. package/dist/shims/web-vitals.js.map +1 -1
  370. package/dist/utils/base-path.js.map +1 -1
  371. package/dist/utils/cache-control-metadata.js.map +1 -1
  372. package/dist/utils/domain-locale.js.map +1 -1
  373. package/dist/utils/encode-cache-tag.d.ts +31 -0
  374. package/dist/utils/encode-cache-tag.js +38 -0
  375. package/dist/utils/encode-cache-tag.js.map +1 -0
  376. package/dist/utils/error-cause.js.map +1 -1
  377. package/dist/utils/hash.js.map +1 -1
  378. package/dist/utils/lazy-chunks.js.map +1 -1
  379. package/dist/utils/manifest-paths.js.map +1 -1
  380. package/dist/utils/mdx-scan.js.map +1 -1
  381. package/dist/utils/navigation-signal.d.ts +6 -0
  382. package/dist/utils/navigation-signal.js +14 -0
  383. package/dist/utils/navigation-signal.js.map +1 -0
  384. package/dist/utils/project.js.map +1 -1
  385. package/dist/utils/public-routes.js.map +1 -1
  386. package/dist/utils/query.js.map +1 -1
  387. package/dist/utils/safe-json-file.js.map +1 -1
  388. package/dist/utils/text-stream.js.map +1 -1
  389. package/dist/utils/vinext-root.js.map +1 -1
  390. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"file":"client-build-config.js","names":[],"sources":["../../src/build/client-build-config.ts"],"sourcesContent":["import type { UserConfig } from \"vite\";\n\n/**\n * Extract the npm package name from a module ID (file path).\n * Returns null if not in node_modules.\n *\n * Handles scoped packages (@org/pkg) and pnpm-style paths\n * (node_modules/.pnpm/pkg@ver/node_modules/pkg).\n */\nfunction getPackageName(id: string): string | null {\n const nmIdx = id.lastIndexOf(\"node_modules/\");\n if (nmIdx === -1) return null;\n const rest = id.slice(nmIdx + \"node_modules/\".length);\n if (rest.startsWith(\"@\")) {\n // Scoped package: @org/pkg\n const parts = rest.split(\"/\");\n return parts.length >= 2 ? parts[0] + \"/\" + parts[1] : null;\n }\n return rest.split(\"/\")[0] || null;\n}\n\n/**\n * Create a manualChunks function for client builds.\n *\n * Splits the client bundle into:\n * - \"framework\" — React, ReactDOM, and scheduler (loaded on every page)\n * - \"vinext\" — vinext shims (router, head, link, etc.)\n *\n * All other vendor code is left to Rollup's default chunk-splitting\n * algorithm. Rollup automatically deduplicates shared modules into\n * common chunks based on the import graph — no manual intervention\n * needed.\n *\n * Why not split every npm package into its own chunk?\n * - Per-package splitting (`vendor-X`) creates 50-200+ chunks for a\n * typical app, far exceeding the ~25-request sweet spot for HTTP/2.\n * - gzip/brotli compress small files poorly — each file restarts with\n * an empty dictionary, losing ~5-15% total compressed size vs fewer\n * larger chunks (Khan Academy measured +2.5% wire size with 10x\n * more files containing less raw code).\n * - ES module evaluation has per-module overhead that compounds on\n * mobile devices.\n * - No major Vite-based framework (Remix, SvelteKit, Astro, TanStack)\n * uses per-package splitting. Next.js only isolates packages >160KB.\n * - Rollup's graph-based splitting already handles the common case\n * well: shared dependencies between routes get their own chunks,\n * and route-specific code stays in route chunks.\n */\nexport function createClientManualChunks(shimsDir: string) {\n return function clientManualChunks(id: string): string | undefined {\n // React framework — always loaded, shared across all pages.\n // Isolating React into its own chunk is the single highest-value\n // split: it's ~130KB compressed, loaded on every page, and its\n // content hash rarely changes between deploys.\n if (id.includes(\"node_modules\")) {\n const pkg = getPackageName(id);\n if (!pkg) return undefined;\n if (pkg === \"react\" || pkg === \"react-dom\" || pkg === \"scheduler\") {\n return \"framework\";\n }\n // Let Rollup handle all other vendor code via its default\n // graph-based splitting. This produces a reasonable number of\n // shared chunks (typically 5-15) based on actual import patterns,\n // with good compression efficiency.\n return undefined;\n }\n\n // vinext shims — small runtime, shared across all pages.\n // Use the absolute shims directory path to avoid matching user files\n // that happen to have \"/shims/\" in their path.\n if (id.startsWith(shimsDir)) {\n return \"vinext\";\n }\n\n return undefined;\n };\n}\n\n/**\n * Rollup output config with manualChunks for client code-splitting.\n * Used by both CLI builds and multi-environment builds.\n *\n * experimentalMinChunkSize merges tiny shared chunks (< 10KB) back into\n * their importers. This reduces HTTP request count and improves gzip\n * compression efficiency — small files restart the compression dictionary,\n * adding ~5-15% wire overhead vs fewer larger chunks.\n */\nexport function createClientOutputConfig(clientManualChunks: (id: string) => string | undefined) {\n return {\n manualChunks: clientManualChunks,\n experimentalMinChunkSize: 10_000,\n };\n}\n\nexport function createClientCodeSplittingConfig(\n clientManualChunks: (id: string) => string | undefined,\n) {\n return {\n minSize: 10_000,\n groups: [\n {\n name(moduleId: string) {\n return clientManualChunks(moduleId) ?? null;\n },\n },\n ],\n };\n}\n\n/**\n * Rollup treeshake configuration for production client builds.\n *\n * Uses the 'recommended' preset as a safe base, then overrides\n * moduleSideEffects to strip unused re-exports from npm packages.\n *\n * The 'no-external' value for moduleSideEffects means:\n * - Local project modules: preserve side effects (CSS imports, polyfills)\n * - node_modules packages: treat as side-effect-free unless exports are used\n *\n * This is the single highest-impact optimization for large barrel-exporting\n * libraries like mermaid, @mui/material, lucide-react, etc. These libraries\n * re-export hundreds of sub-modules through barrel files. Without this,\n * Rollup preserves every sub-module even when only a few exports are consumed.\n *\n * Why 'no-external' instead of false (global side-effect-free)?\n * - User code may rely on import-time side effects (e.g., `import './global.css'`)\n * - 'no-external' is safe for app code while still enabling aggressive DCE for deps\n *\n * Why not the 'smallest' preset?\n * - 'smallest' also sets propertyReadSideEffects: false and\n * tryCatchDeoptimization: false, which can break specific libraries\n * that rely on property access side effects or try/catch for feature detection\n * - 'recommended' + 'no-external' gives most of the benefit with less risk\n *\n * @deprecated Use getClientTreeshakeConfigForVite(viteMajorVersion) instead\n * for Vite version compatibility. Kept for backward compatibility.\n */\nexport const clientTreeshakeConfig = {\n preset: \"recommended\" as const,\n moduleSideEffects: \"no-external\" as const,\n};\n\n/**\n * Returns treeshake configuration appropriate for the Vite version.\n *\n * Rollup (Vite 7) supports presets like \"recommended\" which set multiple\n * treeshake options at once. Rolldown (Vite 8+) doesn't support presets,\n * so we only return moduleSideEffects for Vite 8+.\n *\n * The Rollup \"recommended\" preset sets:\n * - annotations: true (Rolldown default is also true)\n * - manualPureFunctions: [] (Rolldown default is also [])\n * - propertyReadSideEffects: true (Rolldown equivalent is 'always', the default)\n * - unknownGlobalSideEffects: false (Rolldown default is true — this is a known acceptable\n * divergence. Slightly less aggressive DCE on unknown globals, acceptable for client bundles)\n * - correctVarValueBeforeDeclaration and tryCatchDeoptimization (Rolldown handles these differently)\n *\n * The key optimization is moduleSideEffects: \"no-external\", which is supported\n * by both bundlers and provides the DCE benefits for barrel-exporting libraries.\n * It treats node_modules as side-effect-free (enabling aggressive DCE) while\n * preserving side effects in local code.\n */\nexport function getClientTreeshakeConfigForVite(viteMajorVersion: number) {\n if (viteMajorVersion >= 8) {\n // Rolldown (Vite 8+) - no preset support, only specific options.\n // Rolldown's built-in defaults already cover what Rollup's 'recommended'\n // preset provides (annotations, correctContext, tryCatchDeoptimization).\n return {\n moduleSideEffects: \"no-external\" as const,\n };\n }\n // Rollup (Vite 7) - supports presets for convenient option grouping\n return {\n preset: \"recommended\" as const,\n moduleSideEffects: \"no-external\" as const,\n };\n}\n\ntype VinextBuildConfig = NonNullable<UserConfig[\"build\"]>;\ntype VinextBuildBundlerOptions = NonNullable<VinextBuildConfig[\"rolldownOptions\"]>;\ntype VinextBuildConfigWithLegacy = VinextBuildConfig & {\n rollupOptions?: VinextBuildBundlerOptions;\n};\n\nexport function getBuildBundlerOptions(\n build: UserConfig[\"build\"] | undefined,\n): VinextBuildBundlerOptions | undefined {\n const buildConfig = build as VinextBuildConfigWithLegacy | undefined;\n return buildConfig?.rolldownOptions ?? buildConfig?.rollupOptions;\n}\n\nexport function withBuildBundlerOptions(\n viteMajorVersion: number,\n bundlerOptions: VinextBuildBundlerOptions,\n): Partial<VinextBuildConfigWithLegacy> {\n return viteMajorVersion >= 8\n ? { rolldownOptions: bundlerOptions }\n : { rollupOptions: bundlerOptions };\n}\n"],"mappings":";;;;;;;;AASA,SAAS,eAAe,IAA2B;CACjD,MAAM,QAAQ,GAAG,YAAY,gBAAgB;AAC7C,KAAI,UAAU,GAAI,QAAO;CACzB,MAAM,OAAO,GAAG,MAAM,QAAQ,GAAuB;AACrD,KAAI,KAAK,WAAW,IAAI,EAAE;EAExB,MAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,SAAO,MAAM,UAAU,IAAI,MAAM,KAAK,MAAM,MAAM,KAAK;;AAEzD,QAAO,KAAK,MAAM,IAAI,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B/B,SAAgB,yBAAyB,UAAkB;AACzD,QAAO,SAAS,mBAAmB,IAAgC;AAKjE,MAAI,GAAG,SAAS,eAAe,EAAE;GAC/B,MAAM,MAAM,eAAe,GAAG;AAC9B,OAAI,CAAC,IAAK,QAAO,KAAA;AACjB,OAAI,QAAQ,WAAW,QAAQ,eAAe,QAAQ,YACpD,QAAO;AAMT;;AAMF,MAAI,GAAG,WAAW,SAAS,CACzB,QAAO;;;;;;;;;;;;AAgBb,SAAgB,yBAAyB,oBAAwD;AAC/F,QAAO;EACL,cAAc;EACd,0BAA0B;EAC3B;;AAGH,SAAgB,gCACd,oBACA;AACA,QAAO;EACL,SAAS;EACT,QAAQ,CACN,EACE,KAAK,UAAkB;AACrB,UAAO,mBAAmB,SAAS,IAAI;KAE1C,CACF;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BH,MAAa,wBAAwB;CACnC,QAAQ;CACR,mBAAmB;CACpB;;;;;;;;;;;;;;;;;;;;;AAsBD,SAAgB,gCAAgC,kBAA0B;AACxE,KAAI,oBAAoB,EAItB,QAAO,EACL,mBAAmB,eACpB;AAGH,QAAO;EACL,QAAQ;EACR,mBAAmB;EACpB;;AASH,SAAgB,uBACd,OACuC;CACvC,MAAM,cAAc;AACpB,QAAO,aAAa,mBAAmB,aAAa;;AAGtD,SAAgB,wBACd,kBACA,gBACsC;AACtC,QAAO,oBAAoB,IACvB,EAAE,iBAAiB,gBAAgB,GACnC,EAAE,eAAe,gBAAgB"}
1
+ {"version":3,"file":"client-build-config.js","names":[],"sources":["../../src/build/client-build-config.ts"],"sourcesContent":["import type { UserConfig } from \"vite\";\n\n/**\n * Extract the npm package name from a module ID (file path).\n * Returns null if not in node_modules.\n *\n * Handles scoped packages (@org/pkg) and pnpm-style paths\n * (node_modules/.pnpm/pkg@ver/node_modules/pkg).\n */\nfunction getPackageName(id: string): string | null {\n const nmIdx = id.lastIndexOf(\"node_modules/\");\n if (nmIdx === -1) return null;\n const rest = id.slice(nmIdx + \"node_modules/\".length);\n if (rest.startsWith(\"@\")) {\n // Scoped package: @org/pkg\n const parts = rest.split(\"/\");\n return parts.length >= 2 ? parts[0] + \"/\" + parts[1] : null;\n }\n return rest.split(\"/\")[0] || null;\n}\n\n/**\n * Create a manualChunks function for client builds.\n *\n * Splits the client bundle into:\n * - \"framework\" — React, ReactDOM, and scheduler (loaded on every page)\n * - \"vinext\" — vinext shims (router, head, link, etc.)\n *\n * All other vendor code is left to Rollup's default chunk-splitting\n * algorithm. Rollup automatically deduplicates shared modules into\n * common chunks based on the import graph — no manual intervention\n * needed.\n *\n * Why not split every npm package into its own chunk?\n * - Per-package splitting (`vendor-X`) creates 50-200+ chunks for a\n * typical app, far exceeding the ~25-request sweet spot for HTTP/2.\n * - gzip/brotli compress small files poorly — each file restarts with\n * an empty dictionary, losing ~5-15% total compressed size vs fewer\n * larger chunks (Khan Academy measured +2.5% wire size with 10x\n * more files containing less raw code).\n * - ES module evaluation has per-module overhead that compounds on\n * mobile devices.\n * - No major Vite-based framework (Remix, SvelteKit, Astro, TanStack)\n * uses per-package splitting. Next.js only isolates packages >160KB.\n * - Rollup's graph-based splitting already handles the common case\n * well: shared dependencies between routes get their own chunks,\n * and route-specific code stays in route chunks.\n */\nexport function createClientManualChunks(shimsDir: string) {\n return function clientManualChunks(id: string): string | undefined {\n // React framework — always loaded, shared across all pages.\n // Isolating React into its own chunk is the single highest-value\n // split: it's ~130KB compressed, loaded on every page, and its\n // content hash rarely changes between deploys.\n if (id.includes(\"node_modules\")) {\n const pkg = getPackageName(id);\n if (!pkg) return undefined;\n if (pkg === \"react\" || pkg === \"react-dom\" || pkg === \"scheduler\") {\n return \"framework\";\n }\n // Let Rollup handle all other vendor code via its default\n // graph-based splitting. This produces a reasonable number of\n // shared chunks (typically 5-15) based on actual import patterns,\n // with good compression efficiency.\n return undefined;\n }\n\n // vinext shims — small runtime, shared across all pages.\n // Use the absolute shims directory path to avoid matching user files\n // that happen to have \"/shims/\" in their path.\n if (id.startsWith(shimsDir)) {\n return \"vinext\";\n }\n\n return undefined;\n };\n}\n\n/**\n * Rollup output config with manualChunks for client code-splitting.\n * Used by both CLI builds and multi-environment builds.\n *\n * experimentalMinChunkSize merges tiny shared chunks (< 10KB) back into\n * their importers. This reduces HTTP request count and improves gzip\n * compression efficiency — small files restart the compression dictionary,\n * adding ~5-15% wire overhead vs fewer larger chunks.\n */\nexport function createClientOutputConfig(clientManualChunks: (id: string) => string | undefined) {\n return {\n manualChunks: clientManualChunks,\n experimentalMinChunkSize: 10_000,\n };\n}\n\nexport function createClientCodeSplittingConfig(\n clientManualChunks: (id: string) => string | undefined,\n) {\n return {\n minSize: 10_000,\n groups: [\n {\n name(moduleId: string) {\n return clientManualChunks(moduleId) ?? null;\n },\n },\n ],\n };\n}\n\n/**\n * Rollup treeshake configuration for production client builds.\n *\n * Uses the 'recommended' preset as a safe base, then overrides\n * moduleSideEffects to strip unused re-exports from npm packages.\n *\n * The 'no-external' value for moduleSideEffects means:\n * - Local project modules: preserve side effects (CSS imports, polyfills)\n * - node_modules packages: treat as side-effect-free unless exports are used\n *\n * This is the single highest-impact optimization for large barrel-exporting\n * libraries like mermaid, @mui/material, lucide-react, etc. These libraries\n * re-export hundreds of sub-modules through barrel files. Without this,\n * Rollup preserves every sub-module even when only a few exports are consumed.\n *\n * Why 'no-external' instead of false (global side-effect-free)?\n * - User code may rely on import-time side effects (e.g., `import './global.css'`)\n * - 'no-external' is safe for app code while still enabling aggressive DCE for deps\n *\n * Why not the 'smallest' preset?\n * - 'smallest' also sets propertyReadSideEffects: false and\n * tryCatchDeoptimization: false, which can break specific libraries\n * that rely on property access side effects or try/catch for feature detection\n * - 'recommended' + 'no-external' gives most of the benefit with less risk\n *\n * @deprecated Use getClientTreeshakeConfigForVite(viteMajorVersion) instead\n * for Vite version compatibility. Kept for backward compatibility.\n */\nexport const clientTreeshakeConfig = {\n preset: \"recommended\" as const,\n moduleSideEffects: \"no-external\" as const,\n};\n\n/**\n * Returns treeshake configuration appropriate for the Vite version.\n *\n * Rollup (Vite 7) supports presets like \"recommended\" which set multiple\n * treeshake options at once. Rolldown (Vite 8+) doesn't support presets,\n * so we only return moduleSideEffects for Vite 8+.\n *\n * The Rollup \"recommended\" preset sets:\n * - annotations: true (Rolldown default is also true)\n * - manualPureFunctions: [] (Rolldown default is also [])\n * - propertyReadSideEffects: true (Rolldown equivalent is 'always', the default)\n * - unknownGlobalSideEffects: false (Rolldown default is true — this is a known acceptable\n * divergence. Slightly less aggressive DCE on unknown globals, acceptable for client bundles)\n * - correctVarValueBeforeDeclaration and tryCatchDeoptimization (Rolldown handles these differently)\n *\n * The key optimization is moduleSideEffects: \"no-external\", which is supported\n * by both bundlers and provides the DCE benefits for barrel-exporting libraries.\n * It treats node_modules as side-effect-free (enabling aggressive DCE) while\n * preserving side effects in local code.\n */\nexport function getClientTreeshakeConfigForVite(viteMajorVersion: number) {\n if (viteMajorVersion >= 8) {\n // Rolldown (Vite 8+) - no preset support, only specific options.\n // Rolldown's built-in defaults already cover what Rollup's 'recommended'\n // preset provides (annotations, correctContext, tryCatchDeoptimization).\n return {\n moduleSideEffects: \"no-external\" as const,\n };\n }\n // Rollup (Vite 7) - supports presets for convenient option grouping\n return {\n preset: \"recommended\" as const,\n moduleSideEffects: \"no-external\" as const,\n };\n}\n\ntype VinextBuildConfig = NonNullable<UserConfig[\"build\"]>;\ntype VinextBuildBundlerOptions = NonNullable<VinextBuildConfig[\"rolldownOptions\"]>;\ntype VinextBuildConfigWithLegacy = VinextBuildConfig & {\n rollupOptions?: VinextBuildBundlerOptions;\n};\n\nexport function getBuildBundlerOptions(\n build: UserConfig[\"build\"] | undefined,\n): VinextBuildBundlerOptions | undefined {\n const buildConfig = build as VinextBuildConfigWithLegacy | undefined;\n return buildConfig?.rolldownOptions ?? buildConfig?.rollupOptions;\n}\n\nexport function withBuildBundlerOptions(\n viteMajorVersion: number,\n bundlerOptions: VinextBuildBundlerOptions,\n): Partial<VinextBuildConfigWithLegacy> {\n return viteMajorVersion >= 8\n ? { rolldownOptions: bundlerOptions }\n : { rollupOptions: bundlerOptions };\n}\n"],"mappings":";;;;;;;;AASA,SAAS,eAAe,IAA2B;CACjD,MAAM,QAAQ,GAAG,YAAY,gBAAgB;CAC7C,IAAI,UAAU,IAAI,OAAO;CACzB,MAAM,OAAO,GAAG,MAAM,QAAQ,GAAuB;CACrD,IAAI,KAAK,WAAW,IAAI,EAAE;EAExB,MAAM,QAAQ,KAAK,MAAM,IAAI;EAC7B,OAAO,MAAM,UAAU,IAAI,MAAM,KAAK,MAAM,MAAM,KAAK;;CAEzD,OAAO,KAAK,MAAM,IAAI,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B/B,SAAgB,yBAAyB,UAAkB;CACzD,OAAO,SAAS,mBAAmB,IAAgC;EAKjE,IAAI,GAAG,SAAS,eAAe,EAAE;GAC/B,MAAM,MAAM,eAAe,GAAG;GAC9B,IAAI,CAAC,KAAK,OAAO,KAAA;GACjB,IAAI,QAAQ,WAAW,QAAQ,eAAe,QAAQ,aACpD,OAAO;GAMT;;EAMF,IAAI,GAAG,WAAW,SAAS,EACzB,OAAO;;;;;;;;;;;;AAgBb,SAAgB,yBAAyB,oBAAwD;CAC/F,OAAO;EACL,cAAc;EACd,0BAA0B;EAC3B;;AAGH,SAAgB,gCACd,oBACA;CACA,OAAO;EACL,SAAS;EACT,QAAQ,CACN,EACE,KAAK,UAAkB;GACrB,OAAO,mBAAmB,SAAS,IAAI;KAE1C,CACF;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BH,MAAa,wBAAwB;CACnC,QAAQ;CACR,mBAAmB;CACpB;;;;;;;;;;;;;;;;;;;;;AAsBD,SAAgB,gCAAgC,kBAA0B;CACxE,IAAI,oBAAoB,GAItB,OAAO,EACL,mBAAmB,eACpB;CAGH,OAAO;EACL,QAAQ;EACR,mBAAmB;EACpB;;AASH,SAAgB,uBACd,OACuC;CACvC,MAAM,cAAc;CACpB,OAAO,aAAa,mBAAmB,aAAa;;AAGtD,SAAgB,wBACd,kBACA,gBACsC;CACtC,OAAO,oBAAoB,IACvB,EAAE,iBAAiB,gBAAgB,GACnC,EAAE,eAAe,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"build-url.js","names":[],"sources":["../../../src/build/google-fonts/build-url.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/get-google-fonts-url.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/get-google-fonts-url.ts\n//\n// Constructs a Google Fonts CSS API URL from already-validated axis data.\n// The caller is responsible for resolving the actual axis values (e.g. by\n// reading `wght` min..max from the bundled font metadata for variable fonts);\n// this module's only job is URL assembly.\n\nimport { sortFontsVariantValues } from \"./sort-variants.js\";\n\nexport type FontAxes = {\n /** Weight values: explicit faces (\"400\") or a variable range (\"400..800\"). */\n wght?: string[];\n /** Ital values, \"0\" (normal) and/or \"1\" (italic). */\n ital?: string[];\n /** Other variable axes the caller wants to include, e.g. `[[\"opsz\",\"14..32\"]]`. */\n variableAxes?: [string, string][];\n};\n\nexport function buildGoogleFontsUrl(fontFamily: string, axes: FontAxes, display: string): string {\n // A \"variant\" is one combination of axis values that becomes one entry in\n // the URL's variant list. Each variant is a list of [key, value] pairs in\n // the order Google expects.\n const variants: Array<[string, string][]> = [];\n\n if (axes.wght) {\n for (const wght of axes.wght) {\n if (!axes.ital) {\n variants.push([[\"wght\", wght], ...(axes.variableAxes ?? [])]);\n } else {\n for (const ital of axes.ital) {\n variants.push([[\"ital\", ital], [\"wght\", wght], ...(axes.variableAxes ?? [])]);\n }\n }\n }\n } else if (axes.variableAxes) {\n // Variable font with no requested wght: emit only the other variable axes.\n variants.push([...axes.variableAxes]);\n }\n\n // Google requires axis tags within a variant to be ordered with lowercase\n // tags first, then alphabetically. Every variant must agree on the same\n // key order, since the URL takes the key list from variants[0] and\n // applies it to every variant's value list.\n if (axes.variableAxes) {\n for (const variant of variants) {\n variant.sort(([a], [b]) => {\n const aIsLowercase = a.charCodeAt(0) > 96;\n const bIsLowercase = b.charCodeAt(0) > 96;\n if (aIsLowercase && !bIsLowercase) return -1;\n if (bIsLowercase && !aIsLowercase) return 1;\n return a > b ? 1 : -1;\n });\n }\n }\n\n let url = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, \"+\")}`;\n\n if (variants.length > 0) {\n const keyList = variants[0].map(([key]) => key).join(\",\");\n const valueLists = variants\n .map((variant) => variant.map(([, val]) => val).join(\",\"))\n .sort(sortFontsVariantValues)\n .join(\";\");\n url = `${url}:${keyList}@${valueLists}`;\n }\n\n return `${url}&display=${display}`;\n}\n"],"mappings":";;AAmBA,SAAgB,oBAAoB,YAAoB,MAAgB,SAAyB;CAI/F,MAAM,WAAsC,EAAE;AAE9C,KAAI,KAAK,KACP,MAAK,MAAM,QAAQ,KAAK,KACtB,KAAI,CAAC,KAAK,KACR,UAAS,KAAK,CAAC,CAAC,QAAQ,KAAK,EAAE,GAAI,KAAK,gBAAgB,EAAE,CAAE,CAAC;KAE7D,MAAK,MAAM,QAAQ,KAAK,KACtB,UAAS,KAAK;EAAC,CAAC,QAAQ,KAAK;EAAE,CAAC,QAAQ,KAAK;EAAE,GAAI,KAAK,gBAAgB,EAAE;EAAE,CAAC;UAI1E,KAAK,aAEd,UAAS,KAAK,CAAC,GAAG,KAAK,aAAa,CAAC;AAOvC,KAAI,KAAK,aACP,MAAK,MAAM,WAAW,SACpB,SAAQ,MAAM,CAAC,IAAI,CAAC,OAAO;EACzB,MAAM,eAAe,EAAE,WAAW,EAAE,GAAG;EACvC,MAAM,eAAe,EAAE,WAAW,EAAE,GAAG;AACvC,MAAI,gBAAgB,CAAC,aAAc,QAAO;AAC1C,MAAI,gBAAgB,CAAC,aAAc,QAAO;AAC1C,SAAO,IAAI,IAAI,IAAI;GACnB;CAIN,IAAI,MAAM,4CAA4C,WAAW,QAAQ,MAAM,IAAI;AAEnF,KAAI,SAAS,SAAS,GAAG;EACvB,MAAM,UAAU,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,IAAI;EACzD,MAAM,aAAa,SAChB,KAAK,YAAY,QAAQ,KAAK,GAAG,SAAS,IAAI,CAAC,KAAK,IAAI,CAAC,CACzD,KAAK,uBAAuB,CAC5B,KAAK,IAAI;AACZ,QAAM,GAAG,IAAI,GAAG,QAAQ,GAAG;;AAG7B,QAAO,GAAG,IAAI,WAAW"}
1
+ {"version":3,"file":"build-url.js","names":[],"sources":["../../../src/build/google-fonts/build-url.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/get-google-fonts-url.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/get-google-fonts-url.ts\n//\n// Constructs a Google Fonts CSS API URL from already-validated axis data.\n// The caller is responsible for resolving the actual axis values (e.g. by\n// reading `wght` min..max from the bundled font metadata for variable fonts);\n// this module's only job is URL assembly.\n\nimport { sortFontsVariantValues } from \"./sort-variants.js\";\n\nexport type FontAxes = {\n /** Weight values: explicit faces (\"400\") or a variable range (\"400..800\"). */\n wght?: string[];\n /** Ital values, \"0\" (normal) and/or \"1\" (italic). */\n ital?: string[];\n /** Other variable axes the caller wants to include, e.g. `[[\"opsz\",\"14..32\"]]`. */\n variableAxes?: [string, string][];\n};\n\nexport function buildGoogleFontsUrl(fontFamily: string, axes: FontAxes, display: string): string {\n // A \"variant\" is one combination of axis values that becomes one entry in\n // the URL's variant list. Each variant is a list of [key, value] pairs in\n // the order Google expects.\n const variants: Array<[string, string][]> = [];\n\n if (axes.wght) {\n for (const wght of axes.wght) {\n if (!axes.ital) {\n variants.push([[\"wght\", wght], ...(axes.variableAxes ?? [])]);\n } else {\n for (const ital of axes.ital) {\n variants.push([[\"ital\", ital], [\"wght\", wght], ...(axes.variableAxes ?? [])]);\n }\n }\n }\n } else if (axes.variableAxes) {\n // Variable font with no requested wght: emit only the other variable axes.\n variants.push([...axes.variableAxes]);\n }\n\n // Google requires axis tags within a variant to be ordered with lowercase\n // tags first, then alphabetically. Every variant must agree on the same\n // key order, since the URL takes the key list from variants[0] and\n // applies it to every variant's value list.\n if (axes.variableAxes) {\n for (const variant of variants) {\n variant.sort(([a], [b]) => {\n const aIsLowercase = a.charCodeAt(0) > 96;\n const bIsLowercase = b.charCodeAt(0) > 96;\n if (aIsLowercase && !bIsLowercase) return -1;\n if (bIsLowercase && !aIsLowercase) return 1;\n return a > b ? 1 : -1;\n });\n }\n }\n\n let url = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, \"+\")}`;\n\n if (variants.length > 0) {\n const keyList = variants[0].map(([key]) => key).join(\",\");\n const valueLists = variants\n .map((variant) => variant.map(([, val]) => val).join(\",\"))\n .sort(sortFontsVariantValues)\n .join(\";\");\n url = `${url}:${keyList}@${valueLists}`;\n }\n\n return `${url}&display=${display}`;\n}\n"],"mappings":";;AAmBA,SAAgB,oBAAoB,YAAoB,MAAgB,SAAyB;CAI/F,MAAM,WAAsC,EAAE;CAE9C,IAAI,KAAK,MACP,KAAK,MAAM,QAAQ,KAAK,MACtB,IAAI,CAAC,KAAK,MACR,SAAS,KAAK,CAAC,CAAC,QAAQ,KAAK,EAAE,GAAI,KAAK,gBAAgB,EAAE,CAAE,CAAC;MAE7D,KAAK,MAAM,QAAQ,KAAK,MACtB,SAAS,KAAK;EAAC,CAAC,QAAQ,KAAK;EAAE,CAAC,QAAQ,KAAK;EAAE,GAAI,KAAK,gBAAgB,EAAE;EAAE,CAAC;MAI9E,IAAI,KAAK,cAEd,SAAS,KAAK,CAAC,GAAG,KAAK,aAAa,CAAC;CAOvC,IAAI,KAAK,cACP,KAAK,MAAM,WAAW,UACpB,QAAQ,MAAM,CAAC,IAAI,CAAC,OAAO;EACzB,MAAM,eAAe,EAAE,WAAW,EAAE,GAAG;EACvC,MAAM,eAAe,EAAE,WAAW,EAAE,GAAG;EACvC,IAAI,gBAAgB,CAAC,cAAc,OAAO;EAC1C,IAAI,gBAAgB,CAAC,cAAc,OAAO;EAC1C,OAAO,IAAI,IAAI,IAAI;GACnB;CAIN,IAAI,MAAM,4CAA4C,WAAW,QAAQ,MAAM,IAAI;CAEnF,IAAI,SAAS,SAAS,GAAG;EACvB,MAAM,UAAU,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,IAAI;EACzD,MAAM,aAAa,SAChB,KAAK,YAAY,QAAQ,KAAK,GAAG,SAAS,IAAI,CAAC,KAAK,IAAI,CAAC,CACzD,KAAK,uBAAuB,CAC5B,KAAK,IAAI;EACZ,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG;;CAG7B,OAAO,GAAG,IAAI,WAAW"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-axes.js","names":[],"sources":["../../../src/build/google-fonts/get-axes.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/get-font-axes.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/get-font-axes.ts\n//\n// Resolves the axis values that belong in a Google Fonts URL given the\n// caller's requested weights, styles, and (optional) selected variable axes.\n// For variable fonts this is the only place that turns the literal sentinel\n// `\"variable\"` into the actual `min..max` range from the bundled metadata.\n\nimport { googleFontsMetadata } from \"./font-metadata.js\";\nimport type { FontAxes } from \"./build-url.js\";\n\nconst formatAvailableValues = (values: string[]): string =>\n values.map((val) => `\\`${val}\\``).join(\", \");\n\nexport function getFontAxes(\n fontFamily: string,\n weights: string[],\n styles: string[],\n selectedVariableAxes?: string[],\n): FontAxes {\n const hasItalic = styles.includes(\"italic\");\n const hasNormal = styles.includes(\"normal\");\n // Google treats omitted ital as ital=0, so italic-only requests do not need\n // to enumerate ital=0; just ['1'] suffices.\n const ital = hasItalic ? [...(hasNormal ? [\"0\"] : []), \"1\"] : undefined;\n\n // Variable-font sentinel: a single \"variable\" entry means the caller wants\n // the full axis range from metadata, not a list of explicit weights.\n if (weights[0] === \"variable\") {\n const allAxes = googleFontsMetadata[fontFamily].axes;\n if (!allAxes) {\n // Reaching this branch means validate accepted a \"variable\" weight for\n // a non-variable font, which should be impossible because the\n // metadata-based validator rejects this earlier. Treat as an internal\n // invariant.\n throw new Error(\"invariant variable font without axes\");\n }\n\n if (selectedVariableAxes) {\n const definableAxes = allAxes.map(({ tag }) => tag).filter((tag) => tag !== \"wght\");\n if (definableAxes.length === 0) {\n throw new Error(`Font \\`${fontFamily}\\` has no definable \\`axes\\``);\n }\n if (!Array.isArray(selectedVariableAxes)) {\n throw new Error(\n `Invalid axes value for font \\`${fontFamily}\\`, expected an array of axes.\\nAvailable axes: ${formatAvailableValues(definableAxes)}`,\n );\n }\n for (const key of selectedVariableAxes) {\n if (!definableAxes.includes(key)) {\n throw new Error(\n `Invalid axes value \\`${key}\\` for font \\`${fontFamily}\\`.\\nAvailable axes: ${formatAvailableValues(definableAxes)}`,\n );\n }\n }\n }\n\n let weightAxis: string | undefined;\n let variableAxes: [string, string][] | undefined;\n for (const { tag, min, max } of allAxes) {\n if (tag === \"wght\") {\n weightAxis = `${min}..${max}`;\n } else if (selectedVariableAxes?.includes(tag)) {\n if (!variableAxes) variableAxes = [];\n variableAxes.push([tag, `${min}..${max}`]);\n }\n }\n\n return {\n wght: weightAxis ? [weightAxis] : undefined,\n ital,\n variableAxes,\n };\n }\n\n // Non-variable path: weights are explicit face values that the URL builder\n // emits verbatim.\n return {\n wght: weights,\n ital,\n variableAxes: undefined,\n };\n}\n"],"mappings":";;AAWA,MAAM,yBAAyB,WAC7B,OAAO,KAAK,QAAQ,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK;AAE9C,SAAgB,YACd,YACA,SACA,QACA,sBACU;CACV,MAAM,YAAY,OAAO,SAAS,SAAS;CAC3C,MAAM,YAAY,OAAO,SAAS,SAAS;CAG3C,MAAM,OAAO,YAAY,CAAC,GAAI,YAAY,CAAC,IAAI,GAAG,EAAE,EAAG,IAAI,GAAG,KAAA;AAI9D,KAAI,QAAQ,OAAO,YAAY;EAC7B,MAAM,UAAU,oBAAoB,YAAY;AAChD,MAAI,CAAC,QAKH,OAAM,IAAI,MAAM,uCAAuC;AAGzD,MAAI,sBAAsB;GACxB,MAAM,gBAAgB,QAAQ,KAAK,EAAE,UAAU,IAAI,CAAC,QAAQ,QAAQ,QAAQ,OAAO;AACnF,OAAI,cAAc,WAAW,EAC3B,OAAM,IAAI,MAAM,UAAU,WAAW,8BAA8B;AAErE,OAAI,CAAC,MAAM,QAAQ,qBAAqB,CACtC,OAAM,IAAI,MACR,iCAAiC,WAAW,kDAAkD,sBAAsB,cAAc,GACnI;AAEH,QAAK,MAAM,OAAO,qBAChB,KAAI,CAAC,cAAc,SAAS,IAAI,CAC9B,OAAM,IAAI,MACR,wBAAwB,IAAI,gBAAgB,WAAW,uBAAuB,sBAAsB,cAAc,GACnH;;EAKP,IAAI;EACJ,IAAI;AACJ,OAAK,MAAM,EAAE,KAAK,KAAK,SAAS,QAC9B,KAAI,QAAQ,OACV,cAAa,GAAG,IAAI,IAAI;WACf,sBAAsB,SAAS,IAAI,EAAE;AAC9C,OAAI,CAAC,aAAc,gBAAe,EAAE;AACpC,gBAAa,KAAK,CAAC,KAAK,GAAG,IAAI,IAAI,MAAM,CAAC;;AAI9C,SAAO;GACL,MAAM,aAAa,CAAC,WAAW,GAAG,KAAA;GAClC;GACA;GACD;;AAKH,QAAO;EACL,MAAM;EACN;EACA,cAAc,KAAA;EACf"}
1
+ {"version":3,"file":"get-axes.js","names":[],"sources":["../../../src/build/google-fonts/get-axes.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/get-font-axes.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/get-font-axes.ts\n//\n// Resolves the axis values that belong in a Google Fonts URL given the\n// caller's requested weights, styles, and (optional) selected variable axes.\n// For variable fonts this is the only place that turns the literal sentinel\n// `\"variable\"` into the actual `min..max` range from the bundled metadata.\n\nimport { googleFontsMetadata } from \"./font-metadata.js\";\nimport type { FontAxes } from \"./build-url.js\";\n\nconst formatAvailableValues = (values: string[]): string =>\n values.map((val) => `\\`${val}\\``).join(\", \");\n\nexport function getFontAxes(\n fontFamily: string,\n weights: string[],\n styles: string[],\n selectedVariableAxes?: string[],\n): FontAxes {\n const hasItalic = styles.includes(\"italic\");\n const hasNormal = styles.includes(\"normal\");\n // Google treats omitted ital as ital=0, so italic-only requests do not need\n // to enumerate ital=0; just ['1'] suffices.\n const ital = hasItalic ? [...(hasNormal ? [\"0\"] : []), \"1\"] : undefined;\n\n // Variable-font sentinel: a single \"variable\" entry means the caller wants\n // the full axis range from metadata, not a list of explicit weights.\n if (weights[0] === \"variable\") {\n const allAxes = googleFontsMetadata[fontFamily].axes;\n if (!allAxes) {\n // Reaching this branch means validate accepted a \"variable\" weight for\n // a non-variable font, which should be impossible because the\n // metadata-based validator rejects this earlier. Treat as an internal\n // invariant.\n throw new Error(\"invariant variable font without axes\");\n }\n\n if (selectedVariableAxes) {\n const definableAxes = allAxes.map(({ tag }) => tag).filter((tag) => tag !== \"wght\");\n if (definableAxes.length === 0) {\n throw new Error(`Font \\`${fontFamily}\\` has no definable \\`axes\\``);\n }\n if (!Array.isArray(selectedVariableAxes)) {\n throw new Error(\n `Invalid axes value for font \\`${fontFamily}\\`, expected an array of axes.\\nAvailable axes: ${formatAvailableValues(definableAxes)}`,\n );\n }\n for (const key of selectedVariableAxes) {\n if (!definableAxes.includes(key)) {\n throw new Error(\n `Invalid axes value \\`${key}\\` for font \\`${fontFamily}\\`.\\nAvailable axes: ${formatAvailableValues(definableAxes)}`,\n );\n }\n }\n }\n\n let weightAxis: string | undefined;\n let variableAxes: [string, string][] | undefined;\n for (const { tag, min, max } of allAxes) {\n if (tag === \"wght\") {\n weightAxis = `${min}..${max}`;\n } else if (selectedVariableAxes?.includes(tag)) {\n if (!variableAxes) variableAxes = [];\n variableAxes.push([tag, `${min}..${max}`]);\n }\n }\n\n return {\n wght: weightAxis ? [weightAxis] : undefined,\n ital,\n variableAxes,\n };\n }\n\n // Non-variable path: weights are explicit face values that the URL builder\n // emits verbatim.\n return {\n wght: weights,\n ital,\n variableAxes: undefined,\n };\n}\n"],"mappings":";;AAWA,MAAM,yBAAyB,WAC7B,OAAO,KAAK,QAAQ,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK;AAE9C,SAAgB,YACd,YACA,SACA,QACA,sBACU;CACV,MAAM,YAAY,OAAO,SAAS,SAAS;CAC3C,MAAM,YAAY,OAAO,SAAS,SAAS;CAG3C,MAAM,OAAO,YAAY,CAAC,GAAI,YAAY,CAAC,IAAI,GAAG,EAAE,EAAG,IAAI,GAAG,KAAA;CAI9D,IAAI,QAAQ,OAAO,YAAY;EAC7B,MAAM,UAAU,oBAAoB,YAAY;EAChD,IAAI,CAAC,SAKH,MAAM,IAAI,MAAM,uCAAuC;EAGzD,IAAI,sBAAsB;GACxB,MAAM,gBAAgB,QAAQ,KAAK,EAAE,UAAU,IAAI,CAAC,QAAQ,QAAQ,QAAQ,OAAO;GACnF,IAAI,cAAc,WAAW,GAC3B,MAAM,IAAI,MAAM,UAAU,WAAW,8BAA8B;GAErE,IAAI,CAAC,MAAM,QAAQ,qBAAqB,EACtC,MAAM,IAAI,MACR,iCAAiC,WAAW,kDAAkD,sBAAsB,cAAc,GACnI;GAEH,KAAK,MAAM,OAAO,sBAChB,IAAI,CAAC,cAAc,SAAS,IAAI,EAC9B,MAAM,IAAI,MACR,wBAAwB,IAAI,gBAAgB,WAAW,uBAAuB,sBAAsB,cAAc,GACnH;;EAKP,IAAI;EACJ,IAAI;EACJ,KAAK,MAAM,EAAE,KAAK,KAAK,SAAS,SAC9B,IAAI,QAAQ,QACV,aAAa,GAAG,IAAI,IAAI;OACnB,IAAI,sBAAsB,SAAS,IAAI,EAAE;GAC9C,IAAI,CAAC,cAAc,eAAe,EAAE;GACpC,aAAa,KAAK,CAAC,KAAK,GAAG,IAAI,IAAI,MAAM,CAAC;;EAI9C,OAAO;GACL,MAAM,aAAa,CAAC,WAAW,GAAG,KAAA;GAClC;GACA;GACD;;CAKH,OAAO;EACL,MAAM;EACN;EACA,cAAc,KAAA;EACf"}
@@ -1 +1 @@
1
- {"version":3,"file":"sort-variants.js","names":[],"sources":["../../../src/build/google-fonts/sort-variants.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/sort-fonts-variant-values.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/sort-fonts-variant-values.ts\n//\n// Comparator used by `Array.prototype.sort` when assembling the variant\n// segment of a Google Fonts URL. Google's CSS API requires variant values to\n// be ordered numerically, not lexically. The string ordering of\n// `[\"100\",\"1000\",\"300\"]` would put 1000 before 300, and `[\"1,100\",\"0,200\"]`\n// would put the higher ital prefix first; both produce HTTP 400.\n\nexport function sortFontsVariantValues(valA: string, valB: string): number {\n // \"ital,wght\" pair format: compare ital first, then wght when ital matches.\n if (valA.includes(\",\") && valB.includes(\",\")) {\n const [aPrefix, aSuffix] = valA.split(\",\", 2);\n const [bPrefix, bSuffix] = valB.split(\",\", 2);\n\n if (aPrefix === bPrefix) {\n return parseInt(aSuffix) - parseInt(bSuffix);\n }\n return parseInt(aPrefix) - parseInt(bPrefix);\n }\n\n // Plain weight string: numeric compare.\n return parseInt(valA) - parseInt(valB);\n}\n"],"mappings":";AASA,SAAgB,uBAAuB,MAAc,MAAsB;AAEzE,KAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,EAAE;EAC5C,MAAM,CAAC,SAAS,WAAW,KAAK,MAAM,KAAK,EAAE;EAC7C,MAAM,CAAC,SAAS,WAAW,KAAK,MAAM,KAAK,EAAE;AAE7C,MAAI,YAAY,QACd,QAAO,SAAS,QAAQ,GAAG,SAAS,QAAQ;AAE9C,SAAO,SAAS,QAAQ,GAAG,SAAS,QAAQ;;AAI9C,QAAO,SAAS,KAAK,GAAG,SAAS,KAAK"}
1
+ {"version":3,"file":"sort-variants.js","names":[],"sources":["../../../src/build/google-fonts/sort-variants.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/sort-fonts-variant-values.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/sort-fonts-variant-values.ts\n//\n// Comparator used by `Array.prototype.sort` when assembling the variant\n// segment of a Google Fonts URL. Google's CSS API requires variant values to\n// be ordered numerically, not lexically. The string ordering of\n// `[\"100\",\"1000\",\"300\"]` would put 1000 before 300, and `[\"1,100\",\"0,200\"]`\n// would put the higher ital prefix first; both produce HTTP 400.\n\nexport function sortFontsVariantValues(valA: string, valB: string): number {\n // \"ital,wght\" pair format: compare ital first, then wght when ital matches.\n if (valA.includes(\",\") && valB.includes(\",\")) {\n const [aPrefix, aSuffix] = valA.split(\",\", 2);\n const [bPrefix, bSuffix] = valB.split(\",\", 2);\n\n if (aPrefix === bPrefix) {\n return parseInt(aSuffix) - parseInt(bSuffix);\n }\n return parseInt(aPrefix) - parseInt(bPrefix);\n }\n\n // Plain weight string: numeric compare.\n return parseInt(valA) - parseInt(valB);\n}\n"],"mappings":";AASA,SAAgB,uBAAuB,MAAc,MAAsB;CAEzE,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,EAAE;EAC5C,MAAM,CAAC,SAAS,WAAW,KAAK,MAAM,KAAK,EAAE;EAC7C,MAAM,CAAC,SAAS,WAAW,KAAK,MAAM,KAAK,EAAE;EAE7C,IAAI,YAAY,SACd,OAAO,SAAS,QAAQ,GAAG,SAAS,QAAQ;EAE9C,OAAO,SAAS,QAAQ,GAAG,SAAS,QAAQ;;CAI9C,OAAO,SAAS,KAAK,GAAG,SAAS,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","names":[],"sources":["../../../src/build/google-fonts/validate.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/validate-google-font-function-call.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/validate-google-font-function-call.ts\n//\n// Vinext drops Next's SWC-coupled `(functionName, fontFunctionArgument)`\n// calling convention because the plugin already knows the family at parse\n// time. Behaviour is otherwise identical: validate the caller's options\n// against the bundled metadata, default missing fields, and reject anything\n// Google Fonts would reject at request time.\n\nimport { googleFontsMetadata } from \"./font-metadata.js\";\n\nconst ALLOWED_DISPLAY_VALUES = [\"auto\", \"block\", \"swap\", \"fallback\", \"optional\"];\n\ntype GoogleFontOptions = {\n weight?: string | string[];\n style?: string | string[];\n preload?: boolean;\n display?: string;\n axes?: string[];\n fallback?: string[];\n adjustFontFallback?: boolean;\n variable?: string;\n subsets?: string[];\n};\n\ntype ValidatedGoogleFontOptions = {\n fontFamily: string;\n weights: string[];\n styles: string[];\n display: string;\n preload: boolean;\n selectedVariableAxes?: string[];\n fallback?: string[];\n adjustFontFallback: boolean;\n variable?: string;\n subsets: string[];\n};\n\nconst formatAvailableValues = (values: string[]): string =>\n values.map((val) => `\\`${val}\\``).join(\", \");\n\nconst dedupe = (values: string[]): string[] => Array.from(new Set(values));\n\nexport function validateGoogleFontOptions(\n fontFamily: string,\n options: GoogleFontOptions,\n): ValidatedGoogleFontOptions {\n const {\n weight,\n style,\n display = \"swap\",\n axes,\n fallback,\n adjustFontFallback = true,\n variable,\n subsets = [],\n } = options;\n let preload = options.preload ?? true;\n\n const fontFamilyData = googleFontsMetadata[fontFamily];\n if (!fontFamilyData) {\n throw new Error(`Unknown font \\`${fontFamily}\\``);\n }\n\n if (axes !== undefined && !Array.isArray(axes)) {\n throw new Error(\n `Invalid axes value for font \\`${fontFamily}\\`, expected an array of axis names.`,\n );\n }\n\n const availableSubsets = fontFamilyData.subsets;\n if (availableSubsets.length === 0) {\n // No preloadable subsets means preload is meaningless. Silently disable\n // it rather than forcing the caller to opt out.\n preload = false;\n } else if (preload) {\n // Deliberate parity gap: Next.js uses `if (!subsets)` so an explicit\n // `subsets: []` passes silently. vinext rejects it as well, since an\n // empty subsets array with preload enabled is always a caller mistake.\n if (subsets.length === 0) {\n throw new Error(\n `Preload is enabled but no subsets were specified for font \\`${fontFamily}\\`. Please specify subsets or disable preloading if your intended subset can't be preloaded.\\nAvailable subsets: ${formatAvailableValues(availableSubsets)}`,\n );\n }\n for (const subset of subsets) {\n if (!availableSubsets.includes(subset)) {\n throw new Error(\n `Unknown subset \\`${subset}\\` for font \\`${fontFamily}\\`.\\nAvailable subsets: ${formatAvailableValues(availableSubsets)}`,\n );\n }\n }\n }\n\n const fontWeights = fontFamilyData.weights;\n const fontStyles = fontFamilyData.styles;\n\n const weights = !weight ? [] : dedupe(Array.isArray(weight) ? weight : [weight]);\n const styles = !style ? [] : dedupe(Array.isArray(style) ? style : [style]);\n\n if (weights.length === 0) {\n if (fontWeights.includes(\"variable\")) {\n // Caller said \"any weight\" and the font has a variable face, so use it.\n weights.push(\"variable\");\n } else {\n throw new Error(\n `Missing weight for font \\`${fontFamily}\\`.\\nAvailable weights: ${formatAvailableValues(fontWeights)}`,\n );\n }\n }\n\n if (weights.length > 1 && weights.includes(\"variable\")) {\n throw new Error(\n `Unexpected \\`variable\\` in weight array for font \\`${fontFamily}\\`. You only need \\`variable\\`, it includes all available weights.`,\n );\n }\n\n for (const selectedWeight of weights) {\n if (!fontWeights.includes(selectedWeight)) {\n throw new Error(\n `Unknown weight \\`${selectedWeight}\\` for font \\`${fontFamily}\\`.\\nAvailable weights: ${formatAvailableValues(fontWeights)}`,\n );\n }\n }\n\n if (styles.length === 0) {\n if (fontStyles.length === 1) {\n // Italic-only fonts (rare) have no normal face, so use that face.\n styles.push(fontStyles[0]);\n } else {\n styles.push(\"normal\");\n }\n }\n\n for (const selectedStyle of styles) {\n if (!fontStyles.includes(selectedStyle)) {\n throw new Error(\n `Unknown style \\`${selectedStyle}\\` for font \\`${fontFamily}\\`.\\nAvailable styles: ${formatAvailableValues(fontStyles)}`,\n );\n }\n }\n\n if (!ALLOWED_DISPLAY_VALUES.includes(display)) {\n throw new Error(\n `Invalid display value \\`${display}\\` for font \\`${fontFamily}\\`.\\nAvailable display values: ${formatAvailableValues(ALLOWED_DISPLAY_VALUES)}`,\n );\n }\n\n if (axes) {\n if (!fontWeights.includes(\"variable\")) {\n throw new Error(\"Axes can only be defined for variable fonts.\");\n }\n if (weights[0] !== \"variable\") {\n throw new Error(\n \"Axes can only be defined for variable fonts when the weight property is nonexistent or set to `variable`.\",\n );\n }\n }\n\n return {\n fontFamily,\n weights,\n styles,\n display,\n preload,\n selectedVariableAxes: axes,\n fallback,\n adjustFontFallback,\n variable,\n subsets,\n };\n}\n"],"mappings":";;AAWA,MAAM,yBAAyB;CAAC;CAAQ;CAAS;CAAQ;CAAY;CAAW;AA2BhF,MAAM,yBAAyB,WAC7B,OAAO,KAAK,QAAQ,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK;AAE9C,MAAM,UAAU,WAA+B,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC;AAE1E,SAAgB,0BACd,YACA,SAC4B;CAC5B,MAAM,EACJ,QACA,OACA,UAAU,QACV,MACA,UACA,qBAAqB,MACrB,UACA,UAAU,EAAE,KACV;CACJ,IAAI,UAAU,QAAQ,WAAW;CAEjC,MAAM,iBAAiB,oBAAoB;AAC3C,KAAI,CAAC,eACH,OAAM,IAAI,MAAM,kBAAkB,WAAW,IAAI;AAGnD,KAAI,SAAS,KAAA,KAAa,CAAC,MAAM,QAAQ,KAAK,CAC5C,OAAM,IAAI,MACR,iCAAiC,WAAW,sCAC7C;CAGH,MAAM,mBAAmB,eAAe;AACxC,KAAI,iBAAiB,WAAW,EAG9B,WAAU;UACD,SAAS;AAIlB,MAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MACR,+DAA+D,WAAW,mHAAmH,sBAAsB,iBAAiB,GACrO;AAEH,OAAK,MAAM,UAAU,QACnB,KAAI,CAAC,iBAAiB,SAAS,OAAO,CACpC,OAAM,IAAI,MACR,oBAAoB,OAAO,gBAAgB,WAAW,0BAA0B,sBAAsB,iBAAiB,GACxH;;CAKP,MAAM,cAAc,eAAe;CACnC,MAAM,aAAa,eAAe;CAElC,MAAM,UAAU,CAAC,SAAS,EAAE,GAAG,OAAO,MAAM,QAAQ,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;CAChF,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,OAAO,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;AAE3E,KAAI,QAAQ,WAAW,EACrB,KAAI,YAAY,SAAS,WAAW,CAElC,SAAQ,KAAK,WAAW;KAExB,OAAM,IAAI,MACR,6BAA6B,WAAW,0BAA0B,sBAAsB,YAAY,GACrG;AAIL,KAAI,QAAQ,SAAS,KAAK,QAAQ,SAAS,WAAW,CACpD,OAAM,IAAI,MACR,sDAAsD,WAAW,oEAClE;AAGH,MAAK,MAAM,kBAAkB,QAC3B,KAAI,CAAC,YAAY,SAAS,eAAe,CACvC,OAAM,IAAI,MACR,oBAAoB,eAAe,gBAAgB,WAAW,0BAA0B,sBAAsB,YAAY,GAC3H;AAIL,KAAI,OAAO,WAAW,EACpB,KAAI,WAAW,WAAW,EAExB,QAAO,KAAK,WAAW,GAAG;KAE1B,QAAO,KAAK,SAAS;AAIzB,MAAK,MAAM,iBAAiB,OAC1B,KAAI,CAAC,WAAW,SAAS,cAAc,CACrC,OAAM,IAAI,MACR,mBAAmB,cAAc,gBAAgB,WAAW,yBAAyB,sBAAsB,WAAW,GACvH;AAIL,KAAI,CAAC,uBAAuB,SAAS,QAAQ,CAC3C,OAAM,IAAI,MACR,2BAA2B,QAAQ,gBAAgB,WAAW,iCAAiC,sBAAsB,uBAAuB,GAC7I;AAGH,KAAI,MAAM;AACR,MAAI,CAAC,YAAY,SAAS,WAAW,CACnC,OAAM,IAAI,MAAM,+CAA+C;AAEjE,MAAI,QAAQ,OAAO,WACjB,OAAM,IAAI,MACR,4GACD;;AAIL,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,sBAAsB;EACtB;EACA;EACA;EACA;EACD"}
1
+ {"version":3,"file":"validate.js","names":[],"sources":["../../../src/build/google-fonts/validate.ts"],"sourcesContent":["// Ported from Next.js: packages/font/src/google/validate-google-font-function-call.ts\n// https://github.com/vercel/next.js/blob/canary/packages/font/src/google/validate-google-font-function-call.ts\n//\n// Vinext drops Next's SWC-coupled `(functionName, fontFunctionArgument)`\n// calling convention because the plugin already knows the family at parse\n// time. Behaviour is otherwise identical: validate the caller's options\n// against the bundled metadata, default missing fields, and reject anything\n// Google Fonts would reject at request time.\n\nimport { googleFontsMetadata } from \"./font-metadata.js\";\n\nconst ALLOWED_DISPLAY_VALUES = [\"auto\", \"block\", \"swap\", \"fallback\", \"optional\"];\n\ntype GoogleFontOptions = {\n weight?: string | string[];\n style?: string | string[];\n preload?: boolean;\n display?: string;\n axes?: string[];\n fallback?: string[];\n adjustFontFallback?: boolean;\n variable?: string;\n subsets?: string[];\n};\n\ntype ValidatedGoogleFontOptions = {\n fontFamily: string;\n weights: string[];\n styles: string[];\n display: string;\n preload: boolean;\n selectedVariableAxes?: string[];\n fallback?: string[];\n adjustFontFallback: boolean;\n variable?: string;\n subsets: string[];\n};\n\nconst formatAvailableValues = (values: string[]): string =>\n values.map((val) => `\\`${val}\\``).join(\", \");\n\nconst dedupe = (values: string[]): string[] => Array.from(new Set(values));\n\nexport function validateGoogleFontOptions(\n fontFamily: string,\n options: GoogleFontOptions,\n): ValidatedGoogleFontOptions {\n const {\n weight,\n style,\n display = \"swap\",\n axes,\n fallback,\n adjustFontFallback = true,\n variable,\n subsets = [],\n } = options;\n let preload = options.preload ?? true;\n\n const fontFamilyData = googleFontsMetadata[fontFamily];\n if (!fontFamilyData) {\n throw new Error(`Unknown font \\`${fontFamily}\\``);\n }\n\n if (axes !== undefined && !Array.isArray(axes)) {\n throw new Error(\n `Invalid axes value for font \\`${fontFamily}\\`, expected an array of axis names.`,\n );\n }\n\n const availableSubsets = fontFamilyData.subsets;\n if (availableSubsets.length === 0) {\n // No preloadable subsets means preload is meaningless. Silently disable\n // it rather than forcing the caller to opt out.\n preload = false;\n } else if (preload) {\n // Deliberate parity gap: Next.js uses `if (!subsets)` so an explicit\n // `subsets: []` passes silently. vinext rejects it as well, since an\n // empty subsets array with preload enabled is always a caller mistake.\n if (subsets.length === 0) {\n throw new Error(\n `Preload is enabled but no subsets were specified for font \\`${fontFamily}\\`. Please specify subsets or disable preloading if your intended subset can't be preloaded.\\nAvailable subsets: ${formatAvailableValues(availableSubsets)}`,\n );\n }\n for (const subset of subsets) {\n if (!availableSubsets.includes(subset)) {\n throw new Error(\n `Unknown subset \\`${subset}\\` for font \\`${fontFamily}\\`.\\nAvailable subsets: ${formatAvailableValues(availableSubsets)}`,\n );\n }\n }\n }\n\n const fontWeights = fontFamilyData.weights;\n const fontStyles = fontFamilyData.styles;\n\n const weights = !weight ? [] : dedupe(Array.isArray(weight) ? weight : [weight]);\n const styles = !style ? [] : dedupe(Array.isArray(style) ? style : [style]);\n\n if (weights.length === 0) {\n if (fontWeights.includes(\"variable\")) {\n // Caller said \"any weight\" and the font has a variable face, so use it.\n weights.push(\"variable\");\n } else {\n throw new Error(\n `Missing weight for font \\`${fontFamily}\\`.\\nAvailable weights: ${formatAvailableValues(fontWeights)}`,\n );\n }\n }\n\n if (weights.length > 1 && weights.includes(\"variable\")) {\n throw new Error(\n `Unexpected \\`variable\\` in weight array for font \\`${fontFamily}\\`. You only need \\`variable\\`, it includes all available weights.`,\n );\n }\n\n for (const selectedWeight of weights) {\n if (!fontWeights.includes(selectedWeight)) {\n throw new Error(\n `Unknown weight \\`${selectedWeight}\\` for font \\`${fontFamily}\\`.\\nAvailable weights: ${formatAvailableValues(fontWeights)}`,\n );\n }\n }\n\n if (styles.length === 0) {\n if (fontStyles.length === 1) {\n // Italic-only fonts (rare) have no normal face, so use that face.\n styles.push(fontStyles[0]);\n } else {\n styles.push(\"normal\");\n }\n }\n\n for (const selectedStyle of styles) {\n if (!fontStyles.includes(selectedStyle)) {\n throw new Error(\n `Unknown style \\`${selectedStyle}\\` for font \\`${fontFamily}\\`.\\nAvailable styles: ${formatAvailableValues(fontStyles)}`,\n );\n }\n }\n\n if (!ALLOWED_DISPLAY_VALUES.includes(display)) {\n throw new Error(\n `Invalid display value \\`${display}\\` for font \\`${fontFamily}\\`.\\nAvailable display values: ${formatAvailableValues(ALLOWED_DISPLAY_VALUES)}`,\n );\n }\n\n if (axes) {\n if (!fontWeights.includes(\"variable\")) {\n throw new Error(\"Axes can only be defined for variable fonts.\");\n }\n if (weights[0] !== \"variable\") {\n throw new Error(\n \"Axes can only be defined for variable fonts when the weight property is nonexistent or set to `variable`.\",\n );\n }\n }\n\n return {\n fontFamily,\n weights,\n styles,\n display,\n preload,\n selectedVariableAxes: axes,\n fallback,\n adjustFontFallback,\n variable,\n subsets,\n };\n}\n"],"mappings":";;AAWA,MAAM,yBAAyB;CAAC;CAAQ;CAAS;CAAQ;CAAY;CAAW;AA2BhF,MAAM,yBAAyB,WAC7B,OAAO,KAAK,QAAQ,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK;AAE9C,MAAM,UAAU,WAA+B,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC;AAE1E,SAAgB,0BACd,YACA,SAC4B;CAC5B,MAAM,EACJ,QACA,OACA,UAAU,QACV,MACA,UACA,qBAAqB,MACrB,UACA,UAAU,EAAE,KACV;CACJ,IAAI,UAAU,QAAQ,WAAW;CAEjC,MAAM,iBAAiB,oBAAoB;CAC3C,IAAI,CAAC,gBACH,MAAM,IAAI,MAAM,kBAAkB,WAAW,IAAI;CAGnD,IAAI,SAAS,KAAA,KAAa,CAAC,MAAM,QAAQ,KAAK,EAC5C,MAAM,IAAI,MACR,iCAAiC,WAAW,sCAC7C;CAGH,MAAM,mBAAmB,eAAe;CACxC,IAAI,iBAAiB,WAAW,GAG9B,UAAU;MACL,IAAI,SAAS;EAIlB,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MACR,+DAA+D,WAAW,mHAAmH,sBAAsB,iBAAiB,GACrO;EAEH,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,iBAAiB,SAAS,OAAO,EACpC,MAAM,IAAI,MACR,oBAAoB,OAAO,gBAAgB,WAAW,0BAA0B,sBAAsB,iBAAiB,GACxH;;CAKP,MAAM,cAAc,eAAe;CACnC,MAAM,aAAa,eAAe;CAElC,MAAM,UAAU,CAAC,SAAS,EAAE,GAAG,OAAO,MAAM,QAAQ,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;CAChF,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,OAAO,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;CAE3E,IAAI,QAAQ,WAAW,GACrB,IAAI,YAAY,SAAS,WAAW,EAElC,QAAQ,KAAK,WAAW;MAExB,MAAM,IAAI,MACR,6BAA6B,WAAW,0BAA0B,sBAAsB,YAAY,GACrG;CAIL,IAAI,QAAQ,SAAS,KAAK,QAAQ,SAAS,WAAW,EACpD,MAAM,IAAI,MACR,sDAAsD,WAAW,oEAClE;CAGH,KAAK,MAAM,kBAAkB,SAC3B,IAAI,CAAC,YAAY,SAAS,eAAe,EACvC,MAAM,IAAI,MACR,oBAAoB,eAAe,gBAAgB,WAAW,0BAA0B,sBAAsB,YAAY,GAC3H;CAIL,IAAI,OAAO,WAAW,GACpB,IAAI,WAAW,WAAW,GAExB,OAAO,KAAK,WAAW,GAAG;MAE1B,OAAO,KAAK,SAAS;CAIzB,KAAK,MAAM,iBAAiB,QAC1B,IAAI,CAAC,WAAW,SAAS,cAAc,EACrC,MAAM,IAAI,MACR,mBAAmB,cAAc,gBAAgB,WAAW,yBAAyB,sBAAsB,WAAW,GACvH;CAIL,IAAI,CAAC,uBAAuB,SAAS,QAAQ,EAC3C,MAAM,IAAI,MACR,2BAA2B,QAAQ,gBAAgB,WAAW,iCAAiC,sBAAsB,uBAAuB,GAC7I;CAGH,IAAI,MAAM;EACR,IAAI,CAAC,YAAY,SAAS,WAAW,EACnC,MAAM,IAAI,MAAM,+CAA+C;EAEjE,IAAI,QAAQ,OAAO,YACjB,MAAM,IAAI,MACR,4GACD;;CAIL,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,sBAAsB;EACtB;EACA;EACA;EACA;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"layout-classification.js","names":[],"sources":["../../src/build/layout-classification.ts"],"sourcesContent":["/**\n * Layout classification — determines whether each layout in an App Router\n * route tree is static or dynamic via two complementary detection layers:\n *\n * Layer 1: Segment config (`export const dynamic`, `export const revalidate`)\n * Layer 2: Module graph traversal (checks for transitive dynamic shim imports)\n *\n * Layer 3 (probe-based runtime detection) is handled separately in\n * `app-page-execution.ts` at request time.\n *\n * Every result is carried as a `LayoutBuildClassification` tagged variant so\n * operators can trace which layer produced a decision via the structured\n * `ClassificationReason` sidecar without that metadata leaking onto the wire.\n */\n\nimport { classifyLayoutSegmentConfig } from \"./report.js\";\nimport { AppElementsWire } from \"../server/app-elements.js\";\nimport { createAppPageTreePath } from \"../server/app-page-route-wiring.js\";\nimport type {\n ClassificationReason,\n LayoutBuildClassification,\n ModuleGraphStaticReason,\n} from \"./layout-classification-types.js\";\n\ntype ModuleGraphClassification = \"static\" | \"needs-probe\";\n\ntype ModuleGraphClassificationResult = {\n result: ModuleGraphClassification;\n /** First dynamic shim module ID encountered during BFS, when any. */\n firstShimMatch?: string;\n};\n\nexport type ModuleInfoProvider = {\n getModuleInfo(id: string): {\n importedIds: string[];\n dynamicImportedIds: string[];\n } | null;\n};\n\ntype LayoutEntry = {\n /** Rollup/Vite module ID for the layout file. */\n moduleId: string;\n /** Directory depth from the app root, used to build the stable layout ID. */\n treePosition: number;\n /** Segment config source code extracted at build time, or null when absent. */\n segmentConfig?: { code: string } | null;\n};\n\ntype RouteForClassification = {\n layouts: readonly LayoutEntry[];\n routeSegments: string[];\n};\n\n/**\n * BFS traversal of a layout's dependency tree. If any transitive import\n * resolves to a dynamic shim path (headers, cache, server), the layout\n * cannot be proven static at build time and needs a runtime probe.\n *\n * The returned object carries the classification plus the first matching\n * shim module ID (when any). Operators use the shim ID via the debug\n * channel to trace why a layout was flagged for probing.\n */\nexport function classifyLayoutByModuleGraph(\n layoutModuleId: string,\n dynamicShimPaths: ReadonlySet<string>,\n moduleInfo: ModuleInfoProvider,\n): ModuleGraphClassificationResult {\n const visited = new Set<string>();\n const queue: string[] = [layoutModuleId];\n let head = 0;\n\n while (head < queue.length) {\n const currentId = queue[head++]!;\n\n if (visited.has(currentId)) continue;\n visited.add(currentId);\n\n if (dynamicShimPaths.has(currentId)) {\n return { result: \"needs-probe\", firstShimMatch: currentId };\n }\n\n const info = moduleInfo.getModuleInfo(currentId);\n if (!info) continue;\n\n for (const importedId of info.importedIds) {\n if (!visited.has(importedId)) queue.push(importedId);\n }\n for (const dynamicId of info.dynamicImportedIds) {\n if (!visited.has(dynamicId)) queue.push(dynamicId);\n }\n }\n\n return { result: \"static\" };\n}\n\nexport function moduleGraphReason(\n graphResult: ModuleGraphClassificationResult & { result: \"static\" },\n): ModuleGraphStaticReason;\nexport function moduleGraphReason(\n graphResult: ModuleGraphClassificationResult,\n): ClassificationReason;\nexport function moduleGraphReason(\n graphResult: ModuleGraphClassificationResult,\n): ClassificationReason {\n if (graphResult.firstShimMatch === undefined) {\n return { layer: \"module-graph\", result: graphResult.result };\n }\n return {\n layer: \"module-graph\",\n result: graphResult.result,\n firstShimMatch: graphResult.firstShimMatch,\n };\n}\n\nexport function isStaticModuleGraphResult(\n graphResult: ModuleGraphClassificationResult,\n): graphResult is ModuleGraphClassificationResult & { result: \"static\" } {\n return graphResult.result === \"static\";\n}\n\n/**\n * Classifies all layouts across all routes using a two-layer strategy:\n *\n * 1. Segment config (Layer 1) — short-circuits to \"static\" or \"dynamic\"\n * 2. Module graph (Layer 2) — BFS for dynamic shim imports → \"static\" or \"needs-probe\"\n *\n * Shared layouts (same file appearing in multiple routes) are classified once\n * and deduplicated by layout ID.\n *\n * @internal Not called by production code. The `generateBundle` hook in\n * `index.ts` calls `classifyLayoutByModuleGraph` directly and composes\n * via the numeric-index manifest in `route-classification-manifest.ts`.\n * Used only by `tests/layout-classification.test.ts`.\n */\nexport function classifyAllRouteLayouts(\n routes: readonly RouteForClassification[],\n dynamicShimPaths: ReadonlySet<string>,\n moduleInfo: ModuleInfoProvider,\n): Map<string, LayoutBuildClassification> {\n const result = new Map<string, LayoutBuildClassification>();\n\n for (const route of routes) {\n for (const layout of route.layouts) {\n const layoutId = AppElementsWire.encodeLayoutId(\n createAppPageTreePath(route.routeSegments, layout.treePosition),\n );\n\n if (result.has(layoutId)) continue;\n\n // Layer 1: segment config\n if (layout.segmentConfig) {\n const configResult = classifyLayoutSegmentConfig(layout.segmentConfig.code);\n if (configResult.kind !== \"absent\") {\n result.set(layoutId, configResult);\n continue;\n }\n }\n\n // Layer 2: module graph\n const graphResult = classifyLayoutByModuleGraph(\n layout.moduleId,\n dynamicShimPaths,\n moduleInfo,\n );\n const reason = moduleGraphReason(graphResult);\n result.set(layoutId, { kind: graphResult.result, reason });\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,SAAgB,4BACd,gBACA,kBACA,YACiC;CACjC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,QAAkB,CAAC,eAAe;CACxC,IAAI,OAAO;AAEX,QAAO,OAAO,MAAM,QAAQ;EAC1B,MAAM,YAAY,MAAM;AAExB,MAAI,QAAQ,IAAI,UAAU,CAAE;AAC5B,UAAQ,IAAI,UAAU;AAEtB,MAAI,iBAAiB,IAAI,UAAU,CACjC,QAAO;GAAE,QAAQ;GAAe,gBAAgB;GAAW;EAG7D,MAAM,OAAO,WAAW,cAAc,UAAU;AAChD,MAAI,CAAC,KAAM;AAEX,OAAK,MAAM,cAAc,KAAK,YAC5B,KAAI,CAAC,QAAQ,IAAI,WAAW,CAAE,OAAM,KAAK,WAAW;AAEtD,OAAK,MAAM,aAAa,KAAK,mBAC3B,KAAI,CAAC,QAAQ,IAAI,UAAU,CAAE,OAAM,KAAK,UAAU;;AAItD,QAAO,EAAE,QAAQ,UAAU;;AAS7B,SAAgB,kBACd,aACsB;AACtB,KAAI,YAAY,mBAAmB,KAAA,EACjC,QAAO;EAAE,OAAO;EAAgB,QAAQ,YAAY;EAAQ;AAE9D,QAAO;EACL,OAAO;EACP,QAAQ,YAAY;EACpB,gBAAgB,YAAY;EAC7B;;AAGH,SAAgB,0BACd,aACuE;AACvE,QAAO,YAAY,WAAW;;;;;;;;;;;;;;;;AAiBhC,SAAgB,wBACd,QACA,kBACA,YACwC;CACxC,MAAM,yBAAS,IAAI,KAAwC;AAE3D,MAAK,MAAM,SAAS,OAClB,MAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,WAAW,gBAAgB,eAC/B,sBAAsB,MAAM,eAAe,OAAO,aAAa,CAChE;AAED,MAAI,OAAO,IAAI,SAAS,CAAE;AAG1B,MAAI,OAAO,eAAe;GACxB,MAAM,eAAe,4BAA4B,OAAO,cAAc,KAAK;AAC3E,OAAI,aAAa,SAAS,UAAU;AAClC,WAAO,IAAI,UAAU,aAAa;AAClC;;;EAKJ,MAAM,cAAc,4BAClB,OAAO,UACP,kBACA,WACD;EACD,MAAM,SAAS,kBAAkB,YAAY;AAC7C,SAAO,IAAI,UAAU;GAAE,MAAM,YAAY;GAAQ;GAAQ,CAAC;;AAI9D,QAAO"}
1
+ {"version":3,"file":"layout-classification.js","names":[],"sources":["../../src/build/layout-classification.ts"],"sourcesContent":["/**\n * Layout classification — determines whether each layout in an App Router\n * route tree is static or dynamic via two complementary detection layers:\n *\n * Layer 1: Segment config (`export const dynamic`, `export const revalidate`)\n * Layer 2: Module graph traversal (checks for transitive dynamic shim imports)\n *\n * Layer 3 (probe-based runtime detection) is handled separately in\n * `app-page-execution.ts` at request time.\n *\n * Every result is carried as a `LayoutBuildClassification` tagged variant so\n * operators can trace which layer produced a decision via the structured\n * `ClassificationReason` sidecar without that metadata leaking onto the wire.\n */\n\nimport { classifyLayoutSegmentConfig } from \"./report.js\";\nimport { AppElementsWire } from \"../server/app-elements.js\";\nimport { createAppPageTreePath } from \"../server/app-page-route-wiring.js\";\nimport type {\n ClassificationReason,\n LayoutBuildClassification,\n ModuleGraphStaticReason,\n} from \"./layout-classification-types.js\";\n\ntype ModuleGraphClassification = \"static\" | \"needs-probe\";\n\ntype ModuleGraphClassificationResult = {\n result: ModuleGraphClassification;\n /** First dynamic shim module ID encountered during BFS, when any. */\n firstShimMatch?: string;\n};\n\nexport type ModuleInfoProvider = {\n getModuleInfo(id: string): {\n importedIds: string[];\n dynamicImportedIds: string[];\n } | null;\n};\n\ntype LayoutEntry = {\n /** Rollup/Vite module ID for the layout file. */\n moduleId: string;\n /** Directory depth from the app root, used to build the stable layout ID. */\n treePosition: number;\n /** Segment config source code extracted at build time, or null when absent. */\n segmentConfig?: { code: string } | null;\n};\n\ntype RouteForClassification = {\n layouts: readonly LayoutEntry[];\n routeSegments: string[];\n};\n\n/**\n * BFS traversal of a layout's dependency tree. If any transitive import\n * resolves to a dynamic shim path (headers, cache, server), the layout\n * cannot be proven static at build time and needs a runtime probe.\n *\n * The returned object carries the classification plus the first matching\n * shim module ID (when any). Operators use the shim ID via the debug\n * channel to trace why a layout was flagged for probing.\n */\nexport function classifyLayoutByModuleGraph(\n layoutModuleId: string,\n dynamicShimPaths: ReadonlySet<string>,\n moduleInfo: ModuleInfoProvider,\n): ModuleGraphClassificationResult {\n const visited = new Set<string>();\n const queue: string[] = [layoutModuleId];\n let head = 0;\n\n while (head < queue.length) {\n const currentId = queue[head++]!;\n\n if (visited.has(currentId)) continue;\n visited.add(currentId);\n\n if (dynamicShimPaths.has(currentId)) {\n return { result: \"needs-probe\", firstShimMatch: currentId };\n }\n\n const info = moduleInfo.getModuleInfo(currentId);\n if (!info) continue;\n\n for (const importedId of info.importedIds) {\n if (!visited.has(importedId)) queue.push(importedId);\n }\n for (const dynamicId of info.dynamicImportedIds) {\n if (!visited.has(dynamicId)) queue.push(dynamicId);\n }\n }\n\n return { result: \"static\" };\n}\n\nexport function moduleGraphReason(\n graphResult: ModuleGraphClassificationResult & { result: \"static\" },\n): ModuleGraphStaticReason;\nexport function moduleGraphReason(\n graphResult: ModuleGraphClassificationResult,\n): ClassificationReason;\nexport function moduleGraphReason(\n graphResult: ModuleGraphClassificationResult,\n): ClassificationReason {\n if (graphResult.firstShimMatch === undefined) {\n return { layer: \"module-graph\", result: graphResult.result };\n }\n return {\n layer: \"module-graph\",\n result: graphResult.result,\n firstShimMatch: graphResult.firstShimMatch,\n };\n}\n\nexport function isStaticModuleGraphResult(\n graphResult: ModuleGraphClassificationResult,\n): graphResult is ModuleGraphClassificationResult & { result: \"static\" } {\n return graphResult.result === \"static\";\n}\n\n/**\n * Classifies all layouts across all routes using a two-layer strategy:\n *\n * 1. Segment config (Layer 1) — short-circuits to \"static\" or \"dynamic\"\n * 2. Module graph (Layer 2) — BFS for dynamic shim imports → \"static\" or \"needs-probe\"\n *\n * Shared layouts (same file appearing in multiple routes) are classified once\n * and deduplicated by layout ID.\n *\n * @internal Not called by production code. The `generateBundle` hook in\n * `index.ts` calls `classifyLayoutByModuleGraph` directly and composes\n * via the numeric-index manifest in `route-classification-manifest.ts`.\n * Used only by `tests/layout-classification.test.ts`.\n */\nexport function classifyAllRouteLayouts(\n routes: readonly RouteForClassification[],\n dynamicShimPaths: ReadonlySet<string>,\n moduleInfo: ModuleInfoProvider,\n): Map<string, LayoutBuildClassification> {\n const result = new Map<string, LayoutBuildClassification>();\n\n for (const route of routes) {\n for (const layout of route.layouts) {\n const layoutId = AppElementsWire.encodeLayoutId(\n createAppPageTreePath(route.routeSegments, layout.treePosition),\n );\n\n if (result.has(layoutId)) continue;\n\n // Layer 1: segment config\n if (layout.segmentConfig) {\n const configResult = classifyLayoutSegmentConfig(layout.segmentConfig.code);\n if (configResult.kind !== \"absent\") {\n result.set(layoutId, configResult);\n continue;\n }\n }\n\n // Layer 2: module graph\n const graphResult = classifyLayoutByModuleGraph(\n layout.moduleId,\n dynamicShimPaths,\n moduleInfo,\n );\n const reason = moduleGraphReason(graphResult);\n result.set(layoutId, { kind: graphResult.result, reason });\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,SAAgB,4BACd,gBACA,kBACA,YACiC;CACjC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,QAAkB,CAAC,eAAe;CACxC,IAAI,OAAO;CAEX,OAAO,OAAO,MAAM,QAAQ;EAC1B,MAAM,YAAY,MAAM;EAExB,IAAI,QAAQ,IAAI,UAAU,EAAE;EAC5B,QAAQ,IAAI,UAAU;EAEtB,IAAI,iBAAiB,IAAI,UAAU,EACjC,OAAO;GAAE,QAAQ;GAAe,gBAAgB;GAAW;EAG7D,MAAM,OAAO,WAAW,cAAc,UAAU;EAChD,IAAI,CAAC,MAAM;EAEX,KAAK,MAAM,cAAc,KAAK,aAC5B,IAAI,CAAC,QAAQ,IAAI,WAAW,EAAE,MAAM,KAAK,WAAW;EAEtD,KAAK,MAAM,aAAa,KAAK,oBAC3B,IAAI,CAAC,QAAQ,IAAI,UAAU,EAAE,MAAM,KAAK,UAAU;;CAItD,OAAO,EAAE,QAAQ,UAAU;;AAS7B,SAAgB,kBACd,aACsB;CACtB,IAAI,YAAY,mBAAmB,KAAA,GACjC,OAAO;EAAE,OAAO;EAAgB,QAAQ,YAAY;EAAQ;CAE9D,OAAO;EACL,OAAO;EACP,QAAQ,YAAY;EACpB,gBAAgB,YAAY;EAC7B;;AAGH,SAAgB,0BACd,aACuE;CACvE,OAAO,YAAY,WAAW;;;;;;;;;;;;;;;;AAiBhC,SAAgB,wBACd,QACA,kBACA,YACwC;CACxC,MAAM,yBAAS,IAAI,KAAwC;CAE3D,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,WAAW,gBAAgB,eAC/B,sBAAsB,MAAM,eAAe,OAAO,aAAa,CAChE;EAED,IAAI,OAAO,IAAI,SAAS,EAAE;EAG1B,IAAI,OAAO,eAAe;GACxB,MAAM,eAAe,4BAA4B,OAAO,cAAc,KAAK;GAC3E,IAAI,aAAa,SAAS,UAAU;IAClC,OAAO,IAAI,UAAU,aAAa;IAClC;;;EAKJ,MAAM,cAAc,4BAClB,OAAO,UACP,kBACA,WACD;EACD,MAAM,SAAS,kBAAkB,YAAY;EAC7C,OAAO,IAAI,UAAU;GAAE,MAAM,YAAY;GAAQ;GAAQ,CAAC;;CAI9D,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"nitro-route-rules.js","names":[],"sources":["../../src/build/nitro-route-rules.ts"],"sourcesContent":["import { appRouter, type AppRoute } from \"../routing/app-router.js\";\nimport { apiRouter, pagesRouter, type Route } from \"../routing/pages-router.js\";\nimport { buildReportRows, type RouteRow } from \"./report.js\";\n\n// Mirrors Nitro's NitroRouteConfig — hand-rolled because nitropack is not a direct dependency.\nexport type NitroRouteRuleConfig = Record<string, unknown> & {\n swr?: boolean | number;\n cache?: unknown;\n static?: boolean;\n isr?: boolean | number;\n prerender?: boolean;\n};\n\ntype NitroRouteRules = Record<string, { swr: number }>;\n\n/**\n * Scans the filesystem for route files and generates Nitro `routeRules` for ISR routes.\n *\n * Note: this duplicates the filesystem scanning that `printBuildReport` also performs.\n * The `nitro.setup` hook runs during Nitro initialization (before the build), while\n * `printBuildReport` runs after the build, so sharing results is non-trivial. This is\n * a future optimization target.\n *\n * Unlike `printBuildReport`, this path does not receive `prerenderResult`, so routes\n * classified as `unknown` by static analysis (which `printBuildReport` might upgrade\n * to `static` via speculative prerender) are skipped here.\n */\nexport async function collectNitroRouteRules(options: {\n appDir?: string | null;\n pagesDir?: string | null;\n pageExtensions: string[];\n}): Promise<NitroRouteRules> {\n const { appDir, pageExtensions, pagesDir } = options;\n\n let appRoutes: AppRoute[] = [];\n let pageRoutes: Route[] = [];\n let apiRoutes: Route[] = [];\n\n if (appDir) {\n appRoutes = await appRouter(appDir, pageExtensions);\n }\n\n if (pagesDir) {\n const [pages, apis] = await Promise.all([\n pagesRouter(pagesDir, pageExtensions),\n apiRouter(pagesDir, pageExtensions),\n ]);\n pageRoutes = pages;\n apiRoutes = apis;\n }\n\n return generateNitroRouteRules(buildReportRows({ appRoutes, pageRoutes, apiRoutes }));\n}\n\nexport function generateNitroRouteRules(rows: RouteRow[]): NitroRouteRules {\n const rules: NitroRouteRules = {};\n\n for (const row of rows) {\n if (\n row.type === \"isr\" &&\n typeof row.revalidate === \"number\" &&\n Number.isFinite(row.revalidate) &&\n row.revalidate > 0\n ) {\n rules[convertToNitroPattern(row.pattern)] = { swr: row.revalidate };\n }\n }\n\n return rules;\n}\n\n/**\n * Converts vinext's internal `:param` route syntax to Nitro's rou3\n * pattern format. Nitro uses `rou3` for routeRules matching, which\n * supports `*` (single-segment) and `**` (multi-segment) wildcards.\n *\n * /blog/:slug -> /blog/* (single segment)\n * /docs/:slug+ -> /docs/** (one or more segments — catch-all)\n * /docs/:slug* -> /docs/** (zero or more segments — optional catch-all)\n * /about -> /about (unchanged)\n * /:a/:b produces `/*`/`/*` (consecutive single-segment params)\n */\nexport function convertToNitroPattern(pattern: string): string {\n return pattern\n .split(\"/\")\n .map((segment) => {\n if (segment.startsWith(\":\")) {\n // Catch-all (:param+) and optional catch-all (:param*) match multiple segments → **\n // Single dynamic param (:param) matches one segment → *\n return segment.endsWith(\"+\") || segment.endsWith(\"*\") ? \"**\" : \"*\";\n }\n return segment;\n })\n .join(\"/\");\n}\n\nexport function mergeNitroRouteRules(\n existingRouteRules: Record<string, NitroRouteRuleConfig> | undefined,\n generatedRouteRules: NitroRouteRules,\n): {\n routeRules: Record<string, NitroRouteRuleConfig>;\n skippedRoutes: string[];\n} {\n const routeRules = { ...existingRouteRules };\n const skippedRoutes: string[] = [];\n\n for (const [route, generatedRule] of Object.entries(generatedRouteRules)) {\n const existingRule = routeRules[route];\n\n if (existingRule && hasUserDefinedCacheRule(existingRule)) {\n skippedRoutes.push(route);\n continue;\n }\n\n routeRules[route] = {\n ...existingRule,\n ...generatedRule,\n };\n }\n\n return { routeRules, skippedRoutes };\n}\n\nfunction hasUserDefinedCacheRule(rule: NitroRouteRuleConfig): boolean {\n return (\n rule.swr !== undefined ||\n rule.cache !== undefined ||\n rule.static !== undefined ||\n rule.isr !== undefined ||\n rule.prerender !== undefined\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA2BA,eAAsB,uBAAuB,SAIhB;CAC3B,MAAM,EAAE,QAAQ,gBAAgB,aAAa;CAE7C,IAAI,YAAwB,EAAE;CAC9B,IAAI,aAAsB,EAAE;CAC5B,IAAI,YAAqB,EAAE;AAE3B,KAAI,OACF,aAAY,MAAM,UAAU,QAAQ,eAAe;AAGrD,KAAI,UAAU;EACZ,MAAM,CAAC,OAAO,QAAQ,MAAM,QAAQ,IAAI,CACtC,YAAY,UAAU,eAAe,EACrC,UAAU,UAAU,eAAe,CACpC,CAAC;AACF,eAAa;AACb,cAAY;;AAGd,QAAO,wBAAwB,gBAAgB;EAAE;EAAW;EAAY;EAAW,CAAC,CAAC;;AAGvF,SAAgB,wBAAwB,MAAmC;CACzE,MAAM,QAAyB,EAAE;AAEjC,MAAK,MAAM,OAAO,KAChB,KACE,IAAI,SAAS,SACb,OAAO,IAAI,eAAe,YAC1B,OAAO,SAAS,IAAI,WAAW,IAC/B,IAAI,aAAa,EAEjB,OAAM,sBAAsB,IAAI,QAAQ,IAAI,EAAE,KAAK,IAAI,YAAY;AAIvE,QAAO;;;;;;;;;;;;;AAcT,SAAgB,sBAAsB,SAAyB;AAC7D,QAAO,QACJ,MAAM,IAAI,CACV,KAAK,YAAY;AAChB,MAAI,QAAQ,WAAW,IAAI,CAGzB,QAAO,QAAQ,SAAS,IAAI,IAAI,QAAQ,SAAS,IAAI,GAAG,OAAO;AAEjE,SAAO;GACP,CACD,KAAK,IAAI;;AAGd,SAAgB,qBACd,oBACA,qBAIA;CACA,MAAM,aAAa,EAAE,GAAG,oBAAoB;CAC5C,MAAM,gBAA0B,EAAE;AAElC,MAAK,MAAM,CAAC,OAAO,kBAAkB,OAAO,QAAQ,oBAAoB,EAAE;EACxE,MAAM,eAAe,WAAW;AAEhC,MAAI,gBAAgB,wBAAwB,aAAa,EAAE;AACzD,iBAAc,KAAK,MAAM;AACzB;;AAGF,aAAW,SAAS;GAClB,GAAG;GACH,GAAG;GACJ;;AAGH,QAAO;EAAE;EAAY;EAAe;;AAGtC,SAAS,wBAAwB,MAAqC;AACpE,QACE,KAAK,QAAQ,KAAA,KACb,KAAK,UAAU,KAAA,KACf,KAAK,WAAW,KAAA,KAChB,KAAK,QAAQ,KAAA,KACb,KAAK,cAAc,KAAA"}
1
+ {"version":3,"file":"nitro-route-rules.js","names":[],"sources":["../../src/build/nitro-route-rules.ts"],"sourcesContent":["import { appRouter, type AppRoute } from \"../routing/app-router.js\";\nimport { apiRouter, pagesRouter, type Route } from \"../routing/pages-router.js\";\nimport { buildReportRows, type RouteRow } from \"./report.js\";\n\n// Mirrors Nitro's NitroRouteConfig — hand-rolled because nitropack is not a direct dependency.\nexport type NitroRouteRuleConfig = Record<string, unknown> & {\n swr?: boolean | number;\n cache?: unknown;\n static?: boolean;\n isr?: boolean | number;\n prerender?: boolean;\n};\n\ntype NitroRouteRules = Record<string, { swr: number }>;\n\n/**\n * Scans the filesystem for route files and generates Nitro `routeRules` for ISR routes.\n *\n * Note: this duplicates the filesystem scanning that `printBuildReport` also performs.\n * The `nitro.setup` hook runs during Nitro initialization (before the build), while\n * `printBuildReport` runs after the build, so sharing results is non-trivial. This is\n * a future optimization target.\n *\n * Unlike `printBuildReport`, this path does not receive `prerenderResult`, so routes\n * classified as `unknown` by static analysis (which `printBuildReport` might upgrade\n * to `static` via speculative prerender) are skipped here.\n */\nexport async function collectNitroRouteRules(options: {\n appDir?: string | null;\n pagesDir?: string | null;\n pageExtensions: string[];\n}): Promise<NitroRouteRules> {\n const { appDir, pageExtensions, pagesDir } = options;\n\n let appRoutes: AppRoute[] = [];\n let pageRoutes: Route[] = [];\n let apiRoutes: Route[] = [];\n\n if (appDir) {\n appRoutes = await appRouter(appDir, pageExtensions);\n }\n\n if (pagesDir) {\n const [pages, apis] = await Promise.all([\n pagesRouter(pagesDir, pageExtensions),\n apiRouter(pagesDir, pageExtensions),\n ]);\n pageRoutes = pages;\n apiRoutes = apis;\n }\n\n return generateNitroRouteRules(buildReportRows({ appRoutes, pageRoutes, apiRoutes }));\n}\n\nexport function generateNitroRouteRules(rows: RouteRow[]): NitroRouteRules {\n const rules: NitroRouteRules = {};\n\n for (const row of rows) {\n if (\n row.type === \"isr\" &&\n typeof row.revalidate === \"number\" &&\n Number.isFinite(row.revalidate) &&\n row.revalidate > 0\n ) {\n rules[convertToNitroPattern(row.pattern)] = { swr: row.revalidate };\n }\n }\n\n return rules;\n}\n\n/**\n * Converts vinext's internal `:param` route syntax to Nitro's rou3\n * pattern format. Nitro uses `rou3` for routeRules matching, which\n * supports `*` (single-segment) and `**` (multi-segment) wildcards.\n *\n * /blog/:slug -> /blog/* (single segment)\n * /docs/:slug+ -> /docs/** (one or more segments — catch-all)\n * /docs/:slug* -> /docs/** (zero or more segments — optional catch-all)\n * /about -> /about (unchanged)\n * /:a/:b produces `/*`/`/*` (consecutive single-segment params)\n */\nexport function convertToNitroPattern(pattern: string): string {\n return pattern\n .split(\"/\")\n .map((segment) => {\n if (segment.startsWith(\":\")) {\n // Catch-all (:param+) and optional catch-all (:param*) match multiple segments → **\n // Single dynamic param (:param) matches one segment → *\n return segment.endsWith(\"+\") || segment.endsWith(\"*\") ? \"**\" : \"*\";\n }\n return segment;\n })\n .join(\"/\");\n}\n\nexport function mergeNitroRouteRules(\n existingRouteRules: Record<string, NitroRouteRuleConfig> | undefined,\n generatedRouteRules: NitroRouteRules,\n): {\n routeRules: Record<string, NitroRouteRuleConfig>;\n skippedRoutes: string[];\n} {\n const routeRules = { ...existingRouteRules };\n const skippedRoutes: string[] = [];\n\n for (const [route, generatedRule] of Object.entries(generatedRouteRules)) {\n const existingRule = routeRules[route];\n\n if (existingRule && hasUserDefinedCacheRule(existingRule)) {\n skippedRoutes.push(route);\n continue;\n }\n\n routeRules[route] = {\n ...existingRule,\n ...generatedRule,\n };\n }\n\n return { routeRules, skippedRoutes };\n}\n\nfunction hasUserDefinedCacheRule(rule: NitroRouteRuleConfig): boolean {\n return (\n rule.swr !== undefined ||\n rule.cache !== undefined ||\n rule.static !== undefined ||\n rule.isr !== undefined ||\n rule.prerender !== undefined\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA2BA,eAAsB,uBAAuB,SAIhB;CAC3B,MAAM,EAAE,QAAQ,gBAAgB,aAAa;CAE7C,IAAI,YAAwB,EAAE;CAC9B,IAAI,aAAsB,EAAE;CAC5B,IAAI,YAAqB,EAAE;CAE3B,IAAI,QACF,YAAY,MAAM,UAAU,QAAQ,eAAe;CAGrD,IAAI,UAAU;EACZ,MAAM,CAAC,OAAO,QAAQ,MAAM,QAAQ,IAAI,CACtC,YAAY,UAAU,eAAe,EACrC,UAAU,UAAU,eAAe,CACpC,CAAC;EACF,aAAa;EACb,YAAY;;CAGd,OAAO,wBAAwB,gBAAgB;EAAE;EAAW;EAAY;EAAW,CAAC,CAAC;;AAGvF,SAAgB,wBAAwB,MAAmC;CACzE,MAAM,QAAyB,EAAE;CAEjC,KAAK,MAAM,OAAO,MAChB,IACE,IAAI,SAAS,SACb,OAAO,IAAI,eAAe,YAC1B,OAAO,SAAS,IAAI,WAAW,IAC/B,IAAI,aAAa,GAEjB,MAAM,sBAAsB,IAAI,QAAQ,IAAI,EAAE,KAAK,IAAI,YAAY;CAIvE,OAAO;;;;;;;;;;;;;AAcT,SAAgB,sBAAsB,SAAyB;CAC7D,OAAO,QACJ,MAAM,IAAI,CACV,KAAK,YAAY;EAChB,IAAI,QAAQ,WAAW,IAAI,EAGzB,OAAO,QAAQ,SAAS,IAAI,IAAI,QAAQ,SAAS,IAAI,GAAG,OAAO;EAEjE,OAAO;GACP,CACD,KAAK,IAAI;;AAGd,SAAgB,qBACd,oBACA,qBAIA;CACA,MAAM,aAAa,EAAE,GAAG,oBAAoB;CAC5C,MAAM,gBAA0B,EAAE;CAElC,KAAK,MAAM,CAAC,OAAO,kBAAkB,OAAO,QAAQ,oBAAoB,EAAE;EACxE,MAAM,eAAe,WAAW;EAEhC,IAAI,gBAAgB,wBAAwB,aAAa,EAAE;GACzD,cAAc,KAAK,MAAM;GACzB;;EAGF,WAAW,SAAS;GAClB,GAAG;GACH,GAAG;GACJ;;CAGH,OAAO;EAAE;EAAY;EAAe;;AAGtC,SAAS,wBAAwB,MAAqC;CACpE,OACE,KAAK,QAAQ,KAAA,KACb,KAAK,UAAU,KAAA,KACf,KAAK,WAAW,KAAA,KAChB,KAAK,QAAQ,KAAA,KACb,KAAK,cAAc,KAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"precompress.js","names":[],"sources":["../../src/build/precompress.ts"],"sourcesContent":["/**\n * Build-time precompression for hashed static assets.\n *\n * Generates .br (brotli q5), .gz (gzip l8), and .zst (zstd l8) files\n * alongside compressible assets in dist/client/assets/. Served directly by\n * the production server — no per-request compression needed for immutable\n * build output.\n *\n * Only targets assets/ (hashed, immutable) — public directory files use\n * on-the-fly compression since they may change between deploys.\n */\nimport fsp from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport zlib from \"node:zlib\";\nimport { promisify } from \"node:util\";\n\nconst brotliCompress = promisify(zlib.brotliCompress);\nconst gzip = promisify(zlib.gzip);\nconst zstdCompress = typeof zlib.zstdCompress === \"function\" ? promisify(zlib.zstdCompress) : null;\n\n/** File extensions worth compressing (text-based, not already compressed). */\nconst COMPRESSIBLE_EXTENSIONS = new Set([\n \".js\",\n \".mjs\",\n \".css\",\n \".html\",\n \".json\",\n \".xml\",\n \".svg\",\n \".txt\",\n \".map\",\n \".wasm\",\n]);\n\n/** Below this size, compression overhead exceeds savings. */\nconst MIN_SIZE = 1024;\n\n/**\n * Past ~8 parallel files, mixed-size asset sets spend more time queueing zlib\n * work than making forward progress. Keep the batch size bounded even on\n * higher-core machines.\n */\nconst CONCURRENCY = Math.min(os.availableParallelism(), 8);\n\ntype PrecompressResult = {\n filesCompressed: number;\n totalOriginalBytes: number;\n /** Sum of brotli-compressed sizes (used for compression ratio reporting). */\n totalBrotliBytes: number;\n};\n\n/**\n * Walk a directory recursively, yielding relative paths for regular files.\n */\nasync function* walkFiles(dir: string, base: string = dir): AsyncGenerator<string> {\n let entries;\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return; // directory doesn't exist\n }\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n yield* walkFiles(fullPath, base);\n } else if (entry.isFile()) {\n yield path.relative(base, fullPath);\n }\n }\n}\n\n/**\n * Precompress all compressible hashed assets under `clientDir/assets/`.\n *\n * Writes `.br`, `.gz`, and `.zst` files alongside each original.\n * Safe to re-run — overwrites existing compressed variants with identical\n * output, and never compresses `.br`, `.gz`, or `.zst` files themselves.\n */\nexport async function precompressAssets(\n clientDir: string,\n onProgress?: (completed: number, total: number, file: string) => void,\n): Promise<PrecompressResult> {\n const assetsDir = path.join(clientDir, \"assets\");\n const result: PrecompressResult = {\n filesCompressed: 0,\n totalOriginalBytes: 0,\n totalBrotliBytes: 0,\n };\n\n // Collect compressible file paths, then read + compress in bounded chunks\n // to keep peak memory at O(CONCURRENCY * max_file_size) instead of\n // O(total_assets).\n const filePaths: string[] = [];\n\n for await (const relativePath of walkFiles(assetsDir)) {\n const ext = path.extname(relativePath).toLowerCase();\n\n if (!COMPRESSIBLE_EXTENSIONS.has(ext)) continue;\n // .br/.gz/.zst are intentionally absent from COMPRESSIBLE_EXTENSIONS, so\n // precompressed variants generated by a previous run are never re-compressed.\n\n filePaths.push(path.join(assetsDir, relativePath));\n }\n\n let processed = 0;\n for (let i = 0; i < filePaths.length; i += CONCURRENCY) {\n const chunk = filePaths.slice(i, i + CONCURRENCY);\n await Promise.all(\n chunk.map(async (fullPath) => {\n const content = await fsp.readFile(fullPath);\n // readFile already done before this check — stat()-first would save\n // the read for tiny files but costs an extra syscall per file;\n // sub-1KB hashed assets are rare enough that read-first is cheaper.\n if (content.length < MIN_SIZE) return;\n\n // Compress all variants concurrently within each file\n const compressions: Promise<Buffer>[] = [\n brotliCompress(content, {\n params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 5 },\n }),\n gzip(content, { level: 8 }),\n ];\n if (zstdCompress) {\n compressions.push(\n zstdCompress(content, {\n params: { [zlib.constants.ZSTD_c_compressionLevel]: 8 },\n }),\n );\n }\n\n const results = await Promise.all(compressions);\n const [brContent, gzContent, zstdContent] = results;\n\n const writes = [\n fsp.writeFile(fullPath + \".br\", brContent),\n fsp.writeFile(fullPath + \".gz\", gzContent),\n ];\n if (zstdContent) {\n writes.push(fsp.writeFile(fullPath + \".zst\", zstdContent));\n }\n await Promise.all(writes);\n\n // Increment counters only after all writes succeed, so partial\n // failures (e.g. ENOSPC mid-write) don't inflate the reported totals.\n result.filesCompressed++;\n result.totalOriginalBytes += content.length;\n result.totalBrotliBytes += brContent.length;\n }),\n );\n // Report progress once per chunk to avoid non-deterministic ordering\n // within Promise.all (smaller files complete before larger ones).\n // Progress tracks all files (including skipped ones below MIN_SIZE),\n // which differs from filesCompressed (only files actually compressed).\n processed += chunk.length;\n onProgress?.(processed, filePaths.length, path.basename(chunk[chunk.length - 1]));\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,MAAM,iBAAiB,UAAU,KAAK,eAAe;AACrD,MAAM,OAAO,UAAU,KAAK,KAAK;AACjC,MAAM,eAAe,OAAO,KAAK,iBAAiB,aAAa,UAAU,KAAK,aAAa,GAAG;;AAG9F,MAAM,0BAA0B,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,MAAM,WAAW;;;;;;AAOjB,MAAM,cAAc,KAAK,IAAI,GAAG,sBAAsB,EAAE,EAAE;;;;AAY1D,gBAAgB,UAAU,KAAa,OAAe,KAA6B;CACjF,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,IAAI,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;SACnD;AACN;;AAEF,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;AAC3C,MAAI,MAAM,aAAa,CACrB,QAAO,UAAU,UAAU,KAAK;WACvB,MAAM,QAAQ,CACvB,OAAM,KAAK,SAAS,MAAM,SAAS;;;;;;;;;;AAYzC,eAAsB,kBACpB,WACA,YAC4B;CAC5B,MAAM,YAAY,KAAK,KAAK,WAAW,SAAS;CAChD,MAAM,SAA4B;EAChC,iBAAiB;EACjB,oBAAoB;EACpB,kBAAkB;EACnB;CAKD,MAAM,YAAsB,EAAE;AAE9B,YAAW,MAAM,gBAAgB,UAAU,UAAU,EAAE;EACrD,MAAM,MAAM,KAAK,QAAQ,aAAa,CAAC,aAAa;AAEpD,MAAI,CAAC,wBAAwB,IAAI,IAAI,CAAE;AAIvC,YAAU,KAAK,KAAK,KAAK,WAAW,aAAa,CAAC;;CAGpD,IAAI,YAAY;AAChB,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,aAAa;EACtD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,YAAY;AACjD,QAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,aAAa;GAC5B,MAAM,UAAU,MAAM,IAAI,SAAS,SAAS;AAI5C,OAAI,QAAQ,SAAS,SAAU;GAG/B,MAAM,eAAkC,CACtC,eAAe,SAAS,EACtB,QAAQ,GAAG,KAAK,UAAU,uBAAuB,GAAG,EACrD,CAAC,EACF,KAAK,SAAS,EAAE,OAAO,GAAG,CAAC,CAC5B;AACD,OAAI,aACF,cAAa,KACX,aAAa,SAAS,EACpB,QAAQ,GAAG,KAAK,UAAU,0BAA0B,GAAG,EACxD,CAAC,CACH;GAIH,MAAM,CAAC,WAAW,WAAW,eADb,MAAM,QAAQ,IAAI,aAAa;GAG/C,MAAM,SAAS,CACb,IAAI,UAAU,WAAW,OAAO,UAAU,EAC1C,IAAI,UAAU,WAAW,OAAO,UAAU,CAC3C;AACD,OAAI,YACF,QAAO,KAAK,IAAI,UAAU,WAAW,QAAQ,YAAY,CAAC;AAE5D,SAAM,QAAQ,IAAI,OAAO;AAIzB,UAAO;AACP,UAAO,sBAAsB,QAAQ;AACrC,UAAO,oBAAoB,UAAU;IACrC,CACH;AAKD,eAAa,MAAM;AACnB,eAAa,WAAW,UAAU,QAAQ,KAAK,SAAS,MAAM,MAAM,SAAS,GAAG,CAAC;;AAGnF,QAAO"}
1
+ {"version":3,"file":"precompress.js","names":[],"sources":["../../src/build/precompress.ts"],"sourcesContent":["/**\n * Build-time precompression for hashed static assets.\n *\n * Generates .br (brotli q5), .gz (gzip l8), and .zst (zstd l8) files\n * alongside compressible assets in dist/client/assets/. Served directly by\n * the production server — no per-request compression needed for immutable\n * build output.\n *\n * Only targets assets/ (hashed, immutable) — public directory files use\n * on-the-fly compression since they may change between deploys.\n */\nimport fsp from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport zlib from \"node:zlib\";\nimport { promisify } from \"node:util\";\n\nconst brotliCompress = promisify(zlib.brotliCompress);\nconst gzip = promisify(zlib.gzip);\nconst zstdCompress = typeof zlib.zstdCompress === \"function\" ? promisify(zlib.zstdCompress) : null;\n\n/** File extensions worth compressing (text-based, not already compressed). */\nconst COMPRESSIBLE_EXTENSIONS = new Set([\n \".js\",\n \".mjs\",\n \".css\",\n \".html\",\n \".json\",\n \".xml\",\n \".svg\",\n \".txt\",\n \".map\",\n \".wasm\",\n]);\n\n/** Below this size, compression overhead exceeds savings. */\nconst MIN_SIZE = 1024;\n\n/**\n * Past ~8 parallel files, mixed-size asset sets spend more time queueing zlib\n * work than making forward progress. Keep the batch size bounded even on\n * higher-core machines.\n */\nconst CONCURRENCY = Math.min(os.availableParallelism(), 8);\n\ntype PrecompressResult = {\n filesCompressed: number;\n totalOriginalBytes: number;\n /** Sum of brotli-compressed sizes (used for compression ratio reporting). */\n totalBrotliBytes: number;\n};\n\n/**\n * Walk a directory recursively, yielding relative paths for regular files.\n */\nasync function* walkFiles(dir: string, base: string = dir): AsyncGenerator<string> {\n let entries;\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return; // directory doesn't exist\n }\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n yield* walkFiles(fullPath, base);\n } else if (entry.isFile()) {\n yield path.relative(base, fullPath);\n }\n }\n}\n\n/**\n * Precompress all compressible hashed assets under `clientDir/assets/`.\n *\n * Writes `.br`, `.gz`, and `.zst` files alongside each original.\n * Safe to re-run — overwrites existing compressed variants with identical\n * output, and never compresses `.br`, `.gz`, or `.zst` files themselves.\n */\nexport async function precompressAssets(\n clientDir: string,\n onProgress?: (completed: number, total: number, file: string) => void,\n): Promise<PrecompressResult> {\n const assetsDir = path.join(clientDir, \"assets\");\n const result: PrecompressResult = {\n filesCompressed: 0,\n totalOriginalBytes: 0,\n totalBrotliBytes: 0,\n };\n\n // Collect compressible file paths, then read + compress in bounded chunks\n // to keep peak memory at O(CONCURRENCY * max_file_size) instead of\n // O(total_assets).\n const filePaths: string[] = [];\n\n for await (const relativePath of walkFiles(assetsDir)) {\n const ext = path.extname(relativePath).toLowerCase();\n\n if (!COMPRESSIBLE_EXTENSIONS.has(ext)) continue;\n // .br/.gz/.zst are intentionally absent from COMPRESSIBLE_EXTENSIONS, so\n // precompressed variants generated by a previous run are never re-compressed.\n\n filePaths.push(path.join(assetsDir, relativePath));\n }\n\n let processed = 0;\n for (let i = 0; i < filePaths.length; i += CONCURRENCY) {\n const chunk = filePaths.slice(i, i + CONCURRENCY);\n await Promise.all(\n chunk.map(async (fullPath) => {\n const content = await fsp.readFile(fullPath);\n // readFile already done before this check — stat()-first would save\n // the read for tiny files but costs an extra syscall per file;\n // sub-1KB hashed assets are rare enough that read-first is cheaper.\n if (content.length < MIN_SIZE) return;\n\n // Compress all variants concurrently within each file\n const compressions: Promise<Buffer>[] = [\n brotliCompress(content, {\n params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 5 },\n }),\n gzip(content, { level: 8 }),\n ];\n if (zstdCompress) {\n compressions.push(\n zstdCompress(content, {\n params: { [zlib.constants.ZSTD_c_compressionLevel]: 8 },\n }),\n );\n }\n\n const results = await Promise.all(compressions);\n const [brContent, gzContent, zstdContent] = results;\n\n const writes = [\n fsp.writeFile(fullPath + \".br\", brContent),\n fsp.writeFile(fullPath + \".gz\", gzContent),\n ];\n if (zstdContent) {\n writes.push(fsp.writeFile(fullPath + \".zst\", zstdContent));\n }\n await Promise.all(writes);\n\n // Increment counters only after all writes succeed, so partial\n // failures (e.g. ENOSPC mid-write) don't inflate the reported totals.\n result.filesCompressed++;\n result.totalOriginalBytes += content.length;\n result.totalBrotliBytes += brContent.length;\n }),\n );\n // Report progress once per chunk to avoid non-deterministic ordering\n // within Promise.all (smaller files complete before larger ones).\n // Progress tracks all files (including skipped ones below MIN_SIZE),\n // which differs from filesCompressed (only files actually compressed).\n processed += chunk.length;\n onProgress?.(processed, filePaths.length, path.basename(chunk[chunk.length - 1]));\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,MAAM,iBAAiB,UAAU,KAAK,eAAe;AACrD,MAAM,OAAO,UAAU,KAAK,KAAK;AACjC,MAAM,eAAe,OAAO,KAAK,iBAAiB,aAAa,UAAU,KAAK,aAAa,GAAG;;AAG9F,MAAM,0BAA0B,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,MAAM,WAAW;;;;;;AAOjB,MAAM,cAAc,KAAK,IAAI,GAAG,sBAAsB,EAAE,EAAE;;;;AAY1D,gBAAgB,UAAU,KAAa,OAAe,KAA6B;CACjF,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,IAAI,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;SACnD;EACN;;CAEF,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;EAC3C,IAAI,MAAM,aAAa,EACrB,OAAO,UAAU,UAAU,KAAK;OAC3B,IAAI,MAAM,QAAQ,EACvB,MAAM,KAAK,SAAS,MAAM,SAAS;;;;;;;;;;AAYzC,eAAsB,kBACpB,WACA,YAC4B;CAC5B,MAAM,YAAY,KAAK,KAAK,WAAW,SAAS;CAChD,MAAM,SAA4B;EAChC,iBAAiB;EACjB,oBAAoB;EACpB,kBAAkB;EACnB;CAKD,MAAM,YAAsB,EAAE;CAE9B,WAAW,MAAM,gBAAgB,UAAU,UAAU,EAAE;EACrD,MAAM,MAAM,KAAK,QAAQ,aAAa,CAAC,aAAa;EAEpD,IAAI,CAAC,wBAAwB,IAAI,IAAI,EAAE;EAIvC,UAAU,KAAK,KAAK,KAAK,WAAW,aAAa,CAAC;;CAGpD,IAAI,YAAY;CAChB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,aAAa;EACtD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,YAAY;EACjD,MAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,aAAa;GAC5B,MAAM,UAAU,MAAM,IAAI,SAAS,SAAS;GAI5C,IAAI,QAAQ,SAAS,UAAU;GAG/B,MAAM,eAAkC,CACtC,eAAe,SAAS,EACtB,QAAQ,GAAG,KAAK,UAAU,uBAAuB,GAAG,EACrD,CAAC,EACF,KAAK,SAAS,EAAE,OAAO,GAAG,CAAC,CAC5B;GACD,IAAI,cACF,aAAa,KACX,aAAa,SAAS,EACpB,QAAQ,GAAG,KAAK,UAAU,0BAA0B,GAAG,EACxD,CAAC,CACH;GAIH,MAAM,CAAC,WAAW,WAAW,eAAe,MADtB,QAAQ,IAAI,aAAa;GAG/C,MAAM,SAAS,CACb,IAAI,UAAU,WAAW,OAAO,UAAU,EAC1C,IAAI,UAAU,WAAW,OAAO,UAAU,CAC3C;GACD,IAAI,aACF,OAAO,KAAK,IAAI,UAAU,WAAW,QAAQ,YAAY,CAAC;GAE5D,MAAM,QAAQ,IAAI,OAAO;GAIzB,OAAO;GACP,OAAO,sBAAsB,QAAQ;GACrC,OAAO,oBAAoB,UAAU;IACrC,CACH;EAKD,aAAa,MAAM;EACnB,aAAa,WAAW,UAAU,QAAQ,KAAK,SAAS,MAAM,MAAM,SAAS,GAAG,CAAC;;CAGnF,OAAO"}
@@ -107,6 +107,22 @@ type PrerenderAppOptionsInternal = PrerenderAppOptions & {
107
107
  port: number;
108
108
  };
109
109
  };
110
+ /**
111
+ * Reconstruct the RSC payload from a prerender HTML response by parsing the
112
+ * inline bootstrap chunk scripts emitted by createRscEmbedTransform.
113
+ *
114
+ * Returns null when the HTML contains no chunk scripts at all — the caller
115
+ * should fall back to a second handler invocation. This is reachable when
116
+ * middleware short-circuits the App Router pipeline with a custom 200 HTML
117
+ * response that never went through createRscEmbedTransform.
118
+ *
119
+ * Throws on partial or malformed embeds (chunks present but no done marker,
120
+ * tampered chunk JSON, etc.) — those are real vinext-internal regressions.
121
+ *
122
+ * Safe regex usage: safeJsonStringify (used by createRscEmbedTransform) escapes
123
+ * all '<' and '>' in the embedded JSON, preventing false </script> matches.
124
+ */
125
+ declare function extractRscPayloadFromPrerenderedHtml(html: string): string | null;
110
126
  /**
111
127
  * Determine the HTML output file path for a URL.
112
128
  * Respects trailingSlash config.
@@ -189,5 +205,5 @@ declare function writePrerenderIndex(routes: PrerenderRouteResult[], outDir: str
189
205
  trailingSlash?: boolean;
190
206
  }): void;
191
207
  //#endregion
192
- export { PrerenderResult, PrerenderRouteResult, StaticParamsMap, getOutputPath, getRscOutputPath, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
208
+ export { PrerenderResult, PrerenderRouteResult, StaticParamsMap, extractRscPayloadFromPrerenderedHtml, getOutputPath, getRscOutputPath, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
193
209
  //# sourceMappingURL=prerender.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
2
+ import { VINEXT_PRERENDER_SECRET_HEADER } from "../server/headers.js";
2
3
  import { headersContextFromRequest, runWithHeadersContext } from "../shims/headers.js";
3
4
  import { NoOpCacheHandler, _consumeRequestScopedCacheLife, getCacheHandler, setCacheHandler } from "../shims/cache.js";
4
5
  import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
@@ -31,6 +32,61 @@ function getErrorMessageWithStack(err) {
31
32
  /** Sentinel path used to trigger 404 rendering without a real route match. */
32
33
  const NOT_FOUND_SENTINEL_PATH = "/__vinext_nonexistent_for_404__";
33
34
  const DEFAULT_CONCURRENCY = Math.min(os.availableParallelism(), 8);
35
+ const RSC_CHUNK_SCRIPT_PREFIX = "self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];";
36
+ const RSC_DONE_MARKER = "__VINEXT_RSC_DONE__=true";
37
+ const RSC_CHUNK_FULL_PREFIX = `${RSC_CHUNK_SCRIPT_PREFIX}self.__VINEXT_RSC_CHUNKS__.push(`;
38
+ /**
39
+ * Reconstruct the RSC payload from a prerender HTML response by parsing the
40
+ * inline bootstrap chunk scripts emitted by createRscEmbedTransform.
41
+ *
42
+ * Returns null when the HTML contains no chunk scripts at all — the caller
43
+ * should fall back to a second handler invocation. This is reachable when
44
+ * middleware short-circuits the App Router pipeline with a custom 200 HTML
45
+ * response that never went through createRscEmbedTransform.
46
+ *
47
+ * Throws on partial or malformed embeds (chunks present but no done marker,
48
+ * tampered chunk JSON, etc.) — those are real vinext-internal regressions.
49
+ *
50
+ * Safe regex usage: safeJsonStringify (used by createRscEmbedTransform) escapes
51
+ * all '<' and '>' in the embedded JSON, preventing false <\/script> matches.
52
+ */
53
+ function extractRscPayloadFromPrerenderedHtml(html) {
54
+ const scriptPattern = /<script(?:\s[^>]*)?>([\s\S]*?)<\/script>/gi;
55
+ const chunks = [];
56
+ let sawDone = false;
57
+ let match;
58
+ while ((match = scriptPattern.exec(html)) !== null) {
59
+ const script = (match[1] ?? "").trim().replace(/;$/, "");
60
+ if (script === `self.${RSC_DONE_MARKER}`) {
61
+ sawDone = true;
62
+ continue;
63
+ }
64
+ if (script.startsWith(RSC_CHUNK_SCRIPT_PREFIX)) chunks.push(parseRscChunkPushArgument(script));
65
+ }
66
+ if (chunks.length === 0 && !sawDone) return null;
67
+ if (chunks.length === 0) throw new Error("[vinext] Malformed prerender RSC embed: done marker present without chunk scripts");
68
+ if (!sawDone) throw new Error("[vinext] Malformed prerender RSC embed: missing __VINEXT_RSC_DONE__ marker");
69
+ return chunks.join("");
70
+ }
71
+ /**
72
+ * Parse the JSON-string argument of a single chunk-push script. The script
73
+ * shape is exactly `<prefix>(<safeJsonStringify(chunk)>)` because the writer
74
+ * concatenates those literals — so the body always starts with the full
75
+ * prefix and ends with `)`. JSON.parse on the slice catches any tampering or
76
+ * trailing code.
77
+ */
78
+ function parseRscChunkPushArgument(script) {
79
+ if (!script.startsWith(RSC_CHUNK_FULL_PREFIX) || !script.endsWith(")")) throw new Error("[vinext] Malformed prerender RSC embed: unexpected chunk script shape");
80
+ const jsonSource = script.slice(RSC_CHUNK_FULL_PREFIX.length, -1);
81
+ let parsed;
82
+ try {
83
+ parsed = JSON.parse(jsonSource);
84
+ } catch {
85
+ throw new Error("[vinext] Malformed prerender RSC embed: invalid chunk JSON");
86
+ }
87
+ if (typeof parsed !== "string") throw new Error("[vinext] Malformed prerender RSC embed: chunk payload is not a string");
88
+ return parsed;
89
+ }
34
90
  /**
35
91
  * Run an array of async tasks with bounded concurrency.
36
92
  * Results are returned in the same order as `items`.
@@ -167,7 +223,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
167
223
  ownedProdServerHandle = srv;
168
224
  return srv;
169
225
  })()).port}`;
170
- const secretHeaders = prerenderSecret ? { "x-vinext-prerender-secret": prerenderSecret } : {};
226
+ const secretHeaders = prerenderSecret ? { [VINEXT_PRERENDER_SECRET_HEADER]: prerenderSecret } : {};
171
227
  const renderPage = (urlPath) => fetch(`${baseUrl}${urlPath}`, {
172
228
  headers: secretHeaders,
173
229
  redirect: "manual"
@@ -379,7 +435,7 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
379
435
  ownedProdServerHandle = srv;
380
436
  return srv;
381
437
  })()).port}`;
382
- const secretHeaders = prerenderSecret ? { "x-vinext-prerender-secret": prerenderSecret } : {};
438
+ const secretHeaders = prerenderSecret ? { [VINEXT_PRERENDER_SECRET_HEADER]: prerenderSecret } : {};
383
439
  rscHandler = (req) => {
384
440
  const parsed = new URL(req.url);
385
441
  const url = `${baseUrl}${parsed.pathname}${parsed.search}`;
@@ -595,25 +651,30 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
595
651
  error: "RSC handler returned no prerender HTML"
596
652
  };
597
653
  const html = htmlRender.html;
598
- const rscRequest = new Request(`http://localhost${urlPath}`, { headers: {
599
- Accept: "text/x-component",
600
- RSC: "1"
601
- } });
602
- const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
603
- const rscData = rscRes.ok ? await rscRes.text() : null;
654
+ let rscData = extractRscPayloadFromPrerenderedHtml(html);
655
+ if (rscData === null) {
656
+ const rscRequest = new Request(`http://localhost${urlPath}`, { headers: {
657
+ Accept: "text/x-component",
658
+ RSC: "1"
659
+ } });
660
+ const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
661
+ if (!rscRes.ok) {
662
+ await rscRes.body?.cancel();
663
+ throw new Error(`[vinext] prerenderApp: RSC fallback returned ${rscRes.status} for ${urlPath}`);
664
+ }
665
+ rscData = await rscRes.text();
666
+ }
604
667
  const outputFiles = [];
605
668
  const htmlOutputPath = getOutputPath(urlPath, config.trailingSlash);
606
669
  const htmlFullPath = path.join(outDir, htmlOutputPath);
607
670
  fs.mkdirSync(path.dirname(htmlFullPath), { recursive: true });
608
671
  fs.writeFileSync(htmlFullPath, html, "utf-8");
609
672
  outputFiles.push(htmlOutputPath);
610
- if (rscData !== null) {
611
- const rscOutputPath = getRscOutputPath(urlPath);
612
- const rscFullPath = path.join(outDir, rscOutputPath);
613
- fs.mkdirSync(path.dirname(rscFullPath), { recursive: true });
614
- fs.writeFileSync(rscFullPath, rscData, "utf-8");
615
- outputFiles.push(rscOutputPath);
616
- }
673
+ const rscOutputPath = getRscOutputPath(urlPath);
674
+ const rscFullPath = path.join(outDir, rscOutputPath);
675
+ fs.mkdirSync(path.dirname(rscFullPath), { recursive: true });
676
+ fs.writeFileSync(rscFullPath, rscData, "utf-8");
677
+ outputFiles.push(rscOutputPath);
617
678
  const renderedCacheControl = resolveRenderedCacheControl(htmlRender.requestCacheLife ?? {}, htmlCacheControl, config.expireTime);
618
679
  const renderedRevalidate = typeof revalidate === "number" ? renderedCacheControl.revalidate === void 0 ? revalidate : Math.min(revalidate, renderedCacheControl.revalidate) : renderedCacheControl.revalidate ?? revalidate;
619
680
  return {
@@ -755,6 +816,6 @@ function writePrerenderIndex(routes, outDir, options) {
755
816
  fs.writeFileSync(path.join(outDir, "vinext-prerender.json"), JSON.stringify(index, null, 2), "utf-8");
756
817
  }
757
818
  //#endregion
758
- export { getOutputPath, getRscOutputPath, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
819
+ export { extractRscPayloadFromPrerenderedHtml, getOutputPath, getRscOutputPath, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
759
820
 
760
821
  //# sourceMappingURL=prerender.js.map