vinext 0.0.45 → 0.0.47

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 (311) hide show
  1. package/README.md +7 -5
  2. package/dist/build/prerender.d.ts +2 -1
  3. package/dist/build/prerender.js +80 -17
  4. package/dist/build/prerender.js.map +1 -1
  5. package/dist/build/report.d.ts +1 -1
  6. package/dist/build/route-classification-injector.d.ts +35 -0
  7. package/dist/build/route-classification-injector.js +61 -0
  8. package/dist/build/route-classification-injector.js.map +1 -0
  9. package/dist/build/route-classification-manifest.d.ts +1 -1
  10. package/dist/build/standalone.js +4 -3
  11. package/dist/build/standalone.js.map +1 -1
  12. package/dist/build/static-export.d.ts +1 -1
  13. package/dist/check.js +30 -18
  14. package/dist/check.js.map +1 -1
  15. package/dist/cli-args.d.ts +31 -0
  16. package/dist/cli-args.js +104 -0
  17. package/dist/cli-args.js.map +1 -0
  18. package/dist/cli.js +6 -19
  19. package/dist/cli.js.map +1 -1
  20. package/dist/cloudflare/kv-cache-handler.js +29 -9
  21. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  22. package/dist/config/config-matchers.js +1 -0
  23. package/dist/config/config-matchers.js.map +1 -1
  24. package/dist/config/next-config.d.ts +42 -4
  25. package/dist/config/next-config.js +27 -0
  26. package/dist/config/next-config.js.map +1 -1
  27. package/dist/deploy.js +18 -23
  28. package/dist/deploy.js.map +1 -1
  29. package/dist/entries/app-rsc-entry.d.ts +4 -3
  30. package/dist/entries/app-rsc-entry.js +435 -2317
  31. package/dist/entries/app-rsc-entry.js.map +1 -1
  32. package/dist/entries/app-rsc-manifest.d.ts +24 -0
  33. package/dist/entries/app-rsc-manifest.js +155 -0
  34. package/dist/entries/app-rsc-manifest.js.map +1 -0
  35. package/dist/entries/pages-server-entry.js +18 -105
  36. package/dist/entries/pages-server-entry.js.map +1 -1
  37. package/dist/index.js +82 -85
  38. package/dist/index.js.map +1 -1
  39. package/dist/plugins/fonts.js +54 -32
  40. package/dist/plugins/fonts.js.map +1 -1
  41. package/dist/plugins/rsc-client-shim-excludes.d.ts +6 -0
  42. package/dist/plugins/rsc-client-shim-excludes.js +28 -0
  43. package/dist/plugins/rsc-client-shim-excludes.js.map +1 -0
  44. package/dist/routing/app-route-graph.d.ts +109 -0
  45. package/dist/routing/app-route-graph.js +819 -0
  46. package/dist/routing/app-route-graph.js.map +1 -0
  47. package/dist/routing/app-router.d.ts +2 -79
  48. package/dist/routing/app-router.js +7 -621
  49. package/dist/routing/app-router.js.map +1 -1
  50. package/dist/routing/route-pattern.d.ts +9 -0
  51. package/dist/routing/route-pattern.js +90 -0
  52. package/dist/routing/route-pattern.js.map +1 -0
  53. package/dist/routing/route-trie.js +10 -11
  54. package/dist/routing/route-trie.js.map +1 -1
  55. package/dist/server/app-browser-entry.js +94 -232
  56. package/dist/server/app-browser-entry.js.map +1 -1
  57. package/dist/server/app-browser-error.d.ts +3 -4
  58. package/dist/server/app-browser-error.js +8 -4
  59. package/dist/server/app-browser-error.js.map +1 -1
  60. package/dist/server/app-browser-navigation-controller.d.ts +73 -0
  61. package/dist/server/app-browser-navigation-controller.js +282 -0
  62. package/dist/server/app-browser-navigation-controller.js.map +1 -0
  63. package/dist/server/app-browser-state.d.ts +1 -1
  64. package/dist/server/app-browser-state.js.map +1 -1
  65. package/dist/server/app-elements.js +1 -5
  66. package/dist/server/app-elements.js.map +1 -1
  67. package/dist/server/app-fallback-renderer.d.ts +57 -0
  68. package/dist/server/app-fallback-renderer.js +79 -0
  69. package/dist/server/app-fallback-renderer.js.map +1 -0
  70. package/dist/server/app-hook-warning-suppression.d.ts +7 -0
  71. package/dist/server/app-hook-warning-suppression.js +12 -0
  72. package/dist/server/app-hook-warning-suppression.js.map +1 -0
  73. package/dist/server/app-middleware.d.ts +32 -0
  74. package/dist/server/app-middleware.js +147 -0
  75. package/dist/server/app-middleware.js.map +1 -0
  76. package/dist/server/app-mounted-slots-header.d.ts +17 -0
  77. package/dist/server/app-mounted-slots-header.js +21 -0
  78. package/dist/server/app-mounted-slots-header.js.map +1 -0
  79. package/dist/server/app-page-boundary-render.d.ts +4 -2
  80. package/dist/server/app-page-boundary-render.js +50 -30
  81. package/dist/server/app-page-boundary-render.js.map +1 -1
  82. package/dist/server/app-page-boundary.d.ts +12 -1
  83. package/dist/server/app-page-boundary.js +27 -12
  84. package/dist/server/app-page-boundary.js.map +1 -1
  85. package/dist/server/app-page-cache.d.ts +22 -5
  86. package/dist/server/app-page-cache.js +90 -11
  87. package/dist/server/app-page-cache.js.map +1 -1
  88. package/dist/server/app-page-dispatch.d.ts +123 -0
  89. package/dist/server/app-page-dispatch.js +348 -0
  90. package/dist/server/app-page-dispatch.js.map +1 -0
  91. package/dist/server/app-page-element-builder.d.ts +61 -0
  92. package/dist/server/app-page-element-builder.js +139 -0
  93. package/dist/server/app-page-element-builder.js.map +1 -0
  94. package/dist/server/app-page-execution.d.ts +4 -3
  95. package/dist/server/app-page-execution.js +5 -8
  96. package/dist/server/app-page-execution.js.map +1 -1
  97. package/dist/server/app-page-head.d.ts +55 -0
  98. package/dist/server/app-page-head.js +196 -0
  99. package/dist/server/app-page-head.js.map +1 -0
  100. package/dist/server/app-page-method.d.ts +16 -0
  101. package/dist/server/app-page-method.js +30 -0
  102. package/dist/server/app-page-method.js.map +1 -0
  103. package/dist/server/app-page-params.d.ts +8 -0
  104. package/dist/server/app-page-params.js +28 -0
  105. package/dist/server/app-page-params.js.map +1 -0
  106. package/dist/server/app-page-render.d.ts +7 -2
  107. package/dist/server/app-page-render.js +131 -32
  108. package/dist/server/app-page-render.js.map +1 -1
  109. package/dist/server/app-page-request.d.ts +23 -8
  110. package/dist/server/app-page-request.js +51 -6
  111. package/dist/server/app-page-request.js.map +1 -1
  112. package/dist/server/app-page-response.d.ts +1 -0
  113. package/dist/server/app-page-response.js +3 -7
  114. package/dist/server/app-page-response.js.map +1 -1
  115. package/dist/server/app-page-route-wiring.d.ts +29 -5
  116. package/dist/server/app-page-route-wiring.js +30 -8
  117. package/dist/server/app-page-route-wiring.js.map +1 -1
  118. package/dist/server/app-page-stream.d.ts +10 -0
  119. package/dist/server/app-page-stream.js +5 -1
  120. package/dist/server/app-page-stream.js.map +1 -1
  121. package/dist/server/app-post-middleware-context.d.ts +16 -0
  122. package/dist/server/app-post-middleware-context.js +28 -0
  123. package/dist/server/app-post-middleware-context.js.map +1 -0
  124. package/dist/server/app-prerender-endpoints.d.ts +19 -0
  125. package/dist/server/app-prerender-endpoints.js +96 -0
  126. package/dist/server/app-prerender-endpoints.js.map +1 -0
  127. package/dist/server/app-prerender-static-params.d.ts +16 -0
  128. package/dist/server/app-prerender-static-params.js +14 -0
  129. package/dist/server/app-prerender-static-params.js.map +1 -0
  130. package/dist/server/app-request-context.d.ts +22 -0
  131. package/dist/server/app-request-context.js +30 -0
  132. package/dist/server/app-request-context.js.map +1 -0
  133. package/dist/server/app-route-handler-cache.d.ts +4 -0
  134. package/dist/server/app-route-handler-cache.js +11 -3
  135. package/dist/server/app-route-handler-cache.js.map +1 -1
  136. package/dist/server/app-route-handler-dispatch.d.ts +43 -0
  137. package/dist/server/app-route-handler-dispatch.js +149 -0
  138. package/dist/server/app-route-handler-dispatch.js.map +1 -0
  139. package/dist/server/app-route-handler-execution.d.ts +8 -3
  140. package/dist/server/app-route-handler-execution.js +25 -4
  141. package/dist/server/app-route-handler-execution.js.map +1 -1
  142. package/dist/server/app-route-handler-response.d.ts +6 -3
  143. package/dist/server/app-route-handler-response.js +52 -11
  144. package/dist/server/app-route-handler-response.js.map +1 -1
  145. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  146. package/dist/server/app-route-handler-runtime.js +107 -1
  147. package/dist/server/app-route-handler-runtime.js.map +1 -1
  148. package/dist/server/app-router-entry.js.map +1 -1
  149. package/dist/server/app-rsc-error-handler.d.ts +21 -0
  150. package/dist/server/app-rsc-error-handler.js +30 -0
  151. package/dist/server/app-rsc-error-handler.js.map +1 -0
  152. package/dist/server/app-rsc-errors.d.ts +27 -0
  153. package/dist/server/app-rsc-errors.js +42 -0
  154. package/dist/server/app-rsc-errors.js.map +1 -0
  155. package/dist/server/app-rsc-handler.d.ts +117 -0
  156. package/dist/server/app-rsc-handler.js +260 -0
  157. package/dist/server/app-rsc-handler.js.map +1 -0
  158. package/dist/server/app-rsc-request-normalization.d.ts +40 -0
  159. package/dist/server/app-rsc-request-normalization.js +63 -0
  160. package/dist/server/app-rsc-request-normalization.js.map +1 -0
  161. package/dist/server/app-rsc-response-finalizer.d.ts +30 -0
  162. package/dist/server/app-rsc-response-finalizer.js +38 -0
  163. package/dist/server/app-rsc-response-finalizer.js.map +1 -0
  164. package/dist/server/app-rsc-route-matching.d.ts +40 -0
  165. package/dist/server/app-rsc-route-matching.js +66 -0
  166. package/dist/server/app-rsc-route-matching.js.map +1 -0
  167. package/dist/server/app-segment-config.d.ts +33 -0
  168. package/dist/server/app-segment-config.js +86 -0
  169. package/dist/server/app-segment-config.js.map +1 -0
  170. package/dist/server/app-server-action-execution.d.ts +88 -1
  171. package/dist/server/app-server-action-execution.js +257 -5
  172. package/dist/server/app-server-action-execution.js.map +1 -1
  173. package/dist/server/app-ssr-entry.d.ts +7 -0
  174. package/dist/server/app-ssr-entry.js +30 -9
  175. package/dist/server/app-ssr-entry.js.map +1 -1
  176. package/dist/server/app-ssr-stream.d.ts +4 -2
  177. package/dist/server/app-ssr-stream.js +29 -2
  178. package/dist/server/app-ssr-stream.js.map +1 -1
  179. package/dist/server/app-static-generation.d.ts +15 -0
  180. package/dist/server/app-static-generation.js +20 -0
  181. package/dist/server/app-static-generation.js.map +1 -0
  182. package/dist/server/cache-control.d.ts +24 -0
  183. package/dist/server/cache-control.js +33 -0
  184. package/dist/server/cache-control.js.map +1 -0
  185. package/dist/server/dev-error-overlay-store.d.ts +23 -0
  186. package/dist/server/dev-error-overlay-store.js +67 -0
  187. package/dist/server/dev-error-overlay-store.js.map +1 -0
  188. package/dist/server/dev-error-overlay.d.ts +15 -0
  189. package/dist/server/dev-error-overlay.js +548 -0
  190. package/dist/server/dev-error-overlay.js.map +1 -0
  191. package/dist/server/dev-route-files.d.ts +7 -0
  192. package/dist/server/dev-route-files.js +73 -0
  193. package/dist/server/dev-route-files.js.map +1 -0
  194. package/dist/server/dev-server.js +4 -0
  195. package/dist/server/dev-server.js.map +1 -1
  196. package/dist/server/file-based-metadata.d.ts +17 -0
  197. package/dist/server/file-based-metadata.js +356 -0
  198. package/dist/server/file-based-metadata.js.map +1 -0
  199. package/dist/server/implicit-tags.d.ts +6 -0
  200. package/dist/server/implicit-tags.js +42 -0
  201. package/dist/server/implicit-tags.js.map +1 -0
  202. package/dist/server/instrumentation-runtime.d.ts +44 -0
  203. package/dist/server/instrumentation-runtime.js +29 -0
  204. package/dist/server/instrumentation-runtime.js.map +1 -0
  205. package/dist/server/instrumentation.js.map +1 -1
  206. package/dist/server/isr-cache.d.ts +16 -3
  207. package/dist/server/isr-cache.js +56 -8
  208. package/dist/server/isr-cache.js.map +1 -1
  209. package/dist/server/metadata-route-build-data.d.ts +25 -0
  210. package/dist/server/metadata-route-build-data.js +150 -0
  211. package/dist/server/metadata-route-build-data.js.map +1 -0
  212. package/dist/server/metadata-route-response.d.ts +17 -0
  213. package/dist/server/metadata-route-response.js +187 -0
  214. package/dist/server/metadata-route-response.js.map +1 -0
  215. package/dist/server/metadata-routes.d.ts +42 -4
  216. package/dist/server/metadata-routes.js +127 -11
  217. package/dist/server/metadata-routes.js.map +1 -1
  218. package/dist/server/middleware-matcher.d.ts +15 -0
  219. package/dist/server/middleware-matcher.js +102 -0
  220. package/dist/server/middleware-matcher.js.map +1 -0
  221. package/dist/server/middleware-request-headers.js +2 -1
  222. package/dist/server/middleware-request-headers.js.map +1 -1
  223. package/dist/server/middleware-runtime.d.ts +39 -0
  224. package/dist/server/middleware-runtime.js +159 -0
  225. package/dist/server/middleware-runtime.js.map +1 -0
  226. package/dist/server/middleware.d.ts +4 -36
  227. package/dist/server/middleware.js +18 -228
  228. package/dist/server/middleware.js.map +1 -1
  229. package/dist/server/pages-page-data.d.ts +7 -2
  230. package/dist/server/pages-page-data.js +10 -5
  231. package/dist/server/pages-page-data.js.map +1 -1
  232. package/dist/server/pages-page-response.d.ts +2 -1
  233. package/dist/server/pages-page-response.js +5 -3
  234. package/dist/server/pages-page-response.js.map +1 -1
  235. package/dist/server/prerender-work-unit-setup.d.ts +7 -0
  236. package/dist/server/prerender-work-unit-setup.js +30 -0
  237. package/dist/server/prerender-work-unit-setup.js.map +1 -0
  238. package/dist/server/prod-server.js +10 -14
  239. package/dist/server/prod-server.js.map +1 -1
  240. package/dist/server/request-pipeline.d.ts +46 -5
  241. package/dist/server/request-pipeline.js +84 -5
  242. package/dist/server/request-pipeline.js.map +1 -1
  243. package/dist/server/rsc-stream-hints.d.ts +7 -0
  244. package/dist/server/rsc-stream-hints.js +38 -0
  245. package/dist/server/rsc-stream-hints.js.map +1 -0
  246. package/dist/server/seed-cache.js +19 -8
  247. package/dist/server/seed-cache.js.map +1 -1
  248. package/dist/server/server-action-not-found.d.ts +9 -0
  249. package/dist/server/server-action-not-found.js +40 -0
  250. package/dist/server/server-action-not-found.js.map +1 -0
  251. package/dist/shims/cache-runtime.js +28 -11
  252. package/dist/shims/cache-runtime.js.map +1 -1
  253. package/dist/shims/cache.d.ts +39 -4
  254. package/dist/shims/cache.js +93 -16
  255. package/dist/shims/cache.js.map +1 -1
  256. package/dist/shims/error-boundary.d.ts +66 -5
  257. package/dist/shims/error-boundary.js +106 -4
  258. package/dist/shims/error-boundary.js.map +1 -1
  259. package/dist/shims/fetch-cache.d.ts +4 -1
  260. package/dist/shims/fetch-cache.js +55 -13
  261. package/dist/shims/fetch-cache.js.map +1 -1
  262. package/dist/shims/font-google-base.d.ts +5 -4
  263. package/dist/shims/font-google-base.js +61 -13
  264. package/dist/shims/font-google-base.js.map +1 -1
  265. package/dist/shims/headers.d.ts +14 -2
  266. package/dist/shims/headers.js +127 -17
  267. package/dist/shims/headers.js.map +1 -1
  268. package/dist/shims/image.js +116 -10
  269. package/dist/shims/image.js.map +1 -1
  270. package/dist/shims/internal/make-hanging-promise.d.ts +16 -0
  271. package/dist/shims/internal/make-hanging-promise.js +46 -0
  272. package/dist/shims/internal/make-hanging-promise.js.map +1 -0
  273. package/dist/shims/internal/work-unit-async-storage.d.ts +26 -3
  274. package/dist/shims/internal/work-unit-async-storage.js +6 -3
  275. package/dist/shims/internal/work-unit-async-storage.js.map +1 -1
  276. package/dist/shims/metadata.d.ts +38 -26
  277. package/dist/shims/metadata.js +75 -45
  278. package/dist/shims/metadata.js.map +1 -1
  279. package/dist/shims/navigation.d.ts +10 -1
  280. package/dist/shims/navigation.js +18 -1
  281. package/dist/shims/navigation.js.map +1 -1
  282. package/dist/shims/navigation.react-server.d.ts +2 -2
  283. package/dist/shims/navigation.react-server.js +2 -2
  284. package/dist/shims/navigation.react-server.js.map +1 -1
  285. package/dist/shims/offline.d.ts +5 -0
  286. package/dist/shims/offline.js +17 -0
  287. package/dist/shims/offline.js.map +1 -0
  288. package/dist/shims/request-state-types.d.ts +3 -2
  289. package/dist/shims/root-params.d.ts +11 -0
  290. package/dist/shims/root-params.js +24 -0
  291. package/dist/shims/root-params.js.map +1 -0
  292. package/dist/shims/router.js +1 -1
  293. package/dist/shims/server.d.ts +3 -1
  294. package/dist/shims/server.js +83 -5
  295. package/dist/shims/server.js.map +1 -1
  296. package/dist/shims/thenable-params.d.ts +5 -0
  297. package/dist/shims/thenable-params.js +37 -0
  298. package/dist/shims/thenable-params.js.map +1 -0
  299. package/dist/shims/unified-request-context.d.ts +3 -2
  300. package/dist/shims/unified-request-context.js +3 -0
  301. package/dist/shims/unified-request-context.js.map +1 -1
  302. package/dist/shims/use-merged-ref.d.ts +7 -0
  303. package/dist/shims/use-merged-ref.js +40 -0
  304. package/dist/shims/use-merged-ref.js.map +1 -0
  305. package/dist/utils/cache-control-metadata.d.ts +6 -0
  306. package/dist/utils/cache-control-metadata.js +16 -0
  307. package/dist/utils/cache-control-metadata.js.map +1 -0
  308. package/package.json +6 -1
  309. package/dist/server/middleware-codegen.d.ts +0 -54
  310. package/dist/server/middleware-codegen.js +0 -414
  311. package/dist/server/middleware-codegen.js.map +0 -1
@@ -1,8 +1,7 @@
1
1
  import { resolveEntryPath } from "./runtime-entry-module.js";
2
2
  import { isProxyFile } from "../server/middleware.js";
3
- import { generateMiddlewareMatcherCode, generateNormalizePathCode, generateRouteMatchNormalizationCode, generateSafeRegExpCode } from "../server/middleware-codegen.js";
3
+ import { buildAppRscManifestCode } from "./app-rsc-manifest.js";
4
4
  import { generateDevOriginCheckCode } from "../server/dev-origin-check.js";
5
- import fs from "node:fs";
6
5
  //#region src/entries/app-rsc-entry.ts
7
6
  /**
8
7
  * App Router RSC entry generator.
@@ -13,29 +12,31 @@ import fs from "node:fs";
13
12
  *
14
13
  * Previously housed in server/app-dev-server.ts.
15
14
  */
16
- const configMatchersPath = resolveEntryPath("../config/config-matchers.js", import.meta.url);
17
- const requestPipelinePath = resolveEntryPath("../server/request-pipeline.js", import.meta.url);
15
+ const DEFAULT_EXPIRE_TIME = 31536e3;
18
16
  const middlewareRequestHeadersPath = resolveEntryPath("../server/middleware-request-headers.js", import.meta.url);
19
- const requestContextShimPath = resolveEntryPath("../shims/request-context.js", import.meta.url);
20
17
  const normalizePathModulePath = resolveEntryPath("../server/normalize-path.js", import.meta.url);
