vinext 0.0.51 → 0.0.53

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 (423) hide show
  1. package/README.md +1 -1
  2. package/dist/build/clean-output.d.ts +14 -0
  3. package/dist/build/clean-output.js +36 -0
  4. package/dist/build/clean-output.js.map +1 -0
  5. package/dist/build/precompress.d.ts +7 -7
  6. package/dist/build/precompress.js +18 -17
  7. package/dist/build/precompress.js.map +1 -1
  8. package/dist/build/prerender.d.ts +9 -16
  9. package/dist/build/prerender.js +88 -50
  10. package/dist/build/prerender.js.map +1 -1
  11. package/dist/build/run-prerender.js +10 -1
  12. package/dist/build/run-prerender.js.map +1 -1
  13. package/dist/build/static-export.d.ts +5 -0
  14. package/dist/build/static-export.js +8 -3
  15. package/dist/build/static-export.js.map +1 -1
  16. package/dist/check.js +4 -0
  17. package/dist/check.js.map +1 -1
  18. package/dist/cli-args.d.ts +1 -0
  19. package/dist/cli-args.js +5 -0
  20. package/dist/cli-args.js.map +1 -1
  21. package/dist/cli.js +58 -4
  22. package/dist/cli.js.map +1 -1
  23. package/dist/client/instrumentation-client-inject.d.ts +34 -0
  24. package/dist/client/instrumentation-client-inject.js +57 -0
  25. package/dist/client/instrumentation-client-inject.js.map +1 -0
  26. package/dist/client/navigation-runtime.d.ts +60 -0
  27. package/dist/client/navigation-runtime.js +171 -0
  28. package/dist/client/navigation-runtime.js.map +1 -0
  29. package/dist/client/pages-router-link-navigation.d.ts +26 -0
  30. package/dist/client/pages-router-link-navigation.js +14 -0
  31. package/dist/client/pages-router-link-navigation.js.map +1 -0
  32. package/dist/client/vinext-next-data.d.ts +14 -3
  33. package/dist/client/vinext-next-data.js +50 -1
  34. package/dist/client/vinext-next-data.js.map +1 -0
  35. package/dist/client/window-next.d.ts +10 -2
  36. package/dist/client/window-next.js.map +1 -1
  37. package/dist/cloudflare/kv-cache-handler.js +2 -1
  38. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  39. package/dist/cloudflare/tpr.js +1 -1
  40. package/dist/cloudflare/tpr.js.map +1 -1
  41. package/dist/config/config-matchers.d.ts +63 -16
  42. package/dist/config/config-matchers.js +145 -9
  43. package/dist/config/config-matchers.js.map +1 -1
  44. package/dist/config/next-config.d.ts +32 -5
  45. package/dist/config/next-config.js +55 -15
  46. package/dist/config/next-config.js.map +1 -1
  47. package/dist/deploy.js +130 -46
  48. package/dist/deploy.js.map +1 -1
  49. package/dist/entries/app-browser-entry.js +9 -3
  50. package/dist/entries/app-browser-entry.js.map +1 -1
  51. package/dist/entries/app-rsc-entry.d.ts +4 -2
  52. package/dist/entries/app-rsc-entry.js +76 -16
  53. package/dist/entries/app-rsc-entry.js.map +1 -1
  54. package/dist/entries/app-rsc-manifest.d.ts +1 -0
  55. package/dist/entries/app-rsc-manifest.js +53 -6
  56. package/dist/entries/app-rsc-manifest.js.map +1 -1
  57. package/dist/entries/app-ssr-entry.d.ts +3 -3
  58. package/dist/entries/app-ssr-entry.js +4 -4
  59. package/dist/entries/app-ssr-entry.js.map +1 -1
  60. package/dist/entries/pages-client-entry.js +40 -3
  61. package/dist/entries/pages-client-entry.js.map +1 -1
  62. package/dist/entries/pages-server-entry.js +261 -31
  63. package/dist/entries/pages-server-entry.js.map +1 -1
  64. package/dist/entries/runtime-entry-module.d.ts +2 -1
  65. package/dist/entries/runtime-entry-module.js +9 -3
  66. package/dist/entries/runtime-entry-module.js.map +1 -1
  67. package/dist/index.js +161 -46
  68. package/dist/index.js.map +1 -1
  69. package/dist/plugins/css-data-url.d.ts +7 -0
  70. package/dist/plugins/css-data-url.js +81 -0
  71. package/dist/plugins/css-data-url.js.map +1 -0
  72. package/dist/plugins/fonts.js +30 -5
  73. package/dist/plugins/fonts.js.map +1 -1
  74. package/dist/plugins/middleware-server-only.d.ts +54 -0
  75. package/dist/plugins/middleware-server-only.js +91 -0
  76. package/dist/plugins/middleware-server-only.js.map +1 -0
  77. package/dist/plugins/optimize-imports.js +4 -4
  78. package/dist/plugins/optimize-imports.js.map +1 -1
  79. package/dist/plugins/strip-server-exports.js +5 -8
  80. package/dist/plugins/strip-server-exports.js.map +1 -1
  81. package/dist/routing/app-route-graph.d.ts +20 -1
  82. package/dist/routing/app-route-graph.js +58 -6
  83. package/dist/routing/app-route-graph.js.map +1 -1
  84. package/dist/routing/app-router.d.ts +2 -2
  85. package/dist/routing/app-router.js +2 -2
  86. package/dist/routing/app-router.js.map +1 -1
  87. package/dist/routing/route-trie.js +13 -18
  88. package/dist/routing/route-trie.js.map +1 -1
  89. package/dist/routing/utils.d.ts +12 -1
  90. package/dist/routing/utils.js +18 -1
  91. package/dist/routing/utils.js.map +1 -1
  92. package/dist/server/api-handler.js +153 -42
  93. package/dist/server/api-handler.js.map +1 -1
  94. package/dist/server/app-browser-action-result.d.ts +16 -1
  95. package/dist/server/app-browser-action-result.js +15 -1
  96. package/dist/server/app-browser-action-result.js.map +1 -1
  97. package/dist/server/app-browser-entry.js +309 -155
  98. package/dist/server/app-browser-entry.js.map +1 -1
  99. package/dist/server/app-browser-interception-context.d.ts +24 -0
  100. package/dist/server/app-browser-interception-context.js +32 -0
  101. package/dist/server/app-browser-interception-context.js.map +1 -0
  102. package/dist/server/app-browser-navigation-controller.d.ts +3 -1
  103. package/dist/server/app-browser-navigation-controller.js +5 -1
  104. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  105. package/dist/server/app-browser-rsc-redirect.d.ts +2 -1
  106. package/dist/server/app-browser-rsc-redirect.js +2 -2
  107. package/dist/server/app-browser-rsc-redirect.js.map +1 -1
  108. package/dist/server/app-browser-state.d.ts +18 -1
  109. package/dist/server/app-browser-state.js +19 -1
  110. package/dist/server/app-browser-state.js.map +1 -1
  111. package/dist/server/app-browser-stream.d.ts +5 -14
  112. package/dist/server/app-browser-stream.js +13 -7
  113. package/dist/server/app-browser-stream.js.map +1 -1
  114. package/dist/server/app-browser-visible-commit.d.ts +2 -1
  115. package/dist/server/app-browser-visible-commit.js +1 -0
  116. package/dist/server/app-browser-visible-commit.js.map +1 -1
  117. package/dist/server/app-elements-wire.d.ts +10 -5
  118. package/dist/server/app-elements-wire.js +84 -2
  119. package/dist/server/app-elements-wire.js.map +1 -1
  120. package/dist/server/app-elements.d.ts +3 -2
  121. package/dist/server/app-elements.js +3 -2
  122. package/dist/server/app-elements.js.map +1 -1
  123. package/dist/server/app-fallback-renderer.d.ts +12 -3
  124. package/dist/server/app-fallback-renderer.js +15 -8
  125. package/dist/server/app-fallback-renderer.js.map +1 -1
  126. package/dist/server/app-history-state.js +6 -2
  127. package/dist/server/app-history-state.js.map +1 -1
  128. package/dist/server/app-interception-context-header.d.ts +33 -0
  129. package/dist/server/app-interception-context-header.js +44 -0
  130. package/dist/server/app-interception-context-header.js.map +1 -0
  131. package/dist/server/app-middleware.d.ts +13 -0
  132. package/dist/server/app-middleware.js +3 -1
  133. package/dist/server/app-middleware.js.map +1 -1
  134. package/dist/server/app-mounted-slots-header.d.ts +19 -0
  135. package/dist/server/app-mounted-slots-header.js +40 -1
  136. package/dist/server/app-mounted-slots-header.js.map +1 -1
  137. package/dist/server/app-optimistic-routing.d.ts +54 -0
  138. package/dist/server/app-optimistic-routing.js +208 -0
  139. package/dist/server/app-optimistic-routing.js.map +1 -0
  140. package/dist/server/app-page-boundary-render.d.ts +1 -0
  141. package/dist/server/app-page-boundary-render.js +2 -0
  142. package/dist/server/app-page-boundary-render.js.map +1 -1
  143. package/dist/server/app-page-boundary.d.ts +1 -0
  144. package/dist/server/app-page-boundary.js +2 -0
  145. package/dist/server/app-page-boundary.js.map +1 -1
  146. package/dist/server/app-page-cache.d.ts +15 -1
  147. package/dist/server/app-page-cache.js +68 -7
  148. package/dist/server/app-page-cache.js.map +1 -1
  149. package/dist/server/app-page-dispatch.d.ts +5 -0
  150. package/dist/server/app-page-dispatch.js +39 -5
  151. package/dist/server/app-page-dispatch.js.map +1 -1
  152. package/dist/server/app-page-element-builder.d.ts +2 -1
  153. package/dist/server/app-page-element-builder.js +7 -3
  154. package/dist/server/app-page-element-builder.js.map +1 -1
  155. package/dist/server/app-page-execution.d.ts +29 -1
  156. package/dist/server/app-page-execution.js +91 -4
  157. package/dist/server/app-page-execution.js.map +1 -1
  158. package/dist/server/app-page-head.d.ts +1 -0
  159. package/dist/server/app-page-head.js +29 -2
  160. package/dist/server/app-page-head.js.map +1 -1
  161. package/dist/server/app-page-probe.js +1 -1
  162. package/dist/server/app-page-render-observation.js +1 -1
  163. package/dist/server/app-page-render.d.ts +3 -0
  164. package/dist/server/app-page-render.js +7 -3
  165. package/dist/server/app-page-render.js.map +1 -1
  166. package/dist/server/app-page-response.d.ts +11 -1
  167. package/dist/server/app-page-response.js +18 -5
  168. package/dist/server/app-page-response.js.map +1 -1
  169. package/dist/server/app-page-route-wiring.d.ts +1 -0
  170. package/dist/server/app-page-route-wiring.js +35 -15
  171. package/dist/server/app-page-route-wiring.js.map +1 -1
  172. package/dist/server/app-page-stream.d.ts +4 -0
  173. package/dist/server/app-page-stream.js +3 -0
  174. package/dist/server/app-page-stream.js.map +1 -1
  175. package/dist/server/app-prerender-static-params.d.ts +2 -1
  176. package/dist/server/app-prerender-static-params.js +44 -8
  177. package/dist/server/app-prerender-static-params.js.map +1 -1
  178. package/dist/server/app-route-handler-cache.d.ts +2 -2
  179. package/dist/server/app-route-handler-cache.js +3 -2
  180. package/dist/server/app-route-handler-cache.js.map +1 -1
  181. package/dist/server/app-route-handler-dispatch.d.ts +7 -1
  182. package/dist/server/app-route-handler-dispatch.js +4 -1
  183. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  184. package/dist/server/app-route-handler-execution.d.ts +18 -2
  185. package/dist/server/app-route-handler-execution.js +1 -0
  186. package/dist/server/app-route-handler-execution.js.map +1 -1
  187. package/dist/server/app-route-handler-response.js +6 -5
  188. package/dist/server/app-route-handler-response.js.map +1 -1
  189. package/dist/server/app-router-entry.js +6 -2
  190. package/dist/server/app-router-entry.js.map +1 -1
  191. package/dist/server/app-rsc-handler.d.ts +11 -1
  192. package/dist/server/app-rsc-handler.js +48 -21
  193. package/dist/server/app-rsc-handler.js.map +1 -1
  194. package/dist/server/app-rsc-render-mode.d.ts +4 -3
  195. package/dist/server/app-rsc-render-mode.js +7 -1
  196. package/dist/server/app-rsc-render-mode.js.map +1 -1
  197. package/dist/server/app-rsc-request-normalization.d.ts +4 -1
  198. package/dist/server/app-rsc-request-normalization.js +6 -2
  199. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  200. package/dist/server/app-rsc-response-finalizer.d.ts +8 -1
  201. package/dist/server/app-rsc-response-finalizer.js +10 -3
  202. package/dist/server/app-rsc-response-finalizer.js.map +1 -1
  203. package/dist/server/app-rsc-route-matching.js +2 -2
  204. package/dist/server/app-rsc-route-matching.js.map +1 -1
  205. package/dist/server/app-segment-config.d.ts +4 -1
  206. package/dist/server/app-segment-config.js +6 -1
  207. package/dist/server/app-segment-config.js.map +1 -1
  208. package/dist/server/app-server-action-execution.d.ts +1 -0
  209. package/dist/server/app-server-action-execution.js +5 -1
  210. package/dist/server/app-server-action-execution.js.map +1 -1
  211. package/dist/server/app-ssr-entry.d.ts +2 -0
  212. package/dist/server/app-ssr-entry.js +92 -55
  213. package/dist/server/app-ssr-entry.js.map +1 -1
  214. package/dist/server/app-ssr-stream.d.ts +30 -2
  215. package/dist/server/app-ssr-stream.js +95 -8
  216. package/dist/server/app-ssr-stream.js.map +1 -1
  217. package/dist/server/app-static-generation.d.ts +1 -0
  218. package/dist/server/app-static-generation.js +2 -1
  219. package/dist/server/app-static-generation.js.map +1 -1
  220. package/dist/server/artifact-compatibility.d.ts +1 -1
  221. package/dist/server/artifact-compatibility.js.map +1 -1
  222. package/dist/server/cache-headers.d.ts +7 -0
  223. package/dist/server/cache-headers.js +19 -0
  224. package/dist/server/cache-headers.js.map +1 -0
  225. package/dist/server/cache-proof.d.ts +49 -3
  226. package/dist/server/cache-proof.js +78 -22
  227. package/dist/server/cache-proof.js.map +1 -1
  228. package/dist/server/client-reuse-manifest.d.ts +99 -0
  229. package/dist/server/client-reuse-manifest.js +212 -0
  230. package/dist/server/client-reuse-manifest.js.map +1 -0
  231. package/dist/server/default-global-error-module.d.ts +20 -0
  232. package/dist/server/default-global-error-module.js +20 -0
  233. package/dist/server/default-global-error-module.js.map +1 -0
  234. package/dist/server/default-not-found-module.d.ts +20 -0
  235. package/dist/server/default-not-found-module.js +20 -0
  236. package/dist/server/default-not-found-module.js.map +1 -0
  237. package/dist/server/dev-server.d.ts +10 -2
  238. package/dist/server/dev-server.js +99 -36
  239. package/dist/server/dev-server.js.map +1 -1
  240. package/dist/server/edge-api-runtime.d.ts +5 -0
  241. package/dist/server/edge-api-runtime.js +8 -0
  242. package/dist/server/edge-api-runtime.js.map +1 -0
  243. package/dist/server/headers.d.ts +22 -1
  244. package/dist/server/headers.js +22 -1
  245. package/dist/server/headers.js.map +1 -1
  246. package/dist/server/http-error-responses.d.ts +16 -1
  247. package/dist/server/http-error-responses.js +21 -1
  248. package/dist/server/http-error-responses.js.map +1 -1
  249. package/dist/server/image-optimization.d.ts +13 -4
  250. package/dist/server/image-optimization.js +15 -4
  251. package/dist/server/image-optimization.js.map +1 -1
  252. package/dist/server/isr-cache.d.ts +6 -2
  253. package/dist/server/isr-cache.js +20 -4
  254. package/dist/server/isr-cache.js.map +1 -1
  255. package/dist/server/middleware-runtime.d.ts +15 -0
  256. package/dist/server/middleware-runtime.js +59 -7
  257. package/dist/server/middleware-runtime.js.map +1 -1
  258. package/dist/server/middleware.d.ts +1 -1
  259. package/dist/server/middleware.js +5 -3
  260. package/dist/server/middleware.js.map +1 -1
  261. package/dist/server/navigation-planner.d.ts +9 -3
  262. package/dist/server/navigation-planner.js +98 -25
  263. package/dist/server/navigation-planner.js.map +1 -1
  264. package/dist/server/navigation-trace.d.ts +2 -1
  265. package/dist/server/navigation-trace.js +1 -0
  266. package/dist/server/navigation-trace.js.map +1 -1
  267. package/dist/server/pages-api-route.d.ts +45 -1
  268. package/dist/server/pages-api-route.js +27 -4
  269. package/dist/server/pages-api-route.js.map +1 -1
  270. package/dist/server/pages-body-parser-config.d.ts +60 -0
  271. package/dist/server/pages-body-parser-config.js +79 -0
  272. package/dist/server/pages-body-parser-config.js.map +1 -0
  273. package/dist/server/pages-data-route.d.ts +77 -0
  274. package/dist/server/pages-data-route.js +98 -0
  275. package/dist/server/pages-data-route.js.map +1 -0
  276. package/dist/server/pages-default-404.d.ts +31 -0
  277. package/dist/server/pages-default-404.js +40 -0
  278. package/dist/server/pages-default-404.js.map +1 -0
  279. package/dist/server/pages-i18n.d.ts +51 -1
  280. package/dist/server/pages-i18n.js +61 -1
  281. package/dist/server/pages-i18n.js.map +1 -1
  282. package/dist/server/pages-node-compat.d.ts +10 -0
  283. package/dist/server/pages-node-compat.js +12 -1
  284. package/dist/server/pages-node-compat.js.map +1 -1
  285. package/dist/server/pages-page-data.d.ts +69 -2
  286. package/dist/server/pages-page-data.js +47 -31
  287. package/dist/server/pages-page-data.js.map +1 -1
  288. package/dist/server/pages-page-response.d.ts +13 -1
  289. package/dist/server/pages-page-response.js +16 -11
  290. package/dist/server/pages-page-response.js.map +1 -1
  291. package/dist/server/prerender-route-params.d.ts +14 -0
  292. package/dist/server/prerender-route-params.js +94 -0
  293. package/dist/server/prerender-route-params.js.map +1 -0
  294. package/dist/server/prod-server.d.ts +15 -37
  295. package/dist/server/prod-server.js +143 -107
  296. package/dist/server/prod-server.js.map +1 -1
  297. package/dist/server/proxy-trust.d.ts +41 -0
  298. package/dist/server/proxy-trust.js +70 -0
  299. package/dist/server/proxy-trust.js.map +1 -0
  300. package/dist/server/request-pipeline.d.ts +13 -4
  301. package/dist/server/request-pipeline.js +32 -14
  302. package/dist/server/request-pipeline.js.map +1 -1
  303. package/dist/server/seed-cache.d.ts +12 -31
  304. package/dist/server/seed-cache.js +30 -37
  305. package/dist/server/seed-cache.js.map +1 -1
  306. package/dist/server/server-action-not-found.js +8 -3
  307. package/dist/server/server-action-not-found.js.map +1 -1
  308. package/dist/server/skip-cache-proof.d.ts +41 -0
  309. package/dist/server/skip-cache-proof.js +101 -0
  310. package/dist/server/skip-cache-proof.js.map +1 -0
  311. package/dist/server/static-file-cache.d.ts +1 -1
  312. package/dist/server/static-file-cache.js +8 -7
  313. package/dist/server/static-file-cache.js.map +1 -1
  314. package/dist/server/streaming-metadata.d.ts +5 -0
  315. package/dist/server/streaming-metadata.js +10 -0
  316. package/dist/server/streaming-metadata.js.map +1 -0
  317. package/dist/shims/app-router-scroll-state.d.ts +12 -0
  318. package/dist/shims/app-router-scroll-state.js +38 -0
  319. package/dist/shims/app-router-scroll-state.js.map +1 -0
  320. package/dist/shims/app-router-scroll.d.ts +14 -0
  321. package/dist/shims/app-router-scroll.js +100 -0
  322. package/dist/shims/app-router-scroll.js.map +1 -0
  323. package/dist/shims/before-interactive-context.d.ts +30 -0
  324. package/dist/shims/before-interactive-context.js +10 -0
  325. package/dist/shims/before-interactive-context.js.map +1 -0
  326. package/dist/shims/cache-runtime.d.ts +1 -1
  327. package/dist/shims/cache-runtime.js +14 -1
  328. package/dist/shims/cache-runtime.js.map +1 -1
  329. package/dist/shims/client-locale.d.ts +15 -0
  330. package/dist/shims/client-locale.js +13 -0
  331. package/dist/shims/client-locale.js.map +1 -0
  332. package/dist/shims/default-global-error.d.ts +32 -0
  333. package/dist/shims/default-global-error.js +181 -0
  334. package/dist/shims/default-global-error.js.map +1 -0
  335. package/dist/shims/default-not-found.d.ts +12 -0
  336. package/dist/shims/default-not-found.js +61 -0
  337. package/dist/shims/default-not-found.js.map +1 -0
  338. package/dist/shims/document.d.ts +59 -3
  339. package/dist/shims/document.js +36 -5
  340. package/dist/shims/document.js.map +1 -1
  341. package/dist/shims/error-boundary.d.ts +2 -2
  342. package/dist/shims/font-local.d.ts +5 -0
  343. package/dist/shims/font-local.js +6 -2
  344. package/dist/shims/font-local.js.map +1 -1
  345. package/dist/shims/form.js +13 -6
  346. package/dist/shims/form.js.map +1 -1
  347. package/dist/shims/head.js +4 -4
  348. package/dist/shims/head.js.map +1 -1
  349. package/dist/shims/headers.d.ts +6 -2
  350. package/dist/shims/headers.js +64 -21
  351. package/dist/shims/headers.js.map +1 -1
  352. package/dist/shims/image.d.ts +1 -1
  353. package/dist/shims/image.js +4 -4
  354. package/dist/shims/image.js.map +1 -1
  355. package/dist/shims/internal/pages-data-target.d.ts +58 -0
  356. package/dist/shims/internal/pages-data-target.js +91 -0
  357. package/dist/shims/internal/pages-data-target.js.map +1 -0
  358. package/dist/shims/internal/pages-data-url.d.ts +42 -0
  359. package/dist/shims/internal/pages-data-url.js +73 -0
  360. package/dist/shims/internal/pages-data-url.js.map +1 -0
  361. package/dist/shims/link.d.ts +21 -3
  362. package/dist/shims/link.js +189 -30
  363. package/dist/shims/link.js.map +1 -1
  364. package/dist/shims/metadata.d.ts +2 -1
  365. package/dist/shims/metadata.js +65 -6
  366. package/dist/shims/metadata.js.map +1 -1
  367. package/dist/shims/navigation.d.ts +8 -2
  368. package/dist/shims/navigation.js +67 -23
  369. package/dist/shims/navigation.js.map +1 -1
  370. package/dist/shims/og.d.ts +18 -2
  371. package/dist/shims/og.js +49 -1
  372. package/dist/shims/og.js.map +1 -0
  373. package/dist/shims/request-state-types.d.ts +1 -1
  374. package/dist/shims/root-params.d.ts +3 -1
  375. package/dist/shims/root-params.js +11 -3
  376. package/dist/shims/root-params.js.map +1 -1
  377. package/dist/shims/router-state.d.ts +1 -0
  378. package/dist/shims/router-state.js.map +1 -1
  379. package/dist/shims/router.d.ts +12 -5
  380. package/dist/shims/router.js +535 -86
  381. package/dist/shims/router.js.map +1 -1
  382. package/dist/shims/script.js +86 -12
  383. package/dist/shims/script.js.map +1 -1
  384. package/dist/shims/server.d.ts +21 -4
  385. package/dist/shims/server.js +30 -9
  386. package/dist/shims/server.js.map +1 -1
  387. package/dist/shims/slot.js +5 -1
  388. package/dist/shims/slot.js.map +1 -1
  389. package/dist/shims/unified-request-context.d.ts +1 -1
  390. package/dist/shims/url-safety.d.ts +23 -1
  391. package/dist/shims/url-safety.js +29 -2
  392. package/dist/shims/url-safety.js.map +1 -1
  393. package/dist/shims/url-utils.d.ts +2 -1
  394. package/dist/shims/url-utils.js +15 -4
  395. package/dist/shims/url-utils.js.map +1 -1
  396. package/dist/typegen.d.ts +10 -0
  397. package/dist/typegen.js +242 -0
  398. package/dist/typegen.js.map +1 -0
  399. package/dist/utils/asset-prefix.d.ts +33 -5
  400. package/dist/utils/asset-prefix.js +39 -6
  401. package/dist/utils/asset-prefix.js.map +1 -1
  402. package/dist/utils/cache-control-metadata.d.ts +2 -1
  403. package/dist/utils/cache-control-metadata.js +1 -3
  404. package/dist/utils/cache-control-metadata.js.map +1 -1
  405. package/dist/utils/domain-locale.d.ts +2 -1
  406. package/dist/utils/domain-locale.js +9 -1
  407. package/dist/utils/domain-locale.js.map +1 -1
  408. package/dist/utils/html-limited-bots.d.ts +5 -0
  409. package/dist/utils/html-limited-bots.js +15 -0
  410. package/dist/utils/html-limited-bots.js.map +1 -0
  411. package/dist/utils/lazy-chunks.d.ts +1 -1
  412. package/dist/utils/lazy-chunks.js +1 -1
  413. package/dist/utils/lazy-chunks.js.map +1 -1
  414. package/dist/utils/prerender-output-paths.d.ts +15 -0
  415. package/dist/utils/prerender-output-paths.js +24 -0
  416. package/dist/utils/prerender-output-paths.js.map +1 -0
  417. package/dist/utils/query.d.ts +23 -1
  418. package/dist/utils/query.js +46 -2
  419. package/dist/utils/query.js.map +1 -1
  420. package/dist/utils/record.d.ts +5 -0
  421. package/dist/utils/record.js +8 -0
  422. package/dist/utils/record.js.map +1 -0
  423. package/package.json +11 -3
