vinext 1.0.1 → 1.2.0

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 (334) hide show
  1. package/README.md +93 -10
  2. package/dist/build/app-page-static-eligibility.d.ts +15 -0
  3. package/dist/build/app-page-static-eligibility.js +82 -0
  4. package/dist/build/client-build-config.d.ts +10 -0
  5. package/dist/build/client-build-config.js +18 -2
  6. package/dist/build/pages-client-assets-module.d.ts +8 -0
  7. package/dist/build/pages-client-assets-module.js +21 -1
  8. package/dist/build/prerender-paths.js +8 -81
  9. package/dist/build/prerender.js +33 -8
  10. package/dist/build/run-prerender.js +1 -1
  11. package/dist/build/server-manifest.d.ts +7 -0
  12. package/dist/build/server-manifest.js +11 -1
  13. package/dist/build/ssr-manifest.d.ts +2 -0
  14. package/dist/build/ssr-manifest.js +6 -2
  15. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  16. package/dist/cache/cache-adapters-virtual.js +3 -3
  17. package/dist/check.js +7 -6
  18. package/dist/cli.js +2 -1
  19. package/dist/client/client-redirect-matcher.d.ts +14 -0
  20. package/dist/client/client-redirect-matcher.js +37 -0
  21. package/dist/client/client-redirects.d.ts +27 -0
  22. package/dist/client/client-redirects.js +28 -0
  23. package/dist/client/client-rewrites.d.ts +2 -1
  24. package/dist/client/client-rewrites.js +1 -1
  25. package/dist/client/navigation-runtime.d.ts +11 -0
  26. package/dist/client/navigation-runtime.js +1 -1
  27. package/dist/config/config-matchers.d.ts +26 -11
  28. package/dist/config/config-matchers.js +81 -82
  29. package/dist/config/destination-params.d.ts +47 -0
  30. package/dist/config/destination-params.js +116 -0
  31. package/dist/config/next-config.d.ts +14 -0
  32. package/dist/config/next-config.js +21 -3
  33. package/dist/entries/app-rsc-entry.d.ts +6 -0
  34. package/dist/entries/app-rsc-entry.js +84 -49
  35. package/dist/entries/pages-client-entry.js +3 -1
  36. package/dist/entries/pages-server-entry.js +13 -37
  37. package/dist/index.d.ts +7 -2
  38. package/dist/index.js +193 -73
  39. package/dist/init-cloudflare.d.ts +9 -0
  40. package/dist/init-cloudflare.js +107 -31
  41. package/dist/init-platform.d.ts +4 -0
  42. package/dist/init-platform.js +27 -2
  43. package/dist/init.d.ts +1 -0
  44. package/dist/init.js +67 -3
  45. package/dist/plugins/ast-scope.d.ts +7 -0
  46. package/dist/plugins/ast-scope.js +59 -3
  47. package/dist/plugins/cache-flight-codec.d.ts +19 -0
  48. package/dist/plugins/cache-flight-codec.js +68 -0
  49. package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
  50. package/dist/plugins/commonjs-esm-facade.js +80 -0
  51. package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
  52. package/dist/plugins/dynamic-preload-metadata.js +9 -8
  53. package/dist/plugins/fonts.d.ts +8 -11
  54. package/dist/plugins/fonts.js +71 -34
  55. package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
  56. package/dist/plugins/ignore-dynamic-requests.js +3 -4
  57. package/dist/plugins/middleware-server-only.d.ts +5 -4
  58. package/dist/plugins/middleware-server-only.js +8 -5
  59. package/dist/plugins/pages-node-externals.js +1 -1
  60. package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
  61. package/dist/plugins/rsc-scan-glob-imports.js +50 -0
  62. package/dist/plugins/strip-server-exports.js +262 -98
  63. package/dist/plugins/transform-result.d.ts +9 -5
  64. package/dist/plugins/transitive-externals.js +1 -1
  65. package/dist/plugins/typeof-window.d.ts +2 -2
  66. package/dist/plugins/use-cache-callable.js +8 -2
  67. package/dist/plugins/worker-server-only-imports.d.ts +20 -0
  68. package/dist/plugins/worker-server-only-imports.js +31 -0
  69. package/dist/plugins/worker-use-server.d.ts +25 -0
  70. package/dist/plugins/worker-use-server.js +129 -0
  71. package/dist/routing/file-matcher.d.ts +1 -0
  72. package/dist/routing/file-matcher.js +1 -1
  73. package/dist/server/api-handler.d.ts +1 -0
  74. package/dist/server/api-handler.js +19 -8
  75. package/dist/server/app-browser-action-result.d.ts +1 -1
  76. package/dist/server/app-browser-action-result.js +9 -2
  77. package/dist/server/app-browser-entry.js +11 -7
  78. package/dist/server/app-browser-history-controller.d.ts +2 -0
  79. package/dist/server/app-browser-history-controller.js +8 -0
  80. package/dist/server/app-browser-interception-context.js +26 -2
  81. package/dist/server/app-browser-navigation-controller.d.ts +7 -1
  82. package/dist/server/app-browser-navigation-controller.js +94 -11
  83. package/dist/server/app-browser-server-action-client.js +1 -1
  84. package/dist/server/app-fallback-renderer.d.ts +2 -0
  85. package/dist/server/app-fallback-renderer.js +1 -0
  86. package/dist/server/app-history-state.d.ts +3 -0
  87. package/dist/server/app-history-state.js +7 -0
  88. package/dist/server/app-middleware.d.ts +11 -0
  89. package/dist/server/app-middleware.js +19 -3
  90. package/dist/server/app-optimistic-routing.js +1 -1
  91. package/dist/server/app-page-boundary-render.d.ts +5 -0
  92. package/dist/server/app-page-boundary-render.js +6 -4
  93. package/dist/server/app-page-cache-finalizer.d.ts +11 -0
  94. package/dist/server/app-page-cache-finalizer.js +41 -7
  95. package/dist/server/app-page-cache-render.d.ts +18 -0
  96. package/dist/server/app-page-cache-render.js +63 -43
  97. package/dist/server/app-page-cache.d.ts +6 -1
  98. package/dist/server/app-page-cache.js +28 -8
  99. package/dist/server/app-page-dispatch.d.ts +12 -2
  100. package/dist/server/app-page-dispatch.js +51 -33
  101. package/dist/server/app-page-element-builder.d.ts +7 -0
  102. package/dist/server/app-page-element-builder.js +27 -42
  103. package/dist/server/app-page-execution.d.ts +15 -12
  104. package/dist/server/app-page-execution.js +30 -40
  105. package/dist/server/app-page-head.d.ts +5 -0
  106. package/dist/server/app-page-head.js +9 -3
  107. package/dist/server/app-page-ppr-runtime.js +1 -1
  108. package/dist/server/app-page-probe.d.ts +0 -109
  109. package/dist/server/app-page-probe.js +3 -136
  110. package/dist/server/app-page-render-identity.d.ts +0 -1
  111. package/dist/server/app-page-render-identity.js +1 -1
  112. package/dist/server/app-page-render-observation.js +3 -3
  113. package/dist/server/app-page-render.d.ts +13 -5
  114. package/dist/server/app-page-render.js +219 -119
  115. package/dist/server/app-page-request.d.ts +0 -1
  116. package/dist/server/app-page-request.js +54 -17
  117. package/dist/server/app-page-response.d.ts +4 -3
  118. package/dist/server/app-page-response.js +4 -3
  119. package/dist/server/app-page-route-wiring.d.ts +17 -5
  120. package/dist/server/app-page-route-wiring.js +57 -35
  121. package/dist/server/app-page-rsc-render-status.d.ts +48 -0
  122. package/dist/server/app-page-rsc-render-status.js +54 -0
  123. package/dist/server/app-page-stream.d.ts +15 -8
  124. package/dist/server/app-page-stream.js +6 -0
  125. package/dist/server/app-pages-bridge.d.ts +1 -1
  126. package/dist/server/app-pages-bridge.js +2 -3
  127. package/dist/server/app-prerender-endpoints.js +9 -6
  128. package/dist/server/app-request-stage-dispatch.js +7 -3
  129. package/dist/server/app-request-stage-independent-entry.js +29 -9
  130. package/dist/server/app-response-header-provenance.d.ts +2 -0
  131. package/dist/server/app-response-header-provenance.js +10 -4
  132. package/dist/server/app-response-stage-entry.js +10 -2
  133. package/dist/server/app-route-handler-cache.d.ts +1 -0
  134. package/dist/server/app-route-handler-cache.js +1 -0
  135. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  136. package/dist/server/app-route-handler-dispatch.js +6 -3
  137. package/dist/server/app-route-handler-execution.d.ts +1 -0
  138. package/dist/server/app-route-handler-execution.js +1 -0
  139. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  140. package/dist/server/app-route-handler-runtime.js +45 -24
  141. package/dist/server/app-router-entry.js +27 -8
  142. package/dist/server/app-rsc-cache-busting.js +1 -1
  143. package/dist/server/app-rsc-errors.js +20 -16
  144. package/dist/server/app-rsc-handler.d.ts +11 -0
  145. package/dist/server/app-rsc-handler.js +291 -125
  146. package/dist/server/app-rsc-request-normalization.d.ts +3 -1
  147. package/dist/server/app-rsc-request-normalization.js +4 -2
  148. package/dist/server/app-rsc-response-stage.d.ts +1 -1
  149. package/dist/server/app-rsc-response-stage.js +10 -6
  150. package/dist/server/app-rsc-route-matching.d.ts +9 -1
  151. package/dist/server/app-rsc-route-matching.js +36 -19
  152. package/dist/server/app-segment-config.d.ts +2 -1
  153. package/dist/server/app-server-action-execution.d.ts +2 -1
  154. package/dist/server/app-server-action-execution.js +62 -23
  155. package/dist/server/app-ssr-entry.d.ts +3 -2
  156. package/dist/server/app-ssr-entry.js +8 -5
  157. package/dist/server/app-ssr-stream.d.ts +6 -1
  158. package/dist/server/app-ssr-stream.js +34 -15
  159. package/dist/server/app-worker-stages.d.ts +7 -2
  160. package/dist/server/app-worker-stages.js +4 -4
  161. package/dist/server/cacheability-request.js +11 -7
  162. package/dist/server/client-build-files.d.ts +16 -0
  163. package/dist/server/client-build-files.js +36 -0
  164. package/dist/server/client-reuse-manifest.js +1 -1
  165. package/dist/server/config-headers.js +2 -0
  166. package/dist/server/dev-server.js +50 -29
  167. package/dist/server/dev-stack-sourcemap.js +18 -23
  168. package/dist/server/document-initial-head.d.ts +0 -11
  169. package/dist/server/document-initial-head.js +1 -1
  170. package/dist/server/forwarded-middleware-context.d.ts +5 -0
  171. package/dist/server/forwarded-middleware-context.js +22 -0
  172. package/dist/server/headers.d.ts +18 -1
  173. package/dist/server/headers.js +19 -2
  174. package/dist/server/http-conditional.d.ts +11 -0
  175. package/dist/server/http-conditional.js +10 -1
  176. package/dist/server/http-error-responses.d.ts +1 -1
  177. package/dist/server/image-optimization.d.ts +54 -0
  178. package/dist/server/image-optimization.js +118 -9
  179. package/dist/server/isr-cache.d.ts +10 -0
  180. package/dist/server/isr-cache.js +43 -8
  181. package/dist/server/metadata-route-response.js +9 -8
  182. package/dist/server/middleware-matcher-pattern.js +8 -4
  183. package/dist/server/middleware-matcher.d.ts +2 -0
  184. package/dist/server/middleware-matcher.js +23 -2
  185. package/dist/server/middleware-response-headers.js +2 -0
  186. package/dist/server/middleware-runtime.d.ts +13 -0
  187. package/dist/server/middleware-runtime.js +33 -20
  188. package/dist/server/middleware.d.ts +1 -1
  189. package/dist/server/middleware.js +3 -1
  190. package/dist/server/multi-stage.d.ts +19 -0
  191. package/dist/server/navigation-planner.js +1 -1
  192. package/dist/server/node-response-signal.d.ts +16 -0
  193. package/dist/server/node-response-signal.js +26 -0
  194. package/dist/server/normalize-path.d.ts +0 -13
  195. package/dist/server/normalize-path.js +1 -34
  196. package/dist/server/open-redirect.d.ts +47 -0
  197. package/dist/server/open-redirect.js +74 -1
  198. package/dist/server/pages-api-route.d.ts +3 -2
  199. package/dist/server/pages-api-route.js +4 -3
  200. package/dist/server/pages-cache-tag.d.ts +12 -0
  201. package/dist/server/pages-cache-tag.js +19 -0
  202. package/dist/server/pages-client-assets.d.ts +2 -0
  203. package/dist/server/pages-data-route.d.ts +10 -0
  204. package/dist/server/pages-data-route.js +19 -2
  205. package/dist/server/pages-document-asset-props.d.ts +5 -1
  206. package/dist/server/pages-document-asset-props.js +13 -5
  207. package/dist/server/pages-document-splice.d.ts +19 -0
  208. package/dist/server/pages-document-splice.js +55 -0
  209. package/dist/server/pages-i18n.d.ts +12 -1
  210. package/dist/server/pages-i18n.js +18 -4
  211. package/dist/server/pages-node-compat.d.ts +16 -5
  212. package/dist/server/pages-node-compat.js +20 -12
  213. package/dist/server/pages-page-data.d.ts +32 -24
  214. package/dist/server/pages-page-data.js +50 -120
  215. package/dist/server/pages-page-handler.d.ts +0 -6
  216. package/dist/server/pages-page-handler.js +209 -130
  217. package/dist/server/pages-page-response.d.ts +12 -6
  218. package/dist/server/pages-page-response.js +41 -34
  219. package/dist/server/pages-request-pipeline.d.ts +10 -2
  220. package/dist/server/pages-request-pipeline.js +37 -18
  221. package/dist/server/pages-request-stage-entry.d.ts +6 -0
  222. package/dist/server/pages-request-stage-entry.js +79 -16
  223. package/dist/server/pages-response-stage-entry.js +4 -0
  224. package/dist/server/pages-revalidate.js +1 -1
  225. package/dist/server/pages-router-entry.js +4 -3
  226. package/dist/server/prerender-manifest.d.ts +3 -3
  227. package/dist/server/prerender-manifest.js +4 -3
  228. package/dist/server/prerender-work-unit-setup.js +4 -0
  229. package/dist/server/prod-server.d.ts +20 -7
  230. package/dist/server/prod-server.js +343 -129
  231. package/dist/server/proper-error.d.ts +4 -0
  232. package/dist/server/proper-error.js +31 -0
  233. package/dist/server/request-pipeline.d.ts +24 -9
  234. package/dist/server/request-pipeline.js +24 -9
  235. package/dist/server/response-compression.d.ts +43 -0
  236. package/dist/server/response-compression.js +111 -0
  237. package/dist/server/revalidation-request.d.ts +1 -0
  238. package/dist/server/revalidation-request.js +1 -1
  239. package/dist/server/rsc-stream-hints.js +31 -2
  240. package/dist/server/seed-cache.d.ts +1 -0
  241. package/dist/server/seed-cache.js +15 -11
  242. package/dist/server/server-reference-loader.d.ts +4 -0
  243. package/dist/server/server-reference-loader.js +29 -0
  244. package/dist/server/socket-error-backstop.d.ts +23 -1
  245. package/dist/server/socket-error-backstop.js +69 -17
  246. package/dist/server/static-file-signal.d.ts +15 -4
  247. package/dist/server/static-file-signal.js +46 -12
  248. package/dist/server/worker-image-optimization.d.ts +34 -0
  249. package/dist/server/worker-image-optimization.js +86 -0
  250. package/dist/server/worker-stages.d.ts +1 -1
  251. package/dist/server/worker-stages.js +2 -2
  252. package/dist/server/worker-utils.d.ts +8 -3
  253. package/dist/server/worker-utils.js +12 -7
  254. package/dist/shims/app-router-scroll.js +4 -1
  255. package/dist/shims/app-router-tree-context.d.ts +5 -0
  256. package/dist/shims/app-router-tree-context.js +27 -0
  257. package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
  258. package/dist/shims/cache-adapter-instantiate.js +118 -0
  259. package/dist/shims/cache-callable-runtime.d.ts +6 -42
  260. package/dist/shims/cache-callable-runtime.js +65 -174
  261. package/dist/shims/cache-flight-arguments.d.ts +22 -0
  262. package/dist/shims/cache-flight-arguments.js +56 -0
  263. package/dist/shims/cache-handler.js +1 -9
  264. package/dist/shims/cache-request-state.d.ts +15 -0
  265. package/dist/shims/cache-request-state.js +31 -8
  266. package/dist/shims/cache-runtime.d.ts +12 -20
  267. package/dist/shims/cache-runtime.js +166 -225
  268. package/dist/shims/cache.d.ts +4 -4
  269. package/dist/shims/cache.js +44 -16
  270. package/dist/shims/cacheability-classification.d.ts +7 -0
  271. package/dist/shims/cdn-cache.d.ts +8 -0
  272. package/dist/shims/cdn-cache.js +17 -1
  273. package/dist/shims/disable-smooth-scroll.d.ts +3 -0
  274. package/dist/shims/disable-smooth-scroll.js +36 -0
  275. package/dist/shims/document-placeholders.d.ts +19 -0
  276. package/dist/shims/document-placeholders.js +45 -0
  277. package/dist/shims/document.js +21 -5
  278. package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
  279. package/dist/shims/dynamic-preload-chunks.js +13 -9
  280. package/dist/shims/dynamic.d.ts +1 -1
  281. package/dist/shims/dynamic.js +60 -8
  282. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  283. package/dist/shims/error-boundary-navigation.js +19 -1
  284. package/dist/shims/error-boundary.d.ts +10 -10
  285. package/dist/shims/error-boundary.js +26 -7
  286. package/dist/shims/fetch-cache-state.d.ts +66 -0
  287. package/dist/shims/fetch-cache-state.js +92 -0
  288. package/dist/shims/fetch-cache.d.ts +3 -61
  289. package/dist/shims/fetch-cache.js +161 -108
  290. package/dist/shims/form.js +1 -1
  291. package/dist/shims/head.d.ts +10 -6
  292. package/dist/shims/head.js +15 -13
  293. package/dist/shims/headers.d.ts +13 -0
  294. package/dist/shims/headers.js +47 -4
  295. package/dist/shims/image.js +2 -1
  296. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
  297. package/dist/shims/internal/app-page-props-cache-key.js +24 -15
  298. package/dist/shims/internal/headers-state.d.ts +0 -1
  299. package/dist/shims/internal/work-store-scope.d.ts +5 -0
  300. package/dist/shims/internal/work-store-scope.js +24 -0
  301. package/dist/shims/metadata.js +3 -3
  302. package/dist/shims/navigation-errors.d.ts +6 -0
  303. package/dist/shims/navigation-errors.js +11 -1
  304. package/dist/shims/navigation.d.ts +7 -0
  305. package/dist/shims/navigation.js +45 -9
  306. package/dist/shims/request-context.d.ts +11 -0
  307. package/dist/shims/request-state-types.d.ts +2 -1
  308. package/dist/shims/router.js +12 -20
  309. package/dist/shims/server.d.ts +16 -0
  310. package/dist/shims/server.js +24 -4
  311. package/dist/shims/thenable-params.d.ts +4 -0
  312. package/dist/shims/thenable-params.js +25 -3
  313. package/dist/shims/unified-request-context.d.ts +1 -1
  314. package/dist/shims/unified-request-context.js +1 -0
  315. package/dist/shims/url-utils.d.ts +2 -0
  316. package/dist/shims/url-utils.js +3 -1
  317. package/dist/utils/client-runtime-metadata.js +1 -1
  318. package/dist/utils/hash.d.ts +7 -0
  319. package/dist/utils/hash.js +11 -1
  320. package/dist/utils/mdx-scan.d.ts +5 -0
  321. package/dist/utils/mdx-scan.js +8 -1
  322. package/dist/utils/package-name.d.ts +6 -0
  323. package/dist/utils/package-name.js +12 -1
  324. package/dist/utils/plugin-rsc-version.d.ts +4 -0
  325. package/dist/utils/plugin-rsc-version.js +15 -0
  326. package/dist/utils/prerender-output-paths.js +15 -4
  327. package/dist/utils/public-file-path.d.ts +10 -2
  328. package/dist/utils/public-file-path.js +13 -9
  329. package/dist/utils/public-routes.js +2 -2
  330. package/dist/utils/react-version.d.ts +3 -0
  331. package/dist/utils/react-version.js +1 -1
  332. package/dist/utils/regex-safety.d.ts +10 -0
  333. package/dist/utils/regex-safety.js +70 -18
  334. package/package.json +9 -5