21
- const appRouteHandlerRuntimePath = resolveEntryPath("../server/app-route-handler-runtime.js", import.meta.url);
22
- const appRouteHandlerPolicyPath = resolveEntryPath("../server/app-route-handler-policy.js", import.meta.url);
23
- const appRouteHandlerExecutionPath = resolveEntryPath("../server/app-route-handler-execution.js", import.meta.url);
18
+ const appRscHandlerPath = resolveEntryPath("../server/app-rsc-handler.js", import.meta.url);
19
+ const appRouteHandlerDispatchPath = resolveEntryPath("../server/app-route-handler-dispatch.js", import.meta.url);
24
20
  const appServerActionExecutionPath = resolveEntryPath("../server/app-server-action-execution.js", import.meta.url);
25
- const appRouteHandlerCachePath = resolveEntryPath("../server/app-route-handler-cache.js", import.meta.url);
26
- const appPageCachePath = resolveEntryPath("../server/app-page-cache.js", import.meta.url);
21
+ const appRscErrorsPath = resolveEntryPath("../server/app-rsc-errors.js", import.meta.url);
27
22
  const appPageExecutionPath = resolveEntryPath("../server/app-page-execution.js", import.meta.url);
28
- const appPageBoundaryRenderPath = resolveEntryPath("../server/app-page-boundary-render.js", import.meta.url);
23
+ const appFallbackRendererPath = resolveEntryPath("../server/app-fallback-renderer.js", import.meta.url);
29
24
  const appElementsPath = resolveEntryPath("../server/app-elements.js", import.meta.url);
30
25
  const appPageRouteWiringPath = resolveEntryPath("../server/app-page-route-wiring.js", import.meta.url);
31
- const appPageRenderPath = resolveEntryPath("../server/app-page-render.js", import.meta.url);
32
- const appPageResponsePath = resolveEntryPath("../server/app-page-response.js", import.meta.url);
33
- const cspPath = resolveEntryPath("../server/csp.js", import.meta.url);
26
+ const appPageHeadPath = resolveEntryPath("../server/app-page-head.js", import.meta.url);
27
+ const appPageParamsPath = resolveEntryPath("../server/app-page-params.js", import.meta.url);
28
+ const appPageDispatchPath = resolveEntryPath("../server/app-page-dispatch.js", import.meta.url);
34
29
  const appPageRequestPath = resolveEntryPath("../server/app-page-request.js", import.meta.url);