package/README.md CHANGED
@@ -558,7 +558,7 @@ These are intentional exclusions. For things that are missing today but on the r
558
558
 
559
559
  These are gaps we'd like to close — distinct from the [intentional exclusions](#whats-not-supported-and-wont-be) above.
560
560
 
561
- - **Image optimization doesn't happen at build time.** Remote images work via `@unpic/react` (auto-detects 28 CDN providers). Local images are routed through a `/_vinext/image` endpoint that can resize and transcode on Cloudflare Workers (via the Images binding) in production, but no build-time optimization or static resizing occurs.
561
+ - **Image optimization doesn't happen at build time.** Remote images work via `@unpic/react` (auto-detects 28 CDN providers). Local images are routed through a `/_next/image` endpoint that can resize and transcode on Cloudflare Workers (via the Images binding) in production, but no build-time optimization or static resizing occurs.
562
562
  - **Google Fonts are loaded from the CDN, not self-hosted.** No `size-adjust` fallback font metrics. Local fonts work but `@font-face` CSS is injected at runtime, not extracted at build time.
563
563
  - **Route segment config** — `runtime` and `preferredRegion` are ignored (everything runs in the same environment).
564
564
  - **Node.js production server (`vinext start`)** works for testing but is less complete than Workers deployment. Cloudflare Workers is the primary target.