@@ -35,7 +35,9 @@ export type NormalizedRscRequest = {
35
35
  *
36
36
  * 1. Parse URL
37
37
  * 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses
38
- * `//` to `/`. If the guard ran after normalization, `//evil.com` → `/evil.com`
38
+ * `//` to `/`. Literal repeated slashes / backslashes get Next.js's 308 to the
39
+ * collapsed path; encoded leading `%2F` / `%5C` get a 404. If the guard ran after
40
+ * normalization, `//evil.com` → `/evil.com`
39
41
  * would bypass the check and reach the trailing-slash redirector, which echoes the
40
42
  * path into a `Location` header that browsers interpret as protocol-relative.
41
43
  * 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must
@@ -69,7 +69,9 @@ function tryNormalizeComparablePathAndSearch(value, basePath, baseUrl) {
69
69
  *
70
70
  * 1. Parse URL
71
71
  * 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses
72
- * `//` to `/`. If the guard ran after normalization, `//evil.com` → `/evil.com`
72
+ * `//` to `/`. Literal repeated slashes / backslashes get Next.js's 308 to the
73
+ * collapsed path; encoded leading `%2F` / `%5C` get a 404. If the guard ran after
74
+ * normalization, `//evil.com` → `/evil.com`
73
75
  * would bypass the check and reach the trailing-slash redirector, which echoes the
74
76
  * path into a `Location` header that browsers interpret as protocol-relative.
75
77
  * 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must
@@ -91,7 +93,7 @@ function tryNormalizeComparablePathAndSearch(value, basePath, baseUrl) {
91
93
  */
92
94
  function normalizeRscRequest(request, basePath, allowOutsideBasePath = false, assetPrefix = "") {
93
95
  const url = new URL(request.url);
94
- const protoGuard = guardProtocolRelativeUrl(url.pathname);
96
+ const protoGuard = guardProtocolRelativeUrl(url.pathname, url.search);
95
97
  if (protoGuard) return protoGuard;
96
98
  let decoded;
97
99
  try {
@@ -2,5 +2,5 @@ import { VinextResponseStageDispatchOptions } from "./multi-stage.js";
2
2
  import { AppWorkerResponseStageProps } from "./app-worker-stages.js";
3
3
  import { AppRscHandlerRoute, CreateAppRscHandlerOptions } from "./app-rsc-handler.js";
4
4
  //#region src/server/app-rsc-response-stage.d.ts
5
- export declare function renderAppWorkerResponseStage<TRoute extends AppRscHandlerRoute>(options: CreateAppRscHandlerOptions<TRoute>, rawRequest: Request, ctx: unknown, props: AppWorkerResponseStageProps, _stageOptions?: VinextResponseStageDispatchOptions): Promise<Response>;
5
+ export declare function renderAppWorkerResponseStage<TRoute extends AppRscHandlerRoute>(options: CreateAppRscHandlerOptions<TRoute>, rawRequest: Request, ctx: unknown, props: AppWorkerResponseStageProps, stageOptions?: VinextResponseStageDispatchOptions): Promise<Response>;
6
6
  //#endregion
@@ -3,6 +3,7 @@ import { closeAfterResponse, closeAfterResponseWithBody, createRequestContext, r
3
3
  import { VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
4
4
  import { getRequestExecutionContext } from "../shims/request-context.js";
5
5
  import "./headers.js";
6
+ import { runOutsideWorkStoreScope } from "../shims/internal/work-store-scope.js";
6
7
  import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders } from "./request-pipeline.js";
7
8
  import { applyEffectiveRequestCookieHeader, getDraftModeCookieHeader, headersContextFromRequest, restoreDraftModeTransition } from "../shims/headers.js";
8
9
  import { ensureFetchPatch, setCurrentFetchSoftTags } from "../shims/fetch-cache.js";
@@ -60,14 +61,15 @@ function rematchAppWorkerResponseStageRoute(options, normalized, props) {
60
61
  let match = null;
61
62
  if (props.matchKind === "interception") {
62
63
  if (normalized.interceptionContextHeader !== null) match = options.matchInterceptRoute?.(props.routePathname, normalized.interceptionContextHeader, props.interceptionId) ?? null;
63
- } else if (props.matchKind === "request") match = options.matchRequestRoute?.(props.routePathname) ?? null;
64
+ } else if (props.matchKind === "raw-request") match = options.matchRawRequestRoute?.(props.routePathname) ?? null;
65
+ else if (props.matchKind === "request") match = options.matchRequestRoute?.(props.routePathname) ?? null;
64
66
  else match = options.matchRoute(props.routePathname);
65
67
  if (!match) return null;
66
68
  if (match.route.pattern !== props.routePattern || !haveSamePageParams(match.params, props.params)) return null;
67
69
  return match;
68
70
  }
69
- async function renderAppWorkerResponseStage(options, rawRequest, ctx, props, _stageOptions) {
70
- if (props.protocolVersion !== 10 || props.buildId !== options.buildId) return new Response("Incompatible vinext App response stage", { status: 409 });
71
+ async function renderAppWorkerResponseStage(options, rawRequest, ctx, props, stageOptions) {
72
+ if (props.protocolVersion !== 11 || props.buildId !== options.buildId) return new Response("Incompatible vinext App response stage", { status: 409 });
71
73
  if (props.kind === "app-full-request") return new Response("Invalid vinext App response stage", { status: 400 });
72
74
  options.registerCacheAdapters();
73
75
  await options.ensureInstrumentation?.();
@@ -152,7 +154,8 @@ async function renderAppWorkerResponseStage(options, rawRequest, ctx, props, _st
152
154
  const pageRequest = props.isDataRequest ? cloneRequestWithUrl(request, new URL(props.resolvedUrl, request.url).toString()) : request;
153
155
  const preHandlerHeaders = props.preHandlerHeaders === null ? void 0 : new Headers(props.preHandlerHeaders);
154
156
  preHandlerHeaders?.delete("Content-Length");
155
- let pagesResponse = await options.renderPagesFallback({
157
+ const renderPagesFallback = options.renderPagesFallback;
158
+ let pagesResponse = await runOutsideWorkStoreScope(() => renderPagesFallback({
156
159
  allowRscDocumentFallback: props.allowRscDocumentFallback,
157
160
  appRouteMatch: props.appRouteMatch ? { route: {
158
161
  isDynamic: props.appRouteMatch.isDynamic,
@@ -167,7 +170,7 @@ async function renderAppWorkerResponseStage(options, rawRequest, ctx, props, _st
167
170
  pagesDataRequest: props.isDataRequest ? request : null,
168
171
  request: pageRequest,
169
172
  url: new URL(props.requestUrl)
170
- });
173
+ }));
171
174
  if (!pagesResponse) return new Response("Invalid vinext App response stage", { status: 404 });
172
175
  if (preHandlerHeaders) pagesResponse = stripInheritedResponseStageCookies(pagesResponse, preHandlerHeaders);
173
176
  return pagesResponse;
@@ -232,7 +235,8 @@ async function renderAppWorkerResponseStage(options, rawRequest, ctx, props, _st
232
235
  route,
233
236
  searchParams,
234
237
  scriptNonce: props.scriptNonce ?? void 0,
235
- renderMode: normalized.renderMode
238
+ renderMode: normalized.renderMode,
239
+ renderWholeDocument: stageOptions?.renderWholeDocument === true
236
240
  });
237
241
  }, {
238
242
  cacheComponents: options.createPprFallbackShells !== void 0,
@@ -120,7 +120,11 @@ type AppRscInterceptLookupEntry = {
120
120
  slotId: string | null;
121
121
  targetRouteGraphId: string | null;
122
122
  };
123
- export declare function createAppRscRouteMatcher<Route extends AppRscRouteForMatching>(routes: Route[]): {
123
+ /**
124
+ * @param decodedPathnamePatterns Static page patterns that answer their
125
+ * segment-decoded pathname (built by `buildAppDecodedPathnamePatterns`).
126
+ */
127
+ export declare function createAppRscRouteMatcher<Route extends AppRscRouteForMatching>(routes: Route[], decodedPathnamePatterns?: readonly string[]): {
124
128
  hasInterceptionId(interceptionId: string): boolean;
125
129
  matchRoute(url: string): {
126
130
  route: Route;
@@ -130,6 +134,10 @@ export declare function createAppRscRouteMatcher<Route extends AppRscRouteForMat
130
134
  route: Route;
131
135
  params: AppRscRouteParams;
132
136
  } | null;
137
+ matchRawRequestRoute(url: string): {
138
+ route: Route;
139
+ params: AppRscRouteParams;
140
+ } | null;
133
141
  findIntercept(pathname: string, sourcePathname?: string | null, interceptionId?: string | null): AppRscInterceptMatch | null;
134
142
  };
135
143
  //#endregion
@@ -1,4 +1,4 @@
1
- import { decodeMatchedParams, splitPathSegments, splitPathnameForRouteMatch } from "../routing/utils.js";
1
+ import { decodeMatchedParams, decodeRouteSegment, splitPathSegments, splitPathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { createAppRouteGraphInterceptionId } from "../routing/app-route-ids.js";
3
3
  import { buildRouteTrie, trieMatchRaw } from "../routing/route-trie.js";
4
4
  import { extractRawRoutePatternParams, matchRoutePatternPrefix, matchRoutePatternRaw } from "../routing/route-pattern.js";
@@ -18,17 +18,6 @@ function appRscPathnameParts(pathname, isNormalized = false) {
18
18
  const normalizedPathname = pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "");
19
19
  return isNormalized ? splitPathSegments(normalizedPathname) : splitPathnameForRouteMatch(normalizedPathname);
20
20
  }
21
- function appRscInterceptionSourcePathnameParts(pathname) {
22
- const pathOnly = pathname.split("?")[0];
23
- const normalizedPathname = pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "");
24
- return splitPathSegments(normalizedPathname).map((segment) => {
25
- try {
26
- return decodeURIComponent(segment);
27
- } catch {
28
- return segment;
29
- }
30
- });
31
- }
32
21
  function isAppRouteHandlerRoute(route) {
33
22
  return route.routeHandler != null || typeof route.__loadRouteHandler === "function";
34
23
  }
@@ -36,11 +25,38 @@ function normalizeMatchedParamsForRoute(result) {
36
25
  if (isAppRouteHandlerRoute(result.route)) decodeMatchedParams(result.params);
37
26
  else canonicalizeAppPageParams(result.params);
38
27
  }
39
- function createAppRscRouteMatcher(routes) {
28
+ /**
29
+ * @param decodedPathnamePatterns Static page patterns that answer their
30
+ * segment-decoded pathname (built by `buildAppDecodedPathnamePatterns`).
31
+ */
32
+ function createAppRscRouteMatcher(routes, decodedPathnamePatterns = []) {
40
33
  const routeTrie = buildRouteTrie(routes);
41
34
  const interceptLookup = createInterceptLookup(routes);
42
35
  const interceptionIds = new Set(interceptLookup.flatMap((entry) => entry.interceptionId === null ? [] : [entry.interceptionId]));
43
36
  const routeIndexes = new Map(routes.map((route, index) => [route, index]));
37
+ const decodedPathnamePatternSet = new Set(decodedPathnamePatterns);
38
+ const decodedPathnameRoutes = /* @__PURE__ */ new Map();
39
+ for (const route of routes) {
40
+ const key = route.patternParts.join("/");
41
+ if (decodedPathnamePatternSet.has(route.pattern) && !decodedPathnameRoutes.has(key)) decodedPathnameRoutes.set(key, route);
42
+ }
43
+ function matchRawRequestParts(rawParts) {
44
+ const result = trieMatchRaw(routeTrie, rawParts);
45
+ if (result) normalizeMatchedParamsForRoute(result);
46
+ return result;
47
+ }
48
+ function matchRequestParts(rawParts) {
49
+ const result = matchRawRequestParts(rawParts);
50
+ if (!result) return null;
51
+ if (decodedPathnameRoutes.size > 0 && result.route.patternParts.some((part) => part.startsWith(":")) && !isAppRouteHandlerRoute(result.route) && rawParts.some((part) => part.includes("%"))) {
52
+ const route = decodedPathnameRoutes.get(rawParts.map(decodeRouteSegment).join("/"));
53
+ if (route && route !== result.route) return {
54
+ route,
55
+ params: createRouteParams()
56
+ };
57
+ }
58
+ return result;
59
+ }
44
60
  return {
45
61
  hasInterceptionId(interceptionId) {
46
62
  return interceptionIds.has(interceptionId);
@@ -54,16 +70,16 @@ function createAppRscRouteMatcher(routes) {
54
70
  return result;
55
71
  },
56
72
  matchRequestRoute(url) {
57
- const result = trieMatchRaw(routeTrie, appRscPathnameParts(url, true));
58
- if (!result) return null;
59
- normalizeMatchedParamsForRoute(result);
60
- return result;
73
+ return matchRequestParts(appRscPathnameParts(url, true));
74
+ },
75
+ matchRawRequestRoute(url) {
76
+ return matchRawRequestParts(appRscPathnameParts(url, true));
61
77
  },
62
78
  findIntercept(pathname, sourcePathname = null, interceptionId = null) {
63
79
  if (sourcePathname === null) return null;
64
80
  const urlParts = appRscPathnameParts(pathname, true);
65
- const sourceParts = appRscInterceptionSourcePathnameParts(sourcePathname);
66
- const matchedSourceRoute = trieMatchRaw(routeTrie, sourceParts);
81
+ const sourceParts = appRscPathnameParts(sourcePathname, true);
82
+ const matchedSourceRoute = matchRawRequestParts(sourceParts);
67
83
  for (const entry of interceptLookup) {
68
84
  if (interceptionId !== null && entry.interceptionId !== interceptionId) continue;
69
85
  if (!matchInterceptSource(sourceParts, entry)) continue;
@@ -75,6 +91,7 @@ function createAppRscRouteMatcher(routes) {
75
91
  const sourceRoute = routes[concreteSourceRouteIndex];
76
92
  if (sourceRoute && isAppRouteHandlerRoute(sourceRoute)) continue;
77
93
  const matchedSourceParams = concreteSourceRoute ? concreteSourceRoute.params : sourceRoute ? matchSlotOwnerSourceParams(sourceParts, sourceRoute.patternParts, entry.sourceMatchPatternParts !== null) : null;
94
+ if (!concreteSourceRoute && matchedSourceParams) canonicalizeAppPageParams(matchedSourceParams);
78
95
  if (matchedSourceParams === null && entry.sourceMatchPatternParts === null) continue;
79
96
  const sourceParams = matchedSourceParams && entry.sourceMatchPatternParts !== null ? pickPatternParams(matchedSourceParams, entry.sourceMatchPatternParts) : matchedSourceParams ?? createRouteParams();
80
97
  return {
@@ -1,4 +1,5 @@
1
- import { FetchCacheMode } from "../shims/fetch-cache.js";
1
+ import { FetchCacheMode } from "../shims/fetch-cache-state.js";
2
+ import "../shims/fetch-cache.js";
2
3
  //#region src/server/app-segment-config.d.ts
3
4
  type AppRouteSegmentDynamic = "auto" | "error" | "force-dynamic" | "force-static";
4
5
  type AppRouteSegmentConfigModule = {
@@ -1,7 +1,8 @@
1
1
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
2
2
  import { ActionRevalidationKind } from "../shims/cache-request-state.js";
3
3
  import { AppRenderErrorContextOverrides } from "./app-rsc-error-handler.js";
4
- import { FetchCacheMode } from "../shims/fetch-cache.js";
4
+ import { FetchCacheMode } from "../shims/fetch-cache-state.js";
5
+ import "../shims/fetch-cache.js";
5
6
  import { HeadersAccessPhase } from "../shims/internal/headers-state.js";
6
7
  import "../shims/headers.js";
7
8
  import { ReactFormState } from "react-dom/client";
@@ -1,3 +1,4 @@
1
+ import { runOutsideRequestScopes } from "../shims/internal/als-registry.js";
1
2
  import { VINEXT_MW_CTX_HEADER } from "../utils/protocol-headers.js";
2
3
  import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
3
4
  import { parseEdgeRequestCookieHeader } from "../utils/parse-cookie.js";
@@ -18,16 +19,34 @@ import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, createRscReques
18
19
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
19
20
  import { applyEdgeRuntimeHeader } from "./app-page-response.js";
20
21
  import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
22
+ import { hasDigest } from "./app-rsc-errors.js";
21
23
  import { buildAppPageTags } from "./implicit-tags.js";
24
+ import "./app-page-stream.js";
22
25
  import { getSetCookieName } from "./cookie-utils.js";
23
26
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
24
27
  import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
25
- import "./app-page-stream.js";
26
28
  import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
27
29
  import { createRootParamsUsageController, runWithRootParamsUsage } from "../shims/root-params.js";
28
30
  import { createStaticGenerationHeadersContext } from "./app-static-generation.js";
29
31
  import { markAppRscResponseConfigHeadersApplied } from "./app-rsc-response-finalizer.js";
30
32
  //#region src/server/app-server-action-execution.ts
33
+ /**
34
+ * Run a server-reference load or decode outside every request scope.
35
+ *
36
+ * Server reference modules are imported on the first request that names them
37
+ * — `loadServerAction()` imports the action's module, and the decoders import
38
+ * any reference the body carries — and a dynamic `import()` carries
39
+ * AsyncLocalStorage into the module's top-level evaluation. Module-scope
40
+ * `cookies()`/`headers()` would then capture that first caller's request for
41
+ * every later caller in the isolate. Next.js evaluates action modules outside
42
+ * the request store, where those calls throw.
43
+ *
44
+ * The result is awaited inside the callback because `decodeReply()` returns a
45
+ * lazy thenable that only resolves (and imports) its references once awaited.
46
+ */
47
+ function loadOutsideRequestScopes(load) {
48
+ return runOutsideRequestScopes(async () => await load());
49
+ }
31
50
  function parseBoundProgressiveActionReference(body, referencePrefix) {
32
51
  const fieldPrefix = `$ACTION_${referencePrefix}:`;
33
52
  const chunks = /* @__PURE__ */ new Map();
@@ -473,6 +492,25 @@ function shouldUseForwardedActionRedirectStatus(options) {
473
492
  if (!options.currentRoute || !options.targetRoute || !options.resolveRouteRuntime) return false;
474
493
  return normalizeRuntime(options.resolveRouteRuntime(options.currentRoute)) !== normalizeRuntime(options.resolveRouteRuntime(options.targetRoute));
475
494
  }
495
+ /**
496
+ * A failed fetch action's result, as Next.js sends it: a rejected promise.
497
+ * React's Flight error channel reports the rejection through the render's
498
+ * onError and gives the client error its digest, where an Error value would
499
+ * reach the client without one.
500
+ */
501
+ function createRejectedActionResult(error) {
502
+ const result = Promise.reject(error);
503
+ result.catch(() => {});
504
+ return result;
505
+ }
506
+ /**
507
+ * In development the render's onError logs a rejected action result unless it
508
+ * already has a digest; log those here, so each failure is logged once. As in
509
+ * Next.js, a thrown string is not logged in development.
510
+ */
511
+ function logRejectedActionResult(error) {
512
+ if (process.env.NODE_ENV === "production" || hasDigest(error)) console.error("[vinext] Server action error:", error);
513
+ }
476
514
  function getActionHttpFallbackStatus(error) {
477
515
  const digest = getNextErrorDigest(error);
478
516
  if (!digest) return null;
@@ -559,7 +597,7 @@ async function handleProgressiveServerActionRequest(options) {
559
597
  clearRequestContext: options.clearRequestContext,
560
598
  getAndClearPendingCookies: options.getAndClearPendingCookies
561
599
  });
562
- const action = await options.decodeAction(body);
600
+ const action = await loadOutsideRequestScopes(() => options.decodeAction(body));
563
601
  if (!isAppServerActionFunction(action)) {
564
602
  if (options.hasPageRoute) return createActionNotFoundResponse(null, {
565
603
  clearRequestContext: options.clearRequestContext,
@@ -579,6 +617,7 @@ async function handleProgressiveServerActionRequest(options) {
579
617
  let actionRedirect = null;
580
618
  let actionError = void 0;
581
619
  let actionFailed = false;
620
+ let actionFailedWithError = false;
582
621
  let actionThrew = false;
583
622
  let actionResult;
584
623
  const rootParamsUsage = createRootParamsUsageController();
@@ -591,7 +630,8 @@ async function handleProgressiveServerActionRequest(options) {
591
630
  if (!actionRedirect) {
592
631
  actionError = error;
593
632
  actionFailed = true;
594
- if (!(getActionHttpFallbackStatus(error) !== null || isServerActionNotFoundError(error, null))) {
633
+ actionFailedWithError = getActionHttpFallbackStatus(error) === null && !isServerActionNotFoundError(error, null);
634
+ if (actionFailedWithError) {
595
635
  console.error("[vinext] Server action error:", error);
596
636
  options.reportRequestError(error, {
597
637
  path: options.cleanPathname,
@@ -616,6 +656,15 @@ async function handleProgressiveServerActionRequest(options) {
616
656
  const actionPendingCookies = dedupePendingCookies(options.getAndClearPendingCookies());
617
657
  const actionDraftCookie = options.getDraftModeCookieHeader();
618
658
  const revalidationKind = resolveActionRevalidationKind(actionPendingCookies.length > 0 || Boolean(actionDraftCookie));
659
+ if (actionFailedWithError) {
660
+ options.clearRequestContext();
661
+ const headers = new Headers();
662
+ mergeMiddlewareResponseHeaders(headers, options.middlewareHeaders);
663
+ headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
664
+ for (const cookie of actionPendingCookies) headers.append("Set-Cookie", cookie);
665
+ if (actionDraftCookie) headers.append("Set-Cookie", actionDraftCookie);
666
+ return internalServerErrorResponse(process.env.NODE_ENV === "production" ? void 0 : "Server action failed: " + getServerActionFailureMessage(actionError), { headers });
667
+ }
619
668
  if (actionFailed) return {
620
669
  kind: "form-state",
621
670
  formState: null,
@@ -627,7 +676,7 @@ async function handleProgressiveServerActionRequest(options) {
627
676
  };
628
677
  return {
629
678
  kind: "form-state",
630
- formState: await options.decodeFormState(actionResult, body) ?? null,
679
+ formState: await loadOutsideRequestScopes(() => options.decodeFormState(actionResult, body)) ?? null,
631
680
  pendingCookies: actionPendingCookies,
632
681
  draftCookie: actionDraftCookie,
633
682
  revalidationKind
@@ -680,29 +729,18 @@ async function handleProgressiveServerActionRequest(options) {
680
729
  * with status 500 carrying the rejected action result, so the nearest client
681
730
  * error boundary catches it (see action-handler.ts, the `isFetchAction` branch
682
731
  * of the generic error path). vinext mirrors that by rendering a Flight stream
683
- * with `returnValue: { ok: false }` and no page root (the action never ran, so
732
+ * with a rejected `returnValue: { ok: false }` and no page root (the action never ran, so
684
733
  * nothing was revalidated and the page render is skipped). A bare 413 plain
685
734
  * response would bypass the boundary and surface the wrong status/content-type.
686
735
  */
687
736
  async function renderFetchActionBodyExceededResponse(options) {
688
737
  const error = createBodyExceededError(options.maxActionBodySizeLabel);
689
- console.error("[vinext] Server action error:", error);
690
- options.reportRequestError(error, {
691
- path: options.cleanPathname,
692
- method: options.request.method,
693
- headers: Object.fromEntries(options.request.headers.entries())
694
- }, {
695
- routerKind: "App Router",
696
- routePath: options.currentRouteMatch?.route.pattern ?? options.cleanPathname,
697
- routeType: "action",
698
- renderSource: "react-server-components-payload",
699
- revalidateReason: void 0
700
- });
738
+ logRejectedActionResult(error);
701
739
  getAndClearActionRevalidationKind();
702
740
  options.getAndClearPendingCookies();
703
741
  const returnValue = {
704
742
  ok: false,
705
- data: options.sanitizeErrorForClient(error)
743
+ data: createRejectedActionResult(options.sanitizeErrorForClient(error))
706
744
  };
707
745
  const temporaryReferences = options.createTemporaryReferenceSet();
708
746
  const onRenderError = options.createRscOnErrorHandler(options.request, options.cleanPathname, options.currentRouteMatch?.route.pattern ?? options.cleanPathname, {
@@ -727,6 +765,7 @@ async function renderFetchActionBodyExceededResponse(options) {
727
765
  }
728
766
  async function handleServerActionRscRequest(options) {
729
767
  if (options.request.method.toUpperCase() !== "POST" || !options.actionId) return null;
768
+ const actionId = options.actionId;
730
769
  const csrfResponse = validateCsrfOrigin(options.request, options.allowedOrigins);
731
770
  if (csrfResponse) return csrfResponse;
732
771
  if (parseInt(options.request.headers.get("content-length") || "0", 10) > options.maxActionBodySize) {
@@ -738,7 +777,7 @@ async function handleServerActionRscRequest(options) {
738
777
  if (options.contentType.startsWith("multipart/form-data")) {
739
778
  let loadedAction;
740
779
  try {
741
- loadedAction = await options.loadServerAction(options.actionId);
780
+ loadedAction = await loadOutsideRequestScopes(() => options.loadServerAction(actionId));
742
781
  } catch (error) {
743
782
  if (isServerActionNotFoundError(error, options.actionId)) return createActionNotFoundResponse(options.actionId, {
744
783
  clearRequestContext: options.clearRequestContext,
@@ -768,7 +807,7 @@ async function handleServerActionRscRequest(options) {
768
807
  if (action === void 0) {
769
808
  let loadedAction;
770
809
  try {
771
- loadedAction = await options.loadServerAction(options.actionId);
810
+ loadedAction = await loadOutsideRequestScopes(() => options.loadServerAction(actionId));
772
811
  } catch (error) {
773
812
  if (isServerActionNotFoundError(error, options.actionId)) return createActionNotFoundResponse(options.actionId, {
774
813
  clearRequestContext: options.clearRequestContext,
@@ -783,7 +822,7 @@ async function handleServerActionRscRequest(options) {
783
822
  action = loadedAction;
784
823
  }
785
824
  const temporaryReferences = options.createTemporaryReferenceSet();
786
- const args = await options.decodeReply(body, { temporaryReferences });
825
+ const args = await loadOutsideRequestScopes(() => options.decodeReply(body, { temporaryReferences }));
787
826
  let returnValue;
788
827
  let actionRedirect = null;
789
828
  let actionStatus = 200;
@@ -815,10 +854,10 @@ async function handleServerActionRscRequest(options) {
815
854
  };
816
855
  } else {
817
856
  actionStatus = 500;
818
- console.error("[vinext] Server action error:", error);
857
+ logRejectedActionResult(error);
819
858
  returnValue = {
820
859
  ok: false,
821
- data: options.sanitizeErrorForClient(error)
860
+ data: createRejectedActionResult(options.sanitizeErrorForClient(error))
822
861
  };
823
862
  }
824
863
  }
@@ -46,8 +46,9 @@ export declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navCont
46
46
  initialDevServerError?: unknown;
47
47
  /** Report an SSR/Fizz render failure through instrumentation. */
48
48
  onSsrError?: (error: unknown) => unknown;
49
- /** Mirror inline Flight chunks into Next.js's `self.__next_f` transport. */
50
- mirrorNextFlight?: boolean;
49
+ /** Mirror inline Flight chunks into Next.js's `self.__next_f` transport, or start
50
+ * mirroring once a function returns true. */
51
+ mirrorNextFlight?: boolean | (() => boolean);
51
52
  /** When true, wait for the full React tree (including Suspense boundaries)
52
53
  * to resolve before returning the HTML stream. Used for static prerender
53
54
  * and ISR cache writes to avoid caching fallback content. */
@@ -1,8 +1,6 @@
1
1
  import "./server-globals.js";
2
2
  import { notFoundResponse } from "./http-error-responses.js";
3
- import { isOpenRedirectShaped } from "./open-redirect.js";
4
- import { AppElementsWire } from "./app-elements-wire.js";
5
- import "./app-elements.js";
3
+ import { isOpenRedirectShaped, repeatedSlashRedirectResponse } from "./open-redirect.js";
6
4
  import { isPprFallbackShellAbortError } from "../shims/ppr-fallback-shell.js";
7
5
  import { onRenderDynamicLatched } from "../shims/internal/headers-state.js";
8
6
  import { AppRouterContext } from "../shims/internal/app-router-context.js";
@@ -12,21 +10,24 @@ import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonSt
12
10
  import { getClientTraceMetadataHTML, markClientTraceMetadataBlock } from "./client-trace-metadata.js";
13
11
  import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
14
12
  import { setPagesClientAssets } from "./pages-client-assets.js";
13
+ import { AppElementsWire } from "./app-elements-wire.js";
14
+ import "./app-elements.js";
15
15
  import { ServerInsertedHTMLContext, clearServerInsertedHTML, getBfcacheIdMapContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "../shims/navigation-context-state.js";
16
16
  import "../shims/navigation-server.js";
17
17
  import DefaultGlobalError from "../shims/default-global-error.js";
18
18
  import { BfcacheIdentityMapContext, ElementsContext, Slot } from "../shims/slot.js";
19
19
  import { getNextErrorDigest } from "./next-error-digest.js";
20
20
  import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
21
- import { createClientPageSsrSearchParamsSource } from "./app-page-search-params-observation.js";
22
21
  import { isAppRenderAbortError } from "./app-render-abort-error.js";
23
22
  import { RSC_FORM_STATE_GLOBAL } from "../client/browser-globals.js";
24
23
  import { createNavigationRuntimeRenderedSearchScript, createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, waitAtLeastOneReactRenderTask } from "./app-ssr-stream.js";
25
24
  import { createInitialBfcacheMaps } from "./app-bfcache-identity.js";
26
25
  import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
26
+ import { createClientPageSsrSearchParamsSource } from "./app-page-search-params-observation.js";
27
27
  import { runWithRootParamsScope } from "../shims/root-params.js";
28
28
  import { runWithNavigationContext } from "../shims/navigation-state.js";
29
29
  import { startCandidateSearchParamsGate } from "./app-ssr-search-params-gate.js";
30
+ import { withAppRouterTree } from "../shims/app-router-tree-context.js";
30
31
  import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
31
32
  import { renderBeforeInteractiveInlineScripts } from "./before-interactive-head.js";
32
33
  import { createInitialDevServerErrorScript } from "./dev-initial-server-error.js";
@@ -246,7 +247,7 @@ async function handleSsr(rscStream, navContext, fontData, options) {
246
247
  beforeInteractiveInlineScripts.push(script);
247
248
  };
248
249
  const treeWithBeforeInteractive = createElement(BeforeInteractiveContext.Provider, { value: registerBeforeInteractiveInlineScript }, ssrTree);
249
- const ssrRoot = withScriptNonce(treeWithBeforeInteractive, options?.scriptNonce);
250
+ const ssrRoot = withScriptNonce(withAppRouterTree(treeWithBeforeInteractive), options?.scriptNonce);
250
251
  const bootstrapModuleUrl = extractBootstrapModuleUrl(await import.meta.viteRsc.loadBootstrapScriptContent("index"));
251
252
  const errorMetaRenderer = createSsrErrorMetaRenderer({ basePath: options?.basePath });
252
253
  const pprFallbackShellSignal = options?.pprFallbackShellSignal;
@@ -357,6 +358,8 @@ async function handleSsr(rscStream, navContext, fontData, options) {
357
358
  }
358
359
  var app_ssr_entry_default = { async fetch(request) {
359
360
  const url = new URL(request.url);
361
+ const slashRedirect = repeatedSlashRedirectResponse(url.pathname + url.search);
362
+ if (slashRedirect) return slashRedirect;
360
363
  if (isOpenRedirectShaped(url.pathname)) return notFoundResponse();
361
364
  const result = await (await import.meta.viteRsc.loadModule("rsc", "index")).default(request);
362
365
  if (result instanceof Response) return result;
@@ -6,7 +6,12 @@ type RscEmbedTransform = {
6
6
  getRawBuffer(): Promise<ArrayBuffer>;
7
7
  };
8
8
  type RscEmbedTransformOptions = {
9
- mirrorNextFlight?: boolean;
9
+ /**
10
+ * Mirror inline Flight chunks into Next.js's `self.__next_f` transport. A
11
+ * function is checked at each flush; once it returns true, the chunks
12
+ * already embedded are mirrored first.
13
+ */
14
+ mirrorNextFlight?: boolean | (() => boolean);
10
15
  scriptNonce?: string;
11
16
  getInitialNavigationCacheMetadata?: () => InitialNavigationCacheMetadata;
12
17
  };
@@ -49,12 +49,36 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
49
49
  const reader = embedStream.getReader();
50
50
  let pendingChunks = [];
51
51
  const rawChunks = [];
52
+ let embeddedRawChunkCount = 0;
53
+ let rawBufferTaken = false;
54
+ let finalized = false;
52
55
  let reading = false;
53
56
  let mirroredNextFlightBootstrap = false;
54
57
  const textDecoder = new TextDecoder("utf-8", {
55
58
  fatal: true,
56
59
  ignoreBOM: true
57
60
  });
61
+ function decodeChunk(bytes) {
62
+ try {
63
+ return textDecoder.decode(bytes);
64
+ } catch {
65
+ return [3, bytesToBase64(bytes)];
66
+ }
67
+ }
68
+ function releaseRawChunks() {
69
+ const mayStartMirror = typeof options.mirrorNextFlight === "function" && !mirroredNextFlightBootstrap && !finalized;
70
+ if (rawBufferTaken && !mayStartMirror) rawChunks.length = 0;
71
+ }
72
+ function startNextFlightMirror() {
73
+ if (mirroredNextFlightBootstrap) return "";
74
+ const mirror = options.mirrorNextFlight;
75
+ if (mirror !== true && (typeof mirror !== "function" || !mirror())) return "";
76
+ mirroredNextFlightBootstrap = true;
77
+ let scripts = createInlineScriptTag(createNextFlightBootstrapScript(), options.scriptNonce) + createInlineScriptTag(createNextFlightCleanupScript(), options.scriptNonce);
78
+ for (let index = 0; index < embeddedRawChunkCount; index++) scripts += createInlineScriptTag(createNextFlightChunkScript(decodeChunk(rawChunks[index])), options.scriptNonce);
79
+ releaseRawChunks();
80
+ return scripts;
81
+ }
58
82
  async function pumpReader() {
59
83
  if (reading) return;
60
84
  reading = true;
@@ -63,11 +87,7 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
63
87
  const result = await reader.read();
64
88
  if (result.done) break;
65
89
  rawChunks.push(result.value);
66
- try {
67
- pendingChunks.push(textDecoder.decode(result.value));
68
- } catch {
69
- pendingChunks.push([3, bytesToBase64(result.value)]);
70
- }
90
+ pendingChunks.push(decodeChunk(result.value));
71
91
  }
72
92
  } catch (error) {
73
93
  if (process.env.NODE_ENV !== "production") console.warn("[vinext] RSC embed stream read error:", error);
@@ -80,8 +100,10 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
80
100
  return {
81
101
  flush() {
82
102
  if (pendingChunks.length === 0) return "";
103
+ const mirrorScripts = startNextFlightMirror();
83
104
  const chunks = pendingChunks;
84
105
  pendingChunks = [];
106
+ embeddedRawChunkCount += chunks.length;
85
107
  const embeddedChunks = [];
86
108
  let textChunks = [];
87
109
  let textLength = 0;
@@ -100,30 +122,27 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
100
122
  embeddedChunks.push(chunk);
101
123
  }
102
124
  flushTextChunks();
103
- let scripts = "";
125
+ let scripts = mirrorScripts;
104
126
  for (const chunk of embeddedChunks) {
105
127
  scripts += createInlineScriptTag(createNavigationRuntimeRscChunkScript(chunk), options.scriptNonce);
106
- if (options.mirrorNextFlight) {
107
- if (!mirroredNextFlightBootstrap) {
108
- scripts += createInlineScriptTag(createNextFlightBootstrapScript(), options.scriptNonce);
109
- scripts += createInlineScriptTag(createNextFlightCleanupScript(), options.scriptNonce);
110
- mirroredNextFlightBootstrap = true;
111
- }
112
- scripts += createInlineScriptTag(createNextFlightChunkScript(chunk), options.scriptNonce);
113
- }
128
+ if (mirroredNextFlightBootstrap) scripts += createInlineScriptTag(createNextFlightChunkScript(chunk), options.scriptNonce);
114
129
  }
115
130
  return scripts;
116
131
  },
117
132
  async finalize() {
118
133
  await pumpPromise;
119
134
  let scripts = this.flush();
135
+ scripts += startNextFlightMirror();
136
+ finalized = true;
137
+ releaseRawChunks();
120
138
  scripts += createInlineScriptTag(createNavigationRuntimeRscDoneScript(options.getInitialNavigationCacheMetadata?.()), options.scriptNonce);
121
139
  return scripts;
122
140
  },
123
141
  async getRawBuffer() {
124
142
  await pumpPromise;
125
143
  const buffer = concatUint8Arrays(rawChunks);
126
- rawChunks.length = 0;
144
+ rawBufferTaken = true;
145
+ releaseRawChunks();
127
146
  return buffer.buffer;
128
147
  }
129
148
  };
@@ -3,7 +3,7 @@ import { VinextResponseStageCacheability, VinextResponseStageDispatchOptions, Vi
3
3
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
4
4
  import { TrustedPrerenderState } from "./prerender-route-params.js";
5
5
  //#region src/server/app-worker-stages.d.ts
6
- export declare const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 10;
6
+ export declare const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 11;
7
7
  export declare const APP_METADATA_RESPONSE_STAGE_NO_MATCH_HEADER = "x-vinext-app-metadata-stage-no-match";
8
8
  type AppPageParams = Record<string, string | string[]>;
9
9
  type AppWorkerResponseStageEnvelope = {
@@ -18,6 +18,11 @@ type AppWorkerResponseStageEnvelope = {
18
18
  };
19
19
  type AppFullRequestWorkerResponseStageProps = AppWorkerResponseStageEnvelope & {
20
20
  kind: "app-full-request";
21
+ /**
22
+ * The public request is HEAD. It is dispatched as GET so a transport that
23
+ * applies HEAD semantics cannot drop the static-file signal's body.
24
+ */
25
+ headRequest: boolean;
21
26
  prerenderDiscovery: boolean;
22
27
  staticFileSignalToken: string;
23
28
  trustedPrerenderState: TrustedPrerenderState | null;
@@ -34,7 +39,7 @@ export type AppMatchedWorkerResponseStageProps = AppWorkerResponseStageEnvelope
34
39
  interceptionContext: string | null;
35
40
  interceptionId: string | null;
36
41
  isRscRequest: boolean;
37
- matchKind: "interception" | "request" | "resolved";
42
+ matchKind: "interception" | "raw-request" | "request" | "resolved";
38
43
  mountedSlotsHeader: string | null;
39
44
  params: AppPageParams;
40
45
  resolvedUrl: string;