35
- const appRouteHandlerResponsePath = resolveEntryPath("../server/app-route-handler-response.js", import.meta.url);
36
- const routeTriePath = resolveEntryPath("../routing/route-trie.js", import.meta.url);
37
- const metadataRoutesPath = resolveEntryPath("../server/metadata-routes.js", import.meta.url);
38
- const errorCausePath = resolveEntryPath("../utils/error-cause.js", import.meta.url);
30
+ const appSegmentConfigPath = resolveEntryPath("../server/app-segment-config.js", import.meta.url);
31
+ const appRscRouteMatchingPath = resolveEntryPath("../server/app-rsc-route-matching.js", import.meta.url);
32
+ const rscStreamHintsPath = resolveEntryPath("../server/rsc-stream-hints.js", import.meta.url);
33
+ const isrCachePath = resolveEntryPath("../server/isr-cache.js", import.meta.url);
34
+ const thenableParamsShimPath = resolveEntryPath("../shims/thenable-params.js", import.meta.url);
35
+ const appPageElementBuilderPath = resolveEntryPath("../server/app-page-element-builder.js", import.meta.url);
36
+ const instrumentationRuntimePath = resolveEntryPath("../server/instrumentation-runtime.js", import.meta.url);
37
+ const appRscErrorHandlerPath = resolveEntryPath("../server/app-rsc-error-handler.js", import.meta.url);
38
+ const appRequestContextPath = resolveEntryPath("../server/app-request-context.js", import.meta.url);
39
+ const appHookWarningSuppressionPath = resolveEntryPath("../server/app-hook-warning-suppression.js", import.meta.url);
39
40
  /**
40
41
  * Generate the virtual RSC entry module.
41
42
  *
@@ -55,134 +56,21 @@ function generateRscEntry(appDir, routes, middlewarePath, metadataRoutes, global
55
56
  const headers = config?.headers ?? [];
56
57
  const allowedOrigins = config?.allowedOrigins ?? [];
57
58
  const bodySizeLimit = config?.bodySizeLimit ?? 1 * 1024 * 1024;
59
+ const expireTime = config?.expireTime ?? DEFAULT_EXPIRE_TIME;
58
60
  const i18nConfig = config?.i18n ?? null;
59
61
  const hasPagesDir = config?.hasPagesDir ?? false;
60
62
  const publicFiles = config?.publicFiles ?? [];
61
- const imports = [];
62
- const importMap = /* @__PURE__ */ new Map();
63
- let importIdx = 0;
64
- function getImportVar(filePath) {
65
- if (importMap.has(filePath)) return importMap.get(filePath);
66
- const varName = `mod_${importIdx++}`;
67
- const absPath = filePath.replace(/\\/g, "/");
68
- imports.push(`import * as ${varName} from ${JSON.stringify(absPath)};`);
69
- importMap.set(filePath, varName);
70
- return varName;
71
- }
72
- for (const route of routes) {
73
- if (route.pagePath) getImportVar(route.pagePath);
74
- if (route.routePath) getImportVar(route.routePath);
75
- for (const layout of route.layouts) getImportVar(layout);
76
- for (const tmpl of route.templates) getImportVar(tmpl);
77
- if (route.loadingPath) getImportVar(route.loadingPath);
78
- if (route.errorPath) getImportVar(route.errorPath);
79
- if (route.layoutErrorPaths) {
80
- for (const ep of route.layoutErrorPaths) if (ep) getImportVar(ep);
81
- }
82
- if (route.notFoundPath) getImportVar(route.notFoundPath);
83
- for (const nfp of route.notFoundPaths || []) if (nfp) getImportVar(nfp);
84
- if (route.forbiddenPath) getImportVar(route.forbiddenPath);
85
- if (route.unauthorizedPath) getImportVar(route.unauthorizedPath);
86
- for (const slot of route.parallelSlots) {
87
- if (slot.pagePath) getImportVar(slot.pagePath);
88
- if (slot.defaultPath) getImportVar(slot.defaultPath);
89
- if (slot.layoutPath) getImportVar(slot.layoutPath);
90
- if (slot.loadingPath) getImportVar(slot.loadingPath);
91
- if (slot.errorPath) getImportVar(slot.errorPath);
92
- for (const ir of slot.interceptingRoutes) {
93
- getImportVar(ir.pagePath);
94
- for (const layoutPath of ir.layoutPaths) getImportVar(layoutPath);
95
- }
96
- }
97
- }
98
- const routeEntries = routes.map((route, routeIdx) => {
99
- const layoutVars = route.layouts.map((l) => getImportVar(l));
100
- const templateVars = route.templates.map((t) => getImportVar(t));
101
- const notFoundVars = (route.notFoundPaths || []).map((nf) => nf ? getImportVar(nf) : "null");
102
- const slotEntries = route.parallelSlots.map((slot) => {
103
- const interceptEntries = slot.interceptingRoutes.map((ir) => ` {
104
- convention: ${JSON.stringify(ir.convention)},
105
- targetPattern: ${JSON.stringify(ir.targetPattern)},
106
- interceptLayouts: [${ir.layoutPaths.map((layoutPath) => getImportVar(layoutPath)).join(", ")}],
107
- page: ${getImportVar(ir.pagePath)},
108
- params: ${JSON.stringify(ir.params)},
109
- }`);
110
- return ` ${JSON.stringify(slot.key)}: {
111
- name: ${JSON.stringify(slot.name)},
112
- page: ${slot.pagePath ? getImportVar(slot.pagePath) : "null"},
113
- default: ${slot.defaultPath ? getImportVar(slot.defaultPath) : "null"},
114
- layout: ${slot.layoutPath ? getImportVar(slot.layoutPath) : "null"},
115
- loading: ${slot.loadingPath ? getImportVar(slot.loadingPath) : "null"},
116
- error: ${slot.errorPath ? getImportVar(slot.errorPath) : "null"},
117
- layoutIndex: ${slot.layoutIndex},
118
- routeSegments: ${JSON.stringify(slot.routeSegments)},
119
- intercepts: [
120
- ${interceptEntries.join(",\n")}
121
- ],
122
- }`;
123
- });
124
- const layoutErrorVars = (route.layoutErrorPaths || []).map((ep) => ep ? getImportVar(ep) : "null");
125
- return ` {
126
- __buildTimeClassifications: __VINEXT_CLASS(${routeIdx}), // evaluated once at module load
127
- __buildTimeReasons: __classDebug ? __VINEXT_CLASS_REASONS(${routeIdx}) : null,
128
- pattern: ${JSON.stringify(route.pattern)},
129
- patternParts: ${JSON.stringify(route.patternParts)},
130
- isDynamic: ${route.isDynamic},
131
- params: ${JSON.stringify(route.params)},
132
- page: ${route.pagePath ? getImportVar(route.pagePath) : "null"},
133
- routeHandler: ${route.routePath ? getImportVar(route.routePath) : "null"},
134
- layouts: [${layoutVars.join(", ")}],
135
- routeSegments: ${JSON.stringify(route.routeSegments)},
136
- templateTreePositions: ${JSON.stringify(route.templateTreePositions)},
137
- layoutTreePositions: ${JSON.stringify(route.layoutTreePositions)},
138
- templates: [${templateVars.join(", ")}],
139
- errors: [${layoutErrorVars.join(", ")}],
140
- slots: {
141
- ${slotEntries.join(",\n")}
142
- },
143
- loading: ${route.loadingPath ? getImportVar(route.loadingPath) : "null"},
144
- error: ${route.errorPath ? getImportVar(route.errorPath) : "null"},
145
- notFound: ${route.notFoundPath ? getImportVar(route.notFoundPath) : "null"},
146
- notFounds: [${notFoundVars.join(", ")}],
147
- forbidden: ${route.forbiddenPath ? getImportVar(route.forbiddenPath) : "null"},
148
- unauthorized: ${route.unauthorizedPath ? getImportVar(route.unauthorizedPath) : "null"},
149
- }`;
150
- });
151
- const rootRoute = routes.find((r) => r.pattern === "/");
152
- const rootNotFoundVar = rootRoute?.notFoundPath ? getImportVar(rootRoute.notFoundPath) : null;
153
- const rootForbiddenVar = rootRoute?.forbiddenPath ? getImportVar(rootRoute.forbiddenPath) : null;
154
- const rootUnauthorizedVar = rootRoute?.unauthorizedPath ? getImportVar(rootRoute.unauthorizedPath) : null;
155
- const rootLayoutVars = rootRoute ? rootRoute.layouts.map((l) => getImportVar(l)) : [];
156
- const globalErrorVar = globalErrorPath ? getImportVar(globalErrorPath) : null;
157
- const effectiveMetaRoutes = metadataRoutes ?? [];
158
- const dynamicMetaRoutes = effectiveMetaRoutes.filter((r) => r.isDynamic);
159
- for (const mr of dynamicMetaRoutes) getImportVar(mr.filePath);
160
- const metaRouteEntries = effectiveMetaRoutes.map((mr) => {
161
- const patternParts = mr.isDynamic && mr.servedUrl.includes("[") ? JSON.stringify(mr.servedUrl.split("/").filter(Boolean).map((seg) => {
162
- if (seg.startsWith("[[...") && seg.endsWith("]]")) return ":" + seg.slice(5, -2) + "*";
163
- if (seg.startsWith("[...") && seg.endsWith("]")) return ":" + seg.slice(4, -1) + "+";
164
- if (seg.startsWith("[") && seg.endsWith("]")) return ":" + seg.slice(1, -1);
165
- return seg;
166
- })) : null;
167
- if (mr.isDynamic) return ` {
168
- type: ${JSON.stringify(mr.type)},
169
- isDynamic: true,
170
- servedUrl: ${JSON.stringify(mr.servedUrl)},
171
- contentType: ${JSON.stringify(mr.contentType)},
172
- module: ${getImportVar(mr.filePath)},${patternParts ? `\n patternParts: ${patternParts},` : ""}
173
- }`;
174
- let fileDataBase64 = "";
175
- try {
176
- fileDataBase64 = fs.readFileSync(mr.filePath).toString("base64");
177
- } catch {}
178
- return ` {
179
- type: ${JSON.stringify(mr.type)},
180
- isDynamic: false,
181
- servedUrl: ${JSON.stringify(mr.servedUrl)},
182
- contentType: ${JSON.stringify(mr.contentType)},
183
- fileDataBase64: ${JSON.stringify(fileDataBase64)},
184
- }`;
63
+ const { imports, routeEntries, metaRouteEntries, generateStaticParamsEntries, rootNotFoundVar, rootForbiddenVar, rootUnauthorizedVar, rootLayoutVars, globalErrorVar } = buildAppRscManifestCode({
64
+ routes,
65
+ metadataRoutes,
66
+ globalErrorPath
185
67
  });
68
+ const loadPrerenderPagesRoutesCode = hasPagesDir ? `
69
+ async function __loadPrerenderPagesRoutes() {
70
+ const __gspSsrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
71
+ return __gspSsrEntry.pageRoutes;
72
+ }
73
+ ` : "";
186
74
  return `
187
75
  import {
188
76
  renderToReadableStream as _renderToReadableStream,
@@ -191,180 +79,91 @@ import {
191
79
  loadServerAction,
192
80
  createTemporaryReferenceSet,
193
81
  } from "@vitejs/plugin-rsc/rsc";
194
- import { AsyncLocalStorage } from "node:async_hooks";
82
+ import { createRscRenderer } from ${JSON.stringify(rscStreamHintsPath)};
195
83
 
196
- // React Flight emits HL hints with "stylesheet" for CSS, but the HTML spec
197
- // requires "style" for <link rel="preload">. Fix at the source so every
198
- // consumer (SSR embed, client-side navigation, server actions) gets clean data.
199
- //
200
- // Flight lines are newline-delimited, so we buffer partial lines across chunks
201
- // to guarantee the regex never sees a split hint.
202
- function renderToReadableStream(model, options) {
203
- const _hlFixRe = /(\\d*:HL\\[.*?),"stylesheet"(\\]|,)/g;
204
- const stream = _renderToReadableStream(model, options);
205
- const decoder = new TextDecoder();
206
- const encoder = new TextEncoder();
207
- let carry = "";
208
- return stream.pipeThrough(new TransformStream({
209
- transform(chunk, controller) {
210
- const text = carry + decoder.decode(chunk, { stream: true });
211
- const lastNl = text.lastIndexOf("\\n");
212
- if (lastNl === -1) {
213
- carry = text;
214
- return;
215
- }
216
- carry = text.slice(lastNl + 1);
217
- controller.enqueue(encoder.encode(text.slice(0, lastNl + 1).replace(_hlFixRe, '$1,"style"$2')));
218
- },
219
- flush(controller) {
220
- const text = carry + decoder.decode();
221
- if (text) controller.enqueue(encoder.encode(text.replace(_hlFixRe, '$1,"style"$2')));
222
- }
223
- }));
224
- }
84
+ const renderToReadableStream = createRscRenderer(_renderToReadableStream);
225
85
  import { createElement } from "react";
226
- import { setNavigationContext as _setNavigationContextOrig, getNavigationContext as _getNavigationContext } from "next/navigation";
227
- import { setHeadersContext, headersContextFromRequest, getDraftModeCookieHeader, getAndClearPendingCookies, consumeDynamicUsage, markDynamicUsage, applyMiddlewareRequestHeaders, getHeadersContext, setHeadersAccessPhase } from "next/headers";
228
- import { NextRequest, NextFetchEvent } from "next/server";
86
+ import { getNavigationContext as _getNavigationContext } from "next/navigation";
87
+ import { headersContextFromRequest, getDraftModeCookieHeader, getAndClearPendingCookies, consumeDynamicUsage, consumeInvalidDynamicUsageError, setHeadersAccessPhase } from "next/headers";
229
88
  import { mergeMetadata, resolveModuleMetadata, mergeViewport, resolveModuleViewport } from "vinext/metadata";
230
89
  ${middlewarePath ? `import * as middlewareModule from ${JSON.stringify(middlewarePath.replace(/\\/g, "/"))};` : ""}
231
- ${instrumentationPath ? `import * as _instrumentation from ${JSON.stringify(instrumentationPath.replace(/\\/g, "/"))};` : ""}
232
- ${effectiveMetaRoutes.length > 0 ? `import { sitemapToXml, robotsToText, manifestToJson } from ${JSON.stringify(metadataRoutesPath)};` : ""}
233
- import { requestContextFromRequest, normalizeHost, matchRedirect, matchRewrite, matchHeaders, isExternalUrl, proxyExternalRequest, sanitizeDestination } from ${JSON.stringify(configMatchersPath)};
90
+ ${instrumentationPath ? `import * as _instrumentation from ${JSON.stringify(instrumentationPath.replace(/\\/g, "/"))};
91
+ import { ensureInstrumentationRegistered as __ensureInstrumentationRegistered } from ${JSON.stringify(instrumentationRuntimePath)};` : ""}
92
+ import { createAppRscHandler as __createAppRscHandler } from ${JSON.stringify(appRscHandlerPath)};
234
93
  import { decodePathParams as __decodePathParams } from ${JSON.stringify(normalizePathModulePath)};
235
94
  import { buildRequestHeadersFromMiddlewareResponse as __buildRequestHeadersFromMiddlewareResponse } from ${JSON.stringify(middlewareRequestHeadersPath)};
236
- import { validateCsrfOrigin, validateServerActionPayload, validateImageUrl, guardProtocolRelativeUrl, hasBasePath, stripBasePath, normalizeTrailingSlash, processMiddlewareHeaders } from ${JSON.stringify(requestPipelinePath)};
237
- import {
238
- isKnownDynamicAppRoute as __isKnownDynamicAppRoute,
239
- } from ${JSON.stringify(appRouteHandlerRuntimePath)};
240
95
  import {
241
- getAppRouteHandlerRevalidateSeconds as __getAppRouteHandlerRevalidateSeconds,
242
- hasAppRouteHandlerDefaultExport as __hasAppRouteHandlerDefaultExport,
243
- resolveAppRouteHandlerMethod as __resolveAppRouteHandlerMethod,
244
- shouldReadAppRouteHandlerCache as __shouldReadAppRouteHandlerCache,
245
- } from ${JSON.stringify(appRouteHandlerPolicyPath)};
246
- import {
247
- executeAppRouteHandler as __executeAppRouteHandler,
248
- } from ${JSON.stringify(appRouteHandlerExecutionPath)};
96
+ dispatchAppRouteHandler as __dispatchAppRouteHandler,
97
+ } from ${JSON.stringify(appRouteHandlerDispatchPath)};
249
98
  import {
250
99
  handleProgressiveServerActionRequest as __handleProgressiveServerActionRequest,
100
+ handleServerActionRscRequest as __handleServerActionRscRequest,
101
+ readActionBodyWithLimit as __readBodyWithLimit,
102
+ readActionFormDataWithLimit as __readFormDataWithLimit,
251
103
  } from ${JSON.stringify(appServerActionExecutionPath)};
252
- import { readAppRouteHandlerCacheResponse as __readAppRouteHandlerCacheResponse } from ${JSON.stringify(appRouteHandlerCachePath)};
253
- import { readAppPageCacheResponse as __readAppPageCacheResponse } from ${JSON.stringify(appPageCachePath)};
104
+ import {
105
+ sanitizeErrorForClient as __sanitizeErrorForClient,
106
+ } from ${JSON.stringify(appRscErrorsPath)};
107
+ import { createAppRscOnErrorHandler } from ${JSON.stringify(appRscErrorHandlerPath)};
254
108
  import {
255
109
  buildAppPageFontLinkHeader as __buildAppPageFontLinkHeader,
256
- buildAppPageSpecialErrorResponse as __buildAppPageSpecialErrorResponse,
257
- readAppPageTextStream as __readAppPageTextStream,
258
110
  resolveAppPageSpecialError as __resolveAppPageSpecialError,
259
- teeAppPageRscStreamForCapture as __teeAppPageRscStreamForCapture,
260
111
  } from ${JSON.stringify(appPageExecutionPath)};
261
112
  import {
262
- renderAppPageErrorBoundary as __renderAppPageErrorBoundary,
263
- renderAppPageHttpAccessFallback as __renderAppPageHttpAccessFallback,
264
- } from ${JSON.stringify(appPageBoundaryRenderPath)};
113
+ createAppFallbackRenderer as __createAppFallbackRenderer,
114
+ } from ${JSON.stringify(appFallbackRendererPath)};
265
115
  import {
266
116
  APP_INTERCEPTION_CONTEXT_KEY as __APP_INTERCEPTION_CONTEXT_KEY,
267
117
  createAppPayloadRouteId as __createAppPayloadRouteId,
268
118
  } from ${JSON.stringify(appElementsPath)};
269
119
  import {
270
- buildAppPageElements as __buildAppPageElements,
271
- createAppPageTreePath as __createAppPageTreePath,
272
120
  resolveAppPageChildSegments as __resolveAppPageChildSegments,
273
121
  } from ${JSON.stringify(appPageRouteWiringPath)};
122
+ import { buildPageElements as __buildPageElements } from ${JSON.stringify(appPageElementBuilderPath)};
123
+ import {
124
+ resolveAppPageSegmentParams as __resolveAppPageSegmentParams,
125
+ } from ${JSON.stringify(appPageParamsPath)};
274
126
  import {
275
- renderAppPageLifecycle as __renderAppPageLifecycle,
276
- } from ${JSON.stringify(appPageRenderPath)};
127
+ collectAppPageSearchParams as __collectAppPageSearchParams,
128
+ } from ${JSON.stringify(appPageHeadPath)};
277
129
  import {
278
- mergeMiddlewareResponseHeaders as __mergeMiddlewareResponseHeaders,
279
- } from ${JSON.stringify(appPageResponsePath)};
280
- import { getScriptNonceFromHeaderSources as __getScriptNonceFromHeaderSources } from ${JSON.stringify(cspPath)};
130
+ dispatchAppPage as __dispatchAppPage,
131
+ } from ${JSON.stringify(appPageDispatchPath)};
281
132
  import {
282
- resolveAppPageActionRerenderTarget as __resolveAppPageActionRerenderTarget,
283
- buildAppPageElement as __buildAppPageElement,
284
- resolveAppPageIntercept as __resolveAppPageIntercept,
285
- validateAppPageDynamicParams as __validateAppPageDynamicParams,
133
+ resolveAppPageGenerateStaticParamsSources as __resolveAppPageGenerateStaticParamsSources,
286
134
  } from ${JSON.stringify(appPageRequestPath)};
287
135
  import {
288
- applyRouteHandlerMiddlewareContext as __applyRouteHandlerMiddlewareContext,
289
- } from ${JSON.stringify(appRouteHandlerResponsePath)};
290
- import { _consumeRequestScopedCacheLife, getCacheHandler } from "next/cache";
291
- import { getRequestExecutionContext as _getRequestExecutionContext } from ${JSON.stringify(requestContextShimPath)};
292
- import { ensureFetchPatch as _ensureFetchPatch, getCollectedFetchTags, setCurrentFetchSoftTags } from "vinext/fetch-cache";
293
- import { buildRouteTrie as _buildRouteTrie, trieMatch as _trieMatch } from ${JSON.stringify(routeTriePath)};
136
+ resolveAppPageFetchCacheMode as __resolveAppPageFetchCacheMode,
137
+ resolveAppPageSegmentConfig as __resolveAppPageSegmentConfig,
138
+ } from ${JSON.stringify(appSegmentConfigPath)};
139
+ import { makeThenableParams } from ${JSON.stringify(thenableParamsShimPath)};
140
+ import {
141
+ createAppRscRouteMatcher as __createAppRscRouteMatcher,
142
+ } from ${JSON.stringify(appRscRouteMatchingPath)};
143
+ import {
144
+ appIsrHtmlKey as __isrHtmlKey,
145
+ appIsrRscKey as __isrRscKey,
146
+ appIsrRouteKey as __isrRouteKey,
147
+ isrGet as __isrGet,
148
+ isrSet as __isrSet,
149
+ triggerBackgroundRegeneration as __triggerBackgroundRegeneration,
150
+ } from ${JSON.stringify(isrCachePath)};
294
151
  // Import server-only state module to register ALS-backed accessors.
295
152
  import "vinext/navigation-state";
296
- import { runWithRequestContext as _runWithUnifiedCtx, createRequestContext as _createUnifiedCtx } from "vinext/unified-request-context";
297
153
  import { reportRequestError as _reportRequestError } from "vinext/instrumentation";
298
- import { flattenErrorCauses as __flattenErrorCauses } from ${JSON.stringify(errorCausePath)};
299
154
  import { getSSRFontLinks as _getSSRFontLinks, getSSRFontStyles as _getSSRFontStylesGoogle, getSSRFontPreloads as _getSSRFontPreloadsGoogle } from "next/font/google";
300
155
  import { getSSRFontStyles as _getSSRFontStylesLocal, getSSRFontPreloads as _getSSRFontPreloadsLocal } from "next/font/local";
301
156
  function _getSSRFontStyles() { return [..._getSSRFontStylesGoogle(), ..._getSSRFontStylesLocal()]; }
302
157
  function _getSSRFontPreloads() { return [..._getSSRFontPreloadsGoogle(), ..._getSSRFontPreloadsLocal()]; }
303
- ${hasPagesDir ? `// Note: pageRoutes loaded lazily via SSR env in /__vinext/prerender/pages-static-paths handler` : ""}
158
+ ${hasPagesDir ? `// Pages Router routes are loaded lazily from the SSR environment for internal prerender requests.` : ""}
304
159
 
305
- // ALS used to suppress the expected "Invalid hook call" dev warning when
306
- // layout/page components are probed outside React's render cycle. Patching
307
- // console.error once at module load (instead of per-request) avoids the
308
- // concurrent-request issue where request A's suppression filter could
309
- // swallow real errors from request B.
310
- const _suppressHookWarningAls = new AsyncLocalStorage();
311
- const _origConsoleError = console.error;
312
- console.error = (...args) => {
313
- if (_suppressHookWarningAls.getStore() === true &&
314
- typeof args[0] === "string" &&
315
- args[0].includes("Invalid hook call")) return;
316
- _origConsoleError.apply(console, args);
317
- };
318
-
319
- // Set navigation context in the ALS-backed store. "use client" components
320
- // rendered during SSR need the pathname/searchParams/params but the SSR
321
- // environment has a separate module instance of next/navigation.
322
- // Use _getNavigationContext() to read the current context — never cache
323
- // it in a module-level variable (that would leak between concurrent requests).
324
- function setNavigationContext(ctx) {
325
- _setNavigationContextOrig(ctx);
326
- }
160
+ // Suppress expected "Invalid hook call" dev warning when layout/page
161
+ // components are probed outside React's render cycle. The import patches
162
+ // console.error once at module load (side-effect) and exposes the ALS
163
+ // so per-route dispatch can opt into suppression via .run(true, ...).
164
+ import { suppressHookWarningAls } from ${JSON.stringify(appHookWarningSuppressionPath)};
165
+ import { clearAppRequestContext as __clearRequestContext, setAppNavigationContext as setNavigationContext } from ${JSON.stringify(appRequestContextPath)};
327
166
 
328
- // ISR cache is disabled in dev mode — every request re-renders fresh,
329
- // matching Next.js dev behavior. Cache-Control headers are still emitted
330
- // based on export const revalidate for testing purposes.
331
- // Production ISR uses the MemoryCacheHandler (or configured KV handler).
332
- //
333
- // These helpers are inlined instead of imported from isr-cache.js because
334
- // the virtual RSC entry module runs in the RSC Vite environment which
335
- // cannot use dynamic imports at the module-evaluation level for server-only
336
- // modules, and direct imports must use the pre-computed absolute paths.
337
- async function __isrGet(key) {
338
- const handler = getCacheHandler();
339
- const result = await handler.get(key);
340
- if (!result || !result.value) return null;
341
- return { value: result, isStale: result.cacheState === "stale" };
342
- }
343
- async function __isrSet(key, data, revalidateSeconds, tags) {
344
- const handler = getCacheHandler();
345
- await handler.set(key, data, { revalidate: revalidateSeconds, tags: Array.isArray(tags) ? tags : [] });
346
- }
347
- function __pageCacheTags(pathname, extraTags) {
348
- const tags = [pathname, "_N_T_" + pathname];
349
- // Layout hierarchy tags — matches Next.js getDerivedTags.
350
- tags.push("_N_T_/layout");
351
- const segments = pathname.split("/");
352
- let built = "";
353
- for (let i = 1; i < segments.length; i++) {
354
- if (segments[i]) {
355
- built += "/" + segments[i];
356
- tags.push("_N_T_" + built + "/layout");
357
- }
358
- }
359
- // Leaf page tag — revalidatePath(path, "page") targets this.
360
- tags.push("_N_T_" + built + "/page");
361
- if (Array.isArray(extraTags)) {
362
- for (const tag of extraTags) {
363
- if (!tags.includes(tag)) tags.push(tag);
364
- }
365
- }
366
- return tags;
367
- }
368
167
  // Note: cache entries are written with \`headers: undefined\`. Next.js stores
369
168
  // response headers (e.g. set-cookie from cookies().set() during render) in the
370
169
  // cache entry so they can be replayed on HIT. We don't do this because:
@@ -375,68 +174,6 @@ function __pageCacheTags(pathname, extraTags) {
375
174
  // In practice this means ISR-cached responses won't replay render-time set-cookie
376
175
  // headers — but that case is already prevented by the dynamic-usage opt-out.
377
176
  // TODO: capture render-time response headers for full Next.js parity.
378
- const __pendingRegenerations = new Map();
379
- function __triggerBackgroundRegeneration(key, renderFn) {
380
- if (__pendingRegenerations.has(key)) return;
381
- const promise = renderFn()
382
- .catch((err) => console.error("[vinext] ISR regen failed for " + key + ":", err))
383
- .finally(() => __pendingRegenerations.delete(key));
384
- __pendingRegenerations.set(key, promise);
385
- const ctx = _getRequestExecutionContext();
386
- if (ctx && typeof ctx.waitUntil === "function") ctx.waitUntil(promise);
387
- }
388
- // HTML and RSC are stored under separate keys — matching Next.js's file-system
389
- // layout (.html / .rsc) — so each request type reads and writes its own key
390
- // independently with no races or partial-entry sentinels.
391
- //
392
- // Key format: "app:<buildId>:<pathname>:<suffix>"
393
- // Long-pathname fallback: "app:<buildId>:__hash:<fnv1a64(pathname)>:<suffix>"
394
- // Without buildId (should not happen in production): "app:<pathname>:<suffix>"
395
- // The 200-char threshold keeps the full key well under Cloudflare KV's 512-byte limit
396
- // even after adding the build ID and suffix. FNV-1a 64 is used for the hash (two
397
- // 32-bit rounds) to give a ~64-bit output with negligible collision probability for
398
- // realistic pathname lengths.
399
- // Keep prefix construction and hashing logic in sync with isrCacheKey() in server/isr-cache.ts.
400
- function __isrFnv1a64(s) {
401
- // h1 uses the standard FNV-1a 32-bit offset basis (0x811c9dc5).
402
- let h1 = 0x811c9dc5;
403
- for (let i = 0; i < s.length; i++) { h1 ^= s.charCodeAt(i); h1 = (h1 * 0x01000193) >>> 0; }
404
- // h2 uses a different seed (0x050c5d1f — the FNV-1a hash of the string "vinext")
405
- // so the two rounds are independently seeded and their outputs are decorrelated.
406
- // Concatenating two independently-seeded 32-bit FNV-1a hashes gives an effective
407
- // 64-bit hash. A random non-standard seed would also work; we derive it from a
408
- // fixed string so the choice is auditable and deterministic across rebuilds.
409
- let h2 = 0x050c5d1f;
410
- for (let i = 0; i < s.length; i++) { h2 ^= s.charCodeAt(i); h2 = (h2 * 0x01000193) >>> 0; }
411
- return h1.toString(36) + h2.toString(36);
412
- }
413
- function __isrCacheKey(pathname, suffix) {
414
- const normalized = pathname === "/" ? "/" : pathname.replace(/\\/$/, "");
415
- // __VINEXT_BUILD_ID is replaced at compile time by Vite's define plugin.
416
- const buildId = process.env.__VINEXT_BUILD_ID;
417
- const prefix = buildId ? "app:" + buildId : "app";
418
- const key = prefix + ":" + normalized + ":" + suffix;
419
- if (key.length <= 200) return key;
420
- // Pathname too long — hash it to keep under KV's 512-byte key limit.
421
- return prefix + ":__hash:" + __isrFnv1a64(normalized) + ":" + suffix;
422
- }
423
- function __isrHtmlKey(pathname) { return __isrCacheKey(pathname, "html"); }
424
- function __isrRscKey(pathname, mountedSlotsHeader) {
425
- if (!mountedSlotsHeader) return __isrCacheKey(pathname, "rsc");
426
- return __isrCacheKey(pathname, "rsc:" + __isrFnv1a64(mountedSlotsHeader));
427
- }
428
- function __normalizeMountedSlotsHeader(raw) {
429
- if (!raw) return null;
430
- const normalized = Array.from(
431
- new Set(
432
- raw
433
- .split(/\\s+/)
434
- .filter(Boolean),
435
- ),
436
- ).sort().join(" ");
437
- return normalized || null;
438
- }
439
- function __isrRouteKey(pathname) { return __isrCacheKey(pathname, "route"); }
440
177
  // Verbose cache logging — opt in with NEXT_PRIVATE_DEBUG_CACHE=1.
441
178
  // Matches the env var Next.js uses for its own cache debug output so operators
442
179
  // have a single knob for all cache tracing.
@@ -454,173 +191,19 @@ const __classDebug = process.env.VINEXT_DEBUG_CLASSIFICATION
454
191
  }
455
192
  : undefined;
456
193
 
457
- // Normalize null-prototype objects from matchPattern() into thenable objects
458
- // that work both as Promises (for Next.js 15+ async params) and as plain
459
- // objects with synchronous property access (for pre-15 code like params.id).
460
- //
461
- // matchPattern() uses Object.create(null), producing objects without
462
- // Object.prototype. The RSC serializer rejects these. Spreading ({...obj})
463
- // restores a normal prototype. Object.assign onto the Promise preserves
464
- // synchronous property access (params.id, params.slug) that existing
465
- // components and test fixtures rely on.
466
- function makeThenableParams(obj) {
467
- const plain = { ...obj };
468
- return Object.assign(Promise.resolve(plain), plain);
469
- }
470
-
471
- // djb2 hash — matches Next.js's stringHash for digest generation.
472
- // Produces a stable numeric string from error message + stack.
473
- function __errorDigest(str) {
474
- let hash = 5381;
475
- for (let i = str.length - 1; i >= 0; i--) {
476
- hash = (hash * 33) ^ str.charCodeAt(i);
477
- }
478
- return (hash >>> 0).toString();
479
- }
480
-
481
- // Sanitize an error for client consumption. In production, replaces the error
482
- // with a generic Error that only carries a digest hash (matching Next.js
483
- // behavior). In development, returns the original error for debugging.
484
- // Navigation errors (redirect, notFound, etc.) are always passed through
485
- // unchanged since their digests are used for client-side routing.
486
- function __sanitizeErrorForClient(error) {
487
- // Navigation errors must pass through with their digest intact
488
- if (__resolveAppPageSpecialError(error)) {
489
- return error;
490
- }
491
- // In development, pass through the original error for debugging
492
- if (process.env.NODE_ENV !== "production") {
493
- return error;
494
- }
495
- // In production, create a sanitized error with only a digest hash
496
- const msg = error instanceof Error ? error.message : String(error);
497
- const stack = error instanceof Error ? (error.stack || "") : "";
498
- const sanitized = new Error(
499
- "An error occurred in the Server Components render. " +
500
- "The specific message is omitted in production builds to avoid leaking sensitive details. " +
501
- "A digest property is included on this error instance which may provide additional details about the nature of the error."
502
- );
503
- sanitized.digest = __errorDigest(msg + stack);
504
- return sanitized;
505
- }
506
-
507
- // onError callback for renderToReadableStream — preserves the digest for
508
- // Next.js navigation errors (redirect, notFound, forbidden, unauthorized)
509
- // thrown during RSC streaming (e.g. inside Suspense boundaries).
510
- // For non-navigation errors in production, generates a digest hash so the
511
- // error can be correlated with server logs without leaking details.
512
- function rscOnError(error, requestInfo, errorContext) {
513
- if (error && typeof error === "object" && "digest" in error) {
514
- return String(error.digest);
515
- }
516
-
517
- // In dev, detect the "Only plain objects" RSC serialization error and emit
518
- // an actionable hint. This error occurs when a Server Component passes a
519
- // class instance, ES module namespace object, or null-prototype object as a
520
- // prop to a Client Component.
521
- //
522
- // Root cause: Vite bundles modules as true ESM (module namespace objects
523
- // have a null-like internal slot), while Next.js's webpack build produces
524
- // plain CJS-wrapped objects with __esModule:true. React's RSC serializer
525
- // accepts the latter as plain objects but rejects the former — which means
526
- // code that accidentally passes "import * as X" works in webpack/Next.js
527
- // but correctly fails in vinext.
528
- //
529
- // Common triggers:
530
- // - "import * as utils from './utils'" passed as a prop
531
- // - class instances (new Foo()) passed as props
532
- // - Date / Map / Set instances passed as props
533
- // - Objects with Object.create(null) (null prototype)
534
- if (
535
- process.env.NODE_ENV !== "production" &&
536
- error instanceof Error &&
537
- error.message.includes("Only plain objects, and a few built-ins, can be passed to Client Components")
538
- ) {
539
- console.error(
540
- "[vinext] RSC serialization error: a non-plain object was passed from a Server Component to a Client Component.\\n" +
541
- "\\n" +
542
- "Common causes:\\n" +
543
- " * Passing a module namespace (import * as X) directly as a prop.\\n" +
544
- " Unlike Next.js (webpack), Vite produces real ESM module namespace objects\\n" +
545
- " which are not serializable. Fix: pass individual values instead,\\n" +
546
- " e.g. <Comp value={module.value} />\\n" +
547
- " * Passing a class instance (new Foo()) as a prop.\\n" +
548
- " Fix: convert to a plain object, e.g. { id: foo.id, name: foo.name }\\n" +
549
- " * Passing a Date, Map, or Set. Use .toISOString(), [...map.entries()], etc.\\n" +
550
- " * Passing Object.create(null). Use { ...obj } to restore a prototype.\\n" +
551
- "\\n" +
552
- "Original error:",
553
- error.message,
554
- );
555
- return undefined;
556
- }
557
-
558
- if (requestInfo && errorContext && error) {
559
- _reportRequestError(
560
- error instanceof Error ? error : new Error(String(error)),
561
- requestInfo,
562
- errorContext,
563
- );
564
- }
565
-
566
- // In production, generate a digest hash for non-navigation errors
567
- if (process.env.NODE_ENV === "production" && error) {
568
- const msg = error instanceof Error ? error.message : String(error);
569
- const stack = error instanceof Error ? (error.stack || "") : "";
570
- return __errorDigest(msg + stack);
571
- }
572
- return undefined;
573
- }
574
-
575
- function createRscOnErrorHandler(request, pathname, routePath) {
576
- const requestInfo = {
577
- path: pathname,
578
- method: request.method,
579
- headers: Object.fromEntries(request.headers.entries()),
580
- };
581
- const errorContext = {
582
- routerKind: "App Router",
583
- routePath: routePath || pathname,
584
- routeType: "render",
585
- };
586
- return function(error) {
587
- return rscOnError(error, requestInfo, errorContext);
588
- };
194
+ function __resolveRouteFetchCacheMode(route) {
195
+ return __resolveAppPageFetchCacheMode({
196
+ layouts: route.layouts,
197
+ page: route.page,
198
+ });
589
199
  }
590
200
 
591
201
  ${imports.join("\n")}
592
202
 
593
- ${instrumentationPath ? `// Run instrumentation register() exactly once, lazily on the first request.
594
- // Previously this was a top-level await, which blocked the entire module graph
595
- // from finishing initialization until register() resolved adding that latency
596
- // to every cold start. Moving it here preserves the "runs before any request is
597
- // handled" guarantee while not blocking V8 isolate initialization.
598
- // On Cloudflare Workers, module evaluation happens synchronously in the isolate
599
- // startup phase; a top-level await extends that phase and increases cold-start
600
- // wall time for all requests, not just the first.
601
- let __instrumentationInitialized = false;
602
- let __instrumentationInitPromise = null;
603
- async function __ensureInstrumentation() {
604
- if (process.env.VINEXT_PRERENDER === "1") return;
605
- if (__instrumentationInitialized) return;
606
- if (__instrumentationInitPromise) return __instrumentationInitPromise;
607
- __instrumentationInitPromise = (async () => {
608
- if (typeof _instrumentation.register === "function") {
609
- await _instrumentation.register();
610
- }
611
- // Store the onRequestError handler on globalThis so it is visible to
612
- // reportRequestError() (imported as _reportRequestError above) regardless
613
- // of which Vite environment module graph it is called from. With
614
- // @vitejs/plugin-rsc the RSC and SSR environments run in the same Node.js
615
- // process and share globalThis. With @cloudflare/vite-plugin everything
616
- // runs inside the Worker so globalThis is the Worker's global — also correct.
617
- if (typeof _instrumentation.onRequestError === "function") {
618
- globalThis.__VINEXT_onRequestErrorHandler__ = _instrumentation.onRequestError;
619
- }
620
- __instrumentationInitialized = true;
621
- })();
622
- return __instrumentationInitPromise;
623
- }` : ""}
203
+ ${instrumentationPath ? `// Lazy instrumentation initialisation is handled by ensureInstrumentationRegistered
204
+ // (imported from vinext/instrumentation-runtime). The generated entry only passes
205
+ // the user module in; all bookkeeping (initialized flag, shared promise, prerender
206
+ // skip) lives in the typed helper so it can be unit-tested independently.` : ""}
624
207
 
625
208
  // Build-time layout classification dispatch. Replaced in generateBundle
626
209
  // with a switch statement that returns a pre-computed per-layout
@@ -643,7 +226,7 @@ function __VINEXT_CLASS_REASONS(routeIdx) {
643
226
  const routes = [
644
227
  ${routeEntries.join(",\n")}
645
228
  ];
646
- const _routeTrie = _buildRouteTrie(routes);
229
+ const __routeMatcher = __createAppRscRouteMatcher(routes);
647
230
 
648
231
  const metadataRoutes = [
649
232
  ${metaRouteEntries.join(",\n")}
@@ -653,176 +236,43 @@ const rootNotFoundModule = ${rootNotFoundVar ? rootNotFoundVar : "null"};
653
236
  const rootForbiddenModule = ${rootForbiddenVar ? rootForbiddenVar : "null"};
654
237
  const rootUnauthorizedModule = ${rootUnauthorizedVar ? rootUnauthorizedVar : "null"};
655
238
  const rootLayouts = [${rootLayoutVars.join(", ")}];
656
- const __APP_PAGE_EMPTY_MW_CTX = { headers: null, status: null };
657
239
 
658
- /**
659
- * Render an HTTP access fallback page (not-found/forbidden/unauthorized) with layouts and noindex meta.
660
- * Returns null if no matching component is available.
661
- *
662
- * @param opts.boundaryComponent - Override the boundary component (for layout-level notFound)
663
- * @param opts.layouts - Override the layouts to wrap with (for layout-level notFound, excludes the throwing layout)
664
- */
665
- async function renderHTTPAccessFallbackPage(route, statusCode, isRscRequest, request, opts, scriptNonce, middlewareContext) {
666
- return __renderAppPageHttpAccessFallback({
667
- boundaryComponent: opts?.boundaryComponent ?? null,
240
+ const createRscOnErrorHandler = (request, pathname, routePath) =>
241
+ createAppRscOnErrorHandler(_reportRequestError, request, pathname, routePath);
242
+
243
+ const __fallbackRenderer = __createAppFallbackRenderer({
244
+ rootBoundaries: {
245
+ rootForbiddenModule,
246
+ rootLayouts,
247
+ rootNotFoundModule,
248
+ rootUnauthorizedModule,
249
+ },
250
+ globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
251
+ metadataRoutes,
252
+ ssrLoader() {
253
+ return import.meta.viteRsc.loadModule("ssr", "index");
254
+ },
255
+ fontProviders: {
668
256
  buildFontLinkHeader: __buildAppPageFontLinkHeader,
669
- clearRequestContext() {
670
- setHeadersContext(null);
671
- setNavigationContext(null);
672
- },
673
- createRscOnErrorHandler(pathname, routePath) {
674
- return createRscOnErrorHandler(request, pathname, routePath);
675
- },
676
257
  getFontLinks: _getSSRFontLinks,
677
258
  getFontPreloads: _getSSRFontPreloads,
678
259
  getFontStyles: _getSSRFontStyles,
679
- getNavigationContext: _getNavigationContext,
680
- globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
681
- isRscRequest,
682
- layoutModules: opts?.layouts ?? null,
683
- loadSsrHandler() {
684
- return import.meta.viteRsc.loadModule("ssr", "index");
685
- },
686
- makeThenableParams,
687
- matchedParams: opts?.matchedParams ?? route?.params ?? {},
688
- middlewareContext: middlewareContext ?? __APP_PAGE_EMPTY_MW_CTX,
689
- requestUrl: request.url,
690
- resolveChildSegments: __resolveAppPageChildSegments,
691
- rootForbiddenModule: rootForbiddenModule,
692
- rootLayouts: rootLayouts,
693
- rootNotFoundModule: rootNotFoundModule,
694
- rootUnauthorizedModule: rootUnauthorizedModule,
695
- route,
696
- renderToReadableStream,
697
- scriptNonce,
698
- statusCode,
699
- });
700
- }
701
-
702
- /** Convenience: render a not-found page (404) */
703
- async function renderNotFoundPage(route, isRscRequest, request, matchedParams, scriptNonce, middlewareContext) {
704
- return renderHTTPAccessFallbackPage(route, 404, isRscRequest, request, { matchedParams }, scriptNonce, middlewareContext);
705
- }
706
-
707
- /**
708
- * Render an error.tsx boundary page when a server component or generateMetadata() throws.
709
- * Returns null if no error boundary component is available for this route.
710
- *
711
- * Next.js returns HTTP 200 when error.tsx catches an error (the error is "handled"
712
- * by the boundary). This matches that behavior intentionally.
713
- */
714
- async function renderErrorBoundaryPage(route, error, isRscRequest, request, matchedParams, scriptNonce, middlewareContext) {
715
- return __renderAppPageErrorBoundary({
716
- buildFontLinkHeader: __buildAppPageFontLinkHeader,
717
- clearRequestContext() {
718
- setHeadersContext(null);
719
- setNavigationContext(null);
720
- },
721
- createRscOnErrorHandler(pathname, routePath) {
722
- return createRscOnErrorHandler(request, pathname, routePath);
723
- },
724
- error,
725
- getFontLinks: _getSSRFontLinks,
726
- getFontPreloads: _getSSRFontPreloads,
727
- getFontStyles: _getSSRFontStyles,
728
- getNavigationContext: _getNavigationContext,
729
- globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
730
- isRscRequest,
731
- loadSsrHandler() {
732
- return import.meta.viteRsc.loadModule("ssr", "index");
733
- },
734
- makeThenableParams,
735
- matchedParams: matchedParams ?? route?.params ?? {},
736
- middlewareContext: middlewareContext ?? __APP_PAGE_EMPTY_MW_CTX,
737
- requestUrl: request.url,
738
- resolveChildSegments: __resolveAppPageChildSegments,
739
- rootLayouts: rootLayouts,
740
- route,
741
- renderToReadableStream,
742
- sanitizeErrorForClient: __sanitizeErrorForClient,
743
- scriptNonce,
744
- });
745
- }
260
+ },
261
+ makeThenableParams,
262
+ sanitizer: __sanitizeErrorForClient,
263
+ rscRenderer: renderToReadableStream,
264
+ getNavigationContext: _getNavigationContext,
265
+ resolveChildSegments: __resolveAppPageChildSegments,
266
+ clearRequestContext() {
267
+ __clearRequestContext();
268
+ },
269
+ createRscOnErrorHandler(request, pathname, routePath) {
270
+ return createRscOnErrorHandler(request, pathname, routePath);
271
+ },
272
+ });
746
273
 
747
274
  function matchRoute(url) {
748
- const pathname = url.split("?")[0];
749
- let normalizedUrl = pathname === "/" ? "/" : pathname.replace(/\\/$/, "");
750
- // NOTE: Do NOT decodeURIComponent here. The caller is responsible for decoding
751
- // the pathname exactly once at the request entry point. Decoding again here
752
- // would cause inconsistent path matching between middleware and routing.
753
- const urlParts = normalizedUrl.split("/").filter(Boolean);
754
- return _trieMatch(_routeTrie, urlParts);
755
- }
756
-
757
- function __createStaticFileSignal(pathname, _mwCtx) {
758
- const headers = new Headers({
759
- "x-vinext-static-file": encodeURIComponent(pathname),
760
- });
761
- if (_mwCtx.headers) {
762
- for (const [key, value] of _mwCtx.headers) {
763
- headers.append(key, value);
764
- }
765
- }
766
- return new Response(null, {
767
- status: _mwCtx.status ?? 200,
768
- headers,
769
- });
770
- }
771
-
772
- // matchPattern is kept for findIntercept (linear scan over small interceptLookup array).
773
- function matchPattern(urlParts, patternParts) {
774
- const params = Object.create(null);
775
- for (let i = 0; i < patternParts.length; i++) {
776
- const pp = patternParts[i];
777
- if (pp.endsWith("+")) {
778
- if (i !== patternParts.length - 1) return null;
779
- const paramName = pp.slice(1, -1);
780
- const remaining = urlParts.slice(i);
781
- if (remaining.length === 0) return null;
782
- params[paramName] = remaining;
783
- return params;
784
- }
785
- if (pp.endsWith("*")) {
786
- if (i !== patternParts.length - 1) return null;
787
- const paramName = pp.slice(1, -1);
788
- params[paramName] = urlParts.slice(i);
789
- return params;
790
- }
791
- if (pp.startsWith(":")) {
792
- if (i >= urlParts.length) return null;
793
- params[pp.slice(1)] = urlParts[i];
794
- continue;
795
- }
796
- if (i >= urlParts.length || urlParts[i] !== pp) return null;
797
- }
798
- if (urlParts.length !== patternParts.length) return null;
799
- return params;
800
- }
801
-
802
- function mergeMatchedParams(sourceParams, targetParams) {
803
- return Object.assign(Object.create(null), sourceParams, targetParams);
804
- }
805
-
806
- // Build a global intercepting route lookup for RSC navigation.
807
- // Maps target URL patterns to { sourceRouteIndex, slotKey, interceptPage, interceptLayouts, params }.
808
- const interceptLookup = [];
809
- for (let ri = 0; ri < routes.length; ri++) {
810
- const r = routes[ri];
811
- if (!r.slots) continue;
812
- for (const [slotKey, slotMod] of Object.entries(r.slots)) {
813
- if (!slotMod.intercepts) continue;
814
- for (const intercept of slotMod.intercepts) {
815
- interceptLookup.push({
816
- sourceRouteIndex: ri,
817
- slotKey,
818
- targetPattern: intercept.targetPattern,
819
- targetPatternParts: intercept.targetPattern.split("/").filter(Boolean),
820
- interceptLayouts: intercept.interceptLayouts,
821
- page: intercept.page,
822
- params: intercept.params,
823
- });
824
- }
825
- }
275
+ return __routeMatcher.matchRoute(url);
826
276
  }
827
277
 
828
278
  /**
@@ -830,188 +280,23 @@ for (let ri = 0; ri < routes.length; ri++) {
830
280
  * Returns the match info or null.
831
281
  */
832
282
  function findIntercept(pathname, sourcePathname = null) {
833
- const urlParts = pathname.split("/").filter(Boolean);
834
- for (const entry of interceptLookup) {
835
- const params = matchPattern(urlParts, entry.targetPatternParts);
836
- if (params !== null) {
837
- let sourceParams = Object.create(null);
838
- if (sourcePathname !== null) {
839
- const sourceRoute = routes[entry.sourceRouteIndex];
840
- const sourceParts = sourcePathname.split("/").filter(Boolean);
841
- const matchedSourceParams = sourceRoute
842
- ? matchPattern(sourceParts, sourceRoute.patternParts)
843
- : null;
844
- if (matchedSourceParams !== null) {
845
- sourceParams = matchedSourceParams;
846
- }
847
- }
848
- return { ...entry, matchedParams: mergeMatchedParams(sourceParams, params) };
849
- }
850
- }
851
- return null;
283
+ return __routeMatcher.findIntercept(pathname, sourcePathname);
852
284
  }
853
285
 
854
286
  async function buildPageElements(route, params, routePath, pageRequest) {
855
- const {
856
- opts,
857
- searchParams,
858
- isRscRequest,
859
- request,
860
- mountedSlotsHeader,
861
- } = pageRequest;
862
- const hasPageModule = !!route.page;
863
- const PageComponent = route.page?.default;
864
- if (hasPageModule && !PageComponent) {
865
- const _interceptionContext = opts?.interceptionContext ?? null;
866
- const _noExportRouteId = __createAppPayloadRouteId(routePath, _interceptionContext);
867
- let _noExportRootLayout = null;
868
- if (route.layouts?.length > 0) {
869
- // Compute the root layout tree path for this error payload using the
870
- // canonical helper so it stays aligned with buildAppPageElements().
871
- const _tp = route.layoutTreePositions?.[0] ?? 0;
872
- _noExportRootLayout = __createAppPageTreePath(route.routeSegments, _tp);
873
- }
874
- return {
875
- [__APP_INTERCEPTION_CONTEXT_KEY]: _interceptionContext,
876
- __route: _noExportRouteId,
877
- __rootLayout: _noExportRootLayout,
878
- [_noExportRouteId]: createElement("div", null, "Page has no default export"),
879
- };
880
- }
881
-
882
- // Resolve metadata and viewport from layouts and page.
883
- //
884
- // generateMetadata() accepts a "parent" (Promise of ResolvedMetadata) as its
885
- // second argument (Next.js 13+). The parent resolves to the accumulated
886
- // merged metadata of all ancestor segments, enabling patterns like:
887
- //
888
- // const previousImages = (await parent).openGraph?.images ?? []
889
- // return { openGraph: { images: ['/new-image.jpg', ...previousImages] } }
890
- //
891
- // Next.js uses an eager-execution-with-serial-resolution approach:
892
- // all generateMetadata() calls are kicked off concurrently, but each
893
- // segment's "parent" promise resolves only after the preceding segment's
894
- // metadata is resolved and merged. This preserves concurrency for I/O-bound
895
- // work while guaranteeing that parent data is available when needed.
896
- //
897
- // We build a chain: layoutParentPromises[0] = Promise.resolve({}) (no parent
898
- // for root layout), layoutParentPromises[i+1] resolves to merge(layouts[0..i]),
899
- // and pageParentPromise resolves to merge(all layouts).
900
- //
901
- // IMPORTANT: Layout metadata errors are swallowed (.catch(() => null)) because
902
- // a layout's generateMetadata() failing should not crash the page.
903
- // Page metadata errors are NOT swallowed — if the page's generateMetadata()
904
- // throws, the error propagates out of buildPageElement() so the caller can
905
- // route it to the nearest error.tsx boundary (or global-error.tsx).
906
- const layoutMods = route.layouts.filter(Boolean);
907
-
908
- // Convert URLSearchParams → plain object for page generateMetadata() and
909
- // pageProps.searchParams. Built before the layout loop so the page metadata
910
- // call (below) and pageProps can reference the same object.
911
- // NOTE: Layouts do NOT receive searchParams in generateMetadata() — only
912
- // pages do. This matches Next.js behavior (resolve-metadata.ts:777).
913
- const spObj = Object.create(null);
914
- let hasSearchParams = false;
915
- if (searchParams && searchParams.forEach) {
916
- searchParams.forEach(function(v, k) {
917
- hasSearchParams = true;
918
- if (k in spObj) {
919
- spObj[k] = Array.isArray(spObj[k]) ? spObj[k].concat(v) : [spObj[k], v];
920
- } else {
921
- spObj[k] = v;
922
- }
923
- });
924
- }
925
-
926
- // Build the parent promise chain and kick off metadata resolution in one pass.
927
- // Each layout module is called exactly once. layoutMetaPromises[i] is the
928
- // promise for layout[i]'s own metadata result.
929
- //
930
- // All calls are kicked off immediately (concurrent I/O), but each layout's
931
- // "parent" promise only resolves after the preceding layout's metadata is done.
932
- const layoutMetaPromises = [];
933
- let accumulatedMetaPromise = Promise.resolve({});
934
- for (let i = 0; i < layoutMods.length; i++) {
935
- const parentForThisLayout = accumulatedMetaPromise;
936
- // Kick off this layout's metadata resolution now (concurrent with others).
937
- const metaPromise = resolveModuleMetadata(layoutMods[i], params, undefined, parentForThisLayout)
938
- .catch((err) => { console.error("[vinext] Layout generateMetadata() failed:", err); return null; });
939
- layoutMetaPromises.push(metaPromise);
940
- // Advance accumulator: resolves to merged(layouts[0..i]) once layout[i] is done.
941
- accumulatedMetaPromise = metaPromise.then(async (result) =>
942
- result ? mergeMetadata([await parentForThisLayout, result]) : await parentForThisLayout
943
- );
944
- }
945
- // Page's parent is the fully-accumulated layout metadata.
946
- const pageParentPromise = accumulatedMetaPromise;
947
-
948
- const [layoutMetaResults, layoutVpResults, pageMeta, pageVp] = await Promise.all([
949
- Promise.all(layoutMetaPromises),
950
- Promise.all(layoutMods.map((mod) => resolveModuleViewport(mod, params).catch((err) => { console.error("[vinext] Layout generateViewport() failed:", err); return null; }))),
951
- route.page ? resolveModuleMetadata(route.page, params, spObj, pageParentPromise) : Promise.resolve(null),
952
- route.page ? resolveModuleViewport(route.page, params) : Promise.resolve(null),
953
- ]);
954
-
955
- const metadataList = [...layoutMetaResults.filter(Boolean), ...(pageMeta ? [pageMeta] : [])];
956
- const viewportList = [...layoutVpResults.filter(Boolean), ...(pageVp ? [pageVp] : [])];
957
- const resolvedMetadata = metadataList.length > 0 ? mergeMetadata(metadataList) : null;
958
- const resolvedViewport = mergeViewport(viewportList);
959
-
960
- // Build the route tree from the leaf page, then delegate the boundary/layout/
961
- // template/segment wiring to a typed runtime helper so the generated entry
962
- // stays thin and the wiring logic can be unit tested directly.
963
- const pageProps = { params: makeThenableParams(params) };
964
- if (searchParams) {
965
- // Always provide searchParams prop when the URL object is available, even
966
- // when the query string is empty -- pages that do "await searchParams" need
967
- // it to be a thenable rather than undefined.
968
- pageProps.searchParams = makeThenableParams(spObj);
969
- // If the URL has query parameters, mark the page as dynamic.
970
- // In Next.js, only accessing the searchParams prop signals dynamic usage,
971
- // but a Proxy-based approach doesn't work here because React's RSC debug
972
- // serializer accesses properties on all props (e.g. $$typeof check in
973
- // isClientReference), triggering the Proxy even when user code doesn't
974
- // read searchParams. Checking for non-empty query params is a safe
975
- // approximation: pages with query params in the URL are almost always
976
- // dynamic, and this avoids false positives from React internals.
977
- if (hasSearchParams) markDynamicUsage();
978
- }
979
- // mountedSlotsHeader is threaded through from the handler scope so every
980
- // call site shares one source of truth for request-derived values. Reading
981
- // the same header in two places invites silent drift when a future refactor
982
- // changes only one of them.
983
- const mountedSlotIds = mountedSlotsHeader
984
- ? new Set(mountedSlotsHeader.split(" "))
985
- : null;
986
-
987
- return __buildAppPageElements({
988
- element: PageComponent ? createElement(PageComponent, pageProps) : null,
989
- globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
990
- isRscRequest,
991
- mountedSlotIds,
992
- makeThenableParams,
993
- matchedParams: params,
994
- resolvedMetadata,
995
- resolvedViewport,
996
- interceptionContext: opts?.interceptionContext ?? null,
287
+ return __buildPageElements({
288
+ route,
289
+ params,
997
290
  routePath,
291
+ pageRequest,
292
+ globalErrorModule: ${globalErrorVar ? globalErrorVar : "null"},
998
293
  rootNotFoundModule: ${rootNotFoundVar ? rootNotFoundVar : "null"},
999
- route,
1000
- slotOverrides:
1001
- opts && opts.interceptSlotKey && opts.interceptPage
1002
- ? {
1003
- [opts.interceptSlotKey]: {
1004
- layoutModules: opts.interceptLayouts || null,
1005
- pageModule: opts.interceptPage,
1006
- params: opts.interceptParams || params,
1007
- },
1008
- }
1009
- : null,
294
+ rootForbiddenModule: ${rootForbiddenVar ? rootForbiddenVar : "null"},
295
+ rootUnauthorizedModule: ${rootUnauthorizedVar ? rootUnauthorizedVar : "null"},
296
+ metadataRoutes,
1010
297
  });
1011
298
  }
1012
299
 
1013
- ${middlewarePath ? generateMiddlewareMatcherCode("modern") : ""}
1014
-
1015
300
  const __basePath = ${JSON.stringify(bp)};
1016
301
  const __trailingSlash = ${JSON.stringify(ts)};
1017
302
  const __i18nConfig = ${JSON.stringify(i18nConfig)};
@@ -1020,45 +305,10 @@ const __configRewrites = ${JSON.stringify(rewrites)};
1020
305
  const __configHeaders = ${JSON.stringify(headers)};
1021
306
  const __publicFiles = new Set(${JSON.stringify(publicFiles)});
1022
307
  const __allowedOrigins = ${JSON.stringify(allowedOrigins)};
308
+ const __expireTime = ${JSON.stringify(expireTime)};
1023
309
 
1024
310
  ${generateDevOriginCheckCode(config?.allowedDevOrigins)}
1025
311
 
1026
- // ── ReDoS-safe regex compilation (still needed for middleware matching) ──
1027
- ${generateSafeRegExpCode("modern")}
1028
-
1029
- // ── Path normalization ──────────────────────────────────────────────────
1030
- ${generateNormalizePathCode("modern")}
1031
- ${generateRouteMatchNormalizationCode("modern")}
1032
-
1033
- // ── Config pattern matching, redirects, rewrites, headers, CSRF validation,
1034
- // external URL proxy, cookie parsing, and request context are imported from
1035
- // config-matchers.ts and request-pipeline.ts (see import statements above).
1036
- // This eliminates ~250 lines of duplicated inline code and ensures the
1037
- // single-pass tokenizer in config-matchers.ts is used consistently
1038
- // (fixing the chained .replace() divergence flagged by CodeQL).
1039
-
1040
- /**
1041
- * Build a request context from the live ALS HeadersContext, which reflects
1042
- * any x-middleware-request-* header mutations applied by middleware.
1043
- * Used for afterFiles and fallback rewrite has/missing evaluation — these
1044
- * run after middleware in the App Router execution order.
1045
- */
1046
- function __buildPostMwRequestContext(request) {
1047
- const url = new URL(request.url);
1048
- const ctx = getHeadersContext();
1049
- if (!ctx) return requestContextFromRequest(request);
1050
- // ctx.cookies is a Map<string, string> (HeadersContext), but RequestContext
1051
- // requires a plain Record<string, string> for has/missing cookie evaluation
1052
- // (config-matchers.ts uses obj[key] not Map.get()). Convert here.
1053
- const cookiesRecord = Object.fromEntries(ctx.cookies);
1054
- return {
1055
- headers: ctx.headers,
1056
- cookies: cookiesRecord,
1057
- query: url.searchParams,
1058
- host: normalizeHost(ctx.headers.get("host"), url.hostname),
1059
- };
1060
- }
1061
-
1062
312
  /**
1063
313
  * Maximum server-action request body size.
1064
314
  * Configurable via experimental.serverActions.bodySizeLimit in next.config.
@@ -1068,63 +318,6 @@ function __buildPostMwRequestContext(request) {
1068
318
  */
1069
319
  var __MAX_ACTION_BODY_SIZE = ${JSON.stringify(bodySizeLimit)};
1070
320
 
1071
- /**
1072
- * Read a request body as text with a size limit.
1073
- * Enforces the limit on the actual byte stream to prevent bypasses
1074
- * via chunked transfer-encoding where Content-Length is absent or spoofed.
1075
- */
1076
- async function __readBodyWithLimit(request, maxBytes) {
1077
- if (!request.body) return "";
1078
- var reader = request.body.getReader();
1079
- var decoder = new TextDecoder();
1080
- var chunks = [];
1081
- var totalSize = 0;
1082
- for (;;) {
1083
- var result = await reader.read();
1084
- if (result.done) break;
1085
- totalSize += result.value.byteLength;
1086
- if (totalSize > maxBytes) {
1087
- reader.cancel();
1088
- throw new Error("Request body too large");
1089
- }
1090
- chunks.push(decoder.decode(result.value, { stream: true }));
1091
- }
1092
- chunks.push(decoder.decode());
1093
- return chunks.join("");
1094
- }
1095
-
1096
- /**
1097
- * Read a request body as FormData with a size limit.
1098
- * Consumes the body stream with a byte counter and then parses the
1099
- * collected bytes as multipart form data via the Response constructor.
1100
- */
1101
- async function __readFormDataWithLimit(request, maxBytes) {
1102
- if (!request.body) return new FormData();
1103
- var reader = request.body.getReader();
1104
- var chunks = [];
1105
- var totalSize = 0;
1106
- for (;;) {
1107
- var result = await reader.read();
1108
- if (result.done) break;
1109
- totalSize += result.value.byteLength;
1110
- if (totalSize > maxBytes) {
1111
- reader.cancel();
1112
- throw new Error("Request body too large");
1113
- }
1114
- chunks.push(result.value);
1115
- }
1116
- // Reconstruct a Response with the original Content-Type so that
1117
- // the FormData parser can handle multipart boundaries correctly.
1118
- var combined = new Uint8Array(totalSize);
1119
- var offset = 0;
1120
- for (var chunk of chunks) {
1121
- combined.set(chunk, offset);
1122
- offset += chunk.byteLength;
1123
- }
1124
- var contentType = request.headers.get("content-type") || "";
1125
- return new Response(combined, { headers: { "Content-Type": contentType } }).formData();
1126
- }
1127
-
1128
321
  // Map from route pattern to generateStaticParams function.
1129
322
  // Used by the prerender phase to enumerate dynamic route URLs without
1130
323
  // loading route modules via the dev server.
@@ -1134,1411 +327,336 @@ export const generateStaticParamsMap = {
1134
327
  // to provide parent params for nested dynamic routes, but they don't have a pagePath
1135
328
  // so they are excluded here. Supporting layout-level generateStaticParams requires
1136
329
  // scanning layout.tsx files separately and including them in this map.
1137
- ${routes.filter((r) => r.isDynamic && r.pagePath).map((r) => ` ${JSON.stringify(r.pattern)}: ${getImportVar(r.pagePath)}?.generateStaticParams ?? null,`).join("\n")}
330
+ ${generateStaticParamsEntries.join("\n")}
331
+ };${loadPrerenderPagesRoutesCode}
332
+ const rootParamNamesMap = {
333
+ ${routes.filter((r) => r.isDynamic && r.pagePath && r.rootParamNames && r.rootParamNames.length > 0).map((r) => ` ${JSON.stringify(r.pattern)}: ${JSON.stringify(r.rootParamNames)},`).join("\n")}
1138
334
  };
1139
335
 
1140
- export default async function handler(request, ctx) {
1141
- ${instrumentationPath ? `// Ensure instrumentation.register() has run before handling the first request.
1142
- // This is a no-op after the first call (guarded by __instrumentationInitialized).
1143
- await __ensureInstrumentation();
1144
- ` : ""}
1145
- // Wrap the entire request in a single unified ALS scope for per-request
1146
- // isolation. All state modules (headers, navigation, cache, fetch-cache,
1147
- // execution-context) read from this store via isInsideUnifiedScope().
1148
- const headersCtx = headersContextFromRequest(request);
1149
- const __uCtx = _createUnifiedCtx({
1150
- headersContext: headersCtx,
1151
- executionContext: ctx ?? _getRequestExecutionContext() ?? null,
1152
- unstableCacheRevalidation: "background",
1153
- });
1154
- return _runWithUnifiedCtx(__uCtx, async () => {
1155
- _ensureFetchPatch();
1156
- const __reqCtx = requestContextFromRequest(request);
1157
- // Per-request container for middleware state. Passed into
1158
- // _handleRequest which fills in .headers and .status;
1159
- // avoids module-level variables that race on Workers.
1160
- const _mwCtx = { headers: null, requestHeaders: null, status: null };
1161
- let response;
1162
- try {
1163
- response = await _handleRequest(request, __reqCtx, _mwCtx);
1164
- } catch (err) {
1165
- // Dev only: embed err.cause chain into err.message/err.stack so Vite's
1166
- // dev-server "Internal server error:" logger (which builds output from
1167
- // message + stack only) reveals the underlying root cause (ECONNREFUSED,
1168
- // role missing, workerd socket error, etc.) instead of dropping it.
1169
- // Skipped in production because Node's util.inspect / workerd's logger
1170
- // already render .cause natively, so flattening would double-print it.
1171
- // NODE_ENV is build-time-replaced by Vite, so the prod bundle compiles
1172
- // this branch out entirely.
1173
- if (process.env.NODE_ENV !== "production") {
1174
- __flattenErrorCauses(err);
1175
- }
1176
- throw err;
1177
- }
1178
- // Apply custom headers from next.config.js to non-redirect responses.
1179
- // Skip redirects (3xx) because Response.redirect() creates immutable headers,
1180
- // and Next.js doesn't apply custom headers to redirects anyway.
1181
- if (response && response.headers && !(response.status >= 300 && response.status < 400)) {
1182
- if (__configHeaders.length) {
1183
- const url = new URL(request.url);
1184
- let pathname;
1185
- try { pathname = __normalizePath(__normalizePathnameForRouteMatch(url.pathname)); } catch { pathname = url.pathname; }
1186
- ${bp ? `if (pathname.startsWith(${JSON.stringify(bp)})) pathname = pathname.slice(${JSON.stringify(bp)}.length) || "/";` : ""}
1187
- const extraHeaders = matchHeaders(pathname, __configHeaders, __reqCtx);
1188
- for (const h of extraHeaders) {
1189
- // Use append() for headers where multiple values must coexist
1190
- // (Vary, Set-Cookie). Using set() on these would destroy
1191
- // existing values like "Vary: RSC, Accept" which are critical
1192
- // for correct CDN caching behavior.
1193
- const lk = h.key.toLowerCase();
1194
- if (lk === "vary" || lk === "set-cookie") {
1195
- response.headers.append(h.key, h.value);
1196
- } else if (!response.headers.has(lk)) {
1197
- // Middleware headers take precedence: skip config keys already
1198
- // set by middleware so middleware headers always win.
1199
- response.headers.set(h.key, h.value);
1200
- }
1201
- }
1202
- }
1203
- }
1204
- return response;
1205
- });
1206
- }
1207
-
1208
- async function _handleRequest(request, __reqCtx, _mwCtx) {
1209
- const __reqStart = process.env.NODE_ENV !== "production" ? performance.now() : 0;
1210
- // __reqStart is included in the timing header so the Node logging middleware
1211
- // can compute true compile time as: handlerStart - middlewareStart.
1212
- // Format: "handlerStart,compileMs,renderMs" - all as integers (ms). Dev-only.
1213
- const url = new URL(request.url);
1214
-
1215
- // ── Cross-origin request protection (dev only) ─────────────────────
1216
- // Block requests from non-localhost origins to prevent data exfiltration.
1217
- // Skipped in production — Vite replaces NODE_ENV at build time.
1218
- if (process.env.NODE_ENV !== "production") {
1219
- const __originBlock = __validateDevRequestOrigin(request);
1220
- if (__originBlock) return __originBlock;
1221
- }
1222
-
1223
- // Guard against protocol-relative URL open redirects (see request-pipeline.ts).
1224
- const __protoGuard = guardProtocolRelativeUrl(url.pathname);
1225
- if (__protoGuard) return __protoGuard;
1226
-
1227
- // Decode percent-encoding segment-wise and normalize pathname to canonical form.
1228
- // This preserves encoded path delimiters like %2F within a single segment.
1229
- // __normalizePath collapses //foo///bar → /foo/bar, resolves . and .. segments.
1230
- let decodedUrlPathname;
1231
- try { decodedUrlPathname = __normalizePathnameForRouteMatchStrict(url.pathname); } catch (e) {
1232
- return new Response("Bad Request", { status: 400 });
1233
- }
1234
- let pathname = __normalizePath(decodedUrlPathname);
1235
-
1236
- ${bp ? `
1237
- if (!hasBasePath(pathname, __basePath) && !pathname.startsWith("/__vinext/")) {
1238
- return new Response("Not Found", { status: 404 });
1239
- }
1240
- // Strip basePath prefix
1241
- pathname = stripBasePath(pathname, __basePath);
1242
- ` : ""}
1243
-
1244
- // ── Prerender: static-params endpoint ────────────────────────────────
1245
- // Internal endpoint used by prerenderApp() during build to fetch
1246
- // generateStaticParams results via wrangler unstable_startWorker.
1247
- // Gated on VINEXT_PRERENDER=1 to prevent exposure in normal deployments.
1248
- // For Node builds, process.env.VINEXT_PRERENDER is set directly by the
1249
- // prerender orchestrator. For CF Workers builds, wrangler unstable_startWorker
1250
- // injects VINEXT_PRERENDER as a binding which Miniflare exposes via process.env
1251
- // in bundled workers. The /__vinext/ prefix ensures no user route ever conflicts.
1252
- if (pathname === "/__vinext/prerender/static-params") {
1253
- if (process.env.VINEXT_PRERENDER !== "1") {
1254
- return new Response("Not Found", { status: 404 });
1255
- }
1256
- const pattern = url.searchParams.get("pattern");
1257
- if (!pattern) return new Response("missing pattern", { status: 400 });
1258
- const fn = generateStaticParamsMap[pattern];
1259
- if (typeof fn !== "function") return new Response("null", { status: 200, headers: { "content-type": "application/json" } });
1260
- try {
1261
- const parentParams = url.searchParams.get("parentParams");
1262
- const raw = parentParams ? JSON.parse(parentParams) : {};
1263
- // Ensure params is a plain object — reject primitives, arrays, and null
1264
- // so user-authored generateStaticParams always receives { params: {} }
1265
- // rather than { params: 5 } or similar if input is malformed.
1266
- const params = (typeof raw === "object" && raw !== null && !Array.isArray(raw)) ? raw : {};
1267
- const result = await fn({ params });
1268
- return new Response(JSON.stringify(result), { status: 200, headers: { "content-type": "application/json" } });
1269
- } catch (e) {
1270
- return new Response(JSON.stringify({ error: String(e) }), { status: 500, headers: { "content-type": "application/json" } });
1271
- }
1272
- }
1273
-
1274
- ${hasPagesDir ? `
1275
- // ── Prerender: pages-static-paths endpoint ───────────────────────────
1276
- // Internal endpoint used by prerenderPages() during a CF Workers hybrid
1277
- // build to call getStaticPaths() for dynamic Pages Router routes via
1278
- // wrangler unstable_startWorker. Returns JSON-serialised getStaticPaths result.
1279
- // Gated on VINEXT_PRERENDER=1 to prevent exposure in normal deployments.
1280
- // See static-params endpoint above for process.env vs CF vars notes.
1281
- //
1282
- // pageRoutes lives in the SSR environment (virtual:vinext-server-entry).
1283
- // We load it lazily via import.meta.viteRsc.loadModule — the same pattern
1284
- // used by handleSsr() elsewhere in this template. At build time, Vite's RSC
1285
- // plugin transforms this call into a bundled cross-environment import, so it
1286
- // works correctly in the CF Workers production bundle running in Miniflare.
1287
- if (pathname === "/__vinext/prerender/pages-static-paths") {
1288
- if (process.env.VINEXT_PRERENDER !== "1") {
1289
- return new Response("Not Found", { status: 404 });
1290
- }
1291
- const __gspPattern = url.searchParams.get("pattern");
1292
- if (!__gspPattern) return new Response("missing pattern", { status: 400 });
1293
- try {
1294
- const __gspSsrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
1295
- const __pagesRoutes = __gspSsrEntry.pageRoutes;
1296
- const __gspRoute = Array.isArray(__pagesRoutes)
1297
- ? __pagesRoutes.find((r) => r.pattern === __gspPattern)
1298
- : undefined;
1299
- if (!__gspRoute || typeof __gspRoute.module?.getStaticPaths !== "function") {
1300
- return new Response("null", { status: 200, headers: { "content-type": "application/json" } });
1301
- }
1302
- const __localesParam = url.searchParams.get("locales");
1303
- const __locales = __localesParam ? JSON.parse(__localesParam) : [];
1304
- const __defaultLocale = url.searchParams.get("defaultLocale") ?? "";
1305
- const __gspResult = await __gspRoute.module.getStaticPaths({ locales: __locales, defaultLocale: __defaultLocale });
1306
- return new Response(JSON.stringify(__gspResult), { status: 200, headers: { "content-type": "application/json" } });
1307
- } catch (e) {
1308
- return new Response(JSON.stringify({ error: String(e) }), { status: 500, headers: { "content-type": "application/json" } });
1309
- }
1310
- }
1311
- ` : ""}
1312
-
1313
- // Trailing slash normalization (redirect to canonical form)
1314
- const __tsRedirect = normalizeTrailingSlash(pathname, __basePath, __trailingSlash, url.search);
1315
- if (__tsRedirect) return __tsRedirect;
1316
-
1317
- // ── Apply redirects from next.config.js ───────────────────────────────
1318
- if (__configRedirects.length) {
1319
- // Strip .rsc suffix before matching redirect rules - RSC (client-side nav) requests
1320
- // arrive as /some/path.rsc but redirect patterns are defined without it (e.g.
1321
- // /some/path). Without this, soft-nav fetches bypass all config redirects.
1322
- const __redirPathname = pathname.endsWith(".rsc") ? pathname.slice(0, -4) : pathname;
1323
- const __redir = matchRedirect(__redirPathname, __configRedirects, __reqCtx);
1324
- if (__redir) {
1325
- const __redirDest = sanitizeDestination(
1326
- __basePath &&
1327
- !isExternalUrl(__redir.destination) &&
1328
- !hasBasePath(__redir.destination, __basePath)
1329
- ? __basePath + __redir.destination
1330
- : __redir.destination
1331
- );
1332
- return new Response(null, {
1333
- status: __redir.permanent ? 308 : 307,
1334
- headers: { Location: __redirDest },
1335
- });
1336
- }
1337
- }
1338
-
1339
- const isRscRequest = pathname.endsWith(".rsc") || request.headers.get("accept")?.includes("text/x-component");
1340
- // Read mounted-slots header once at the handler scope and thread it through
1341
- // every buildPageElements call site. Previously both the handler and
1342
- // buildPageElements read and normalized it independently, which invited
1343
- // silent drift if a future refactor changed only one path.
1344
- const __mountedSlotsHeader = __normalizeMountedSlotsHeader(
1345
- request.headers.get("x-vinext-mounted-slots"),
1346
- );
1347
- const interceptionContextHeader = request.headers.get("X-Vinext-Interception-Context")?.replaceAll("\0", "") || null;
1348
- let cleanPathname = pathname.replace(/\\.rsc$/, "");
1349
-
1350
- // Middleware response headers and custom rewrite status are stored in
1351
- // _mwCtx (per-request container) so handler() can merge them into
1352
- // every response path without module-level state that races on Workers.
1353
-
1354
- ${middlewarePath ? `
1355
- // In hybrid app+pages dev mode the connect handler already ran middleware
1356
- // and forwarded the results via x-vinext-mw-ctx. Reconstruct _mwCtx from
1357
- // the forwarded data instead of re-running the middleware function.
1358
- // Guarded by NODE_ENV because this header only exists in dev (the connect
1359
- // handler sets it). In production there is no connect handler, so an
1360
- // attacker-supplied header must not be trusted.
1361
- let __mwCtxApplied = false;
1362
- if (process.env.NODE_ENV !== "production") {
1363
- const __mwCtxHeader = request.headers.get("x-vinext-mw-ctx");
1364
- if (__mwCtxHeader) {
1365
- try {
1366
- const __mwCtxData = JSON.parse(__mwCtxHeader);
1367
- if (__mwCtxData.h && __mwCtxData.h.length > 0) {
1368
- // Note: h may include x-middleware-request-* internal headers so
1369
- // applyMiddlewareRequestHeaders() can unpack them below.
1370
- // processMiddlewareHeaders() strips them before any response.
1371
- _mwCtx.headers = new Headers();
1372
- for (const [key, value] of __mwCtxData.h) {
1373
- _mwCtx.headers.append(key, value);
1374
- }
1375
- }
1376
- if (__mwCtxData.s != null) {
1377
- _mwCtx.status = __mwCtxData.s;
1378
- }
1379
- // Apply forwarded middleware rewrite so routing uses the rewritten path.
1380
- // The RSC plugin constructs its Request from the original HTTP request,
1381
- // not from req.url, so the connect handler's req.url rewrite is invisible.
1382
- if (__mwCtxData.r) {
1383
- const __rewriteParsed = new URL(__mwCtxData.r, request.url);
1384
- cleanPathname = __rewriteParsed.pathname;
1385
- url.search = __rewriteParsed.search;
1386
- }
1387
- // Flag set after full context application — if any step fails (e.g. malformed
1388
- // rewrite URL), we fall back to re-running middleware as a safety net.
1389
- __mwCtxApplied = true;
1390
- } catch (e) {
1391
- console.error("[vinext] Failed to parse forwarded middleware context:", e);
1392
- }
1393
- }
1394
- }
1395
- if (!__mwCtxApplied) {
1396
- // Run proxy/middleware if present and path matches.
1397
- // Validate exports match the file type (proxy.ts vs middleware.ts), matching Next.js behavior.
1398
- // https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts
1399
- const _isProxy = ${JSON.stringify(isProxyFile(middlewarePath))};
1400
- const middlewareFn = _isProxy
1401
- ? (middlewareModule.proxy ?? middlewareModule.default)
1402
- : (middlewareModule.middleware ?? middlewareModule.default);
1403
- if (typeof middlewareFn !== "function") {
1404
- const _fileType = _isProxy ? "Proxy" : "Middleware";
1405
- const _expectedExport = _isProxy ? "proxy" : "middleware";
1406
- throw new Error("The " + _fileType + " file must export a function named \`" + _expectedExport + "\` or a \`default\` function.");
1407
- }
1408
- const middlewareMatcher = middlewareModule.config?.matcher;
1409
- if (matchesMiddleware(cleanPathname, middlewareMatcher, request, __i18nConfig)) {
1410
- try {
1411
- // Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.
1412
- // Always construct a new Request with the fully decoded + normalized pathname
1413
- // so middleware and the router see the same canonical path.
1414
- const mwUrl = new URL(request.url);
1415
- mwUrl.pathname = cleanPathname;
1416
- const mwRequest = new Request(mwUrl, request);
1417
- const __mwNextConfig = (__basePath || __i18nConfig) ? { basePath: __basePath, i18n: __i18nConfig ?? undefined } : undefined;
1418
- const nextRequest = mwRequest instanceof NextRequest ? mwRequest : new NextRequest(mwRequest, __mwNextConfig ? { nextConfig: __mwNextConfig } : undefined);
1419
- const mwFetchEvent = new NextFetchEvent({ page: cleanPathname });
1420
- let mwResponse;
1421
- try {
1422
- mwResponse = await middlewareFn(nextRequest, mwFetchEvent);
1423
- } finally {
1424
- const _mwWaitUntil = mwFetchEvent.drainWaitUntil();
1425
- const _mwExecCtx = _getRequestExecutionContext();
1426
- if (_mwExecCtx && typeof _mwExecCtx.waitUntil === "function") { _mwExecCtx.waitUntil(_mwWaitUntil); }
1427
- }
1428
- if (mwResponse) {
1429
- // Check for x-middleware-next (continue)
1430
- if (mwResponse.headers.get("x-middleware-next") === "1") {
1431
- // Middleware wants to continue — collect all headers except the two
1432
- // control headers we've already consumed. x-middleware-request-*
1433
- // headers are kept so applyMiddlewareRequestHeaders() can unpack them;
1434
- // the blanket strip loop after that call removes every remaining
1435
- // x-middleware-* header before the set is merged into the response.
1436
- _mwCtx.headers = new Headers();
1437
- for (const [key, value] of mwResponse.headers) {
1438
- if (key !== "x-middleware-next" && key !== "x-middleware-rewrite") {
1439
- _mwCtx.headers.append(key, value);
1440
- }
1441
- }
1442
- } else {
1443
- // Check for redirect
1444
- if (mwResponse.status >= 300 && mwResponse.status < 400) {
1445
- return mwResponse;
1446
- }
1447
- // Check for rewrite
1448
- const rewriteUrl = mwResponse.headers.get("x-middleware-rewrite");
1449
- if (rewriteUrl) {
1450
- const rewriteParsed = new URL(rewriteUrl, request.url);
1451
- cleanPathname = rewriteParsed.pathname;
1452
- // Carry over query params from the rewrite URL so that
1453
- // searchParams props, useSearchParams(), and navigation context
1454
- // reflect the rewrite destination, not the original request.
1455
- url.search = rewriteParsed.search;
1456
- // Capture custom status code from rewrite (e.g. NextResponse.rewrite(url, { status: 403 }))
1457
- if (mwResponse.status !== 200) {
1458
- _mwCtx.status = mwResponse.status;
1459
- }
1460
- // Also save any other headers from the rewrite response
1461
- _mwCtx.headers = new Headers();
1462
- for (const [key, value] of mwResponse.headers) {
1463
- if (key !== "x-middleware-next" && key !== "x-middleware-rewrite") {
1464
- _mwCtx.headers.append(key, value);
1465
- }
1466
- }
1467
- } else {
1468
- // Middleware returned a custom response
1469
- return mwResponse;
1470
- }
1471
- }
1472
- }
1473
- } catch (err) {
1474
- console.error("[vinext] Middleware error:", err);
1475
- return new Response("Internal Server Error", { status: 500 });
1476
- }
1477
- }
1478
- } // end of if (!__mwCtxApplied)
1479
-
1480
- // Unpack x-middleware-request-* headers into the request context so that
1481
- // headers() returns the middleware-modified headers instead of the original
1482
- // request headers. Strip ALL x-middleware-* headers from the set that will
1483
- // be merged into the outgoing HTTP response — this prefix is reserved for
1484
- // internal routing signals and must never reach clients.
1485
- if (_mwCtx.headers) {
1486
- // Preserve the pre-strip header set so route handlers can reconstruct
1487
- // a request object with middleware header overrides applied.
1488
- _mwCtx.requestHeaders = new Headers(_mwCtx.headers);
1489
- applyMiddlewareRequestHeaders(_mwCtx.headers);
1490
- processMiddlewareHeaders(_mwCtx.headers);
1491
- }
1492
- ` : ""}
1493
-
1494
- const _scriptNonce = __getScriptNonceFromHeaderSources(request.headers, _mwCtx.headers);
1495
-
1496
- // Build post-middleware request context for afterFiles/fallback rewrites.
1497
- // These run after middleware in the App Router execution order and should
1498
- // evaluate has/missing conditions against middleware-modified headers.
1499
- // When no middleware is present, this falls back to requestContextFromRequest.
1500
- const __postMwReqCtx = __buildPostMwRequestContext(request);
1501
-
1502
- // ── Apply beforeFiles rewrites from next.config.js ────────────────────
1503
- // In App Router execution order, beforeFiles runs after middleware so that
1504
- // has/missing conditions can evaluate against middleware-modified headers.
1505
- if (__configRewrites.beforeFiles && __configRewrites.beforeFiles.length) {
1506
- const __rewritten = matchRewrite(cleanPathname, __configRewrites.beforeFiles, __postMwReqCtx);
1507
- if (__rewritten) {
1508
- if (isExternalUrl(__rewritten)) {
1509
- setHeadersContext(null);
1510
- setNavigationContext(null);
1511
- return proxyExternalRequest(request, __rewritten);
1512
- }
1513
- cleanPathname = __rewritten;
1514
- }
1515
- }
1516
-
1517
- // ── Image optimization passthrough (dev mode — no transformation) ───────
1518
- if (cleanPathname === "/_vinext/image") {
1519
- const __imgResult = validateImageUrl(url.searchParams.get("url"), request.url);
1520
- if (__imgResult instanceof Response) return __imgResult;
1521
- // In dev, redirect to the original asset URL so Vite's static serving handles it.
1522
- return Response.redirect(new URL(__imgResult, url.origin).href, 302);
1523
- }
1524
-
1525
- // Handle metadata routes (sitemap.xml, robots.txt, manifest.webmanifest, etc.)
1526
- for (const metaRoute of metadataRoutes) {
1527
- // generateSitemaps() support — paginated sitemaps at /{prefix}/sitemap/{id}.xml
1528
- // When a sitemap module exports generateSitemaps, the base URL (e.g. /products/sitemap.xml)
1529
- // is no longer served. Instead, individual sitemaps are served at /products/sitemap/{id}.xml.
1530
- if (
1531
- metaRoute.type === "sitemap" &&
1532
- metaRoute.isDynamic &&
1533
- typeof metaRoute.module.generateSitemaps === "function"
1534
- ) {
1535
- const sitemapPrefix = metaRoute.servedUrl.slice(0, -4); // strip ".xml"
1536
- // Match exactly /{prefix}/{id}.xml — one segment only (no slashes in id)
1537
- if (cleanPathname.startsWith(sitemapPrefix + "/") && cleanPathname.endsWith(".xml")) {
1538
- const rawId = cleanPathname.slice(sitemapPrefix.length + 1, -4);
1539
- if (rawId.includes("/")) continue; // multi-segment — not a paginated sitemap
1540
- const sitemaps = await metaRoute.module.generateSitemaps();
1541
- const matched = sitemaps.find(function(s) { return String(s.id) === rawId; });
1542
- if (!matched) return new Response("Not Found", { status: 404 });
1543
- // Pass the original typed id from generateSitemaps() so numeric IDs stay numeric.
1544
- // TODO: wrap with makeThenableParams-style Promise when upgrading to Next.js 16
1545
- // full-Promise param semantics (id becomes Promise<string> in v16).
1546
- const result = await metaRoute.module.default({ id: matched.id });
1547
- if (result instanceof Response) return result;
1548
- return new Response(sitemapToXml(result), {
1549
- headers: { "Content-Type": metaRoute.contentType },
1550
- });
1551
- }
1552
- // Skip — the base servedUrl is not served when generateSitemaps exists
1553
- continue;
1554
- }
1555
- // Match metadata route — use pattern matching for dynamic segments,
1556
- // strict equality for static paths.
1557
- var _metaParams = null;
1558
- if (metaRoute.patternParts) {
1559
- var _metaUrlParts = cleanPathname.split("/").filter(Boolean);
1560
- _metaParams = matchPattern(_metaUrlParts, metaRoute.patternParts);
1561
- if (!_metaParams) continue;
1562
- } else if (cleanPathname !== metaRoute.servedUrl) {
1563
- continue;
1564
- }
1565
- if (metaRoute.isDynamic) {
1566
- // Dynamic metadata route — call the default export and serialize
1567
- const metaFn = metaRoute.module.default;
1568
- if (typeof metaFn === "function") {
1569
- const result = await metaFn({ params: makeThenableParams(_metaParams || {}) });
1570
- let body;
1571
- // If it's already a Response (e.g., ImageResponse), return directly
1572
- if (result instanceof Response) return result;
1573
- // Serialize based on type
1574
- if (metaRoute.type === "sitemap") body = sitemapToXml(result);
1575
- else if (metaRoute.type === "robots") body = robotsToText(result);
1576
- else if (metaRoute.type === "manifest") body = manifestToJson(result);
1577
- else body = JSON.stringify(result);
1578
- return new Response(body, {
1579
- headers: { "Content-Type": metaRoute.contentType },
1580
- });
1581
- }
1582
- } else {
1583
- // Static metadata file — decode from embedded base64 data
1584
- try {
1585
- const binary = atob(metaRoute.fileDataBase64);
1586
- const bytes = new Uint8Array(binary.length);
1587
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
1588
- return new Response(bytes, {
1589
- headers: {
1590
- "Content-Type": metaRoute.contentType,
1591
- "Cache-Control": "public, max-age=0, must-revalidate",
1592
- },
1593
- });
1594
- } catch {
1595
- return new Response("Not Found", { status: 404 });
1596
- }
1597
- }
1598
- }
1599
-
1600
- // Serve public/ files as filesystem routes after middleware and before
1601
- // afterFiles/fallback rewrites, matching Next.js routing semantics.
1602
- if (
1603
- (request.method === "GET" || request.method === "HEAD") &&
1604
- !pathname.endsWith(".rsc") &&
1605
- __publicFiles.has(cleanPathname)
1606
- ) {
1607
- setHeadersContext(null);
1608
- setNavigationContext(null);
1609
- return __createStaticFileSignal(cleanPathname, _mwCtx);
1610
- }
1611
-
1612
- // Set navigation context for Server Components.
1613
- // Note: Headers context is already set by runWithRequestContext in the handler wrapper.
1614
- setNavigationContext({
1615
- pathname: cleanPathname,
1616
- searchParams: url.searchParams,
1617
- params: {},
1618
- });
1619
-
1620
- // Handle server action POST requests
1621
- const actionId = request.headers.get("x-rsc-action") ?? request.headers.get("next-action");
1622
- const actionContentType = request.headers.get("content-type") || "";
1623
- const progressiveActionResponse = await __handleProgressiveServerActionRequest({
1624
- actionId,
1625
- allowedOrigins: __allowedOrigins,
336
+ export default __createAppRscHandler({
337
+ basePath: __basePath,
338
+ clearRequestContext() {
339
+ __clearRequestContext();
340
+ },
341
+ configHeaders: __configHeaders,
342
+ configRedirects: __configRedirects,
343
+ configRewrites: __configRewrites,
344
+ dispatchMatchedPage({
1626
345
  cleanPathname,
1627
- clearRequestContext() {
1628
- setHeadersContext(null);
1629
- setNavigationContext(null);
1630
- },
1631
- contentType: actionContentType,
1632
- decodeAction,
1633
- getAndClearPendingCookies,
1634
- getDraftModeCookieHeader,
1635
- maxActionBodySize: __MAX_ACTION_BODY_SIZE,
1636
- middlewareHeaders: _mwCtx.headers,
1637
- readFormDataWithLimit: __readFormDataWithLimit,
1638
- reportRequestError: _reportRequestError,
1639
- request,
1640
- setHeadersAccessPhase,
1641
- });
1642
- if (progressiveActionResponse) return progressiveActionResponse;
1643
-
1644
- if (request.method === "POST" && actionId) {
1645
- // ── CSRF protection ─────────────────────────────────────────────────
1646
- // Verify that the Origin header matches the Host header to prevent
1647
- // cross-site request forgery, matching Next.js server action behavior.
1648
- const csrfResponse = validateCsrfOrigin(request, __allowedOrigins);
1649
- if (csrfResponse) return csrfResponse;
1650
-
1651
- // ── Body size limit ─────────────────────────────────────────────────
1652
- // Reject payloads larger than the configured limit.
1653
- // Check Content-Length as a fast path, then enforce on the actual
1654
- // stream to prevent bypasses via chunked transfer-encoding.
1655
- const contentLength = parseInt(request.headers.get("content-length") || "0", 10);
1656
- if (contentLength > __MAX_ACTION_BODY_SIZE) {
1657
- setHeadersContext(null);
1658
- setNavigationContext(null);
1659
- return new Response("Payload Too Large", { status: 413 });
1660
- }
1661
-
1662
- try {
1663
- let body;
1664
- try {
1665
- body = actionContentType.startsWith("multipart/form-data")
1666
- ? await __readFormDataWithLimit(request, __MAX_ACTION_BODY_SIZE)
1667
- : await __readBodyWithLimit(request, __MAX_ACTION_BODY_SIZE);
1668
- } catch (sizeErr) {
1669
- if (sizeErr && sizeErr.message === "Request body too large") {
1670
- setHeadersContext(null);
1671
- setNavigationContext(null);
1672
- return new Response("Payload Too Large", { status: 413 });
1673
- }
1674
- throw sizeErr;
1675
- }
1676
- const payloadResponse = await validateServerActionPayload(body);
1677
- if (payloadResponse) {
1678
- setHeadersContext(null);
1679
- setNavigationContext(null);
1680
- return payloadResponse;
1681
- }
1682
- const temporaryReferences = createTemporaryReferenceSet();
1683
- const args = await decodeReply(body, { temporaryReferences });
1684
- const action = await loadServerAction(actionId);
1685
- let returnValue;
1686
- let actionRedirect = null;
1687
- const previousHeadersPhase = setHeadersAccessPhase("action");
1688
- try {
1689
- try {
1690
- const data = await action.apply(null, args);
1691
- returnValue = { ok: true, data };
1692
- } catch (e) {
1693
- // Detect redirect() / permanentRedirect() called inside the action.
1694
- // These throw errors with digest "NEXT_REDIRECT;replace;url[;status]".
1695
- // The URL is encodeURIComponent-encoded to prevent semicolons in the URL
1696
- // from corrupting the delimiter-based digest format.
1697
- if (e && typeof e === "object" && "digest" in e) {
1698
- const digest = String(e.digest);
1699
- if (digest.startsWith("NEXT_REDIRECT;")) {
1700
- const parts = digest.split(";");
1701
- actionRedirect = {
1702
- url: decodeURIComponent(parts[2]),
1703
- type: parts[1] || "push", // "push" or "replace"
1704
- status: parts[3] ? parseInt(parts[3], 10) : 307,
1705
- };
1706
- returnValue = { ok: true, data: undefined };
1707
- } else if (digest === "NEXT_NOT_FOUND" || digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;")) {
1708
- // notFound() / forbidden() / unauthorized() in action — package as error
1709
- returnValue = { ok: false, data: e };
1710
- } else {
1711
- // Non-navigation digest error — sanitize in production to avoid
1712
- // leaking internal details (connection strings, paths, etc.)
1713
- console.error("[vinext] Server action error:", e);
1714
- returnValue = { ok: false, data: __sanitizeErrorForClient(e) };
1715
- }
1716
- } else {
1717
- // Unhandled error — sanitize in production to avoid leaking
1718
- // internal details (database errors, file paths, stack traces, etc.)
1719
- console.error("[vinext] Server action error:", e);
1720
- returnValue = { ok: false, data: __sanitizeErrorForClient(e) };
1721
- }
1722
- }
1723
- } finally {
1724
- setHeadersAccessPhase(previousHeadersPhase);
1725
- }
1726
-
1727
- // If the action called redirect(), signal the client to navigate.
1728
- // We can't use a real HTTP redirect (the fetch would follow it automatically
1729
- // and receive a page HTML instead of RSC stream). Instead, we return a 200
1730
- // with x-action-redirect header that the client entry detects and handles.
1731
- if (actionRedirect) {
1732
- const actionPendingCookies = getAndClearPendingCookies();
1733
- const actionDraftCookie = getDraftModeCookieHeader();
1734
- setHeadersContext(null);
1735
- setNavigationContext(null);
1736
- const redirectHeaders = new Headers({
1737
- "Content-Type": "text/x-component; charset=utf-8",
1738
- "Vary": "RSC, Accept",
1739
- });
1740
- __mergeMiddlewareResponseHeaders(redirectHeaders, _mwCtx.headers);
1741
- redirectHeaders.set("x-action-redirect", actionRedirect.url);
1742
- redirectHeaders.set("x-action-redirect-type", actionRedirect.type);
1743
- redirectHeaders.set("x-action-redirect-status", String(actionRedirect.status));
1744
- for (const cookie of actionPendingCookies) {
1745
- redirectHeaders.append("Set-Cookie", cookie);
1746
- }
1747
- if (actionDraftCookie) redirectHeaders.append("Set-Cookie", actionDraftCookie);
1748
- // Send an empty RSC-like body (client will navigate instead of parsing)
1749
- return new Response("", { status: 200, headers: redirectHeaders });
1750
- }
1751
-
1752
- // After the action, re-render the current page so the client
1753
- // gets an updated React tree reflecting any mutations.
1754
- //
1755
- // When the original request came from inside an intercepted modal
1756
- // (X-Vinext-Interception-Context present + source route still
1757
- // matches), rebuild the intercepted tree — otherwise the modal would
1758
- // unmount and the direct route would render in its place. Mirrors
1759
- // the interception resolution used by the GET path.
1760
- const match = matchRoute(cleanPathname);
1761
- let element;
1762
- let errorPattern = match ? match.route.pattern : cleanPathname;
1763
- if (match) {
1764
- const { route: actionRoute, params: actionParams } = match;
1765
- const __actionRerenderTarget = __resolveAppPageActionRerenderTarget({
1766
- cleanPathname,
1767
- currentParams: actionParams,
1768
- currentRoute: actionRoute,
1769
- findIntercept(pathname) {
1770
- return findIntercept(pathname, interceptionContextHeader);
1771
- },
1772
- getRouteParamNames(sourceRoute) {
1773
- return sourceRoute.params;
1774
- },
1775
- getSourceRoute(sourceRouteIndex) {
1776
- return routes[sourceRouteIndex];
1777
- },
1778
- isRscRequest,
1779
- toInterceptOpts(intercept) {
1780
- return {
1781
- interceptionContext: interceptionContextHeader,
1782
- interceptLayouts: intercept.interceptLayouts,
1783
- interceptSlotKey: intercept.slotKey,
1784
- interceptPage: intercept.page,
1785
- interceptParams: intercept.matchedParams,
1786
- };
1787
- },
1788
- });
1789
-
1790
- setNavigationContext({
1791
- pathname: cleanPathname,
1792
- searchParams: url.searchParams,
1793
- params: __actionRerenderTarget.navigationParams,
1794
- });
1795
- element = buildPageElements(
1796
- __actionRerenderTarget.route,
1797
- __actionRerenderTarget.params,
1798
- cleanPathname,
1799
- {
1800
- opts: __actionRerenderTarget.interceptOpts,
1801
- searchParams: url.searchParams,
1802
- isRscRequest,
1803
- request,
1804
- mountedSlotsHeader: __mountedSlotsHeader,
1805
- },
1806
- );
1807
- errorPattern = __actionRerenderTarget.route.pattern;
1808
- } else {
1809
- const _actionRouteId = __createAppPayloadRouteId(cleanPathname, null);
1810
- element = {
1811
- [__APP_INTERCEPTION_CONTEXT_KEY]: null,
1812
- __route: _actionRouteId,
1813
- __rootLayout: null,
1814
- [_actionRouteId]: createElement("div", null, "Page not found"),
1815
- };
1816
- }
1817
-
1818
- const onRenderError = createRscOnErrorHandler(
1819
- request,
1820
- cleanPathname,
1821
- errorPattern,
1822
- );
1823
- const rscStream = renderToReadableStream(
1824
- { root: element, returnValue },
1825
- { temporaryReferences, onError: onRenderError },
1826
- );
1827
-
1828
- // Collect cookies set during the action synchronously (before stream is consumed).
1829
- // Do NOT clear headers/navigation context here — the RSC stream is consumed lazily
1830
- // by the client, and async server components that run during consumption need the
1831
- // context to still be live. The AsyncLocalStorage scope from runWithRequestContext
1832
- // handles cleanup naturally when all async continuations complete.
1833
- const actionPendingCookies = getAndClearPendingCookies();
1834
- const actionDraftCookie = getDraftModeCookieHeader();
1835
-
1836
- const actionHeaders = new Headers({
1837
- "Content-Type": "text/x-component; charset=utf-8",
1838
- "Vary": "RSC, Accept",
1839
- });
1840
- __mergeMiddlewareResponseHeaders(actionHeaders, _mwCtx.headers);
1841
- const actionResponse = new Response(rscStream, {
1842
- status: _mwCtx.status ?? 200,
1843
- headers: actionHeaders,
1844
- });
1845
- if (actionPendingCookies.length > 0 || actionDraftCookie) {
1846
- for (const cookie of actionPendingCookies) {
1847
- actionResponse.headers.append("Set-Cookie", cookie);
1848
- }
1849
- if (actionDraftCookie) actionResponse.headers.append("Set-Cookie", actionDraftCookie);
1850
- }
1851
- return actionResponse;
1852
- } catch (err) {
1853
- getAndClearPendingCookies(); // Clear pending cookies on error
1854
- console.error("[vinext] Server action error:", err);
1855
- _reportRequestError(
1856
- err instanceof Error ? err : new Error(String(err)),
1857
- { path: cleanPathname, method: request.method, headers: Object.fromEntries(request.headers.entries()) },
1858
- { routerKind: "App Router", routePath: cleanPathname, routeType: "action" },
1859
- );
1860
- setHeadersContext(null);
1861
- setNavigationContext(null);
1862
- return new Response(
1863
- process.env.NODE_ENV === "production"
1864
- ? "Internal Server Error"
1865
- : "Server action failed: " + (err && err.message ? err.message : String(err)),
1866
- { status: 500 },
1867
- );
1868
- }
1869
- }
1870
-
1871
- // ── Apply afterFiles rewrites from next.config.js ──────────────────────
1872
- if (__configRewrites.afterFiles && __configRewrites.afterFiles.length) {
1873
- const __afterRewritten = matchRewrite(cleanPathname, __configRewrites.afterFiles, __postMwReqCtx);
1874
- if (__afterRewritten) {
1875
- if (isExternalUrl(__afterRewritten)) {
1876
- setHeadersContext(null);
1877
- setNavigationContext(null);
1878
- return proxyExternalRequest(request, __afterRewritten);
1879
- }
1880
- cleanPathname = __afterRewritten;
1881
- }
1882
- }
1883
-
1884
- let match = matchRoute(cleanPathname);
1885
-
1886
- // ── Fallback rewrites from next.config.js (if no route matched) ───────
1887
- if (!match && __configRewrites.fallback && __configRewrites.fallback.length) {
1888
- const __fallbackRewritten = matchRewrite(cleanPathname, __configRewrites.fallback, __postMwReqCtx);
1889
- if (__fallbackRewritten) {
1890
- if (isExternalUrl(__fallbackRewritten)) {
1891
- setHeadersContext(null);
1892
- setNavigationContext(null);
1893
- return proxyExternalRequest(request, __fallbackRewritten);
1894
- }
1895
- cleanPathname = __fallbackRewritten;
1896
- match = matchRoute(cleanPathname);
1897
- }
1898
- }
1899
-
1900
- if (!match) {
1901
- ${hasPagesDir ? `
1902
- // ── Pages Router fallback ────────────────────────────────────────────
1903
- // When a request doesn't match any App Router route, delegate to the
1904
- // Pages Router handler (available in the SSR environment). This covers
1905
- // both production request serving and prerender fetches from wrangler.
1906
- // RSC requests (.rsc suffix or Accept: text/x-component) cannot be
1907
- // handled by the Pages Router, so skip the delegation for those.
1908
- if (!isRscRequest) {
1909
- const __pagesEntry = await import.meta.viteRsc.loadModule("ssr", "index");
1910
- if (typeof __pagesEntry.renderPage === "function") {
1911
- const __pagesRequestHeaders = _mwCtx.requestHeaders
1912
- ? __buildRequestHeadersFromMiddlewareResponse(request.headers, _mwCtx.requestHeaders)
1913
- : null;
1914
- const __pagesRequest = __pagesRequestHeaders
1915
- ? new Request(request.url, { method: request.method, headers: __pagesRequestHeaders })
1916
- : request;
1917
- // Use segment-wise decoding to preserve encoded path delimiters (%2F).
1918
- // decodeURIComponent would turn /admin%2Fpanel into /admin/panel,
1919
- // changing the path structure and bypassing middleware matchers.
1920
- // Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts
1921
- // https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts
1922
- const __pagesRes = await __pagesEntry.renderPage(
1923
- __pagesRequest,
1924
- __decodePathParams(url.pathname) + (url.search || ""),
1925
- {},
1926
- undefined,
1927
- _mwCtx.requestHeaders,
1928
- );
1929
- // Only return the Pages Router response if it matched a route
1930
- // (non-404). A 404 means the path isn't a Pages route either,
1931
- // so fall through to the App Router not-found page below.
1932
- if (__pagesRes.status !== 404) {
1933
- setHeadersContext(null);
1934
- setNavigationContext(null);
1935
- return __pagesRes;
1936
- }
1937
- }
1938
- }
1939
- ` : ""}
1940
- // Render custom not-found page if available, otherwise plain 404
1941
- const notFoundResponse = await renderNotFoundPage(null, isRscRequest, request, undefined, _scriptNonce, _mwCtx);
1942
- if (notFoundResponse) return notFoundResponse;
1943
- setHeadersContext(null);
1944
- setNavigationContext(null);
1945
- const notFoundHeaders = new Headers();
1946
- __mergeMiddlewareResponseHeaders(notFoundHeaders, _mwCtx.headers);
1947
- return new Response("Not Found", { status: 404, headers: notFoundHeaders });
1948
- }
1949
-
1950
- const { route, params } = match;
1951
- setCurrentFetchSoftTags(__pageCacheTags(cleanPathname, []));
1952
-
1953
- // Update navigation context with matched params
1954
- setNavigationContext({
1955
- pathname: cleanPathname,
1956
- searchParams: url.searchParams,
346
+ handlerStart,
347
+ interceptionContext,
348
+ isRscRequest,
349
+ middlewareContext,
350
+ mountedSlotsHeader,
1957
351
  params,
1958
- });
1959
-
1960
- // Handle route.ts API handlers
1961
- if (route.routeHandler) {
1962
- const handler = route.routeHandler;
1963
- const method = request.method.toUpperCase();
1964
- const revalidateSeconds = __getAppRouteHandlerRevalidateSeconds(handler);
1965
- if (__hasAppRouteHandlerDefaultExport(handler) && process.env.NODE_ENV === "development") {
1966
- console.error(
1967
- "[vinext] Detected default export in route handler " + route.pattern + ". Export a named export for each HTTP method instead.",
1968
- );
1969
- }
1970
-
1971
- const {
1972
- allowHeaderForOptions,
1973
- handlerFn,
1974
- isAutoHead,
1975
- shouldAutoRespondToOptions,
1976
- } = __resolveAppRouteHandlerMethod(handler, method);
1977
-
1978
- if (shouldAutoRespondToOptions) {
1979
- setHeadersContext(null);
1980
- setNavigationContext(null);
1981
- return __applyRouteHandlerMiddlewareContext(
1982
- new Response(null, {
1983
- status: 204,
1984
- headers: { "Allow": allowHeaderForOptions },
1985
- }),
1986
- _mwCtx,
1987
- );
1988
- }
1989
-
1990
- // ISR cache read for route handlers (production only).
1991
- // Only GET/HEAD (auto-HEAD) with finite revalidate > 0 are ISR-eligible.
1992
- // Known-dynamic handlers skip the read entirely so stale cache entries
1993
- // from earlier requests do not replay once the process has learned they
1994
- // access request-specific data.
1995
- if (
1996
- __shouldReadAppRouteHandlerCache({
1997
- dynamicConfig: handler.dynamic,
1998
- handlerFn,
1999
- isAutoHead,
2000
- isKnownDynamic: __isKnownDynamicAppRoute(route.pattern),
2001
- isProduction: process.env.NODE_ENV === "production",
2002
- method,
2003
- revalidateSeconds,
2004
- })
2005
- ) {
2006
- const __cachedRouteResponse = await __readAppRouteHandlerCacheResponse({
2007
- basePath: __basePath,
2008
- buildPageCacheTags: __pageCacheTags,
2009
- cleanPathname,
2010
- clearRequestContext: function() {
2011
- setHeadersContext(null);
2012
- setNavigationContext(null);
2013
- },
2014
- consumeDynamicUsage,
2015
- getCollectedFetchTags,
2016
- handlerFn,
2017
- i18n: __i18nConfig,
2018
- isAutoHead,
2019
- isrDebug: __isrDebug,
2020
- isrGet: __isrGet,
2021
- isrRouteKey: __isrRouteKey,
2022
- isrSet: __isrSet,
2023
- markDynamicUsage,
2024
- middlewareContext: _mwCtx,
2025
- params,
2026
- requestUrl: request.url,
2027
- revalidateSearchParams: url.searchParams,
2028
- revalidateSeconds,
2029
- routePattern: route.pattern,
2030
- runInRevalidationContext: async function(renderFn) {
2031
- const __revalHeadCtx = { headers: new Headers(), cookies: new Map() };
2032
- const __revalUCtx = _createUnifiedCtx({
2033
- headersContext: __revalHeadCtx,
2034
- executionContext: _getRequestExecutionContext(),
2035
- unstableCacheRevalidation: "foreground",
2036
- });
2037
- await _runWithUnifiedCtx(__revalUCtx, async () => {
2038
- _ensureFetchPatch();
2039
- setCurrentFetchSoftTags(__pageCacheTags(cleanPathname, []));
2040
- await renderFn();
2041
- });
2042
- },
2043
- scheduleBackgroundRegeneration: __triggerBackgroundRegeneration,
2044
- setNavigationContext,
2045
- });
2046
- if (__cachedRouteResponse) {
2047
- return __cachedRouteResponse;
2048
- }
2049
- }
2050
-
2051
- if (typeof handlerFn === "function") {
2052
- return __executeAppRouteHandler({
2053
- basePath: __basePath,
2054
- buildPageCacheTags: __pageCacheTags,
2055
- cleanPathname,
2056
- clearRequestContext: function() {
2057
- setHeadersContext(null);
2058
- setNavigationContext(null);
2059
- },
2060
- consumeDynamicUsage,
2061
- executionContext: _getRequestExecutionContext(),
2062
- getAndClearPendingCookies,
2063
- getCollectedFetchTags,
2064
- getDraftModeCookieHeader,
2065
- handler,
2066
- handlerFn,
2067
- i18n: __i18nConfig,
2068
- isAutoHead,
2069
- isProduction: process.env.NODE_ENV === "production",
2070
- isrDebug: __isrDebug,
2071
- isrRouteKey: __isrRouteKey,
2072
- isrSet: __isrSet,
2073
- markDynamicUsage,
2074
- method,
2075
- middlewareContext: _mwCtx,
2076
- middlewareRequestHeaders: _mwCtx.requestHeaders,
2077
- params: makeThenableParams(params),
2078
- reportRequestError: _reportRequestError,
2079
- request,
2080
- revalidateSeconds,
2081
- routePattern: route.pattern,
2082
- setHeadersAccessPhase,
2083
- });
2084
- }
2085
- setHeadersContext(null);
2086
- setNavigationContext(null);
2087
- return __applyRouteHandlerMiddlewareContext(
2088
- new Response(null, {
2089
- status: 405,
2090
- }),
2091
- _mwCtx,
2092
- );
2093
- }
2094
-
2095
- // Build the component tree: layouts wrapping the page
2096
- const hasPageModule = !!route.page;
2097
- const PageComponent = route.page?.default;
2098
- if (hasPageModule && !PageComponent) {
2099
- setHeadersContext(null);
2100
- setNavigationContext(null);
2101
- return new Response("Page has no default export", { status: 500 });
2102
- }
2103
-
2104
- // Read route segment config from page module exports
2105
- let revalidateSeconds = typeof route.page?.revalidate === "number" ? route.page.revalidate : null;
2106
- const dynamicConfig = route.page?.dynamic; // 'auto' | 'force-dynamic' | 'force-static' | 'error'
2107
- const dynamicParamsConfig = route.page?.dynamicParams; // true (default) | false
2108
- const isForceStatic = dynamicConfig === "force-static";
2109
- const isDynamicError = dynamicConfig === "error";
2110
-
2111
- // force-static: replace headers/cookies context with empty values and
2112
- // clear searchParams so dynamic APIs return defaults instead of real data
2113
- if (isForceStatic) {
2114
- setHeadersContext({ headers: new Headers(), cookies: new Map() });
2115
- setNavigationContext({
2116
- pathname: cleanPathname,
2117
- searchParams: new URLSearchParams(),
2118
- params,
2119
- });
2120
- }
2121
-
2122
- // dynamic = 'error': install an access error so request APIs fail with the
2123
- // static-generation message even for legacy sync property access.
2124
- if (isDynamicError) {
2125
- const errorMsg = 'Page with \`dynamic = "error"\` used a dynamic API. ' +
2126
- 'This page was expected to be fully static, but headers(), cookies(), ' +
2127
- 'or searchParams was accessed. Remove the dynamic API usage or change ' +
2128
- 'the dynamic config to "auto" or "force-dynamic".';
2129
- setHeadersContext({
2130
- headers: new Headers(),
2131
- cookies: new Map(),
2132
- accessError: new Error(errorMsg),
352
+ request,
353
+ route,
354
+ scriptNonce,
355
+ searchParams,
356
+ }) {
357
+ const PageComponent = route.page?.default;
358
+ const __segmentConfig = __resolveAppPageSegmentConfig({
359
+ layouts: route.layouts,
360
+ page: route.page,
2133
361
  });
2134
- setNavigationContext({
2135
- pathname: cleanPathname,
2136
- searchParams: new URLSearchParams(),
2137
- params,
362
+ const __generateStaticParams = __resolveAppPageGenerateStaticParamsSources({
363
+ layouts: route.layouts,
364
+ layoutTreePositions: route.layoutTreePositions,
365
+ page: route.page,
366
+ routeSegments: route.routeSegments,
2138
367
  });
2139
- }
2140
-
2141
- // force-dynamic: set no-store Cache-Control
2142
- const isForceDynamic = dynamicConfig === "force-dynamic";
2143
-
2144
- // ── ISR cache read (production only) ─────────────────────────────────────
2145
- // Read from cache BEFORE generateStaticParams and all rendering work.
2146
- // This is the critical performance optimization: on a cache hit we skip
2147
- // ALL expensive work (generateStaticParams, buildPageElement, layout probe,
2148
- // page probe, renderToReadableStream, SSR). Both HTML and RSC requests
2149
- // (client-side navigation / prefetch) are served from cache.
2150
- //
2151
- // HTML and RSC are stored under separate keys (matching Next.js's .html/.rsc
2152
- // file layout) so each request type reads and writes independently — no races,
2153
- // no partial-entry sentinels, no read-before-write hacks needed.
2154
- //
2155
- // force-static and dynamic='error' are compatible with ISR — they control
2156
- // how dynamic APIs behave during rendering, not whether results are cached.
2157
- // Only force-dynamic truly bypasses the ISR cache.
2158
- if (
2159
- process.env.NODE_ENV === "production" &&
2160
- !isForceDynamic &&
2161
- (isRscRequest || !_scriptNonce) &&
2162
- revalidateSeconds !== null && revalidateSeconds > 0 && revalidateSeconds !== Infinity
2163
- ) {
2164
- const __cachedPageResponse = await __readAppPageCacheResponse({
368
+ const _asyncRouteParams = makeThenableParams(params);
369
+ return __dispatchAppPage({
370
+ buildPageElement(targetRoute, targetParams, targetOpts, targetSearchParams) {
371
+ return buildPageElements(targetRoute, targetParams, cleanPathname, {
372
+ opts: targetOpts,
373
+ searchParams: targetSearchParams,
374
+ isRscRequest,
375
+ request,
376
+ mountedSlotsHeader,
377
+ });
378
+ },
2165
379
  cleanPathname,
2166
- clearRequestContext: function() {
2167
- setHeadersContext(null);
2168
- setNavigationContext(null);
380
+ clearRequestContext() {
381
+ __clearRequestContext();
2169
382
  },
383
+ createRscOnErrorHandler(pathname, routePath) {
384
+ return createRscOnErrorHandler(request, pathname, routePath);
385
+ },
386
+ debugClassification: __classDebug,
387
+ dynamicConfig: __segmentConfig.dynamicConfig,
388
+ dynamicParamsConfig: __segmentConfig.dynamicParamsConfig,
389
+ fetchCache: __segmentConfig.fetchCache ?? null,
390
+ findIntercept(pathname) {
391
+ return findIntercept(pathname, interceptionContext);
392
+ },
393
+ generateStaticParams: __generateStaticParams,
394
+ getFontLinks: _getSSRFontLinks,
395
+ getFontPreloads: _getSSRFontPreloads,
396
+ getFontStyles: _getSSRFontStyles,
397
+ getNavigationContext: _getNavigationContext,
398
+ getSourceRoute(sourceRouteIndex) {
399
+ return routes[sourceRouteIndex];
400
+ },
401
+ hasGenerateStaticParams: __generateStaticParams.length > 0,
402
+ hasPageDefaultExport: !!PageComponent,
403
+ hasPageModule: !!route.page,
404
+ handlerStart,
405
+ interceptionContext,
406
+ expireSeconds: __expireTime,
407
+ isProduction: process.env.NODE_ENV === "production",
2170
408
  isRscRequest,
2171
409
  isrDebug: __isrDebug,
2172
410
  isrGet: __isrGet,
2173
411
  isrHtmlKey: __isrHtmlKey,
2174
412
  isrRscKey: __isrRscKey,
2175
413
  isrSet: __isrSet,
2176
- mountedSlotsHeader: __mountedSlotsHeader,
2177
- revalidateSeconds,
2178
- renderFreshPageForCache: async function() {
2179
- // Re-render the page to produce fresh HTML + RSC data for the cache
2180
- // Use an empty headers context for background regeneration — not the original
2181
- // user request — to prevent user-specific cookies/auth headers from leaking
2182
- // into content that is cached and served to all subsequent users.
2183
- const __revalHeadCtx = { headers: new Headers(), cookies: new Map() };
2184
- const __revalUCtx = _createUnifiedCtx({
2185
- headersContext: __revalHeadCtx,
2186
- executionContext: _getRequestExecutionContext(),
2187
- unstableCacheRevalidation: "foreground",
2188
- });
2189
- return _runWithUnifiedCtx(__revalUCtx, async () => {
2190
- _ensureFetchPatch();
2191
- setCurrentFetchSoftTags(__pageCacheTags(cleanPathname, []));
2192
- setNavigationContext({ pathname: cleanPathname, searchParams: new URLSearchParams(), params });
2193
- // Slot context (X-Vinext-Mounted-Slots) is inherited from the
2194
- // triggering request so the regen result is cached under the
2195
- // correct slot-variant key.
2196
- const __revalElement = await buildPageElements(
2197
- route,
414
+ loadSsrHandler() {
415
+ return import.meta.viteRsc.loadModule("ssr", "index");
416
+ },
417
+ middlewareContext,
418
+ mountedSlotsHeader,
419
+ params,
420
+ probeLayoutAt(li) {
421
+ const LayoutComp = route.layouts[li]?.default;
422
+ if (!LayoutComp) return null;
423
+ return LayoutComp({
424
+ params: makeThenableParams(__resolveAppPageSegmentParams(
425
+ route.routeSegments,
426
+ route.layoutTreePositions?.[li] ?? 0,
2198
427
  params,
2199
- cleanPathname,
2200
- {
2201
- opts: undefined,
2202
- searchParams: new URLSearchParams(),
2203
- isRscRequest,
2204
- request,
2205
- mountedSlotsHeader: __mountedSlotsHeader,
2206
- },
2207
- );
2208
- const __revalOnError = createRscOnErrorHandler(request, cleanPathname, route.pattern);
2209
- const __revalRscStream = renderToReadableStream(__revalElement, { onError: __revalOnError });
2210
- const __revalRscCapture = __teeAppPageRscStreamForCapture(__revalRscStream, true);
2211
- const __revalFontData = { links: _getSSRFontLinks(), styles: _getSSRFontStyles(), preloads: _getSSRFontPreloads() };
2212
- const __revalSsrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
2213
- const __revalHtmlStream = await __revalSsrEntry.handleSsr(
2214
- __revalRscCapture.responseStream,
2215
- _getNavigationContext(),
2216
- __revalFontData,
2217
- );
2218
- setHeadersContext(null);
2219
- setNavigationContext(null);
2220
- const __freshHtml = await __readAppPageTextStream(__revalHtmlStream);
2221
- const __freshRscData = await __revalRscCapture.capturedRscDataPromise;
2222
- const __pageTags = __pageCacheTags(cleanPathname, getCollectedFetchTags());
2223
- return { html: __freshHtml, rscData: __freshRscData, tags: __pageTags };
428
+ )),
429
+ children: null,
2224
430
  });
2225
431
  },
2226
- scheduleBackgroundRegeneration: __triggerBackgroundRegeneration,
432
+ probePage() {
433
+ if (!PageComponent) return null;
434
+ const _asyncSearchParams = makeThenableParams(
435
+ __collectAppPageSearchParams(searchParams).searchParamsObject,
436
+ );
437
+ return PageComponent({ params: _asyncRouteParams, searchParams: _asyncSearchParams });
438
+ },
439
+ renderErrorBoundaryPage(renderErr) {
440
+ return __fallbackRenderer.renderErrorBoundary(route, renderErr, isRscRequest, request, params, scriptNonce, middlewareContext);
441
+ },
442
+ renderHttpAccessFallbackPage(statusCode, opts, currentMiddlewareContext) {
443
+ return __fallbackRenderer.renderHttpAccessFallback(route, statusCode, isRscRequest, request, opts, scriptNonce, currentMiddlewareContext);
444
+ },
445
+ renderToReadableStream,
446
+ request,
447
+ revalidateSeconds: __segmentConfig.revalidateSeconds,
448
+ resolveRouteFetchCacheMode(targetRoute) {
449
+ return __resolveRouteFetchCacheMode(targetRoute);
450
+ },
451
+ rootForbiddenModule,
452
+ rootNotFoundModule,
453
+ rootUnauthorizedModule,
454
+ route,
455
+ runWithSuppressedHookWarning(probe) {
456
+ return suppressHookWarningAls.run(true, probe);
457
+ },
458
+ scheduleBackgroundRegeneration(key, renderFn, errorContext) {
459
+ __triggerBackgroundRegeneration(key, renderFn, errorContext);
460
+ },
461
+ scriptNonce,
462
+ searchParams,
463
+ setNavigationContext,
2227
464
  });
2228
- if (__cachedPageResponse) {
2229
- return __cachedPageResponse;
2230
- }
2231
- }
2232
-
2233
- // dynamicParams = false: only params from generateStaticParams are allowed.
2234
- // This runs AFTER the ISR cache read so that a cache hit skips this work entirely.
2235
- const __dynamicParamsResponse = await __validateAppPageDynamicParams({
2236
- clearRequestContext() {
2237
- setHeadersContext(null);
2238
- setNavigationContext(null);
2239
- },
2240
- enforceStaticParamsOnly: dynamicParamsConfig === false,
2241
- generateStaticParams: route.page?.generateStaticParams,
2242
- isDynamicRoute: route.isDynamic,
2243
- logGenerateStaticParamsError(err) {
2244
- console.error("[vinext] generateStaticParams error:", err);
2245
- },
465
+ },
466
+ dispatchMatchedRouteHandler({
467
+ cleanPathname,
468
+ middlewareContext,
2246
469
  params,
2247
- });
2248
- if (__dynamicParamsResponse) {
2249
- return __dynamicParamsResponse;
2250
- }
2251
-
2252
- // Check for intercepting routes on RSC requests (client-side navigation).
2253
- // If the target URL matches an intercepting route in a parallel slot,
2254
- // render the source route with the intercepting page in the slot.
2255
- const __interceptResult = await __resolveAppPageIntercept({
2256
- buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams) {
2257
- return buildPageElements(
2258
- interceptRoute,
2259
- interceptParams,
2260
- cleanPathname,
2261
- {
2262
- opts: interceptOpts,
2263
- searchParams: interceptSearchParams,
2264
- isRscRequest,
2265
- request,
2266
- mountedSlotsHeader: __mountedSlotsHeader,
2267
- },
2268
- );
2269
- },
470
+ request,
471
+ route,
472
+ searchParams,
473
+ }) {
474
+ return __dispatchAppRouteHandler({
475
+ basePath: __basePath,
476
+ cleanPathname,
477
+ clearRequestContext() {
478
+ __clearRequestContext();
479
+ },
480
+ i18n: __i18nConfig,
481
+ isrDebug: __isrDebug,
482
+ isrGet: __isrGet,
483
+ isrRouteKey: __isrRouteKey,
484
+ isrSet: __isrSet,
485
+ middlewareContext,
486
+ middlewareRequestHeaders: middlewareContext.requestHeaders,
487
+ params,
488
+ request,
489
+ route: {
490
+ pattern: route.pattern,
491
+ routeHandler: route.routeHandler,
492
+ routeSegments: route.routeSegments,
493
+ },
494
+ scheduleBackgroundRegeneration: __triggerBackgroundRegeneration,
495
+ searchParams,
496
+ });
497
+ },
498
+ ${instrumentationPath ? `ensureInstrumentation() {
499
+ return __ensureInstrumentationRegistered(_instrumentation);
500
+ },` : ""}
501
+ handleProgressiveActionRequest({
502
+ actionId,
2270
503
  cleanPathname,
2271
- currentRoute: route,
2272
- findIntercept(pathname) {
2273
- return findIntercept(pathname, interceptionContextHeader);
2274
- },
2275
- getRouteParamNames(sourceRoute) {
2276
- return sourceRoute.params;
2277
- },
2278
- getSourceRoute(sourceRouteIndex) {
2279
- return routes[sourceRouteIndex];
2280
- },
2281
- isRscRequest,
2282
- renderInterceptResponse(sourceRoute, interceptElement) {
2283
- const interceptOnError = createRscOnErrorHandler(
2284
- request,
2285
- cleanPathname,
2286
- sourceRoute.pattern,
2287
- );
2288
- const interceptStream = renderToReadableStream(interceptElement, {
2289
- onError: interceptOnError,
2290
- });
2291
- // Do NOT clear headers/navigation context here — the RSC stream is consumed lazily
2292
- // by the client, and async server components that run during consumption need the
2293
- // context to still be live. The AsyncLocalStorage scope from runWithRequestContext
2294
- // handles cleanup naturally when all async continuations complete.
2295
- const interceptHeaders = new Headers({
2296
- "Content-Type": "text/x-component; charset=utf-8",
2297
- "Vary": "RSC, Accept",
2298
- });
2299
- __mergeMiddlewareResponseHeaders(interceptHeaders, _mwCtx.headers);
2300
- return new Response(interceptStream, {
2301
- status: _mwCtx.status ?? 200,
2302
- headers: interceptHeaders,
2303
- });
2304
- },
2305
- searchParams: url.searchParams,
2306
- setNavigationContext,
2307
- toInterceptOpts(intercept) {
2308
- return {
2309
- interceptionContext: interceptionContextHeader,
2310
- interceptLayouts: intercept.interceptLayouts,
2311
- interceptSlotKey: intercept.slotKey,
2312
- interceptPage: intercept.page,
2313
- interceptParams: intercept.matchedParams,
2314
- };
2315
- },
2316
- });
2317
- if (__interceptResult.response) {
2318
- return __interceptResult.response;
2319
- }
2320
- const interceptOpts = __interceptResult.interceptOpts;
2321
-
2322
- const __pageBuildResult = await __buildAppPageElement({
2323
- buildPageElement() {
2324
- return buildPageElements(route, params, cleanPathname, {
2325
- opts: interceptOpts,
2326
- searchParams: url.searchParams,
2327
- isRscRequest,
2328
- request,
2329
- mountedSlotsHeader: __mountedSlotsHeader,
2330
- });
2331
- },
2332
- renderErrorBoundaryPage(buildErr) {
2333
- return renderErrorBoundaryPage(route, buildErr, isRscRequest, request, params, _scriptNonce, _mwCtx);
2334
- },
2335
- renderSpecialError(__buildSpecialError) {
2336
- return __buildAppPageSpecialErrorResponse({
2337
- clearRequestContext() {
2338
- setHeadersContext(null);
2339
- setNavigationContext(null);
2340
- },
2341
- middlewareContext: _mwCtx,
2342
- renderFallbackPage(statusCode) {
2343
- return renderHTTPAccessFallbackPage(
2344
- route,
2345
- statusCode,
2346
- isRscRequest,
2347
- request,
2348
- {
2349
- matchedParams: params,
2350
- },
2351
- _scriptNonce,
2352
- // buildAppPageSpecialErrorResponse merges _mwCtx onto this returned
2353
- // fallback response; keep this inner boundary render unmerged so
2354
- // additive headers like Set-Cookie and Vary are not duplicated.
2355
- null,
2356
- );
2357
- },
2358
- requestUrl: request.url,
2359
- specialError: __buildSpecialError,
2360
- });
2361
- },
2362
- resolveSpecialError: __resolveAppPageSpecialError,
2363
- });
2364
- if (__pageBuildResult.response) {
2365
- return __pageBuildResult.response;
2366
- }
2367
- const element = __pageBuildResult.element;
2368
-
2369
- // Note: CSS is automatically injected by @vitejs/plugin-rsc's
2370
- // rscCssTransform — no manual loadCss() call needed.
2371
- const _hasLoadingBoundary = !!(route.loading && route.loading.default);
2372
- const _asyncLayoutParams = makeThenableParams(params);
2373
- return __renderAppPageLifecycle({
504
+ contentType,
505
+ middlewareContext,
506
+ request,
507
+ }) {
508
+ return __handleProgressiveServerActionRequest({
509
+ actionId,
510
+ allowedOrigins: __allowedOrigins,
511
+ cleanPathname,
512
+ clearRequestContext() {
513
+ __clearRequestContext();
514
+ },
515
+ contentType,
516
+ decodeAction,
517
+ getAndClearPendingCookies,
518
+ getDraftModeCookieHeader,
519
+ maxActionBodySize: __MAX_ACTION_BODY_SIZE,
520
+ middlewareHeaders: middlewareContext.headers,
521
+ readFormDataWithLimit: __readFormDataWithLimit,
522
+ reportRequestError: _reportRequestError,
523
+ request,
524
+ setHeadersAccessPhase,
525
+ });
526
+ },
527
+ handleServerActionRequest({
528
+ actionId,
2374
529
  cleanPathname,
2375
- clearRequestContext() {
2376
- setHeadersContext(null);
2377
- setNavigationContext(null);
2378
- },
2379
- consumeDynamicUsage,
2380
- createRscOnErrorHandler(pathname, routePath) {
2381
- return createRscOnErrorHandler(request, pathname, routePath);
2382
- },
2383
- element,
2384
- getDraftModeCookieHeader,
2385
- getFontLinks: _getSSRFontLinks,
2386
- getFontPreloads: _getSSRFontPreloads,
2387
- getFontStyles: _getSSRFontStyles,
2388
- getNavigationContext: _getNavigationContext,
2389
- getPageTags() {
2390
- return __pageCacheTags(cleanPathname, getCollectedFetchTags());
2391
- },
2392
- getRequestCacheLife() {
2393
- return _consumeRequestScopedCacheLife();
2394
- },
2395
- handlerStart: __reqStart,
2396
- hasLoadingBoundary: _hasLoadingBoundary,
2397
- isDynamicError,
2398
- isForceDynamic,
2399
- isForceStatic,
2400
- isProduction: process.env.NODE_ENV === "production",
530
+ contentType,
531
+ interceptionContext,
2401
532
  isRscRequest,
2402
- isrDebug: __isrDebug,
2403
- isrHtmlKey: __isrHtmlKey,
2404
- isrRscKey: __isrRscKey,
2405
- isrSet: __isrSet,
2406
- layoutCount: route.layouts?.length ?? 0,
2407
- loadSsrHandler() {
2408
- return import.meta.viteRsc.loadModule("ssr", "index");
2409
- },
2410
- middlewareContext: _mwCtx,
2411
- params,
2412
- probeLayoutAt(li) {
2413
- const LayoutComp = route.layouts[li]?.default;
2414
- if (!LayoutComp) return null;
2415
- return LayoutComp({ params: _asyncLayoutParams, children: null });
2416
- },
2417
- probePage() {
2418
- if (!PageComponent) return null;
2419
- const _probeSearchObj = {};
2420
- url.searchParams.forEach(function(v, k) {
2421
- if (k in _probeSearchObj) {
2422
- _probeSearchObj[k] = Array.isArray(_probeSearchObj[k])
2423
- ? _probeSearchObj[k].concat(v)
2424
- : [_probeSearchObj[k], v];
2425
- } else {
2426
- _probeSearchObj[k] = v;
2427
- }
2428
- });
2429
- const _asyncSearchParams = makeThenableParams(_probeSearchObj);
2430
- return PageComponent({ params: _asyncLayoutParams, searchParams: _asyncSearchParams });
2431
- },
2432
- classification: {
2433
- getLayoutId(index) {
2434
- const tp = route.layoutTreePositions?.[index] ?? 0;
2435
- return "layout:" + __createAppPageTreePath(route.routeSegments, tp);
533
+ middlewareContext,
534
+ mountedSlotsHeader,
535
+ request,
536
+ searchParams,
537
+ }) {
538
+ return __handleServerActionRscRequest({
539
+ actionId,
540
+ allowedOrigins: __allowedOrigins,
541
+ buildPageElement({
542
+ route: actionRoute,
543
+ params: actionParams,
544
+ cleanPathname: actionCleanPathname,
545
+ interceptOpts,
546
+ searchParams: actionSearchParams,
547
+ isRscRequest: actionIsRscRequest,
548
+ request: actionRequest,
549
+ mountedSlotsHeader: actionMountedSlotsHeader,
550
+ }) {
551
+ return buildPageElements(actionRoute, actionParams, actionCleanPathname, {
552
+ opts: interceptOpts,
553
+ searchParams: actionSearchParams,
554
+ isRscRequest: actionIsRscRequest,
555
+ request: actionRequest,
556
+ mountedSlotsHeader: actionMountedSlotsHeader,
557
+ });
2436
558
  },
2437
- buildTimeClassifications: route.__buildTimeClassifications,
2438
- buildTimeReasons: route.__buildTimeReasons,
2439
- debugClassification: __classDebug,
2440
- async runWithIsolatedDynamicScope(fn) {
2441
- const priorDynamic = consumeDynamicUsage();
2442
- try {
2443
- const result = await fn();
2444
- const dynamicDetected = consumeDynamicUsage();
2445
- return { result, dynamicDetected };
2446
- } finally {
2447
- consumeDynamicUsage();
2448
- if (priorDynamic) markDynamicUsage();
2449
- }
559
+ cleanPathname,
560
+ clearRequestContext() {
561
+ __clearRequestContext();
2450
562
  },
2451
- },
2452
- revalidateSeconds,
2453
- mountedSlotsHeader: __mountedSlotsHeader,
2454
- renderErrorBoundaryResponse(renderErr) {
2455
- return renderErrorBoundaryPage(route, renderErr, isRscRequest, request, params, _scriptNonce, _mwCtx);
2456
- },
2457
- async renderLayoutSpecialError(__layoutSpecialError, li) {
2458
- return __buildAppPageSpecialErrorResponse({
2459
- clearRequestContext() {
2460
- setHeadersContext(null);
2461
- setNavigationContext(null);
2462
- },
2463
- middlewareContext: _mwCtx,
2464
- renderFallbackPage(statusCode) {
2465
- // Find the not-found component from the parent level (the boundary that
2466
- // would catch this in Next.js). Walk up from the throwing layout to find
2467
- // the nearest not-found at a parent layout's directory.
2468
- let parentNotFound = null;
2469
- if (route.notFounds) {
2470
- for (let pi = li - 1; pi >= 0; pi--) {
2471
- if (route.notFounds[pi]?.default) {
2472
- parentNotFound = route.notFounds[pi].default;
2473
- break;
2474
- }
2475
- }
2476
- }
2477
- if (!parentNotFound) parentNotFound = ${rootNotFoundVar ? `${rootNotFoundVar}?.default` : "null"};
2478
- const parentLayouts = route.layouts.slice(0, li);
2479
- return renderHTTPAccessFallbackPage(
2480
- route,
2481
- statusCode,
2482
- isRscRequest,
2483
- request,
2484
- {
2485
- boundaryComponent: parentNotFound,
2486
- layouts: parentLayouts,
2487
- matchedParams: params,
2488
- },
2489
- _scriptNonce,
2490
- // buildAppPageSpecialErrorResponse merges _mwCtx onto this returned
2491
- // fallback response; keep this inner boundary render unmerged so
2492
- // additive headers like Set-Cookie and Vary are not duplicated.
2493
- null,
2494
- );
2495
- },
2496
- requestUrl: request.url,
2497
- specialError: __layoutSpecialError,
2498
- });
2499
- },
2500
- async renderPageSpecialError(specialError) {
2501
- return __buildAppPageSpecialErrorResponse({
2502
- clearRequestContext() {
2503
- setHeadersContext(null);
2504
- setNavigationContext(null);
2505
- },
2506
- middlewareContext: _mwCtx,
2507
- renderFallbackPage(statusCode) {
2508
- return renderHTTPAccessFallbackPage(
2509
- route,
2510
- statusCode,
2511
- isRscRequest,
2512
- request,
2513
- {
2514
- matchedParams: params,
2515
- },
2516
- _scriptNonce,
2517
- // buildAppPageSpecialErrorResponse merges _mwCtx onto this returned
2518
- // fallback response; keep this inner boundary render unmerged so
2519
- // additive headers like Set-Cookie and Vary are not duplicated.
2520
- null,
2521
- );
2522
- },
2523
- requestUrl: request.url,
2524
- specialError,
2525
- });
2526
- },
2527
- renderToReadableStream,
2528
- routeHasLocalBoundary: !!(route?.error?.default) || !!(route?.errors && route.errors.some(function(e) { return e?.default; })),
2529
- routePattern: route.pattern,
2530
- runWithSuppressedHookWarning(probe) {
2531
- // Run inside ALS context so the module-level console.error patch suppresses
2532
- // "Invalid hook call" only for this request's probe — concurrent requests
2533
- // each have their own ALS store and are unaffected.
2534
- return _suppressHookWarningAls.run(true, probe);
2535
- },
2536
- scriptNonce: _scriptNonce,
2537
- waitUntil(__cachePromise) {
2538
- _getRequestExecutionContext()?.waitUntil(__cachePromise);
2539
- },
2540
- });
2541
- }
563
+ contentType,
564
+ createNotFoundElement(actionRouteId) {
565
+ return {
566
+ [__APP_INTERCEPTION_CONTEXT_KEY]: null,
567
+ __route: actionRouteId,
568
+ __rootLayout: null,
569
+ [actionRouteId]: createElement("div", null, "Page not found"),
570
+ };
571
+ },
572
+ createPayloadRouteId(pathnameToRender, currentInterceptionContext) {
573
+ return __createAppPayloadRouteId(pathnameToRender, currentInterceptionContext);
574
+ },
575
+ createRscOnErrorHandler(actionRequest, actionPathname, routePattern) {
576
+ return createRscOnErrorHandler(actionRequest, actionPathname, routePattern);
577
+ },
578
+ createTemporaryReferenceSet,
579
+ decodeReply,
580
+ findIntercept(pathnameToMatch) {
581
+ return findIntercept(pathnameToMatch, interceptionContext);
582
+ },
583
+ getAndClearPendingCookies,
584
+ getDraftModeCookieHeader,
585
+ getRouteParamNames(sourceRoute) {
586
+ return sourceRoute.params;
587
+ },
588
+ getSourceRoute(sourceRouteIndex) {
589
+ return routes[sourceRouteIndex];
590
+ },
591
+ isRscRequest,
592
+ loadServerAction,
593
+ matchRoute(pathnameToMatch) {
594
+ return matchRoute(pathnameToMatch);
595
+ },
596
+ maxActionBodySize: __MAX_ACTION_BODY_SIZE,
597
+ middlewareHeaders: middlewareContext.headers,
598
+ middlewareStatus: middlewareContext.status,
599
+ mountedSlotsHeader,
600
+ readBodyWithLimit: __readBodyWithLimit,
601
+ readFormDataWithLimit: __readFormDataWithLimit,
602
+ renderToReadableStream,
603
+ reportRequestError: _reportRequestError,
604
+ request,
605
+ sanitizeErrorForClient(error) {
606
+ return __sanitizeErrorForClient(error);
607
+ },
608
+ searchParams,
609
+ setHeadersAccessPhase,
610
+ setNavigationContext,
611
+ toInterceptOpts(intercept) {
612
+ return {
613
+ interceptionContext,
614
+ interceptLayouts: intercept.interceptLayouts,
615
+ interceptSlotKey: intercept.slotKey,
616
+ interceptPage: intercept.page,
617
+ interceptParams: intercept.matchedParams,
618
+ };
619
+ },
620
+ });
621
+ },
622
+ i18nConfig: __i18nConfig,
623
+ isMiddlewareProxy: ${JSON.stringify(middlewarePath ? isProxyFile(middlewarePath) : false)},
624
+ ${hasPagesDir ? `loadPrerenderPagesRoutes: __loadPrerenderPagesRoutes,` : ""}
625
+ makeThenableParams,
626
+ matchRoute,
627
+ metadataRoutes,
628
+ middlewareModule: ${middlewarePath ? "middlewareModule" : "null"},
629
+ publicFiles: __publicFiles,
630
+ renderNotFound({ isRscRequest, matchedParams, middlewareContext, request, route, scriptNonce }) {
631
+ return __fallbackRenderer.renderNotFound(route, isRscRequest, request, matchedParams, scriptNonce, middlewareContext);
632
+ },
633
+ ${hasPagesDir ? `async renderPagesFallback({ isRscRequest, middlewareContext, request, url }) {
634
+ if (isRscRequest) return null;
635
+
636
+ const __pagesEntry = await import.meta.viteRsc.loadModule("ssr", "index");
637
+ if (typeof __pagesEntry.renderPage !== "function") return null;
638
+
639
+ const __pagesRequestHeaders = middlewareContext.requestHeaders
640
+ ? __buildRequestHeadersFromMiddlewareResponse(request.headers, middlewareContext.requestHeaders)
641
+ : null;
642
+ const __pagesRequest = __pagesRequestHeaders
643
+ ? new Request(request.url, { method: request.method, headers: __pagesRequestHeaders })
644
+ : request;
645
+ const __pagesRes = await __pagesEntry.renderPage(
646
+ __pagesRequest,
647
+ __decodePathParams(url.pathname) + (url.search || ""),
648
+ {},
649
+ undefined,
650
+ middlewareContext.requestHeaders,
651
+ );
652
+ return __pagesRes.status !== 404 ? __pagesRes : null;
653
+ },` : ""}
654
+ rootParamNamesByPattern: rootParamNamesMap,
655
+ setNavigationContext,
656
+ staticParamsMap: generateStaticParamsMap,
657
+ trailingSlash: __trailingSlash,
658
+ validateDevRequestOrigin: __validateDevRequestOrigin,
659
+ });
2542
660
 
2543
661
  if (import.meta.hot) {
2544
662
  import.meta.hot.accept();