@@ -0,0 +1,14 @@
1
+ //#region src/build/clean-output.d.ts
2
+ type CleanBuildOutputOptions = {
3
+ root: string;
4
+ outDir?: string;
5
+ emptyOutDir?: boolean;
6
+ };
7
+ type CleanBuildOutputResult = {
8
+ cleaned: boolean;
9
+ outDir: string;
10
+ };
11
+ declare function cleanBuildOutput(options: CleanBuildOutputOptions): CleanBuildOutputResult;
12
+ //#endregion
13
+ export { cleanBuildOutput };
14
+ //# sourceMappingURL=clean-output.d.ts.map
@@ -0,0 +1,36 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ //#region src/build/clean-output.ts
4
+ function resolveOutDir(root, outDir) {
5
+ const resolvedRoot = path.resolve(root);
6
+ if (!outDir) return path.join(resolvedRoot, "dist");
7
+ return path.isAbsolute(outDir) ? outDir : path.resolve(resolvedRoot, outDir);
8
+ }
9
+ function isPathInsideOrEqual(parent, child) {
10
+ const relative = path.relative(parent, child);
11
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
12
+ }
13
+ function shouldCleanBuildOutput(options) {
14
+ if (options.emptyOutDir === false) return false;
15
+ if (options.emptyOutDir === true) return true;
16
+ return isPathInsideOrEqual(path.resolve(options.root), resolveOutDir(options.root, options.outDir));
17
+ }
18
+ function cleanBuildOutput(options) {
19
+ const outDir = resolveOutDir(options.root, options.outDir);
20
+ if (!shouldCleanBuildOutput(options)) return {
21
+ cleaned: false,
22
+ outDir
23
+ };
24
+ fs.rmSync(outDir, {
25
+ recursive: true,
26
+ force: true
27
+ });
28
+ return {
29
+ cleaned: true,
30
+ outDir
31
+ };
32
+ }
33
+ //#endregion
34
+ export { cleanBuildOutput };
35
+
36
+ //# sourceMappingURL=clean-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean-output.js","names":[],"sources":["../../src/build/clean-output.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\ntype CleanBuildOutputOptions = {\n root: string;\n outDir?: string;\n emptyOutDir?: boolean;\n};\n\ntype CleanBuildOutputResult = {\n cleaned: boolean;\n outDir: string;\n};\n\nfunction resolveOutDir(root: string, outDir: string | undefined): string {\n const resolvedRoot = path.resolve(root);\n if (!outDir) return path.join(resolvedRoot, \"dist\");\n return path.isAbsolute(outDir) ? outDir : path.resolve(resolvedRoot, outDir);\n}\n\nfunction isPathInsideOrEqual(parent: string, child: string): boolean {\n const relative = path.relative(parent, child);\n return relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n\nfunction shouldCleanBuildOutput(options: CleanBuildOutputOptions): boolean {\n if (options.emptyOutDir === false) return false;\n if (options.emptyOutDir === true) return true;\n\n return isPathInsideOrEqual(\n path.resolve(options.root),\n resolveOutDir(options.root, options.outDir),\n );\n}\n\nexport function cleanBuildOutput(options: CleanBuildOutputOptions): CleanBuildOutputResult {\n const outDir = resolveOutDir(options.root, options.outDir);\n if (!shouldCleanBuildOutput(options)) return { cleaned: false, outDir };\n\n fs.rmSync(outDir, { recursive: true, force: true });\n return { cleaned: true, outDir };\n}\n"],"mappings":";;;AAcA,SAAS,cAAc,MAAc,QAAoC;CACvE,MAAM,eAAe,KAAK,QAAQ,KAAK;CACvC,IAAI,CAAC,QAAQ,OAAO,KAAK,KAAK,cAAc,OAAO;CACnD,OAAO,KAAK,WAAW,OAAO,GAAG,SAAS,KAAK,QAAQ,cAAc,OAAO;;AAG9E,SAAS,oBAAoB,QAAgB,OAAwB;CACnE,MAAM,WAAW,KAAK,SAAS,QAAQ,MAAM;CAC7C,OAAO,aAAa,MAAO,CAAC,SAAS,WAAW,KAAK,IAAI,CAAC,KAAK,WAAW,SAAS;;AAGrF,SAAS,uBAAuB,SAA2C;CACzE,IAAI,QAAQ,gBAAgB,OAAO,OAAO;CAC1C,IAAI,QAAQ,gBAAgB,MAAM,OAAO;CAEzC,OAAO,oBACL,KAAK,QAAQ,QAAQ,KAAK,EAC1B,cAAc,QAAQ,MAAM,QAAQ,OAAO,CAC5C;;AAGH,SAAgB,iBAAiB,SAA0D;CACzF,MAAM,SAAS,cAAc,QAAQ,MAAM,QAAQ,OAAO;CAC1D,IAAI,CAAC,uBAAuB,QAAQ,EAAE,OAAO;EAAE,SAAS;EAAO;EAAQ;CAEvE,GAAG,OAAO,QAAQ;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;CACnD,OAAO;EAAE,SAAS;EAAM;EAAQ"}
@@ -11,16 +11,16 @@ type PrecompressResult = {
11
11
  * Safe to re-run — overwrites existing compressed variants with identical
12
12
  * output, and never compresses `.br`, `.gz`, or `.zst` files themselves.
13
13
  *
14
- * `assetsDir` defaults to `"assets"` (Vite's historical default). When
15
- * `assetPrefix` is configured the build writes assets to a different
16
- * directory (e.g. `"cdn/_next/static"` for a path prefix, or `"_next/static"`
17
- * for an absolute URL prefix); callers should resolve that with
14
+ * `assetsDir` defaults to `"_next/static"` (Next.js's canonical convention,
15
+ * matching `resolveAssetsDir("")`). When `assetPrefix` is configured as a
16
+ * path prefix the build writes assets to a different directory (e.g.
17
+ * `"cdn/_next/static"`); callers should resolve that with
18
18
  * `resolveAssetsDir(assetPrefix)` and thread it through. Without this,
19
- * `assetPrefix` builds would walk an empty `assets/` directory and emit
20
- * zero compressed variants.
19
+ * `assetPrefix` builds would walk an empty `_next/static/` directory and
20
+ * emit zero compressed variants.
21
21
  */
22
22
  declare function precompressAssets(clientDir: string, options?: {
23
- /** Subdirectory under `clientDir` containing hashed assets. Defaults to `"assets"`. */assetsDir?: string;
23
+ /** Subdirectory under `clientDir` containing hashed assets. Defaults to `"_next/static"`. */assetsDir?: string;
24
24
  onProgress?: (completed: number, total: number, file: string) => void;
25
25
  }): Promise<PrecompressResult>;
26
26
  //#endregion
@@ -1,5 +1,6 @@
1
+ import { ASSET_PREFIX_URL_DIR } from "../utils/asset-prefix.js";
1
2
  import path from "node:path";
2
- import fsp from "node:fs/promises";
3
+ import fs from "node:fs/promises";
3
4
  import os from "node:os";
4
5
  import zlib from "node:zlib";
5
6
  import { promisify } from "node:util";
@@ -8,12 +9,12 @@ import { promisify } from "node:util";
8
9
  * Build-time precompression for hashed static assets.
9
10
  *
10
11
  * Generates .br (brotli q5), .gz (gzip l8), and .zst (zstd l8) files
11
- * alongside compressible assets in dist/client/assets/. Served directly by
12
- * the production server — no per-request compression needed for immutable
13
- * build output.
12
+ * alongside compressible assets in dist/client/_next/static/. Served
13
+ * directly by the production server — no per-request compression needed
14
+ * for immutable build output.
14
15
  *
15
- * Only targets assets/ (hashed, immutable) — public directory files use
16
- * on-the-fly compression since they may change between deploys.
16
+ * Only targets the hashed-asset directory (immutable) — public directory
17
+ * files use on-the-fly compression since they may change between deploys.
17
18
  */
18
19
  const brotliCompress = promisify(zlib.brotliCompress);
19
20
  const gzip = promisify(zlib.gzip);
@@ -45,7 +46,7 @@ const CONCURRENCY = Math.min(os.availableParallelism(), 8);
45
46
  async function* walkFiles(dir, base = dir) {
46
47
  let entries;
47
48
  try {
48
- entries = await fsp.readdir(dir, { withFileTypes: true });
49
+ entries = await fs.readdir(dir, { withFileTypes: true });
49
50
  } catch {
50
51
  return;
51
52
  }
@@ -62,16 +63,16 @@ async function* walkFiles(dir, base = dir) {
62
63
  * Safe to re-run — overwrites existing compressed variants with identical
63
64
  * output, and never compresses `.br`, `.gz`, or `.zst` files themselves.
64
65
  *
65
- * `assetsDir` defaults to `"assets"` (Vite's historical default). When
66
- * `assetPrefix` is configured the build writes assets to a different
67
- * directory (e.g. `"cdn/_next/static"` for a path prefix, or `"_next/static"`
68
- * for an absolute URL prefix); callers should resolve that with
66
+ * `assetsDir` defaults to `"_next/static"` (Next.js's canonical convention,
67
+ * matching `resolveAssetsDir("")`). When `assetPrefix` is configured as a
68
+ * path prefix the build writes assets to a different directory (e.g.
69
+ * `"cdn/_next/static"`); callers should resolve that with
69
70
  * `resolveAssetsDir(assetPrefix)` and thread it through. Without this,
70
- * `assetPrefix` builds would walk an empty `assets/` directory and emit
71
- * zero compressed variants.
71
+ * `assetPrefix` builds would walk an empty `_next/static/` directory and
72
+ * emit zero compressed variants.
72
73
  */
73
74
  async function precompressAssets(clientDir, options = {}) {
74
- const { assetsDir: assetsSubdir = "assets", onProgress } = options;
75
+ const { assetsDir: assetsSubdir = ASSET_PREFIX_URL_DIR, onProgress } = options;
75
76
  const assetsDir = path.join(clientDir, assetsSubdir);
76
77
  const result = {
77
78
  filesCompressed: 0,
@@ -88,13 +89,13 @@ async function precompressAssets(clientDir, options = {}) {
88
89
  for (let i = 0; i < filePaths.length; i += CONCURRENCY) {
89
90
  const chunk = filePaths.slice(i, i + CONCURRENCY);
90
91
  await Promise.all(chunk.map(async (fullPath) => {
91
- const content = await fsp.readFile(fullPath);
92
+ const content = await fs.readFile(fullPath);
92
93
  if (content.length < MIN_SIZE) return;
93
94
  const compressions = [brotliCompress(content, { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 5 } }), gzip(content, { level: 8 })];
94
95
  if (zstdCompress) compressions.push(zstdCompress(content, { params: { [zlib.constants.ZSTD_c_compressionLevel]: 8 } }));
95
96
  const [brContent, gzContent, zstdContent] = await Promise.all(compressions);
96
- const writes = [fsp.writeFile(fullPath + ".br", brContent), fsp.writeFile(fullPath + ".gz", gzContent)];
97
- if (zstdContent) writes.push(fsp.writeFile(fullPath + ".zst", zstdContent));
97
+ const writes = [fs.writeFile(fullPath + ".br", brContent), fs.writeFile(fullPath + ".gz", gzContent)];
98
+ if (zstdContent) writes.push(fs.writeFile(fullPath + ".zst", zstdContent));
98
99
  await Promise.all(writes);
99
100
  result.filesCompressed++;
100
101
  result.totalOriginalBytes += content.length;
@@ -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/<assetsDir>/`.\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 *\n * `assetsDir` defaults to `\"assets\"` (Vite's historical default). When\n * `assetPrefix` is configured the build writes assets to a different\n * directory (e.g. `\"cdn/_next/static\"` for a path prefix, or `\"_next/static\"`\n * for an absolute URL prefix); callers should resolve that with\n * `resolveAssetsDir(assetPrefix)` and thread it through. Without this,\n * `assetPrefix` builds would walk an empty `assets/` directory and emit\n * zero compressed variants.\n */\nexport async function precompressAssets(\n clientDir: string,\n options: {\n /** Subdirectory under `clientDir` containing hashed assets. Defaults to `\"assets\"`. */\n assetsDir?: string;\n onProgress?: (completed: number, total: number, file: string) => void;\n } = {},\n): Promise<PrecompressResult> {\n const { assetsDir: assetsSubdir = \"assets\", onProgress } = options;\n const assetsDir = path.join(clientDir, assetsSubdir);\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;;;;;;;;;;;;;;;;;;AAoBzC,eAAsB,kBACpB,WACA,UAII,EAAE,EACsB;CAC5B,MAAM,EAAE,WAAW,eAAe,UAAU,eAAe;CAC3D,MAAM,YAAY,KAAK,KAAK,WAAW,aAAa;CACpD,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"}
1
+ {"version":3,"file":"precompress.js","names":["fsp"],"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/_next/static/. Served\n * directly by the production server — no per-request compression needed\n * for immutable build output.\n *\n * Only targets the hashed-asset directory (immutable) — public directory\n * files use 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\";\nimport { ASSET_PREFIX_URL_DIR } from \"../utils/asset-prefix.js\";\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/<assetsDir>/`.\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 *\n * `assetsDir` defaults to `\"_next/static\"` (Next.js's canonical convention,\n * matching `resolveAssetsDir(\"\")`). When `assetPrefix` is configured as a\n * path prefix the build writes assets to a different directory (e.g.\n * `\"cdn/_next/static\"`); callers should resolve that with\n * `resolveAssetsDir(assetPrefix)` and thread it through. Without this,\n * `assetPrefix` builds would walk an empty `_next/static/` directory and\n * emit zero compressed variants.\n */\nexport async function precompressAssets(\n clientDir: string,\n options: {\n /** Subdirectory under `clientDir` containing hashed assets. Defaults to `\"_next/static\"`. */\n assetsDir?: string;\n onProgress?: (completed: number, total: number, file: string) => void;\n } = {},\n): Promise<PrecompressResult> {\n const { assetsDir: assetsSubdir = ASSET_PREFIX_URL_DIR, onProgress } = options;\n const assetsDir = path.join(clientDir, assetsSubdir);\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":";;;;;;;;;;;;;;;;;;AAkBA,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,MAAMA,GAAI,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;;;;;;;;;;;;;;;;;;AAoBzC,eAAsB,kBACpB,WACA,UAII,EAAE,EACsB;CAC5B,MAAM,EAAE,WAAW,eAAe,sBAAsB,eAAe;CACvE,MAAM,YAAY,KAAK,KAAK,WAAW,aAAa;CACpD,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,MAAMA,GAAI,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,CACbA,GAAI,UAAU,WAAW,OAAO,UAAU,EAC1CA,GAAI,UAAU,WAAW,OAAO,UAAU,CAC3C;GACD,IAAI,aACF,OAAO,KAAKA,GAAI,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"}
@@ -1,12 +1,14 @@
1
1
  import { Route } from "../routing/pages-router.js";
2
2
  import { AppRoute } from "../routing/app-route-graph.js";
3
3
  import { ResolvedNextConfig } from "../config/next-config.js";
4
+ import { MetadataFileRoute } from "../server/metadata-routes.js";
4
5
  import { readPrerenderSecret } from "./server-manifest.js";
5
6
  import { Server } from "node:http";
6
7
 
7
8
  //#region src/build/prerender.d.ts
8
9
  type PrerenderResult = {
9
- /** One entry per route (including skipped/error routes). */routes: PrerenderRouteResult[];
10
+ /** One entry per route (including skipped/error routes). */routes: PrerenderRouteResult[]; /** Additional generated files that are not represented as route entries. */
11
+ outputFiles?: string[];
10
12
  };
11
13
  type PrerenderRouteResult = {
12
14
  /** The route's file-system pattern, e.g. `/blog/:slug`. */route: string;
@@ -83,7 +85,8 @@ type PrerenderPagesOptions = {
83
85
  pagesBundlePath?: string;
84
86
  } & PrerenderOptions;
85
87
  type PrerenderAppOptions = {
86
- /** Discovered app routes. */routes: AppRoute[];
88
+ /** Discovered app routes. */routes: AppRoute[]; /** Discovered file-based metadata routes. Used by static export. */
89
+ metadataRoutes?: readonly MetadataFileRoute[];
87
90
  /**
88
91
  * Absolute path to the pre-built RSC handler bundle (e.g. `dist/server/index.js`).
89
92
  */
@@ -123,15 +126,10 @@ type PrerenderAppOptionsInternal = PrerenderAppOptions & {
123
126
  * all '<' and '>' in the embedded JSON, preventing false </script> matches.
124
127
  */
125
128
  declare function extractRscPayloadFromPrerenderedHtml(html: string): Uint8Array | null;
126
- /**
127
- * Determine the HTML output file path for a URL.
128
- * Respects trailingSlash config.
129
- */
130
- declare function getOutputPath(urlPath: string, trailingSlash: boolean): string;
131
129
  /** Map of route patterns to generateStaticParams functions (or null/undefined). */
132
130
  type StaticParamsMap = Record<string, ((opts: {
133
131
  params: Record<string, string | string[]>;
134
- }) => Promise<Record<string, string | string[]>[]>) | null | undefined>;
132
+ }) => Promise<unknown>) | null | undefined>;
135
133
  /**
136
134
  * Resolve parent dynamic segment params for a route.
137
135
  * Handles top-down generateStaticParams resolution for nested dynamic routes.
@@ -139,7 +137,7 @@ type StaticParamsMap = Record<string, ((opts: {
139
137
  * Uses the `staticParamsMap` (pattern → generateStaticParams) exported from
140
138
  * the production bundle.
141
139
  */
142
- declare function resolveParentParams(childRoute: AppRoute, routeIndex: ReadonlyMap<string, AppRoute>, staticParamsMap: StaticParamsMap): Promise<Record<string, string | string[]>[]>;
140
+ declare function resolveParentParams(childRoute: AppRoute, staticParamsMap: StaticParamsMap): Promise<Record<string, string | string[]>[]>;
143
141
  /**
144
142
  * Run the prerender phase for Pages Router.
145
143
  *
@@ -181,18 +179,13 @@ declare function prerenderPages({
181
179
  */
182
180
  declare function prerenderApp({
183
181
  routes,
182
+ metadataRoutes,
184
183
  outDir,
185
184
  config,
186
185
  mode,
187
186
  rscBundlePath,
188
187
  ...options
189
188
  }: PrerenderAppOptionsInternal): Promise<PrerenderResult>;
190
- /**
191
- * Determine the RSC output file path for a URL.
192
- * "/blog/hello-world" → "blog/hello-world.rsc"
193
- * "/" → "index.rsc"
194
- */
195
- declare function getRscOutputPath(urlPath: string): string;
196
189
  /**
197
190
  * Write `vinext-prerender.json` to `outDir`.
198
191
  *
@@ -205,5 +198,5 @@ declare function writePrerenderIndex(routes: PrerenderRouteResult[], outDir: str
205
198
  trailingSlash?: boolean;
206
199
  }): void;
207
200
  //#endregion
208
- export { PrerenderResult, PrerenderRouteResult, StaticParamsMap, extractRscPayloadFromPrerenderedHtml, getOutputPath, getRscOutputPath, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
201
+ export { PrerenderResult, PrerenderRouteResult, StaticParamsMap, extractRscPayloadFromPrerenderedHtml, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
209
202
  //# sourceMappingURL=prerender.d.ts.map
@@ -1,11 +1,14 @@
1
1
  import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
2
- import { VINEXT_PRERENDER_SECRET_HEADER } from "../server/headers.js";
2
+ import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../server/headers.js";
3
3
  import { normalizeStaticPathsEntry } from "../routing/route-pattern.js";
4
4
  import { headersContextFromRequest, runWithHeadersContext } from "../shims/headers.js";
5
5
  import { NoOpCacheHandler, _consumeRequestScopedCacheLife, getCacheHandler, setCacheHandler } from "../shims/cache.js";
6
6
  import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
7
7
  import { concatUint8Arrays, decodeRscEmbeddedChunk } from "../server/app-rsc-embedded-chunks.js";
8
+ import { navigationRuntimeRscBootstrapExpression } from "../server/app-ssr-stream.js";
9
+ import { encodePrerenderRouteParams, serializePrerenderRouteParamsHeader } from "../server/prerender-route-params.js";
8
10
  import { readPrerenderSecret } from "./server-manifest.js";
11
+ import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
9
12
  import { startProdServer } from "../server/prod-server.js";
10
13
  import fs from "node:fs";
11
14
  import path from "node:path";
@@ -34,9 +37,12 @@ function getErrorMessageWithStack(err) {
34
37
  /** Sentinel path used to trigger 404 rendering without a real route match. */
35
38
  const NOT_FOUND_SENTINEL_PATH = "/__vinext_nonexistent_for_404__";
36
39
  const DEFAULT_CONCURRENCY = Math.min(os.availableParallelism(), 8);
37
- const RSC_CHUNK_SCRIPT_PREFIX = "self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];";
38
- const RSC_DONE_MARKER = "__VINEXT_RSC_DONE__=true";
39
- const RSC_CHUNK_FULL_PREFIX = `${RSC_CHUNK_SCRIPT_PREFIX}self.__VINEXT_RSC_CHUNKS__.push(`;
40
+ const RSC_LEGACY_CHUNK_SCRIPT_PREFIX = "self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];";
41
+ const RSC_LEGACY_DONE_SCRIPT = "self.__VINEXT_RSC_DONE__=true";
42
+ const RSC_LEGACY_CHUNK_FULL_PREFIX = `${RSC_LEGACY_CHUNK_SCRIPT_PREFIX}self.__VINEXT_RSC_CHUNKS__.push(`;
43
+ const RSC_RUNTIME_BOOTSTRAP_EXPRESSION = navigationRuntimeRscBootstrapExpression();
44
+ const RSC_RUNTIME_CHUNK_FULL_PREFIX = `${RSC_RUNTIME_BOOTSTRAP_EXPRESSION}.rsc.push(`;
45
+ const RSC_RUNTIME_DONE_SCRIPT = `${RSC_RUNTIME_BOOTSTRAP_EXPRESSION}.done=true`;
40
46
  /**
41
47
  * Reconstruct the RSC payload from a prerender HTML response by parsing the
42
48
  * inline bootstrap chunk scripts emitted by createRscEmbedTransform.
@@ -59,15 +65,19 @@ function extractRscPayloadFromPrerenderedHtml(html) {
59
65
  let match;
60
66
  while ((match = scriptPattern.exec(html)) !== null) {
61
67
  const script = (match[1] ?? "").trim().replace(/;$/, "");
62
- if (script === `self.${RSC_DONE_MARKER}`) {
68
+ if (script === RSC_RUNTIME_DONE_SCRIPT || script === RSC_LEGACY_DONE_SCRIPT) {
63
69
  sawDone = true;
64
70
  continue;
65
71
  }
66
- if (script.startsWith(RSC_CHUNK_SCRIPT_PREFIX)) chunks.push(decodeRscEmbeddedChunk(parseRscChunkPushArgument(script)));
72
+ if (script.startsWith(RSC_RUNTIME_CHUNK_FULL_PREFIX)) {
73
+ chunks.push(decodeRscEmbeddedChunk(parseRscChunkPushArgument(script, RSC_RUNTIME_CHUNK_FULL_PREFIX)));
74
+ continue;
75
+ }
76
+ if (script.startsWith(RSC_LEGACY_CHUNK_SCRIPT_PREFIX)) chunks.push(decodeRscEmbeddedChunk(parseRscChunkPushArgument(script, RSC_LEGACY_CHUNK_FULL_PREFIX)));
67
77
  }
68
78
  if (chunks.length === 0 && !sawDone) return null;
69
79
  if (chunks.length === 0) throw new Error("[vinext] Malformed prerender RSC embed: done marker present without chunk scripts");
70
- if (!sawDone) throw new Error("[vinext] Malformed prerender RSC embed: missing __VINEXT_RSC_DONE__ marker");
80
+ if (!sawDone) throw new Error("[vinext] Malformed prerender RSC embed: missing RSC done marker");
71
81
  return concatUint8Arrays(chunks);
72
82
  }
73
83
  /**
@@ -77,9 +87,9 @@ function extractRscPayloadFromPrerenderedHtml(html) {
77
87
  * prefix and ends with `)`. JSON.parse on the slice catches any tampering or
78
88
  * trailing code.
79
89
  */
80
- function parseRscChunkPushArgument(script) {
81
- if (!script.startsWith(RSC_CHUNK_FULL_PREFIX) || !script.endsWith(")")) throw new Error("[vinext] Malformed prerender RSC embed: unexpected chunk script shape");
82
- const jsonSource = script.slice(RSC_CHUNK_FULL_PREFIX.length, -1);
90
+ function parseRscChunkPushArgument(script, chunkPrefix) {
91
+ if (!script.startsWith(chunkPrefix) || !script.endsWith(")")) throw new Error("[vinext] Malformed prerender RSC embed: unexpected chunk script shape");
92
+ const jsonSource = script.slice(chunkPrefix.length, -1);
83
93
  let parsed;
84
94
  try {
85
95
  parsed = JSON.parse(jsonSource);
@@ -134,15 +144,25 @@ function buildUrlFromParams(pattern, params) {
134
144
  } else result.push(part);
135
145
  return "/" + result.join("/");
136
146
  }
137
- /**
138
- * Determine the HTML output file path for a URL.
139
- * Respects trailingSlash config.
140
- */
141
- function getOutputPath(urlPath, trailingSlash) {
142
- if (urlPath === "/") return "index.html";
143
- const clean = urlPath.replace(/^\//, "");
144
- if (trailingSlash) return `${clean}/index.html`;
145
- return `${clean}.html`;
147
+ function metadataOutputPath(servedUrl) {
148
+ const pathname = servedUrl.split("?", 1)[0];
149
+ if (!pathname || !pathname.startsWith("/")) return null;
150
+ const segments = pathname.split("/").filter(Boolean);
151
+ if (segments.length === 0 || segments.some((segment) => segment === "." || segment === "..")) return null;
152
+ return segments.join("/");
153
+ }
154
+ function emitStaticMetadataFiles(metadataRoutes, outDir) {
155
+ const outputFiles = [];
156
+ for (const route of metadataRoutes) {
157
+ if (route.isDynamic) continue;
158
+ const outputPath = metadataOutputPath(route.servedUrl);
159
+ if (!outputPath) continue;
160
+ const fullPath = path.join(outDir, ...outputPath.split("/"));
161
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
162
+ fs.copyFileSync(route.filePath, fullPath);
163
+ outputFiles.push(outputPath);
164
+ }
165
+ return outputFiles;
146
166
  }
147
167
  /**
148
168
  * Resolve parent dynamic segment params for a route.
@@ -151,7 +171,7 @@ function getOutputPath(urlPath, trailingSlash) {
151
171
  * Uses the `staticParamsMap` (pattern → generateStaticParams) exported from
152
172
  * the production bundle.
153
173
  */
154
- async function resolveParentParams(childRoute, routeIndex, staticParamsMap) {
174
+ async function resolveParentParams(childRoute, staticParamsMap) {
155
175
  const { patternParts } = childRoute;
156
176
  let lastDynamicIdx = -1;
157
177
  for (let i = patternParts.length - 1; i >= 0; i--) if (patternParts[i].startsWith(":")) {
@@ -164,25 +184,29 @@ async function resolveParentParams(childRoute, routeIndex, staticParamsMap) {
164
184
  const part = patternParts[i];
165
185
  prefixPattern += "/" + part;
166
186
  if (!part.startsWith(":")) continue;
167
- if (routeIndex.get(prefixPattern)?.pagePath) {
168
- const fn = staticParamsMap[prefixPattern];
169
- if (typeof fn === "function") parentSegments.push(fn);
170
- }
187
+ const fn = staticParamsMap[prefixPattern];
188
+ if (typeof fn === "function") parentSegments.push(fn);
171
189
  }
172
190
  if (parentSegments.length === 0) return [];
173
191
  let currentParams = [{}];
192
+ let resolvedAnyParent = false;
174
193
  for (const generateStaticParams of parentSegments) {
175
194
  const nextParams = [];
195
+ let resolvedThisParent = false;
176
196
  for (const parentParams of currentParams) {
177
197
  const results = await generateStaticParams({ params: parentParams });
178
- if (Array.isArray(results)) for (const result of results) nextParams.push({
198
+ if (results === null) continue;
199
+ if (!Array.isArray(results)) return [];
200
+ resolvedThisParent = true;
201
+ resolvedAnyParent = true;
202
+ for (const result of results) nextParams.push({
179
203
  ...parentParams,
180
204
  ...result
181
205
  });
182
206
  }
183
- currentParams = nextParams;
207
+ if (resolvedThisParent) currentParams = nextParams;
184
208
  }
185
- return currentParams;
209
+ return resolvedAnyParent ? currentParams : [];
186
210
  }
187
211
  /**
188
212
  * Run the prerender phase for Pages Router.
@@ -265,6 +289,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
265
289
  const pagesToRender = [];
266
290
  for (const route of bundlePageRoutes) {
267
291
  if (path.basename(route.filePath, path.extname(route.filePath)).startsWith("_")) continue;
292
+ if (route.pattern === "/404") continue;
268
293
  if (!routes.find((r) => r.filePath === route.filePath || r.pattern === route.pattern)) continue;
269
294
  const { type, revalidate: classifiedRevalidate } = classifyPagesRoute(route.filePath);
270
295
  if (type === "ssr") {
@@ -391,9 +416,12 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
391
416
  return result;
392
417
  });
393
418
  results.push(...pageResults);
394
- if (findFileWithExtensions(path.join(pagesDir, "404"), fileMatcher) || findFileWithExtensions(path.join(pagesDir, "_error"), fileMatcher)) try {
395
- const notFoundRes = await renderPage(NOT_FOUND_SENTINEL_PATH);
396
- if ((notFoundRes.headers.get("content-type") ?? "").includes("text/html")) {
419
+ const hasCustom404 = findFileWithExtensions(path.join(pagesDir, "404"), fileMatcher);
420
+ const hasErrorPage = findFileWithExtensions(path.join(pagesDir, "_error"), fileMatcher);
421
+ if (hasCustom404 || hasErrorPage) try {
422
+ const notFoundRes = await renderPage(hasCustom404 ? "/404" : NOT_FOUND_SENTINEL_PATH);
423
+ const contentType = notFoundRes.headers.get("content-type") ?? "";
424
+ if (notFoundRes.status === 404 && contentType.includes("text/html")) {
397
425
  const html404 = await notFoundRes.text();
398
426
  const fullPath = path.join(outDir, "404.html");
399
427
  fs.writeFileSync(fullPath, html404, "utf-8");
@@ -435,7 +463,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
435
463
  * If they succeed, they are marked as rendered. If they throw a DynamicUsageError
436
464
  * or fail, they are marked as skipped with reason 'dynamic'.
437
465
  */
438
- async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...options }) {
466
+ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode, rscBundlePath, ...options }) {
439
467
  const manifestDir = options.manifestDir ?? outDir;
440
468
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
441
469
  const onProgress = options.onProgress;
@@ -474,7 +502,8 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
474
502
  ...secretHeaders,
475
503
  ...Object.fromEntries(req.headers.entries())
476
504
  },
477
- body: req.method !== "GET" && req.method !== "HEAD" ? req.body : void 0
505
+ body: req.method !== "GET" && req.method !== "HEAD" ? req.body : void 0,
506
+ redirect: "manual"
478
507
  });
479
508
  };
480
509
  const staticParamsCache = /* @__PURE__ */ new Map();
@@ -505,7 +534,6 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
505
534
  return false;
506
535
  }
507
536
  });
508
- const routeIndex = new Map(routes.map((r) => [r.pattern, r]));
509
537
  const urlsToRender = [];
510
538
  for (const route of routes) {
511
539
  const renderEntryPath = getAppRouteRenderEntryPath(route);
@@ -556,7 +584,7 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
556
584
  });
557
585
  continue;
558
586
  }
559
- const parentParamSets = await resolveParentParams(route, routeIndex, staticParamsMap);
587
+ const parentParamSets = await resolveParentParams(route, staticParamsMap);
560
588
  let paramSets;
561
589
  if (parentParamSets.length > 0) {
562
590
  paramSets = [];
@@ -570,8 +598,15 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
570
598
  ...parentParams,
571
599
  ...childParams
572
600
  });
601
+ else {
602
+ paramSets = [];
603
+ break;
604
+ }
573
605
  }
574
- } else paramSets = await generateStaticParamsFn({ params: {} });
606
+ } else {
607
+ const results = await generateStaticParamsFn({ params: {} });
608
+ paramSets = Array.isArray(results) || results === null ? results : [];
609
+ }
575
610
  if (paramSets === null) {
576
611
  if (mode === "export") results.push({
577
612
  route: route.pattern,
@@ -599,6 +634,7 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
599
634
  urlsToRender.push({
600
635
  urlPath,
601
636
  routePattern: route.pattern,
637
+ prerenderRouteParams: encodePrerenderRouteParams(route.pattern, params),
602
638
  revalidate,
603
639
  isSpeculative: false
604
640
  });
@@ -615,12 +651,14 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
615
651
  else if (type === "unknown") urlsToRender.push({
616
652
  urlPath: route.pattern,
617
653
  routePattern: route.pattern,
654
+ prerenderRouteParams: null,
618
655
  revalidate: false,
619
656
  isSpeculative: true
620
657
  });
621
658
  else urlsToRender.push({
622
659
  urlPath: route.pattern,
623
660
  routePattern: route.pattern,
661
+ prerenderRouteParams: null,
624
662
  revalidate,
625
663
  isSpeculative: false
626
664
  });
@@ -631,9 +669,12 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
631
669
  * exactly once per URL after this function returns, keeping the callback
632
670
  * at a single, predictable call site.
633
671
  */
634
- async function renderUrl({ urlPath, routePattern, revalidate, isSpeculative }) {
672
+ async function renderUrl({ urlPath, routePattern, prerenderRouteParams, revalidate, isSpeculative }) {
635
673
  try {
636
- const htmlRequest = new Request(`http://localhost${urlPath}`);
674
+ const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParams);
675
+ const htmlHeaders = new Headers();
676
+ if (prerenderRouteParamsHeader !== null) htmlHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
677
+ const htmlRequest = new Request(`http://localhost${urlPath}`, { headers: htmlHeaders });
637
678
  const htmlRender = await runWithHeadersContext(headersContextFromRequest(htmlRequest), async () => {
638
679
  const response = await rscHandler(htmlRequest);
639
680
  const cacheControl = response.headers.get("cache-control") ?? "";
@@ -683,10 +724,12 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
683
724
  const html = htmlRender.html;
684
725
  let rscData = extractRscPayloadFromPrerenderedHtml(html);
685
726
  if (rscData === null) {
686
- const rscRequest = new Request(`http://localhost${urlPath}`, { headers: {
727
+ const rscHeaders = new Headers({
687
728
  Accept: "text/x-component",
688
729
  RSC: "1"
689
- } });
730
+ });
731
+ if (prerenderRouteParamsHeader !== null) rscHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
732
+ const rscRequest = new Request(`http://localhost${urlPath}`, { headers: rscHeaders });
690
733
  const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
691
734
  if (!rscRes.ok) {
692
735
  await rscRes.body?.cancel();
@@ -743,6 +786,7 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
743
786
  return result;
744
787
  });
745
788
  results.push(...appResults);
789
+ const outputFiles = mode === "export" && metadataRoutes.length > 0 ? emitStaticMetadataFiles(metadataRoutes, outDir) : [];
746
790
  try {
747
791
  const notFoundRequest = new Request(`http://localhost${NOT_FOUND_SENTINEL_PATH}`);
748
792
  const notFoundRes = await runWithHeadersContext(headersContextFromRequest(notFoundRequest), () => rscHandler(notFoundRequest));
@@ -764,7 +808,10 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
764
808
  buildId: config.buildId,
765
809
  trailingSlash: config.trailingSlash
766
810
  });
767
- return { routes: results };
811
+ return {
812
+ routes: results,
813
+ ...outputFiles.length > 0 ? { outputFiles } : {}
814
+ };
768
815
  } finally {
769
816
  setCacheHandler(previousHandler);
770
817
  if (previousPrerenderFlag === void 0) delete process.env.VINEXT_PRERENDER;
@@ -772,15 +819,6 @@ async function prerenderApp({ routes, outDir, config, mode, rscBundlePath, ...op
772
819
  if (ownedProdServerHandle) await new Promise((resolve) => ownedProdServerHandle.server.close(() => resolve()));
773
820
  }
774
821
  }
775
- /**
776
- * Determine the RSC output file path for a URL.
777
- * "/blog/hello-world" → "blog/hello-world.rsc"
778
- * "/" → "index.rsc"
779
- */
780
- function getRscOutputPath(urlPath) {
781
- if (urlPath === "/") return "index.rsc";
782
- return urlPath.replace(/^\//, "") + ".rsc";
783
- }
784
822
  function resolveRenderedCacheControl(requestCacheLife, cacheControl, fallbackExpireSeconds) {
785
823
  const sMaxage = parseCacheControlSeconds(cacheControl, "s-maxage");
786
824
  const staleWhileRevalidate = parseCacheControlSeconds(cacheControl, "stale-while-revalidate");
@@ -846,6 +884,6 @@ function writePrerenderIndex(routes, outDir, options) {
846
884
  fs.writeFileSync(path.join(outDir, "vinext-prerender.json"), JSON.stringify(index, null, 2), "utf-8");
847
885
  }
848
886
  //#endregion
849
- export { extractRscPayloadFromPrerenderedHtml, getOutputPath, getRscOutputPath, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
887
+ export { extractRscPayloadFromPrerenderedHtml, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, writePrerenderIndex };
850
888
 
851
889
  //# sourceMappingURL=prerender.js.map