vinext 1.2.1 → 1.3.1

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 (509) hide show
  1. package/README.md +12 -9
  2. package/dist/_virtual/_rolldown/runtime.js +1 -1
  3. package/dist/build/action-owner-manifest.d.ts +3 -2
  4. package/dist/build/action-owner-manifest.js +18 -2
  5. package/dist/build/app-page-static-eligibility.d.ts +17 -1
  6. package/dist/build/app-page-static-eligibility.js +80 -13
  7. package/dist/build/clean-output.d.ts +2 -1
  8. package/dist/build/client-build-config.d.ts +77 -6
  9. package/dist/build/client-build-config.js +176 -12
  10. package/dist/build/client-reference-async.d.ts +48 -0
  11. package/dist/build/client-reference-async.js +123 -0
  12. package/dist/build/client-reference-chunks.d.ts +32 -0
  13. package/dist/build/client-reference-chunks.js +111 -0
  14. package/dist/build/client-reference-groups.d.ts +171 -0
  15. package/dist/build/client-reference-groups.js +572 -0
  16. package/dist/build/css-url-assets.d.ts +2 -1
  17. package/dist/build/google-fonts/fallback-metrics.d.ts +2 -1
  18. package/dist/build/google-fonts/find-font-files-in-css.d.ts +1 -0
  19. package/dist/build/google-fonts/find-font-files-in-css.js +17 -4
  20. package/dist/build/google-fonts/font-metadata.d.ts +2 -1
  21. package/dist/build/google-fonts/validate.d.ts +2 -1
  22. package/dist/build/inline-css.d.ts +2 -1
  23. package/dist/build/layout-classification.d.ts +2 -1
  24. package/dist/build/module-dependency-cache.d.ts +33 -1
  25. package/dist/build/module-dependency-cache.js +51 -1
  26. package/dist/build/next-client-runtime-manifests.d.ts +12 -2
  27. package/dist/build/next-client-runtime-manifests.js +19 -4
  28. package/dist/build/nitro-public-files.d.ts +24 -0
  29. package/dist/build/nitro-public-files.js +60 -0
  30. package/dist/build/pages-client-assets-module.js +1 -1
  31. package/dist/build/precompress.d.ts +2 -1
  32. package/dist/build/precompress.js +1 -1
  33. package/dist/build/prerender-paths.d.ts +2 -1
  34. package/dist/build/prerender-paths.js +5 -2
  35. package/dist/build/prerender-phase.d.ts +2 -1
  36. package/dist/build/prerender-server-pool.js +1 -1
  37. package/dist/build/prerender.d.ts +3 -2
  38. package/dist/build/prerender.js +5 -4
  39. package/dist/build/report.d.ts +2 -1
  40. package/dist/build/route-classification-injector.d.ts +2 -1
  41. package/dist/build/route-classification-manifest.d.ts +2 -1
  42. package/dist/build/run-prerender.d.ts +2 -1
  43. package/dist/build/server-actions-encryption-key.d.ts +24 -0
  44. package/dist/build/server-actions-encryption-key.js +101 -0
  45. package/dist/build/ssr-manifest.d.ts +3 -0
  46. package/dist/build/ssr-manifest.js +34 -2
  47. package/dist/build/standalone.d.ts +2 -1
  48. package/dist/cache/cache-adapters-virtual.d.ts +2 -1
  49. package/dist/cache.js +1 -2
  50. package/dist/check.d.ts +2 -1
  51. package/dist/check.js +4 -0
  52. package/dist/cli-args.d.ts +2 -1
  53. package/dist/cli.js +2 -0
  54. package/dist/client/client-redirect-matcher.js +2 -1
  55. package/dist/client/client-redirects.d.ts +4 -3
  56. package/dist/client/client-redirects.js +2 -1
  57. package/dist/client/client-rewrite-params.d.ts +9 -0
  58. package/dist/client/client-rewrite-params.js +20 -0
  59. package/dist/client/client-rewrites.d.ts +2 -1
  60. package/dist/client/dev-error-overlay-store.d.ts +2 -1
  61. package/dist/client/dev-error-overlay.js +2 -1
  62. package/dist/client/index.js +1 -2
  63. package/dist/client/instrumentation-client-state.js +5 -5
  64. package/dist/client/navigation-runtime.d.ts +28 -3
  65. package/dist/client/navigation-runtime.js +42 -11
  66. package/dist/client/pages-router-link-navigation.d.ts +2 -1
  67. package/dist/client/vinext-next-data.d.ts +25 -2
  68. package/dist/client/window-next.d.ts +2 -1
  69. package/dist/config/config-matchers.d.ts +27 -41
  70. package/dist/config/config-matchers.js +293 -270
  71. package/dist/config/config-source.d.ts +34 -0
  72. package/dist/config/config-source.js +141 -0
  73. package/dist/config/dotenv.d.ts +2 -1
  74. package/dist/config/header-params.d.ts +29 -0
  75. package/dist/config/header-params.js +106 -0
  76. package/dist/config/next-config.d.ts +20 -6
  77. package/dist/config/next-config.js +149 -11
  78. package/dist/config/tsconfig-paths.d.ts +2 -1
  79. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/detector.js +23 -0
  80. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/index.js +2 -0
  81. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/lookup.js +25 -0
  82. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/bmp.js +11 -0
  83. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/cur.js +15 -0
  84. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/dds.js +11 -0
  85. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/gif.js +12 -0
  86. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/heif.js +55 -0
  87. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/icns.js +76 -0
  88. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/ico.js +39 -0
  89. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/index.js +46 -0
  90. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/j2c.js +11 -0
  91. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/jp2.js +21 -0
  92. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/jpg.js +71 -0
  93. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/jxl-stream.js +42 -0
  94. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/jxl.js +46 -0
  95. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/ktx.js +19 -0
  96. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/png.js +28 -0
  97. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/pnm.js +54 -0
  98. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/psd.js +11 -0
  99. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/svg.js +79 -0
  100. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/tga.js +15 -0
  101. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/tiff.js +96 -0
  102. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/utils.js +53 -0
  103. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/types/webp.js +45 -0
  104. package/dist/deps/.pnpm/image-size@2.0.4/deps/image-size/dist/esm/utils/bit-reader.js +38 -0
  105. package/dist/deps/.pnpm/ua-parser-js@1.0.35/deps/ua-parser-js/src/ua-parser.js +2 -2
  106. package/dist/entries/app-browser-entry.d.ts +34 -6
  107. package/dist/entries/app-browser-entry.js +103 -40
  108. package/dist/entries/app-rsc-entry.d.ts +4 -1
  109. package/dist/entries/app-rsc-entry.js +131 -66
  110. package/dist/entries/app-rsc-manifest.d.ts +12 -1
  111. package/dist/entries/app-rsc-manifest.js +73 -1
  112. package/dist/entries/app-ssr-entry.d.ts +4 -1
  113. package/dist/entries/app-ssr-entry.js +9 -2
  114. package/dist/entries/pages-client-entry.d.ts +1 -0
  115. package/dist/entries/pages-client-entry.js +6 -6
  116. package/dist/entries/pages-entry-helpers.js +1 -2
  117. package/dist/entries/pages-server-entry.d.ts +2 -1
  118. package/dist/entries/pages-server-entry.js +18 -15
  119. package/dist/entries/runtime-entry-module.d.ts +1 -0
  120. package/dist/entries/runtime-entry-module.js +4 -1
  121. package/dist/image/image-adapters-virtual.d.ts +2 -1
  122. package/dist/index.d.ts +11 -0
  123. package/dist/index.js +340 -116
  124. package/dist/init.js +17 -0
  125. package/dist/plugins/client-compile-hints.d.ts +30 -0
  126. package/dist/plugins/client-compile-hints.js +88 -0
  127. package/dist/plugins/client-reference-dedup.d.ts +2 -1
  128. package/dist/plugins/cloudflare-dev-module-urls.d.ts +2 -1
  129. package/dist/plugins/dynamic-preload-metadata.d.ts +3 -1
  130. package/dist/plugins/dynamic-preload-metadata.js +1 -1
  131. package/dist/plugins/environment.d.ts +2 -1
  132. package/dist/plugins/fonts.js +9 -12
  133. package/dist/plugins/ignore-dynamic-requests.d.ts +2 -1
  134. package/dist/plugins/import-meta-url.d.ts +2 -1
  135. package/dist/plugins/og-assets.d.ts +2 -1
  136. package/dist/plugins/og-assets.js +2 -3
  137. package/dist/plugins/optimize-imports.d.ts +27 -3
  138. package/dist/plugins/optimize-imports.js +106 -20
  139. package/dist/plugins/pages-node-externals.d.ts +2 -1
  140. package/dist/plugins/remove-console.d.ts +2 -1
  141. package/dist/plugins/require-condition-resolution.d.ts +2 -1
  142. package/dist/plugins/rsc-client-reference-loaders.d.ts +53 -1
  143. package/dist/plugins/rsc-client-reference-loaders.js +447 -27
  144. package/dist/plugins/rsc-client-shim-excludes.js +2 -0
  145. package/dist/plugins/sass.d.ts +2 -1
  146. package/dist/plugins/strip-server-exports.d.ts +2 -1
  147. package/dist/plugins/styled-jsx.d.ts +2 -1
  148. package/dist/plugins/transform-result.d.ts +2 -1
  149. package/dist/plugins/typeof-window.d.ts +2 -1
  150. package/dist/plugins/use-cache-callable.d.ts +5 -0
  151. package/dist/plugins/use-cache-callable.js +30 -8
  152. package/dist/plugins/worker-image-imports.d.ts +1 -0
  153. package/dist/plugins/worker-image-imports.js +2 -2
  154. package/dist/plugins/worker-use-server.d.ts +2 -1
  155. package/dist/routing/app-route-graph.d.ts +41 -1
  156. package/dist/routing/app-route-graph.js +122 -50
  157. package/dist/routing/browser-route-manifest.d.ts +44 -0
  158. package/dist/routing/browser-route-manifest.js +195 -0
  159. package/dist/routing/hashed-route-patterns.d.ts +4 -0
  160. package/dist/routing/hashed-route-patterns.js +22 -0
  161. package/dist/routing/optimistic-route-trie.d.ts +28 -0
  162. package/dist/routing/optimistic-route-trie.js +102 -0
  163. package/dist/routing/route-matching.d.ts +2 -1
  164. package/dist/routing/route-pattern.d.ts +2 -1
  165. package/dist/routing/route-validation.d.ts +6 -1
  166. package/dist/routing/route-validation.js +26 -10
  167. package/dist/server/accept-encoding.d.ts +2 -1
  168. package/dist/server/app-action-forwarding.d.ts +10 -1
  169. package/dist/server/app-action-forwarding.js +15 -1
  170. package/dist/server/app-bfcache-identity.d.ts +2 -1
  171. package/dist/server/app-browser-action-result.d.ts +2 -1
  172. package/dist/server/app-browser-client-reuse-manifest.d.ts +2 -1
  173. package/dist/server/app-browser-entry.js +175 -64
  174. package/dist/server/app-browser-error.d.ts +5 -2
  175. package/dist/server/app-browser-error.js +7 -2
  176. package/dist/server/app-browser-history-controller.d.ts +2 -1
  177. package/dist/server/app-browser-hydration.d.ts +2 -1
  178. package/dist/server/app-browser-interception-context.d.ts +4 -3
  179. package/dist/server/app-browser-mpa-navigation.d.ts +1 -0
  180. package/dist/server/app-browser-mpa-navigation.js +6 -5
  181. package/dist/server/app-browser-navigation-controller.d.ts +17 -6
  182. package/dist/server/app-browser-navigation-controller.js +103 -36
  183. package/dist/server/app-browser-popstate.d.ts +2 -1
  184. package/dist/server/app-browser-rsc-redirect.d.ts +2 -1
  185. package/dist/server/app-browser-server-action-client.d.ts +2 -1
  186. package/dist/server/app-browser-state.d.ts +2 -2
  187. package/dist/server/app-browser-state.js +1 -1
  188. package/dist/server/app-browser-stream.d.ts +2 -1
  189. package/dist/server/app-browser-visible-commit.d.ts +6 -5
  190. package/dist/server/app-client-reference-manifest.d.ts +31 -0
  191. package/dist/server/app-client-reference-manifest.js +46 -0
  192. package/dist/server/app-client-reference-preloader.d.ts +45 -2
  193. package/dist/server/app-client-reference-preloader.js +48 -10
  194. package/dist/server/app-elements-wire.d.ts +2 -1
  195. package/dist/server/app-elements.d.ts +2 -0
  196. package/dist/server/app-elements.js +3 -1
  197. package/dist/server/app-fallback-renderer.d.ts +9 -2
  198. package/dist/server/app-fallback-renderer.js +6 -2
  199. package/dist/server/app-history-state.d.ts +2 -1
  200. package/dist/server/app-inline-css-client.d.ts +2 -1
  201. package/dist/server/app-layout-param-observation.d.ts +2 -1
  202. package/dist/server/app-metadata-tracing.d.ts +2 -1
  203. package/dist/server/app-middleware.d.ts +6 -0
  204. package/dist/server/app-middleware.js +8 -10
  205. package/dist/server/app-optimistic-routing.d.ts +26 -11
  206. package/dist/server/app-optimistic-routing.js +38 -96
  207. package/dist/server/app-page-boundary-render.d.ts +20 -2
  208. package/dist/server/app-page-boundary-render.js +229 -39
  209. package/dist/server/app-page-boundary.d.ts +34 -5
  210. package/dist/server/app-page-boundary.js +40 -7
  211. package/dist/server/app-page-cache-finalizer.d.ts +2 -1
  212. package/dist/server/app-page-cache-finalizer.js +1 -1
  213. package/dist/server/app-page-cache-render.d.ts +10 -2
  214. package/dist/server/app-page-cache-render.js +15 -2
  215. package/dist/server/app-page-cache.js +2 -2
  216. package/dist/server/app-page-dispatch.d.ts +34 -1
  217. package/dist/server/app-page-dispatch.js +165 -34
  218. package/dist/server/app-page-element-builder.d.ts +17 -1
  219. package/dist/server/app-page-element-builder.js +187 -66
  220. package/dist/server/app-page-execution.d.ts +28 -1
  221. package/dist/server/app-page-execution.js +114 -5
  222. package/dist/server/app-page-head.d.ts +95 -3
  223. package/dist/server/app-page-head.js +307 -152
  224. package/dist/server/app-page-http-access-fallback-metadata.d.ts +48 -2
  225. package/dist/server/app-page-http-access-fallback-metadata.js +76 -1
  226. package/dist/server/app-page-method.d.ts +2 -1
  227. package/dist/server/app-page-probe.d.ts +2 -1
  228. package/dist/server/app-page-render-identity.d.ts +2 -1
  229. package/dist/server/app-page-render.d.ts +7 -1
  230. package/dist/server/app-page-render.js +38 -11
  231. package/dist/server/app-page-request.d.ts +14 -1
  232. package/dist/server/app-page-request.js +1 -1
  233. package/dist/server/app-page-route-wiring.d.ts +52 -2
  234. package/dist/server/app-page-route-wiring.js +197 -47
  235. package/dist/server/app-page-rsc-render-status.d.ts +3 -1
  236. package/dist/server/app-page-rsc-render-status.js +1 -0
  237. package/dist/server/app-page-search-params-observation.d.ts +8 -6
  238. package/dist/server/app-page-search-params-observation.js +16 -11
  239. package/dist/server/app-page-segment-plan.d.ts +2 -1
  240. package/dist/server/app-page-stream.d.ts +38 -1
  241. package/dist/server/app-page-stream.js +24 -4
  242. package/dist/server/app-pages-bridge.d.ts +4 -2
  243. package/dist/server/app-pages-bridge.js +11 -7
  244. package/dist/server/app-ppr-fallback-shell-render.d.ts +2 -1
  245. package/dist/server/app-ppr-fallback-shell.d.ts +2 -1
  246. package/dist/server/app-prerender-endpoints.d.ts +2 -1
  247. package/dist/server/app-prerender-static-params.d.ts +2 -1
  248. package/dist/server/app-prerender-static-params.js +3 -2
  249. package/dist/server/app-render-dependency.d.ts +11 -1
  250. package/dist/server/app-render-dependency.js +65 -6
  251. package/dist/server/app-request-stage-independent-entry.d.ts +2 -1
  252. package/dist/server/app-request-stage-independent-entry.js +2 -2
  253. package/dist/server/app-response-stage-entry.d.ts +2 -1
  254. package/dist/server/app-response-stage-entry.js +3 -3
  255. package/dist/server/app-route-handler-cache.d.ts +2 -1
  256. package/dist/server/app-route-handler-dispatch.d.ts +2 -1
  257. package/dist/server/app-route-handler-dispatch.js +4 -2
  258. package/dist/server/app-route-handler-execution.d.ts +4 -1
  259. package/dist/server/app-route-handler-execution.js +9 -2
  260. package/dist/server/app-route-handler-middleware-context.d.ts +2 -1
  261. package/dist/server/app-route-handler-middleware-context.js +3 -3
  262. package/dist/server/app-route-handler-response.d.ts +4 -2
  263. package/dist/server/app-route-handler-response.js +2 -2
  264. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  265. package/dist/server/app-route-handler-runtime.js +20 -3
  266. package/dist/server/app-route-module-loader.d.ts +18 -1
  267. package/dist/server/app-route-module-loader.js +15 -6
  268. package/dist/server/app-route-tree-prefetch.d.ts +2 -1
  269. package/dist/server/app-route-tree.d.ts +20 -0
  270. package/dist/server/app-route-tree.js +18 -0
  271. package/dist/server/app-router-entry.js +2 -2
  272. package/dist/server/app-rsc-combined-handler.js +8 -1
  273. package/dist/server/app-rsc-error-handler.d.ts +2 -1
  274. package/dist/server/app-rsc-errors.d.ts +9 -1
  275. package/dist/server/app-rsc-errors.js +9 -2
  276. package/dist/server/app-rsc-handler.d.ts +6 -2
  277. package/dist/server/app-rsc-handler.js +152 -140
  278. package/dist/server/app-rsc-response-finalizer.d.ts +7 -1
  279. package/dist/server/app-rsc-response-finalizer.js +4 -1
  280. package/dist/server/app-rsc-response-stage.js +3 -1
  281. package/dist/server/app-rsc-route-matching.d.ts +39 -1
  282. package/dist/server/app-rsc-route-matching.js +25 -1
  283. package/dist/server/app-segment-config.d.ts +2 -1
  284. package/dist/server/app-segment-config.js +1 -14
  285. package/dist/server/app-server-action-execution.d.ts +25 -7
  286. package/dist/server/app-server-action-execution.js +53 -59
  287. package/dist/server/app-ssr-entry.d.ts +12 -0
  288. package/dist/server/app-ssr-entry.js +104 -48
  289. package/dist/server/app-ssr-error-meta.d.ts +2 -1
  290. package/dist/server/app-ssr-search-params-gate.d.ts +2 -1
  291. package/dist/server/app-ssr-stream.d.ts +29 -10
  292. package/dist/server/app-ssr-stream.js +311 -67
  293. package/dist/server/app-static-generation.d.ts +2 -1
  294. package/dist/server/app-streamed-client-references.d.ts +39 -0
  295. package/dist/server/app-streamed-client-references.js +156 -0
  296. package/dist/server/app-visited-response-cache.d.ts +2 -1
  297. package/dist/server/app-worker-stages.d.ts +2 -1
  298. package/dist/server/artifact-compatibility.d.ts +2 -1
  299. package/dist/server/bfcache-identity.d.ts +2 -1
  300. package/dist/server/cache-headers.d.ts +2 -1
  301. package/dist/server/cacheability-request.d.ts +2 -1
  302. package/dist/server/client-reuse-manifest.d.ts +2 -1
  303. package/dist/server/config-headers.d.ts +2 -1
  304. package/dist/server/config-headers.js +12 -3
  305. package/dist/server/csp.d.ts +2 -1
  306. package/dist/server/default-global-not-found-module.d.ts +2 -1
  307. package/dist/server/default-http-access-fallback-modules.d.ts +33 -0
  308. package/dist/server/default-http-access-fallback-modules.js +31 -0
  309. package/dist/server/defer-until-stream-consumed.d.ts +34 -0
  310. package/dist/server/defer-until-stream-consumed.js +154 -21
  311. package/dist/server/dev-lockfile.d.ts +32 -2
  312. package/dist/server/dev-lockfile.js +46 -2
  313. package/dist/server/dev-module-runner.d.ts +2 -1
  314. package/dist/server/dev-server.js +10 -3
  315. package/dist/server/fetch-handler.js +1 -2
  316. package/dist/server/file-based-metadata.d.ts +2 -1
  317. package/dist/server/framework-tracer.d.ts +2 -1
  318. package/dist/server/html.d.ts +1 -0
  319. package/dist/server/html.js +19 -1
  320. package/dist/server/http-conditional.d.ts +2 -1
  321. package/dist/server/http-error-responses.d.ts +2 -1
  322. package/dist/server/implicit-tags.d.ts +2 -1
  323. package/dist/server/isolate-shared-promise.d.ts +10 -0
  324. package/dist/server/isolate-shared-promise.js +33 -0
  325. package/dist/server/isr-cache.js +3 -2
  326. package/dist/server/metadata-route-build-data.d.ts +2 -1
  327. package/dist/server/metadata-route-build-data.js +1 -1
  328. package/dist/server/metadata-route-response.d.ts +2 -1
  329. package/dist/server/metadata-route-response.js +1 -1
  330. package/dist/server/metadata-routes.d.ts +2 -1
  331. package/dist/server/middleware-matcher-pattern.d.ts +0 -1
  332. package/dist/server/middleware-matcher-pattern.js +11 -140
  333. package/dist/server/middleware-matcher.js +5 -6
  334. package/dist/server/middleware-response-headers.d.ts +31 -0
  335. package/dist/server/middleware-response-headers.js +57 -1
  336. package/dist/server/middleware-runtime.d.ts +2 -1
  337. package/dist/server/middleware-runtime.js +26 -13
  338. package/dist/server/navigation-planner.d.ts +2 -2
  339. package/dist/server/navigation-trace.d.ts +2 -1
  340. package/dist/server/next-error-digest.d.ts +2 -1
  341. package/dist/server/nitro-public-files-plugin.d.ts +39 -0
  342. package/dist/server/nitro-public-files-plugin.js +106 -0
  343. package/dist/server/nitro-public-files.d.ts +22 -0
  344. package/dist/server/nitro-public-files.js +39 -0
  345. package/dist/server/opentelemetry-api.d.ts +2 -1
  346. package/dist/server/operation-token.d.ts +2 -1
  347. package/dist/server/pages-api-route.d.ts +2 -1
  348. package/dist/server/pages-asset-tags.d.ts +7 -1
  349. package/dist/server/pages-asset-tags.js +9 -2
  350. package/dist/server/pages-body-parser-config.d.ts +2 -1
  351. package/dist/server/pages-client-assets.d.ts +8 -1
  352. package/dist/server/pages-data-route.d.ts +2 -1
  353. package/dist/server/pages-document-asset-props.d.ts +2 -1
  354. package/dist/server/pages-document-initial-props.d.ts +2 -1
  355. package/dist/server/pages-execution-tracing.d.ts +2 -1
  356. package/dist/server/pages-execution-tracing.js +1 -1
  357. package/dist/server/pages-get-initial-props.d.ts +2 -1
  358. package/dist/server/pages-i18n.d.ts +18 -0
  359. package/dist/server/pages-i18n.js +18 -2
  360. package/dist/server/pages-node-compat.d.ts +5 -3
  361. package/dist/server/pages-node-compat.js +35 -16
  362. package/dist/server/pages-page-data.d.ts +7 -1
  363. package/dist/server/pages-page-data.js +35 -12
  364. package/dist/server/pages-page-handler.d.ts +3 -2
  365. package/dist/server/pages-page-handler.js +16 -7
  366. package/dist/server/pages-page-method.d.ts +2 -1
  367. package/dist/server/pages-page-response.d.ts +2 -1
  368. package/dist/server/pages-preview.d.ts +2 -1
  369. package/dist/server/pages-readiness.d.ts +2 -1
  370. package/dist/server/pages-request-pipeline.d.ts +8 -2
  371. package/dist/server/pages-request-pipeline.js +35 -40
  372. package/dist/server/pages-request-stage-entry.d.ts +2 -1
  373. package/dist/server/pages-request-stage-entry.js +16 -6
  374. package/dist/server/pages-response-stage-entry.d.ts +2 -1
  375. package/dist/server/pages-response-stage-entry.js +1 -1
  376. package/dist/server/pages-revalidate.js +1 -0
  377. package/dist/server/pages-route-data-kind.d.ts +2 -1
  378. package/dist/server/pages-router-entry.js +1 -1
  379. package/dist/server/prerender-cache-life-header.d.ts +2 -1
  380. package/dist/server/prerender-route-params.d.ts +2 -1
  381. package/dist/server/prod-server.d.ts +2 -2
  382. package/dist/server/prod-server.js +80 -36
  383. package/dist/server/proxy-trust.d.ts +6 -0
  384. package/dist/server/proxy-trust.js +12 -6
  385. package/dist/server/request-log.d.ts +2 -1
  386. package/dist/server/request-pipeline.d.ts +5 -0
  387. package/dist/server/request-pipeline.js +12 -1
  388. package/dist/server/request-tracing.d.ts +2 -1
  389. package/dist/server/request-tracing.js +1 -1
  390. package/dist/server/response-start-tracing.js +7 -29
  391. package/dist/server/rsc-render-stream.d.ts +4 -0
  392. package/dist/server/rsc-render-stream.js +25 -0
  393. package/dist/server/seed-cache.d.ts +2 -1
  394. package/dist/server/seed-cache.js +1 -1
  395. package/dist/server/server-reference-loader.js +10 -1
  396. package/dist/server/static-file-cache.d.ts +2 -1
  397. package/dist/server/static-file-cache.js +16 -3
  398. package/dist/server/static-layout-client-reuse-proof.d.ts +2 -1
  399. package/dist/server/worker-revalidation-context.d.ts +2 -1
  400. package/dist/server/worker-stages.d.ts +2 -1
  401. package/dist/server/worker-utils.js +1 -1
  402. package/dist/shims/app.d.ts +2 -1
  403. package/dist/shims/cache-callable-runtime.d.ts +2 -1
  404. package/dist/shims/cache-handler.d.ts +8 -3
  405. package/dist/shims/cache-handler.js +32 -11
  406. package/dist/shims/cache-request-state.d.ts +40 -3
  407. package/dist/shims/cache-request-state.js +70 -5
  408. package/dist/shims/cache-runtime.js +47 -19
  409. package/dist/shims/cache.d.ts +2 -2
  410. package/dist/shims/cache.js +17 -8
  411. package/dist/shims/caught-not-found-boundaries.d.ts +15 -0
  412. package/dist/shims/caught-not-found-boundaries.js +48 -0
  413. package/dist/shims/cdn-purge-routes.d.ts +25 -0
  414. package/dist/shims/cdn-purge-routes.js +77 -0
  415. package/dist/shims/client-locale.js +2 -1
  416. package/dist/shims/client-page-root.d.ts +2 -1
  417. package/dist/shims/config.d.ts +2 -1
  418. package/dist/shims/default-forbidden.d.ts +4 -0
  419. package/dist/shims/default-forbidden.js +18 -0
  420. package/dist/shims/default-not-found.d.ts +0 -5
  421. package/dist/shims/default-not-found.js +5 -41
  422. package/dist/shims/default-unauthorized.d.ts +4 -0
  423. package/dist/shims/default-unauthorized.js +18 -0
  424. package/dist/shims/dynamic.js +17 -4
  425. package/dist/shims/error-boundary.d.ts +39 -3
  426. package/dist/shims/error-boundary.js +31 -16
  427. package/dist/shims/font-google.js +1 -2
  428. package/dist/shims/font-utils.d.ts +3 -2
  429. package/dist/shims/font-utils.js +5 -3
  430. package/dist/shims/form.js +9 -4
  431. package/dist/shims/http-access-error-fallback.d.ts +7 -0
  432. package/dist/shims/http-access-error-fallback.js +47 -0
  433. package/dist/shims/image-props.d.ts +93 -0
  434. package/dist/shims/image-props.js +323 -0
  435. package/dist/shims/image.d.ts +4 -39
  436. package/dist/shims/image.js +4 -319
  437. package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +22 -2
  438. package/dist/shims/internal/app-prefetch-fetch-queue.js +71 -18
  439. package/dist/shims/internal/app-route-prefetch-policy.d.ts +14 -3
  440. package/dist/shims/internal/app-route-prefetch-policy.js +29 -4
  441. package/dist/shims/internal/cache-context-accessor.d.ts +2 -1
  442. package/dist/shims/internal/client-reference-loader.d.ts +7 -0
  443. package/dist/shims/internal/client-reference-loader.js +91 -0
  444. package/dist/shims/internal/cookie-serialize.d.ts +2 -1
  445. package/dist/shims/internal/cookies.js +1 -2
  446. package/dist/shims/internal/headers-state.d.ts +9 -0
  447. package/dist/shims/internal/headers-state.js +19 -2
  448. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +6 -2
  449. package/dist/shims/internal/hybrid-client-route-owner-direct.js +31 -14
  450. package/dist/shims/internal/hybrid-client-route-owner.js +8 -8
  451. package/dist/shims/internal/interpolate-as.d.ts +2 -1
  452. package/dist/shims/internal/pages-data-target.d.ts +2 -1
  453. package/dist/shims/internal/pages-data-url.d.ts +2 -1
  454. package/dist/shims/internal/thenable-well-known-properties.d.ts +2 -1
  455. package/dist/shims/link.js +198 -53
  456. package/dist/shims/metadata.d.ts +32 -11
  457. package/dist/shims/metadata.js +88 -26
  458. package/dist/shims/navigation-errors.d.ts +2 -1
  459. package/dist/shims/navigation.d.ts +23 -3
  460. package/dist/shims/navigation.js +91 -25
  461. package/dist/shims/og.js +35 -10
  462. package/dist/shims/pages-router-runtime.d.ts +2 -1
  463. package/dist/shims/ppr-fallback-shell.d.ts +2 -1
  464. package/dist/shims/request-context.d.ts +13 -0
  465. package/dist/shims/router.js +33 -43
  466. package/dist/shims/server.d.ts +31 -4
  467. package/dist/shims/server.js +58 -10
  468. package/dist/shims/thenable-params.d.ts +6 -1
  469. package/dist/shims/thenable-params.js +11 -2
  470. package/dist/shims/unified-request-context.d.ts +5 -0
  471. package/dist/shims/unified-request-context.js +5 -3
  472. package/dist/typegen.d.ts +2 -1
  473. package/dist/utils/asset-prefix.d.ts +7 -0
  474. package/dist/utils/asset-prefix.js +10 -1
  475. package/dist/utils/client-build-manifest.d.ts +4 -1
  476. package/dist/utils/client-build-manifest.js +20 -6
  477. package/dist/utils/client-entry-manifest.d.ts +13 -0
  478. package/dist/utils/client-entry-manifest.js +20 -1
  479. package/dist/utils/client-reference-chunks.d.ts +45 -0
  480. package/dist/utils/client-reference-chunks.js +30 -0
  481. package/dist/utils/client-runtime-metadata.d.ts +10 -1
  482. package/dist/utils/client-runtime-metadata.js +53 -9
  483. package/dist/utils/compare.d.ts +9 -0
  484. package/dist/utils/compare.js +15 -1
  485. package/dist/utils/lazy-chunks.d.ts +18 -0
  486. package/dist/utils/lazy-chunks.js +27 -1
  487. package/dist/utils/manifest-paths.d.ts +14 -0
  488. package/dist/utils/manifest-paths.js +22 -1
  489. package/dist/utils/middleware-request-headers.d.ts +2 -1
  490. package/dist/utils/path-to-regexp.d.ts +21 -0
  491. package/dist/{server/middleware-path-to-regexp.js → utils/path-to-regexp.js} +14 -11
  492. package/dist/utils/plugin-react-version.d.ts +15 -0
  493. package/dist/utils/plugin-react-version.js +64 -0
  494. package/dist/utils/project.d.ts +2 -1
  495. package/dist/utils/project.js +9 -1
  496. package/dist/utils/public-routes.d.ts +2 -0
  497. package/dist/utils/public-routes.js +18 -7
  498. package/dist/utils/query.d.ts +2 -1
  499. package/dist/utils/sorted-array.d.ts +2 -1
  500. package/dist/utils/vite-cli-invocation.d.ts +2 -1
  501. package/package.json +5 -5
  502. package/dist/deps/.pnpm/image-size@2.0.2/deps/image-size/dist/index.js +0 -808
  503. package/dist/server/default-not-found-module.d.ts +0 -19
  504. package/dist/server/default-not-found-module.js +0 -18
  505. package/dist/server/middleware-path-to-regexp.d.ts +0 -13
  506. package/dist/server/rsc-stream-hints.d.ts +0 -21
  507. package/dist/server/rsc-stream-hints.js +0 -227
  508. package/dist/utils/regex-safety.d.ts +0 -17
  509. package/dist/utils/regex-safety.js +0 -789
@@ -1,60 +1,32 @@
1
1
  import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
2
  import { buildRequestHeadersFromMiddlewareResponse, getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
3
- import { analyzeRegexSafety } from "../utils/regex-safety.js";
3
+ import { compileConfigSource, configSourceError, execConfigSource, matchConfigPattern } from "./config-source.js";
4
4
  import { normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
5
5
  import { isExternalUrl } from "../utils/external-url.js";
6
6
  import { attachRequestCfMetadata } from "../server/request-pipeline.js";
7
7
  import { substituteDestinationParams, substituteRedirectDestinationQuery } from "./destination-params.js";
8
+ import { compileHeaderParamTemplate, isValidHeaderPair } from "./header-params.js";
8
9
  //#region src/config/config-matchers.ts
9
10
  /**
10
- * Cache for compiled regex patterns in matchConfigPattern.
11
- *
12
- * Redirect/rewrite patterns are static — they come from next.config.js and
13
- * never change at runtime. Without caching, every request that hits the regex
14
- * branch re-runs the full tokeniser walk + isSafeRegex + new RegExp() for
15
- * every rule in the array. On apps with many locale-prefixed rules (which all
16
- * contain `(` and therefore enter the regex branch) this dominated profiling
17
- * at ~2.4 seconds of CPU self-time.
18
- *
19
- * Value is `null` when safeRegExp rejected the pattern (ReDoS risk), so we
20
- * skip it on subsequent requests too without re-running the scanner.
21
- */
22
- const _compiledPatternCache = /* @__PURE__ */ new Map();
23
- /**
24
- * Cache for compiled header source regexes in matchHeaders.
25
- *
26
- * Each NextHeader rule has a `source` that is run through escapeHeaderSource()
27
- * then safeRegExp() to produce a RegExp. Both are pure functions of the source
28
- * string and the result never changes. Without caching, every request
29
- * re-runs the full escapeHeaderSource tokeniser + isSafeRegex scan + new RegExp()
30
- * for every header rule.
31
- *
32
- * Value is `null` when safeRegExp rejected the pattern (ReDoS risk).
33
- */
34
- const _compiledHeaderSourceCache = /* @__PURE__ */ new Map();
35
- /**
36
11
  * Cache for compiled has/missing condition value regexes in checkSingleCondition.
37
12
  *
38
- * Each has/missing condition may carry a `value` string that is passed directly
39
- * to safeRegExp() for matching against header/cookie/query/host values. The
40
- * condition objects are static (from next.config.js) so the compiled RegExp
41
- * never changes. Without caching, safeRegExp() is called on every request for
42
- * every condition on every rule.
13
+ * Each has/missing condition may carry a `value` string that is compiled as a
14
+ * regex for matching against header/cookie/query/host values. The condition
15
+ * objects are static (from next.config.js) so the compiled RegExp never
16
+ * changes. Without caching, the value is recompiled on every request for every
17
+ * condition on every rule.
43
18
  *
44
- * Value is `null` when safeRegExp rejected the pattern, or `false` when the
45
- * value string was undefined (no regex needed — use exact string comparison).
19
+ * Value is `null` when the value is not a valid regex (use exact string
20
+ * comparison instead).
46
21
  */
47
22
  const _compiledConditionCache = /* @__PURE__ */ new Map();
48
23
  /**
49
- * Generic helper for the regex compilation caches above.
24
+ * Generic helper for the regex compilation cache above.
50
25
  *
51
- * Each cache stores the compiled artifact (or `null` when safeRegExp rejected
52
- * the pattern) the first time a key is seen, and reuses it forever. The
26
+ * The cache stores the compiled artifact (or `null` when the pattern could
27
+ * not be compiled) the first time a key is seen, and reuses it forever. The
53
28
  * `undefined` sentinel distinguishes "not yet seen" from "seen and rejected"
54
- * so we never re-run isSafeRegex on the same input.
55
- *
56
- * Keep the security path intact: `compile()` is responsible for calling
57
- * safeRegExp(); this helper only handles caching.
29
+ * so we never recompile the same input.
58
30
  */
59
31
  function getCachedRegex(cache, key, compile) {
60
32
  let value = cache.get(key);
@@ -106,8 +78,36 @@ function getCachedRegex(cache, key, compile) {
106
78
  * by `applyLocaleToRoutes` for the locale-capture variant)
107
79
  * Both forms benefit from O(1) suffix lookup; the optionality is recorded
108
80
  * on the entry so we know whether to try the no-locale-prefix bucket.
81
+ *
82
+ * The index must match exactly what the compiled source would. So the param
83
+ * name follows path-to-regexp's `\w` name lexer, the alternatives are plain
84
+ * words, possibly empty (a `.*` alternative could span several segments), and
85
+ * the suffix class only admits characters path-to-regexp treats as literals:
86
+ * `:` (param), `*` and `+` (modifiers), `(`, `{`, `?` and `\` are excluded.
109
87
  */
110
- const _LOCALE_STATIC_RE = /^\/:[\w-]+\(([^)]+)\)(\??)\/([a-zA-Z0-9_~.%@!$&'*+,;=:/-]+)$/;
88
+ const _LOCALE_STATIC_RE = /^\/:\w+\(([\w-]*(?:\|[\w-]*)*)\)(\??)\/([a-zA-Z0-9_~.%@!$&',;=/-]+)$/;
89
+ /**
90
+ * ECMAScript's non-Unicode Canonicalize operation, which a RegExp with `i` and
91
+ * without `u` compares characters by: it keeps the original UTF-16 code unit
92
+ * when uppercasing expands it or maps a non-ASCII character to ASCII.
93
+ */
94
+ function canonicalizeIgnoreCase(character) {
95
+ const upper = character.toUpperCase();
96
+ if (upper.length !== 1) return character;
97
+ if (character.charCodeAt(0) >= 128 && upper.charCodeAt(0) < 128) return character;
98
+ return upper;
99
+ }
100
+ /**
101
+ * Folds case as a RegExp with `i` and without `u` compares characters, as
102
+ * the compiled source does: `toLowerCase()` would also read the Kelvin sign
103
+ * as `k`.
104
+ */
105
+ function foldCase(value) {
106
+ if (!/[^ -~]/.test(value)) return value.toUpperCase();
107
+ let folded = "";
108
+ for (let i = 0; i < value.length; i++) folded += canonicalizeIgnoreCase(value[i]);
109
+ return folded;
110
+ }
111
111
  const _redirectIndexCache = /* @__PURE__ */ new WeakMap();
112
112
  /**
113
113
  * Build (or retrieve from cache) the redirect index for a given redirects array.
@@ -124,12 +124,12 @@ function _getRedirectIndex(redirects) {
124
124
  for (let i = 0; i < redirects.length; i++) {
125
125
  const redirect = redirects[i];
126
126
  const m = _LOCALE_STATIC_RE.exec(redirect.source);
127
- if (m) {
127
+ if (m && m[1] !== "" && !m[3].endsWith("/")) {
128
128
  const paramName = redirect.source.slice(2, redirect.source.indexOf("("));
129
129
  const alternation = m[1];
130
130
  const optional = m[2] === "?";
131
131
  const suffix = "/" + m[3];
132
- const altRe = safeRegExp("^(?:" + alternation + ")$", "i");
132
+ const altRe = compileRegExp("^(?:" + alternation + ")$", "i");
133
133
  if (!altRe) {
134
134
  linear.push([i, redirect]);
135
135
  continue;
@@ -141,7 +141,7 @@ function _getRedirectIndex(redirects) {
141
141
  redirect,
142
142
  originalIndex: i
143
143
  };
144
- const bucketKey = suffix.toLowerCase();
144
+ const bucketKey = foldCase(suffix);
145
145
  const bucket = localeStatic.get(bucketKey);
146
146
  if (bucket) bucket.push(entry);
147
147
  else localeStatic.set(bucketKey, [entry]);
@@ -185,77 +185,18 @@ function stripHopByHopRequestHeaders(headers) {
185
185
  for (const token of connectionTokens) headers.delete(token);
186
186
  }
187
187
  /**
188
- * Detect regex patterns vulnerable to catastrophic backtracking (ReDoS).
189
- *
190
- * Uses the same deterministic structural analysis as middleware matcher
191
- * validation. Nested bounded repetition is accepted only when its repeated
192
- * language has fixed width and unambiguous branches; a fixed outer count can
193
- * otherwise still cause polynomially catastrophic backtracking on long near
194
- * misses.
195
- *
196
- * Returns true if the pattern appears safe, false if it's potentially dangerous.
197
- */
198
- function isSafeRegex(pattern, flags) {
199
- return analyzeRegexSafety(pattern, { ignoreCase: flags?.includes("i") }) === null;
200
- }
201
- /**
202
- * Compile a regex pattern safely. Returns the compiled RegExp or null if the
203
- * pattern is invalid or vulnerable to ReDoS.
188
+ * Compile a regex from next.config, or return null when it is invalid.
204
189
  *
205
- * Logs a warning when a pattern is rejected so developers can fix their config.
190
+ * Like Next.js, patterns are not checked for catastrophic backtracking: they
191
+ * run as written.
206
192
  */
207
- function safeRegExp(pattern, flags) {
208
- if (!isSafeRegex(pattern, flags)) {
209
- console.warn(`[vinext] Rejecting potentially unsafe regex pattern (ReDoS risk): ${pattern}\n Nested or ambiguous repetition can cause catastrophic backtracking.\n Simplify the pattern to make repeated matches fixed and unambiguous.`);
210
- return null;
211
- }
193
+ function compileRegExp(pattern, flags) {
212
194
  try {
213
195
  return new RegExp(pattern, flags);
214
196
  } catch {
215
197
  return null;
216
198
  }
217
199
  }
218
- /**
219
- * Convert a Next.js header/rewrite/redirect source pattern into a regex string.
220
- *
221
- * Regex groups in the source (e.g. `(\d+)`) are extracted first, the remaining
222
- * text is escaped/converted in a **single pass** (avoiding chained `.replace()`
223
- * which CodeQL flags as incomplete sanitization), then groups are restored.
224
- */
225
- function escapeHeaderSource(source) {
226
- const S = "";
227
- const groups = [];
228
- const withPlaceholders = source.replace(/\(([^)]+)\)/g, (_m, inner) => {
229
- groups.push(inner);
230
- return `${S}G${groups.length - 1}${S}`;
231
- });
232
- let result = "";
233
- const re = new RegExp(`${S}G(\\d+)${S}|:[\\w-]+|[.+?*]|[^.+?*:\\uE000]+`, "g");
234
- let m;
235
- while ((m = re.exec(withPlaceholders)) !== null) if (m[1] !== void 0) result += `(${groups[Number(m[1])]})`;
236
- else if (m[0].startsWith(":")) {
237
- const constraintMatch = withPlaceholders.slice(re.lastIndex).match(new RegExp(`^${S}G(\\d+)${S}`));
238
- if (constraintMatch) {
239
- re.lastIndex += constraintMatch[0].length;
240
- result += `(${groups[Number(constraintMatch[1])]})`;
241
- } else result += "[^/]+";
242
- } else switch (m[0]) {
243
- case ".":
244
- result += "\\.";
245
- break;
246
- case "+":
247
- result += "\\+";
248
- break;
249
- case "?":
250
- result += "\\?";
251
- break;
252
- case "*":
253
- result += ".*";
254
- break;
255
- default: result += m[0];
256
- }
257
- return result;
258
- }
259
200
  const _BASEPATH_DEFAULT = {
260
201
  basePath: "",
261
202
  hadBasePath: true
@@ -360,11 +301,11 @@ function matchSingleCondition(condition, ctx) {
360
301
  }
361
302
  /**
362
303
  * Return a cached RegExp for a has/missing condition value string, compiling
363
- * on first use. Returns null if safeRegExp rejected the pattern or if the
364
- * value is not a valid regex (fall back to exact string comparison).
304
+ * on first use. Returns null if the value is not a valid regex (fall back to
305
+ * exact string comparison).
365
306
  */
366
307
  function _cachedConditionRegex(value) {
367
- return getCachedRegex(_compiledConditionCache, value, () => safeRegExp(`^${value}$`));
308
+ return getCachedRegex(_compiledConditionCache, value, () => compileRegExp(`^${value}$`));
368
309
  }
369
310
  /**
370
311
  * Check all has/missing conditions for a config rule.
@@ -389,125 +330,13 @@ function checkHasConditions(has, missing, ctx) {
389
330
  return collectConditionParams(has, missing, ctx) !== null;
390
331
  }
391
332
  /**
392
- * If the current position in `str` starts with a parenthesized group, consume
393
- * it and advance `re.lastIndex` past the closing `)`. Returns the group
394
- * contents or null if no group is present.
395
- */
396
- function extractConstraint(str, re) {
397
- if (str[re.lastIndex] !== "(") return null;
398
- const start = re.lastIndex + 1;
399
- let depth = 1;
400
- let i = start;
401
- while (i < str.length && depth > 0) {
402
- if (str[i] === "(") depth++;
403
- else if (str[i] === ")") depth--;
404
- i++;
405
- }
406
- if (depth !== 0) return null;
407
- re.lastIndex = i;
408
- return str.slice(start, i - 1);
409
- }
410
- /**
411
- * Match a Next.js config pattern (from redirects/rewrites sources) against a pathname.
412
- * Returns matched params or null.
413
- *
414
- * Supports:
415
- * :param - matches a single path segment
416
- * :param* - matches zero or more segments (catch-all)
417
- * :param+ - matches one or more segments
418
- * (regex) - inline regex patterns in the source
419
- * :param(constraint) - named param with inline regex constraint
420
- */
421
- /**
422
- * Strip a single trailing slash from a pathname for config-source matching.
423
- *
424
- * Next.js conditionally appends `(/)?` to rewrite/redirect/header source
425
- * regexes when `trailingSlash: true` (see Next.js
426
- * `resolve-rewrites.ts` and `server-utils.ts:checkRewrite`). Rather than
427
- * threading the trailingSlash flag through every matcher, we unconditionally
428
- * strip a trailing slash from the incoming pathname. When `trailingSlash: false`
429
- * the request pipeline emits a normalizing redirect (step 3) before config
430
- * rewrites/redirects (step 6) ever run, so the pathname is already slash-free;
431
- * the unconditional strip is defense-in-depth for that ordering. When
432
- * `trailingSlash: true` it bridges the gap between the canonicalized request
433
- * path (`/rewrite-1/`) and source patterns written without a trailing slash
434
- * (`/rewrite-1`).
435
- *
436
- * The root path `"/"` is preserved as-is.
333
+ * Strip one trailing slash, preserving the root path `"/"`. Compiled sources
334
+ * accept an optional trailing slash, so the redirect index looks up the
335
+ * slash-free form.
437
336
  */
438
337
  function stripTrailingSlashForConfigMatch(value) {
439
338
  return value.length > 1 && value.endsWith("/") ? value.slice(0, -1) : value;
440
339
  }
441
- function configPathEquals(a, b) {
442
- return a.toLowerCase() === b.toLowerCase();
443
- }
444
- function configPathStartsWith(pathname, prefix) {
445
- return pathname.slice(0, prefix.length).toLowerCase() === prefix.toLowerCase();
446
- }
447
- function matchConfigPattern(pathname, pattern) {
448
- const pathnameHadTrailingSlash = pathname.length > 1 && pathname.endsWith("/");
449
- pathname = stripTrailingSlashForConfigMatch(pathname);
450
- if (pathnameHadTrailingSlash) pattern = stripTrailingSlashForConfigMatch(pattern);
451
- const catchAllAnchor = /:[\w-]+[*+]/.test(pattern);
452
- const namedParamCount = (pattern.match(/:[\w-]+/g) || []).length;
453
- if (pattern.includes("(") || pattern.includes("\\") || /:[\w-]+[*+][^/]/.test(pattern) || /:[\w-]+\./.test(pattern) || /[^/]:[\w-]+/.test(pattern) || catchAllAnchor && namedParamCount > 1) try {
454
- const compiled = getCachedRegex(_compiledPatternCache, pattern, () => {
455
- const paramNames = [];
456
- let regexStr = "";
457
- const tokenRe = /:([\w-]+)|[.]|[^:.]+/g;
458
- let tok;
459
- while ((tok = tokenRe.exec(pattern)) !== null) if (tok[1] !== void 0) {
460
- const name = tok[1];
461
- const rest = pattern.slice(tokenRe.lastIndex);
462
- if (rest.startsWith("*") || rest.startsWith("+")) {
463
- const quantifier = rest[0];
464
- tokenRe.lastIndex += 1;
465
- const constraint = extractConstraint(pattern, tokenRe);
466
- paramNames.push(name);
467
- if (constraint !== null) regexStr += `(${constraint})`;
468
- else regexStr += quantifier === "*" ? "(.*)" : "(.+)";
469
- } else {
470
- const constraint = extractConstraint(pattern, tokenRe);
471
- paramNames.push(name);
472
- regexStr += constraint !== null ? `(${constraint})` : "([^/]+)";
473
- }
474
- } else if (tok[0] === ".") regexStr += "\\.";
475
- else regexStr += tok[0];
476
- const re = safeRegExp("^" + regexStr + "$", "i");
477
- return re ? {
478
- re,
479
- paramNames
480
- } : null;
481
- });
482
- if (!compiled) return null;
483
- const match = compiled.re.exec(pathname);
484
- if (!match) return null;
485
- const params = Object.create(null);
486
- for (let i = 0; i < compiled.paramNames.length; i++) params[compiled.paramNames[i]] = match[i + 1] ?? "";
487
- return params;
488
- } catch {}
489
- const catchAllMatch = pattern.match(/:([\w-]+)(\*|\+)$/);
490
- if (catchAllMatch) {
491
- const prefix = pattern.slice(0, pattern.lastIndexOf(":"));
492
- const paramName = catchAllMatch[1];
493
- const isPlus = catchAllMatch[2] === "+";
494
- const prefixNoSlash = prefix.replace(/\/$/, "");
495
- if (!configPathStartsWith(pathname, prefixNoSlash)) return null;
496
- const charAfter = pathname[prefixNoSlash.length];
497
- if (charAfter !== void 0 && charAfter !== "/") return null;
498
- const rest = pathname.slice(prefixNoSlash.length);
499
- if (isPlus && (!rest || rest === "/")) return null;
500
- let restValue = rest.startsWith("/") ? rest.slice(1) : rest;
501
- return { [paramName]: restValue };
502
- }
503
- const parts = pattern.split("/");
504
- const pathParts = pathname.split("/");
505
- if (parts.length !== pathParts.length) return null;
506
- const params = Object.create(null);
507
- for (let i = 0; i < parts.length; i++) if (parts[i].startsWith(":")) params[parts[i].slice(1)] = pathParts[i];
508
- else if (!configPathEquals(parts[i], pathParts[i])) return null;
509
- return params;
510
- }
511
340
  /**
512
341
  * Apply redirect rules from next.config.js.
513
342
  * Returns the redirect info if a redirect was matched, or null.
@@ -550,7 +379,7 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
550
379
  let localeMatch = null;
551
380
  let localeMatchIndex = Infinity;
552
381
  if (index.localeStatic.size > 0) {
553
- const noLocaleBucket = index.localeStatic.get(normalizedPathname.toLowerCase());
382
+ const noLocaleBucket = index.localeStatic.get(foldCase(normalizedPathname));
554
383
  if (noLocaleBucket) for (const entry of noLocaleBucket) {
555
384
  if (!entry.optional) continue;
556
385
  if (entry.originalIndex >= localeMatchIndex) continue;
@@ -559,10 +388,13 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
559
388
  onRuleSourceMatch?.(redirect);
560
389
  const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
561
390
  if (!conditionParams) continue;
562
- localeMatch = resolveRedirectMatch(redirect, {
563
- [entry.paramName]: "",
564
- ...conditionParams
565
- });
391
+ localeMatch = {
392
+ redirect,
393
+ params: {
394
+ [entry.paramName]: "",
395
+ ...conditionParams
396
+ }
397
+ };
566
398
  localeMatchIndex = entry.originalIndex;
567
399
  break;
568
400
  }
@@ -570,7 +402,7 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
570
402
  if (slashTwo !== -1) {
571
403
  const suffix = normalizedPathname.slice(slashTwo);
572
404
  const localePart = normalizedPathname.slice(1, slashTwo);
573
- const localeBucket = index.localeStatic.get(suffix.toLowerCase());
405
+ const localeBucket = index.localeStatic.get(foldCase(suffix));
574
406
  if (localeBucket) for (const entry of localeBucket) {
575
407
  if (entry.originalIndex >= localeMatchIndex) continue;
576
408
  if (!entry.altRe.test(localePart)) continue;
@@ -579,10 +411,13 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
579
411
  onRuleSourceMatch?.(redirect);
580
412
  const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
581
413
  if (!conditionParams) continue;
582
- localeMatch = resolveRedirectMatch(redirect, {
583
- [entry.paramName]: localePart,
584
- ...conditionParams
585
- });
414
+ localeMatch = {
415
+ redirect,
416
+ params: {
417
+ [entry.paramName]: localePart,
418
+ ...conditionParams
419
+ }
420
+ };
586
421
  localeMatchIndex = entry.originalIndex;
587
422
  break;
588
423
  }
@@ -599,10 +434,10 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
599
434
  return resolveRedirectMatch(redirect, {
600
435
  ...params,
601
436
  ...conditionParams
602
- });
437
+ }, basePathState);
603
438
  }
604
439
  }
605
- return localeMatch;
440
+ return localeMatch && resolveRedirectMatch(localeMatch.redirect, localeMatch.params, basePathState);
606
441
  }
607
442
  /**
608
443
  * Apply rewrite rules from next.config.js.
@@ -615,17 +450,17 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
615
450
  function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT, paramsPathname = pathname, onRuleSourceMatch) {
616
451
  for (const rewrite of rewrites) {
617
452
  if (!shouldEvaluateRule(rewrite.basePath, basePathState)) continue;
618
- const matchedParams = matchConfigPattern(pathname, rewrite.source);
619
- if (matchedParams) {
453
+ const matched = execConfigSource(pathname, rewrite.source);
454
+ if (matched) {
620
455
  onRuleSourceMatch?.(rewrite);
621
- const params = paramsPathname === pathname ? matchedParams : matchConfigPattern(paramsPathname, rewrite.source) ?? matchedParams;
456
+ const { params, unmatched } = paramsPathname === pathname ? matched : execConfigSource(paramsPathname, rewrite.source) ?? matched;
622
457
  const conditionParams = rewrite.has || rewrite.missing ? collectConditionParams(rewrite.has, rewrite.missing, ctx) : _emptyParams();
623
458
  if (!conditionParams) continue;
624
459
  const rewriteParams = {
625
460
  ...params,
626
461
  ...conditionParams
627
462
  };
628
- return substituteAndSanitizeRewriteDestination(serializeRewriteDestinationTemplate(rewrite.destination), rewriteParams);
463
+ return substituteAndSanitizeRewriteDestination(serializeRewriteDestinationTemplate(rewrite.destination), rewriteParams, unmatched.filter((key) => !(key in conditionParams)));
629
464
  }
630
465
  }
631
466
  return null;
@@ -663,26 +498,100 @@ function matchesRewriteSource(pathname, rewrite, basePathState = _BASEPATH_DEFAU
663
498
  function substituteAndSanitizeDestination(destination, params, kind) {
664
499
  return sanitizeDestination(substituteDestinationParams(destination, params, kind));
665
500
  }
666
- function resolveRedirectMatch(redirect, params) {
501
+ /**
502
+ * Prepare a redirect destination template as Next.js does before substituting
503
+ * params. processRoutes (load-custom-routes.ts) prefixes a local destination
504
+ * with the basePath (a bare `/` becomes the basePath, except on the
505
+ * default-locale variant), then parseDestination (prepare-destination.ts) parses it
506
+ * with the URL parser, its param tokens escaped so they survive. This trims
507
+ * whitespace, resolves dot segments and backslashes, percent-encodes the path
508
+ * and hash, punycodes the host, drops an empty query or hash, and throws (a
509
+ * 500) on an invalid absolute URL. Captures are substituted afterwards, so the
510
+ * parser never sees them. The query text is kept verbatim, as
511
+ * preserveRedirectDestinationQuery emits it.
512
+ */
513
+ function parseRedirectDestinationTemplate(redirect, params, { basePath, hadBasePath }) {
514
+ let template = redirect.destination;
515
+ const isLocal = template.startsWith("/");
516
+ if (isLocal && basePath && hadBasePath) template = template === "/" && !redirect.defaultLocaleVariant ? basePath : basePath + template;
517
+ template = template.replace(LOCATION_STRIPPED_RE, "");
518
+ let marker = "__ESC_COLON_";
519
+ while (template.toLowerCase().includes(marker.toLowerCase())) marker = `_${marker}`;
520
+ const paramValues = Object.values(params);
521
+ let literalColon = "__ESC_LITERAL_COLON_";
522
+ while (template.includes(literalColon) || paramValues.some((value) => value.includes(literalColon))) literalColon = `_${literalColon}`;
523
+ const keys = Object.keys(params).sort((a, b) => b.length - a.length);
524
+ const keyPattern = keys.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
525
+ const escaped = keys.length === 0 ? template : template.replace(new RegExp(`\\\\.|:(${keyPattern})(?![A-Za-z0-9_])`, "gs"), (match, key) => key ? `${marker}${key}` : match);
526
+ const consumeEscapes = (value) => value.replace(/\\(.)/gs, (_, char) => char === ":" ? literalColon : char);
527
+ let url;
528
+ try {
529
+ url = isLocal ? new URL(`http://n${escaped}`) : new URL(escaped);
530
+ } catch {
531
+ return {
532
+ template,
533
+ literalColon,
534
+ parsed: false
535
+ };
536
+ }
537
+ const hash = url.hash;
538
+ url.search = "";
539
+ url.hash = "";
540
+ const hashIndex = template.indexOf("#");
541
+ const beforeHash = hashIndex === -1 ? template : template.slice(0, hashIndex);
542
+ const queryIndex = beforeHash.indexOf("?");
543
+ const query = queryIndex === -1 ? "" : beforeHash.slice(queryIndex);
544
+ let base = isLocal ? url.pathname : url.href;
545
+ if (!isLocal && (url.username || url.password)) {
546
+ const auth = url.username ? `${encodeURIComponent(url.password ? `${url.username}:${url.password}` : url.username).replace(/%3A/i, ":")}@` : "";
547
+ url.username = "";
548
+ url.password = "";
549
+ base = url.href.replace("//", `//${auth}`);
550
+ }
551
+ if (!isLocal && !SPECIAL_SCHEME_RE.test(url.protocol)) base = base.slice(0, base.length - url.pathname.length) + consumeEscapes(url.pathname);
552
+ const markerRe = new RegExp(`${marker}(${keyPattern})`, "gi");
553
+ const unescape = (value) => keys.length === 0 ? value : value.replace(markerRe, (_, key) => `:${keys.includes(key) ? key : keys.find((k) => k.toLowerCase() === key.toLowerCase())}`);
667
554
  return {
668
- destination: substituteAndSanitizeDestination(redirect.destination, params, "redirect"),
669
- permanent: redirect.permanent,
670
- destinationQuery: substituteRedirectDestinationQuery(redirect.destination, params)
555
+ template: `${unescape(base)}${/^\?&*$/.test(query) ? "" : query}${unescape(consumeEscapes(hash))}`,
556
+ literalColon,
557
+ parsed: true
558
+ };
559
+ }
560
+ const SPECIAL_SCHEME_RE = /^(?:https?|wss?|ftp|file):$/;
561
+ const REDIRECT_ORIGIN_RE = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/?#]*/;
562
+ /**
563
+ * Next.js resolve-routes.ts runs normalizeRepeatedSlashes on the prepared
564
+ * destination's pathname, after params are substituted: backslashes become
565
+ * `/` and runs of slashes collapse, so a basePath-prefixed `//target`
566
+ * becomes `/docs/target`. `destination` starts at the pathname.
567
+ */
568
+ function normalizeRedirectPathnameSlashes(destination) {
569
+ return destination.replace(/^[^?#]*/, (pathname) => pathname.replace(/\\/g, "/").replace(/\/\/+/g, "/"));
570
+ }
571
+ function resolveRedirectMatch(redirect, params, basePathState) {
572
+ const { template: destination, literalColon, parsed } = parseRedirectDestinationTemplate(redirect, params, basePathState);
573
+ const origin = REDIRECT_ORIGIN_RE.exec(destination)?.[0] ?? "";
574
+ return {
575
+ destination: (!parsed ? substituteAndSanitizeDestination(destination, params, "redirect") : origin ? substituteDestinationParams(origin, params, "redirect") + normalizeRedirectPathnameSlashes(substituteDestinationParams(destination.slice(origin.length), params, "redirect")) : normalizeRedirectPathnameSlashes(substituteAndSanitizeDestination(destination, params, "redirect"))).replaceAll(literalColon, ":"),
576
+ statusCode: redirect.statusCode || (redirect.permanent ? 308 : 307),
577
+ destinationQuery: substituteRedirectDestinationQuery(destination, params)
671
578
  };
672
579
  }
673
580
  /**
674
581
  * Match Next.js's rewrite-specific prepareDestination behavior: source params
675
582
  * that are not consumed by the destination path/host are exposed to the target
676
- * page through query.
583
+ * page through query. Optional params that did not match are not params at
584
+ * all in Next.js, so they are neither appended nor count as consumed.
677
585
  *
678
586
  * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/prepare-destination.ts
679
587
  */
680
- function substituteAndSanitizeRewriteDestination(destination, params) {
588
+ function substituteAndSanitizeRewriteDestination(destination, params, unmatchedParams) {
681
589
  const rewritten = substituteAndSanitizeDestination(destination, params, "rewrite");
682
- if (!shouldAppendRewriteParamsToQuery(destination, params)) return rewritten;
590
+ const matchedParams = unmatchedParams.length === 0 ? params : Object.fromEntries(Object.entries(params).filter(([key]) => !unmatchedParams.includes(key)));
591
+ if (!shouldAppendRewriteParamsToQuery(destination, matchedParams)) return rewritten;
683
592
  const existingQueryKeys = getDestinationQueryKeys(destination);
684
593
  const paramsToAppend = [];
685
- for (const [key, value] of Object.entries(params)) {
594
+ for (const [key, value] of Object.entries(matchedParams)) {
686
595
  if (key === "nextInternalLocale" || existingQueryKeys.has(key)) continue;
687
596
  paramsToAppend.push([key, value]);
688
597
  }
@@ -808,6 +717,22 @@ function preserveRedirectDestinationQuery(destination, requestSearch, destinatio
808
717
  }).flat().join("&");
809
718
  return mergedQuery === "" ? `${pathPart}${hash}` : `${pathPart}?${mergedQuery}${hash}`;
810
719
  }
720
+ const LOCATION_STRIPPED_RE = /^[\u0000- ]+|[\u0000- ]+$|[\t\n\r]/g;
721
+ const LOCATION_UNSAFE_RE = /[\u0000-\u0008\u000A-\u001F\u007F]|[^\u0000-\u007F]/gu;
722
+ const LONE_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
723
+ /**
724
+ * Percent-encode what a config-redirect `Location` header cannot carry.
725
+ *
726
+ * The destination template was already parsed like a URL by matchRedirect, so
727
+ * its literal path, hash and host are encoded as Next.js sends them. Captures
728
+ * and the destination query are inserted verbatim, like Next.js, so they can
729
+ * still hold control or non-ASCII characters. Next.js writes those raw and its
730
+ * Node server answers 500 for a character above U+00FF (the Headers
731
+ * constructor throws the same way); vinext percent-encodes them instead.
732
+ */
733
+ function encodeRedirectLocation(location) {
734
+ return location.replace(LONE_SURROGATE_RE, "�").replace(LOCATION_UNSAFE_RE, encodeURIComponent);
735
+ }
811
736
  function parseExternalRewriteUrl(externalUrl) {
812
737
  try {
813
738
  return new URL(externalUrl);
@@ -898,24 +823,119 @@ async function proxyExternalRequest(request, externalUrl) {
898
823
  * when evaluating headers, so this parameter is required.
899
824
  */
900
825
  function matchHeaders(pathname, headers, ctx, basePathState = _BASEPATH_DEFAULT, onRuleSourceMatch) {
901
- const pathnameHadTrailingSlash = pathname.length > 1 && pathname.endsWith("/");
902
- pathname = stripTrailingSlashForConfigMatch(pathname);
903
826
  const result = [];
904
827
  for (const rule of headers) {
905
828
  if (!shouldEvaluateRule(rule.basePath, basePathState)) continue;
906
- const source = pathnameHadTrailingSlash ? stripTrailingSlashForConfigMatch(rule.source) : rule.source;
907
- const sourceRegex = getCachedRegex(_compiledHeaderSourceCache, source, () => safeRegExp("^" + escapeHeaderSource(source) + "$", "i"));
908
- if (sourceRegex && sourceRegex.test(pathname)) {
829
+ const compiledSource = compileConfigSource(rule.source);
830
+ const sourceMatch = compiledSource?.re.exec(pathname);
831
+ if (compiledSource && sourceMatch) {
909
832
  onRuleSourceMatch?.(rule);
910
833
  if (rule.has || rule.missing) {
911
834
  if (!checkHasConditions(rule.has, rule.missing, ctx)) continue;
912
835
  }
913
- result.push(...rule.headers);
836
+ if (!rule.headers.some((header) => header.key.includes(":") || header.value.includes(":"))) {
837
+ result.push(...rule.headers);
838
+ continue;
839
+ }
840
+ const params = collectHeaderSourceParams(sourceMatch, compiledSource.keys);
841
+ if (rule.has) collectHeaderHasParams(rule.has, ctx, params);
842
+ result.push(...substituteHeaderParams(rule.headers, params));
914
843
  }
915
844
  }
916
845
  return result;
917
846
  }
918
847
  /**
848
+ * Collect source params like Next.js's `getPathMatch` with
849
+ * `removeUnnamedParams`: path-to-regexp's `regexpToFunction` keeps captures
850
+ * raw, splits `*`/`+` captures on the key's prefix and suffix, and omits
851
+ * groups that did not participate.
852
+ */
853
+ function collectHeaderSourceParams(match, keys) {
854
+ const params = _emptyParams();
855
+ for (let index = 1; index < match.length; index++) {
856
+ const capture = match[index];
857
+ const key = keys[index - 1];
858
+ if (capture === void 0 || !key || typeof key.name === "number") continue;
859
+ params[key.name] = key.modifier === "*" || key.modifier === "+" ? capture.split(key.prefix + key.suffix) : capture;
860
+ }
861
+ return params;
862
+ }
863
+ /**
864
+ * Add `has` captures like Next.js's `matchHas`, after checkHasConditions has
865
+ * accepted the rule: a condition without a value exposes the matched value
866
+ * under its key (letters only), a value regex exposes its named groups, and a
867
+ * host regex without named groups exposes the matched host as `host`.
868
+ *
869
+ * https://github.com/vercel/next.js/blob/v16.2.7/packages/next/src/shared/lib/router/utils/prepare-destination.ts
870
+ */
871
+ function collectHeaderHasParams(has, ctx, params) {
872
+ for (const condition of has) {
873
+ let value;
874
+ switch (condition.type) {
875
+ case "header":
876
+ value = ctx.headers.get(condition.key) ?? void 0;
877
+ break;
878
+ case "cookie":
879
+ value = Object.hasOwn(ctx.cookies, condition.key) ? ctx.cookies[condition.key] : void 0;
880
+ break;
881
+ case "query": {
882
+ const values = ctx.query.getAll(condition.key);
883
+ value = values.length > 1 ? values : values[0];
884
+ break;
885
+ }
886
+ case "host": value = ctx.host;
887
+ }
888
+ if (!value) continue;
889
+ if (!condition.value) {
890
+ if (condition.type !== "host") params[condition.key.replace(/[^A-Za-z]/g, "")] = value;
891
+ continue;
892
+ }
893
+ const match = _cachedConditionRegex(condition.value)?.exec(Array.isArray(value) ? value[value.length - 1] : value);
894
+ if (!match) continue;
895
+ if (match.groups) Object.assign(params, match.groups);
896
+ else if (condition.type === "host" && match[0]) params.host = match[0];
897
+ }
898
+ }
899
+ const _warnedHeaderParamTemplates = /* @__PURE__ */ new WeakSet();
900
+ /**
901
+ * Substitute params into header keys and values like Next.js's
902
+ * `compileNonPath`, with params inserted verbatim. Source captures are the raw
903
+ * percent-encoded path text, as in Next.js, but `has` captures from decoded
904
+ * cookies or query values can carry characters a header cannot.
905
+ *
906
+ * Next.js fails the whole request when compiling throws (a repeated param
907
+ * used without `*`/`+`, or a has group that did not participate) or when the
908
+ * result is not a valid header. Requests can trigger both, so vinext instead
909
+ * omits only the affected header and logs once per header template.
910
+ */
911
+ function substituteHeaderParams(headers, params) {
912
+ if (Object.keys(params).length === 0) return headers;
913
+ const result = [];
914
+ for (const header of headers) {
915
+ let key;
916
+ let value;
917
+ try {
918
+ key = compileHeaderParamTemplate(header.key, params);
919
+ value = compileHeaderParamTemplate(header.value, params);
920
+ } catch (error) {
921
+ warnHeaderParamTemplate(header, error instanceof Error ? error.message : String(error));
922
+ continue;
923
+ }
924
+ if (key === header.key && value === header.value) result.push(header);
925
+ else if (isValidHeaderPair(key, value)) result.push({
926
+ key,
927
+ value
928
+ });
929
+ else warnHeaderParamTemplate(header, "the substituted header is not a valid HTTP header");
930
+ }
931
+ return result;
932
+ }
933
+ function warnHeaderParamTemplate(header, reason) {
934
+ if (_warnedHeaderParamTemplates.has(header)) return;
935
+ _warnedHeaderParamTemplates.add(header);
936
+ console.warn(`[vinext] Omitting headers() header "${header.key}: ${header.value}" from a response: ${reason}.`);
937
+ }
938
+ /**
919
939
  * Escape a string for inclusion in a regex character class / alternation.
920
940
  * Mirrors `escape-string-regexp` semantics used by Next.js's processRoutes.
921
941
  */
@@ -936,17 +956,19 @@ function _escapeRegexString(value) {
936
956
  * starts with `/:nextInternalLocale(en|sv|nl)` so that locale-prefixed
937
957
  * URLs match. For redirects only, a second variant prefixed with
938
958
  * `/${defaultLocale}` is also emitted, matching Next.js exactly.
939
- * - **Vinext divergence**: we ALSO retain the original (unprefixed) source
940
- * so that requests for the default locale that arrive without a prefix
941
- * still match. Next.js solves this upstream by path-normalising every
942
- * incoming default-locale request to include the prefix
943
- * (`resolve-routes.ts` lines ~251-263); vinext currently does that
944
- * normalisation only inside the pages-server-entry route matcher, so
945
- * the rewrite/redirect matcher would otherwise miss unprefixed paths.
946
- * Keeping the unprefixed variant gives functionally identical behaviour
947
- * without requiring a server-wide path normalisation pass. The original
948
- * source is appended LAST so the locale-aware variants win when both
949
- * forms could match.
959
+ * - Headers get only that variant, exactly like Next.js. Every header
960
+ * matcher caller splices the (domain-aware) default locale into
961
+ * unprefixed request paths first (`normalizeDefaultLocalePathname`,
962
+ * mirroring `resolve-routes.ts`), so `/about` is matched as `/en/about`.
963
+ * `/_next/*` paths are left unprefixed and therefore only match
964
+ * `locale: false` rules, as in Next.js.
965
+ * - **Vinext divergence (redirects/rewrites only)**: we ALSO retain the
966
+ * original (unprefixed) source, appended LAST so the locale-aware
967
+ * variants win when both forms could match. Their callers normalise
968
+ * default-locale paths too, so this is a legacy fallback. Headers never
969
+ * get it: it would apply rules Next.js skips (`/((?!api).*)` would match
970
+ * `/fr/api/x` as `fr/api/x`), and default-locale requests would match
971
+ * both variants, appending additive headers such as `Set-Cookie` twice.
950
972
  *
951
973
  * Destinations that are local (start with `/`) are similarly rewritten with
952
974
  * `/:nextInternalLocale` for the locale-capture variant so the locale
@@ -973,7 +995,8 @@ function applyLocaleToRoutes(routes, i18n, type, options = {}) {
973
995
  const localizedSource = `/${locale}${suffixFor(r.source)}`;
974
996
  out.push({
975
997
  ...r,
976
- source: localizedSource
998
+ source: localizedSource,
999
+ ...type === "redirect" ? { defaultLocaleVariant: true } : {}
977
1000
  });
978
1001
  }
979
1002
  const internalSource = `${internalLocale}${suffixFor(r.source)}`;
@@ -985,9 +1008,9 @@ function applyLocaleToRoutes(routes, i18n, type, options = {}) {
985
1008
  };
986
1009
  if ("destination" in internalRoute && internalDestination !== void 0) internalRoute.destination = internalDestination;
987
1010
  out.push(internalRoute);
988
- out.push(r);
1011
+ if (type !== "header") out.push(r);
989
1012
  }
990
1013
  return out;
991
1014
  }
992
1015
  //#endregion
993
- export { applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, matchesRewriteSource, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
1016
+ export { applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, configSourceError, encodeRedirectLocation, isExternalUrl, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, matchesRewriteSource, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination };