@rangojs/router 0.0.0-experimental.15 → 0.0.0-experimental.150

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 (1284) hide show
  1. package/AGENTS.md +13 -0
  2. package/README.md +308 -451
  3. package/dist/__internal.d.ts +83 -0
  4. package/dist/__internal.d.ts.map +1 -0
  5. package/dist/__internal.js +19 -0
  6. package/dist/__internal.js.map +1 -0
  7. package/dist/__mocks__/version.d.ts +7 -0
  8. package/dist/__mocks__/version.d.ts.map +1 -0
  9. package/dist/__mocks__/version.js +7 -0
  10. package/dist/__mocks__/version.js.map +1 -0
  11. package/dist/__tests__/client-href.test.d.ts +2 -0
  12. package/dist/__tests__/client-href.test.d.ts.map +1 -0
  13. package/dist/__tests__/client-href.test.js +74 -0
  14. package/dist/__tests__/client-href.test.js.map +1 -0
  15. package/dist/__tests__/component-utils.test.d.ts +2 -0
  16. package/dist/__tests__/component-utils.test.d.ts.map +1 -0
  17. package/dist/__tests__/component-utils.test.js +51 -0
  18. package/dist/__tests__/component-utils.test.js.map +1 -0
  19. package/dist/__tests__/event-controller.test.d.ts +2 -0
  20. package/dist/__tests__/event-controller.test.d.ts.map +1 -0
  21. package/dist/__tests__/event-controller.test.js +538 -0
  22. package/dist/__tests__/event-controller.test.js.map +1 -0
  23. package/dist/__tests__/helpers/route-tree.d.ts +118 -0
  24. package/dist/__tests__/helpers/route-tree.d.ts.map +1 -0
  25. package/dist/__tests__/helpers/route-tree.js +374 -0
  26. package/dist/__tests__/helpers/route-tree.js.map +1 -0
  27. package/dist/__tests__/match-result.test.d.ts +2 -0
  28. package/dist/__tests__/match-result.test.d.ts.map +1 -0
  29. package/dist/__tests__/match-result.test.js +154 -0
  30. package/dist/__tests__/match-result.test.js.map +1 -0
  31. package/dist/__tests__/navigation-store.test.d.ts +2 -0
  32. package/dist/__tests__/navigation-store.test.d.ts.map +1 -0
  33. package/dist/__tests__/navigation-store.test.js +440 -0
  34. package/dist/__tests__/navigation-store.test.js.map +1 -0
  35. package/dist/__tests__/partial-update.test.d.ts +2 -0
  36. package/dist/__tests__/partial-update.test.d.ts.map +1 -0
  37. package/dist/__tests__/partial-update.test.js +1009 -0
  38. package/dist/__tests__/partial-update.test.js.map +1 -0
  39. package/dist/__tests__/reverse-types.test.d.ts +8 -0
  40. package/dist/__tests__/reverse-types.test.d.ts.map +1 -0
  41. package/dist/__tests__/reverse-types.test.js +656 -0
  42. package/dist/__tests__/reverse-types.test.js.map +1 -0
  43. package/dist/__tests__/route-definition.test.d.ts +2 -0
  44. package/dist/__tests__/route-definition.test.d.ts.map +1 -0
  45. package/dist/__tests__/route-definition.test.js +55 -0
  46. package/dist/__tests__/route-definition.test.js.map +1 -0
  47. package/dist/__tests__/router-helpers.test.d.ts +2 -0
  48. package/dist/__tests__/router-helpers.test.d.ts.map +1 -0
  49. package/dist/__tests__/router-helpers.test.js +377 -0
  50. package/dist/__tests__/router-helpers.test.js.map +1 -0
  51. package/dist/__tests__/router-integration-2.test.d.ts +2 -0
  52. package/dist/__tests__/router-integration-2.test.d.ts.map +1 -0
  53. package/dist/__tests__/router-integration-2.test.js +426 -0
  54. package/dist/__tests__/router-integration-2.test.js.map +1 -0
  55. package/dist/__tests__/router-integration.test.d.ts +2 -0
  56. package/dist/__tests__/router-integration.test.d.ts.map +1 -0
  57. package/dist/__tests__/router-integration.test.js +1051 -0
  58. package/dist/__tests__/router-integration.test.js.map +1 -0
  59. package/dist/__tests__/search-params.test.d.ts +5 -0
  60. package/dist/__tests__/search-params.test.d.ts.map +1 -0
  61. package/dist/__tests__/search-params.test.js +306 -0
  62. package/dist/__tests__/search-params.test.js.map +1 -0
  63. package/dist/__tests__/segment-system.test.d.ts +2 -0
  64. package/dist/__tests__/segment-system.test.d.ts.map +1 -0
  65. package/dist/__tests__/segment-system.test.js +627 -0
  66. package/dist/__tests__/segment-system.test.js.map +1 -0
  67. package/dist/__tests__/static-handler-types.test.d.ts +8 -0
  68. package/dist/__tests__/static-handler-types.test.d.ts.map +1 -0
  69. package/dist/__tests__/static-handler-types.test.js +63 -0
  70. package/dist/__tests__/static-handler-types.test.js.map +1 -0
  71. package/dist/__tests__/urls.test.d.ts +2 -0
  72. package/dist/__tests__/urls.test.d.ts.map +1 -0
  73. package/dist/__tests__/urls.test.js +421 -0
  74. package/dist/__tests__/urls.test.js.map +1 -0
  75. package/dist/__tests__/use-mount.test.d.ts +2 -0
  76. package/dist/__tests__/use-mount.test.d.ts.map +1 -0
  77. package/dist/__tests__/use-mount.test.js +35 -0
  78. package/dist/__tests__/use-mount.test.js.map +1 -0
  79. package/dist/bin/rango.d.ts +2 -0
  80. package/dist/bin/rango.d.ts.map +1 -0
  81. package/dist/bin/rango.js +1844 -237
  82. package/dist/bin/rango.js.map +1 -0
  83. package/dist/browser/event-controller.d.ts +191 -0
  84. package/dist/browser/event-controller.d.ts.map +1 -0
  85. package/dist/browser/event-controller.js +559 -0
  86. package/dist/browser/event-controller.js.map +1 -0
  87. package/dist/browser/index.d.ts +2 -0
  88. package/dist/browser/index.d.ts.map +1 -0
  89. package/dist/browser/index.js +14 -0
  90. package/dist/browser/index.js.map +1 -0
  91. package/dist/browser/link-interceptor.d.ts +38 -0
  92. package/dist/browser/link-interceptor.d.ts.map +1 -0
  93. package/dist/browser/link-interceptor.js +99 -0
  94. package/dist/browser/link-interceptor.js.map +1 -0
  95. package/dist/browser/logging.d.ts +10 -0
  96. package/dist/browser/logging.d.ts.map +1 -0
  97. package/dist/browser/logging.js +29 -0
  98. package/dist/browser/logging.js.map +1 -0
  99. package/dist/browser/lru-cache.d.ts +17 -0
  100. package/dist/browser/lru-cache.d.ts.map +1 -0
  101. package/dist/browser/lru-cache.js +50 -0
  102. package/dist/browser/lru-cache.js.map +1 -0
  103. package/dist/browser/merge-segment-loaders.d.ts +39 -0
  104. package/dist/browser/merge-segment-loaders.d.ts.map +1 -0
  105. package/dist/browser/merge-segment-loaders.js +102 -0
  106. package/dist/browser/merge-segment-loaders.js.map +1 -0
  107. package/dist/browser/navigation-bridge.d.ts +102 -0
  108. package/dist/browser/navigation-bridge.d.ts.map +1 -0
  109. package/dist/browser/navigation-bridge.js +708 -0
  110. package/dist/browser/navigation-bridge.js.map +1 -0
  111. package/dist/browser/navigation-client.d.ts +25 -0
  112. package/dist/browser/navigation-client.d.ts.map +1 -0
  113. package/dist/browser/navigation-client.js +157 -0
  114. package/dist/browser/navigation-client.js.map +1 -0
  115. package/dist/browser/navigation-store.d.ts +101 -0
  116. package/dist/browser/navigation-store.d.ts.map +1 -0
  117. package/dist/browser/navigation-store.js +625 -0
  118. package/dist/browser/navigation-store.js.map +1 -0
  119. package/dist/browser/partial-update.d.ts +75 -0
  120. package/dist/browser/partial-update.d.ts.map +1 -0
  121. package/dist/browser/partial-update.js +426 -0
  122. package/dist/browser/partial-update.js.map +1 -0
  123. package/dist/browser/react/Link.d.ts +86 -0
  124. package/dist/browser/react/Link.d.ts.map +1 -0
  125. package/dist/browser/react/Link.js +128 -0
  126. package/dist/browser/react/Link.js.map +1 -0
  127. package/dist/browser/react/NavigationProvider.d.ts +63 -0
  128. package/dist/browser/react/NavigationProvider.d.ts.map +1 -0
  129. package/dist/browser/react/NavigationProvider.js +216 -0
  130. package/dist/browser/react/NavigationProvider.js.map +1 -0
  131. package/dist/browser/react/ScrollRestoration.d.ts +75 -0
  132. package/dist/browser/react/ScrollRestoration.d.ts.map +1 -0
  133. package/dist/browser/react/ScrollRestoration.js +57 -0
  134. package/dist/browser/react/ScrollRestoration.js.map +1 -0
  135. package/dist/browser/react/context.d.ts +46 -0
  136. package/dist/browser/react/context.d.ts.map +1 -0
  137. package/dist/browser/react/context.js +10 -0
  138. package/dist/browser/react/context.js.map +1 -0
  139. package/dist/browser/react/index.d.ts +11 -0
  140. package/dist/browser/react/index.d.ts.map +1 -0
  141. package/dist/browser/react/index.js +22 -0
  142. package/dist/browser/react/index.js.map +1 -0
  143. package/dist/browser/react/location-state-shared.d.ts +63 -0
  144. package/dist/browser/react/location-state-shared.d.ts.map +1 -0
  145. package/dist/browser/react/location-state-shared.js +81 -0
  146. package/dist/browser/react/location-state-shared.js.map +1 -0
  147. package/dist/browser/react/location-state.d.ts +23 -0
  148. package/dist/browser/react/location-state.d.ts.map +1 -0
  149. package/dist/browser/react/location-state.js +29 -0
  150. package/dist/browser/react/location-state.js.map +1 -0
  151. package/dist/browser/react/mount-context.d.ts +24 -0
  152. package/dist/browser/react/mount-context.d.ts.map +1 -0
  153. package/dist/browser/react/mount-context.js +24 -0
  154. package/dist/browser/react/mount-context.js.map +1 -0
  155. package/dist/browser/react/use-action.d.ts +64 -0
  156. package/dist/browser/react/use-action.d.ts.map +1 -0
  157. package/dist/browser/react/use-action.js +134 -0
  158. package/dist/browser/react/use-action.js.map +1 -0
  159. package/dist/browser/react/use-client-cache.d.ts +41 -0
  160. package/dist/browser/react/use-client-cache.d.ts.map +1 -0
  161. package/{src/browser/react/use-client-cache.ts → dist/browser/react/use-client-cache.js} +9 -26
  162. package/dist/browser/react/use-client-cache.js.map +1 -0
  163. package/dist/browser/react/use-handle.d.ts +31 -0
  164. package/dist/browser/react/use-handle.d.ts.map +1 -0
  165. package/dist/browser/react/use-handle.js +144 -0
  166. package/dist/browser/react/use-handle.js.map +1 -0
  167. package/dist/browser/react/use-href.d.ts +33 -0
  168. package/dist/browser/react/use-href.d.ts.map +1 -0
  169. package/dist/browser/react/use-href.js +39 -0
  170. package/dist/browser/react/use-href.js.map +1 -0
  171. package/dist/browser/react/use-link-status.d.ts +37 -0
  172. package/dist/browser/react/use-link-status.d.ts.map +1 -0
  173. package/dist/browser/react/use-link-status.js +99 -0
  174. package/dist/browser/react/use-link-status.js.map +1 -0
  175. package/dist/browser/react/use-mount.d.ts +25 -0
  176. package/dist/browser/react/use-mount.d.ts.map +1 -0
  177. package/dist/browser/react/use-mount.js +30 -0
  178. package/dist/browser/react/use-mount.js.map +1 -0
  179. package/dist/browser/react/use-navigation.d.ts +27 -0
  180. package/dist/browser/react/use-navigation.d.ts.map +1 -0
  181. package/dist/browser/react/use-navigation.js +87 -0
  182. package/dist/browser/react/use-navigation.js.map +1 -0
  183. package/dist/browser/react/use-segments.d.ts +38 -0
  184. package/dist/browser/react/use-segments.d.ts.map +1 -0
  185. package/dist/browser/react/use-segments.js +130 -0
  186. package/dist/browser/react/use-segments.js.map +1 -0
  187. package/dist/browser/request-controller.d.ts +26 -0
  188. package/dist/browser/request-controller.d.ts.map +1 -0
  189. package/dist/browser/request-controller.js +147 -0
  190. package/dist/browser/request-controller.js.map +1 -0
  191. package/dist/browser/rsc-router.d.ts +129 -0
  192. package/dist/browser/rsc-router.d.ts.map +1 -0
  193. package/dist/browser/rsc-router.js +195 -0
  194. package/dist/browser/rsc-router.js.map +1 -0
  195. package/dist/browser/scroll-restoration.d.ts +93 -0
  196. package/dist/browser/scroll-restoration.d.ts.map +1 -0
  197. package/dist/browser/scroll-restoration.js +321 -0
  198. package/dist/browser/scroll-restoration.js.map +1 -0
  199. package/dist/browser/segment-structure-assert.d.ts +17 -0
  200. package/dist/browser/segment-structure-assert.d.ts.map +1 -0
  201. package/dist/browser/segment-structure-assert.js +59 -0
  202. package/dist/browser/segment-structure-assert.js.map +1 -0
  203. package/dist/browser/server-action-bridge.d.ts +26 -0
  204. package/dist/browser/server-action-bridge.d.ts.map +1 -0
  205. package/dist/browser/server-action-bridge.js +668 -0
  206. package/dist/browser/server-action-bridge.js.map +1 -0
  207. package/dist/browser/shallow.d.ts +12 -0
  208. package/dist/browser/shallow.d.ts.map +1 -0
  209. package/dist/browser/shallow.js +34 -0
  210. package/dist/browser/shallow.js.map +1 -0
  211. package/dist/browser/types.d.ts +369 -0
  212. package/dist/browser/types.d.ts.map +1 -0
  213. package/dist/browser/types.js +2 -0
  214. package/dist/browser/types.js.map +1 -0
  215. package/dist/build/__tests__/generate-cli.test.d.ts +2 -0
  216. package/dist/build/__tests__/generate-cli.test.d.ts.map +1 -0
  217. package/dist/build/__tests__/generate-cli.test.js +237 -0
  218. package/dist/build/__tests__/generate-cli.test.js.map +1 -0
  219. package/dist/build/__tests__/generate-manifest.test.d.ts +2 -0
  220. package/dist/build/__tests__/generate-manifest.test.d.ts.map +1 -0
  221. package/dist/build/__tests__/generate-manifest.test.js +119 -0
  222. package/dist/build/__tests__/generate-manifest.test.js.map +1 -0
  223. package/dist/build/__tests__/generate-route-types.test.d.ts +2 -0
  224. package/dist/build/__tests__/generate-route-types.test.d.ts.map +1 -0
  225. package/dist/build/__tests__/generate-route-types.test.js +620 -0
  226. package/dist/build/__tests__/generate-route-types.test.js.map +1 -0
  227. package/dist/build/__tests__/per-router-manifest.test.d.ts +2 -0
  228. package/dist/build/__tests__/per-router-manifest.test.d.ts.map +1 -0
  229. package/dist/build/__tests__/per-router-manifest.test.js +308 -0
  230. package/dist/build/__tests__/per-router-manifest.test.js.map +1 -0
  231. package/dist/build/generate-manifest.d.ts +81 -0
  232. package/dist/build/generate-manifest.d.ts.map +1 -0
  233. package/dist/build/generate-manifest.js +276 -0
  234. package/dist/build/generate-manifest.js.map +1 -0
  235. package/dist/build/generate-route-types.d.ts +115 -0
  236. package/dist/build/generate-route-types.d.ts.map +1 -0
  237. package/dist/build/generate-route-types.js +740 -0
  238. package/dist/build/generate-route-types.js.map +1 -0
  239. package/dist/build/index.d.ts +21 -0
  240. package/dist/build/index.d.ts.map +1 -0
  241. package/dist/build/index.js +21 -0
  242. package/dist/build/index.js.map +1 -0
  243. package/dist/build/route-trie.d.ts +71 -0
  244. package/dist/build/route-trie.d.ts.map +1 -0
  245. package/dist/build/route-trie.js +175 -0
  246. package/dist/build/route-trie.js.map +1 -0
  247. package/dist/cache/__tests__/cache-scope.test.d.ts +2 -0
  248. package/dist/cache/__tests__/cache-scope.test.d.ts.map +1 -0
  249. package/dist/cache/__tests__/cache-scope.test.js +208 -0
  250. package/dist/cache/__tests__/cache-scope.test.js.map +1 -0
  251. package/dist/cache/__tests__/document-cache.test.d.ts +2 -0
  252. package/dist/cache/__tests__/document-cache.test.d.ts.map +1 -0
  253. package/dist/cache/__tests__/document-cache.test.js +345 -0
  254. package/dist/cache/__tests__/document-cache.test.js.map +1 -0
  255. package/dist/cache/__tests__/memory-segment-store.test.d.ts +2 -0
  256. package/dist/cache/__tests__/memory-segment-store.test.d.ts.map +1 -0
  257. package/dist/cache/__tests__/memory-segment-store.test.js +425 -0
  258. package/dist/cache/__tests__/memory-segment-store.test.js.map +1 -0
  259. package/dist/cache/__tests__/memory-store.test.d.ts +2 -0
  260. package/dist/cache/__tests__/memory-store.test.d.ts.map +1 -0
  261. package/dist/cache/__tests__/memory-store.test.js +367 -0
  262. package/dist/cache/__tests__/memory-store.test.js.map +1 -0
  263. package/dist/cache/cache-scope.d.ts +102 -0
  264. package/dist/cache/cache-scope.d.ts.map +1 -0
  265. package/dist/cache/cache-scope.js +440 -0
  266. package/dist/cache/cache-scope.js.map +1 -0
  267. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts +2 -0
  268. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts.map +1 -0
  269. package/dist/cache/cf/__tests__/cf-cache-store.test.js +330 -0
  270. package/dist/cache/cf/__tests__/cf-cache-store.test.js.map +1 -0
  271. package/dist/cache/cf/cf-cache-store.d.ts +165 -0
  272. package/dist/cache/cf/cf-cache-store.d.ts.map +1 -0
  273. package/dist/cache/cf/cf-cache-store.js +242 -0
  274. package/dist/cache/cf/cf-cache-store.js.map +1 -0
  275. package/dist/cache/cf/index.d.ts +14 -0
  276. package/dist/cache/cf/index.d.ts.map +1 -0
  277. package/dist/cache/cf/index.js +17 -0
  278. package/dist/cache/cf/index.js.map +1 -0
  279. package/dist/cache/document-cache.d.ts +64 -0
  280. package/dist/cache/document-cache.d.ts.map +1 -0
  281. package/dist/cache/document-cache.js +228 -0
  282. package/dist/cache/document-cache.js.map +1 -0
  283. package/dist/cache/index.d.ts +19 -0
  284. package/dist/cache/index.d.ts.map +1 -0
  285. package/dist/cache/index.js +21 -0
  286. package/dist/cache/index.js.map +1 -0
  287. package/dist/cache/memory-segment-store.d.ts +110 -0
  288. package/dist/cache/memory-segment-store.d.ts.map +1 -0
  289. package/dist/cache/memory-segment-store.js +117 -0
  290. package/dist/cache/memory-segment-store.js.map +1 -0
  291. package/dist/cache/memory-store.d.ts +41 -0
  292. package/dist/cache/memory-store.d.ts.map +1 -0
  293. package/dist/cache/memory-store.js +191 -0
  294. package/dist/cache/memory-store.js.map +1 -0
  295. package/dist/cache/types.d.ts +317 -0
  296. package/dist/cache/types.d.ts.map +1 -0
  297. package/dist/cache/types.js +12 -0
  298. package/dist/cache/types.js.map +1 -0
  299. package/dist/client.d.ts +248 -0
  300. package/dist/client.d.ts.map +1 -0
  301. package/dist/client.js +367 -0
  302. package/dist/client.js.map +1 -0
  303. package/dist/client.rsc.d.ts +26 -0
  304. package/dist/client.rsc.d.ts.map +1 -0
  305. package/dist/client.rsc.js +46 -0
  306. package/dist/client.rsc.js.map +1 -0
  307. package/dist/component-utils.d.ts +36 -0
  308. package/dist/component-utils.d.ts.map +1 -0
  309. package/dist/component-utils.js +61 -0
  310. package/dist/component-utils.js.map +1 -0
  311. package/dist/components/DefaultDocument.d.ts +13 -0
  312. package/dist/components/DefaultDocument.d.ts.map +1 -0
  313. package/dist/components/DefaultDocument.js +15 -0
  314. package/dist/components/DefaultDocument.js.map +1 -0
  315. package/dist/debug.d.ts +58 -0
  316. package/dist/debug.d.ts.map +1 -0
  317. package/dist/debug.js +157 -0
  318. package/dist/debug.js.map +1 -0
  319. package/dist/default-error-boundary.d.ts +11 -0
  320. package/dist/default-error-boundary.d.ts.map +1 -0
  321. package/dist/default-error-boundary.js +45 -0
  322. package/dist/default-error-boundary.js.map +1 -0
  323. package/dist/deps/browser.d.ts +2 -0
  324. package/dist/deps/browser.d.ts.map +1 -0
  325. package/dist/deps/browser.js +3 -0
  326. package/dist/deps/browser.js.map +1 -0
  327. package/dist/deps/html-stream-client.d.ts +2 -0
  328. package/dist/deps/html-stream-client.d.ts.map +1 -0
  329. package/dist/deps/html-stream-client.js +3 -0
  330. package/dist/deps/html-stream-client.js.map +1 -0
  331. package/dist/deps/html-stream-server.d.ts +2 -0
  332. package/dist/deps/html-stream-server.d.ts.map +1 -0
  333. package/dist/deps/html-stream-server.js +3 -0
  334. package/dist/deps/html-stream-server.js.map +1 -0
  335. package/dist/deps/rsc.d.ts +2 -0
  336. package/dist/deps/rsc.d.ts.map +1 -0
  337. package/dist/deps/rsc.js +4 -0
  338. package/dist/deps/rsc.js.map +1 -0
  339. package/dist/deps/ssr.d.ts +2 -0
  340. package/dist/deps/ssr.d.ts.map +1 -0
  341. package/dist/deps/ssr.js +3 -0
  342. package/dist/deps/ssr.js.map +1 -0
  343. package/dist/errors.d.ts +174 -0
  344. package/dist/errors.d.ts.map +1 -0
  345. package/dist/errors.js +241 -0
  346. package/dist/errors.js.map +1 -0
  347. package/dist/handle.d.ts +78 -0
  348. package/dist/handle.d.ts.map +1 -0
  349. package/dist/handle.js +82 -0
  350. package/dist/handle.js.map +1 -0
  351. package/dist/handles/MetaTags.d.ts +14 -0
  352. package/dist/handles/MetaTags.d.ts.map +1 -0
  353. package/dist/handles/MetaTags.js +136 -0
  354. package/dist/handles/MetaTags.js.map +1 -0
  355. package/dist/handles/index.d.ts +6 -0
  356. package/dist/handles/index.d.ts.map +1 -0
  357. package/{src/handles/index.ts → dist/handles/index.js} +1 -1
  358. package/dist/handles/index.js.map +1 -0
  359. package/dist/handles/meta.d.ts +39 -0
  360. package/dist/handles/meta.d.ts.map +1 -0
  361. package/dist/handles/meta.js +202 -0
  362. package/dist/handles/meta.js.map +1 -0
  363. package/dist/host/__tests__/errors.test.d.ts +2 -0
  364. package/dist/host/__tests__/errors.test.d.ts.map +1 -0
  365. package/dist/host/__tests__/errors.test.js +76 -0
  366. package/dist/host/__tests__/errors.test.js.map +1 -0
  367. package/dist/host/__tests__/pattern-comprehensive.test.d.ts +2 -0
  368. package/dist/host/__tests__/pattern-comprehensive.test.d.ts.map +1 -0
  369. package/dist/host/__tests__/pattern-comprehensive.test.js +732 -0
  370. package/dist/host/__tests__/pattern-comprehensive.test.js.map +1 -0
  371. package/dist/host/__tests__/pattern-matcher.test.d.ts +2 -0
  372. package/dist/host/__tests__/pattern-matcher.test.d.ts.map +1 -0
  373. package/dist/host/__tests__/pattern-matcher.test.js +251 -0
  374. package/dist/host/__tests__/pattern-matcher.test.js.map +1 -0
  375. package/dist/host/__tests__/router.test.d.ts +2 -0
  376. package/dist/host/__tests__/router.test.d.ts.map +1 -0
  377. package/dist/host/__tests__/router.test.js +241 -0
  378. package/dist/host/__tests__/router.test.js.map +1 -0
  379. package/dist/host/__tests__/testing.test.d.ts +2 -0
  380. package/dist/host/__tests__/testing.test.d.ts.map +1 -0
  381. package/dist/host/__tests__/testing.test.js +64 -0
  382. package/dist/host/__tests__/testing.test.js.map +1 -0
  383. package/dist/host/__tests__/utils.test.d.ts +2 -0
  384. package/dist/host/__tests__/utils.test.d.ts.map +1 -0
  385. package/dist/host/__tests__/utils.test.js +29 -0
  386. package/dist/host/__tests__/utils.test.js.map +1 -0
  387. package/dist/host/cookie-handler.d.ts +34 -0
  388. package/dist/host/cookie-handler.d.ts.map +1 -0
  389. package/dist/host/cookie-handler.js +124 -0
  390. package/dist/host/cookie-handler.js.map +1 -0
  391. package/dist/host/errors.d.ts +56 -0
  392. package/dist/host/errors.d.ts.map +1 -0
  393. package/dist/host/errors.js +79 -0
  394. package/dist/host/errors.js.map +1 -0
  395. package/dist/host/index.d.ts +29 -0
  396. package/dist/host/index.d.ts.map +1 -0
  397. package/dist/host/index.js +32 -0
  398. package/dist/host/index.js.map +1 -0
  399. package/dist/host/pattern-matcher.d.ts +36 -0
  400. package/dist/host/pattern-matcher.d.ts.map +1 -0
  401. package/dist/host/pattern-matcher.js +172 -0
  402. package/dist/host/pattern-matcher.js.map +1 -0
  403. package/dist/host/router.d.ts +26 -0
  404. package/dist/host/router.d.ts.map +1 -0
  405. package/dist/host/router.js +218 -0
  406. package/dist/host/router.js.map +1 -0
  407. package/dist/host/testing.d.ts +36 -0
  408. package/dist/host/testing.d.ts.map +1 -0
  409. package/dist/host/testing.js +55 -0
  410. package/dist/host/testing.js.map +1 -0
  411. package/dist/host/types.d.ts +115 -0
  412. package/dist/host/types.d.ts.map +1 -0
  413. package/dist/host/types.js +7 -0
  414. package/dist/host/types.js.map +1 -0
  415. package/dist/host/utils.d.ts +21 -0
  416. package/dist/host/utils.d.ts.map +1 -0
  417. package/dist/host/utils.js +23 -0
  418. package/dist/host/utils.js.map +1 -0
  419. package/dist/href-client.d.ts +131 -0
  420. package/dist/href-client.d.ts.map +1 -0
  421. package/dist/href-client.js +64 -0
  422. package/dist/href-client.js.map +1 -0
  423. package/{src/href-context.ts → dist/href-context.d.ts} +7 -11
  424. package/dist/href-context.d.ts.map +1 -0
  425. package/dist/href-context.js +21 -0
  426. package/dist/href-context.js.map +1 -0
  427. package/dist/index.d.ts +73 -0
  428. package/dist/index.d.ts.map +1 -0
  429. package/dist/index.js +91 -0
  430. package/dist/index.js.map +1 -0
  431. package/dist/index.rsc.d.ts +32 -0
  432. package/dist/index.rsc.d.ts.map +1 -0
  433. package/dist/index.rsc.js +40 -0
  434. package/dist/index.rsc.js.map +1 -0
  435. package/dist/internal-debug.d.ts +2 -0
  436. package/dist/internal-debug.d.ts.map +1 -0
  437. package/dist/internal-debug.js +5 -0
  438. package/dist/internal-debug.js.map +1 -0
  439. package/dist/loader.d.ts +14 -0
  440. package/dist/loader.d.ts.map +1 -0
  441. package/dist/loader.js +20 -0
  442. package/dist/loader.js.map +1 -0
  443. package/dist/loader.rsc.d.ts +19 -0
  444. package/dist/loader.rsc.d.ts.map +1 -0
  445. package/dist/loader.rsc.js +99 -0
  446. package/dist/loader.rsc.js.map +1 -0
  447. package/{src/network-error-thrower.tsx → dist/network-error-thrower.d.ts} +4 -8
  448. package/dist/network-error-thrower.d.ts.map +1 -0
  449. package/dist/network-error-thrower.js +14 -0
  450. package/dist/network-error-thrower.js.map +1 -0
  451. package/dist/outlet-context.d.ts +13 -0
  452. package/dist/outlet-context.d.ts.map +1 -0
  453. package/dist/outlet-context.js +3 -0
  454. package/dist/outlet-context.js.map +1 -0
  455. package/dist/prerender/__tests__/param-hash.test.d.ts +2 -0
  456. package/dist/prerender/__tests__/param-hash.test.d.ts.map +1 -0
  457. package/dist/prerender/__tests__/param-hash.test.js +148 -0
  458. package/dist/prerender/__tests__/param-hash.test.js.map +1 -0
  459. package/dist/prerender/param-hash.d.ts +16 -0
  460. package/dist/prerender/param-hash.d.ts.map +1 -0
  461. package/dist/prerender/param-hash.js +36 -0
  462. package/dist/prerender/param-hash.js.map +1 -0
  463. package/dist/prerender/store.d.ts +38 -0
  464. package/dist/prerender/store.d.ts.map +1 -0
  465. package/dist/prerender/store.js +61 -0
  466. package/dist/prerender/store.js.map +1 -0
  467. package/dist/prerender.d.ts +66 -0
  468. package/dist/prerender.d.ts.map +1 -0
  469. package/dist/prerender.js +57 -0
  470. package/dist/prerender.js.map +1 -0
  471. package/dist/reverse.d.ts +196 -0
  472. package/dist/reverse.d.ts.map +1 -0
  473. package/dist/reverse.js +78 -0
  474. package/dist/reverse.js.map +1 -0
  475. package/dist/root-error-boundary.d.ts +33 -0
  476. package/dist/root-error-boundary.d.ts.map +1 -0
  477. package/dist/root-error-boundary.js +165 -0
  478. package/dist/root-error-boundary.js.map +1 -0
  479. package/dist/route-content-wrapper.d.ts +46 -0
  480. package/dist/route-content-wrapper.d.ts.map +1 -0
  481. package/dist/route-content-wrapper.js +77 -0
  482. package/dist/route-content-wrapper.js.map +1 -0
  483. package/dist/route-definition.d.ts +421 -0
  484. package/dist/route-definition.d.ts.map +1 -0
  485. package/dist/route-definition.js +868 -0
  486. package/dist/route-definition.js.map +1 -0
  487. package/dist/route-map-builder.d.ts +155 -0
  488. package/dist/route-map-builder.d.ts.map +1 -0
  489. package/dist/route-map-builder.js +237 -0
  490. package/dist/route-map-builder.js.map +1 -0
  491. package/dist/route-types.d.ts +165 -0
  492. package/dist/route-types.d.ts.map +1 -0
  493. package/dist/route-types.js +7 -0
  494. package/dist/route-types.js.map +1 -0
  495. package/dist/router/__tests__/handler-context.test.d.ts +2 -0
  496. package/dist/router/__tests__/handler-context.test.d.ts.map +1 -0
  497. package/dist/router/__tests__/handler-context.test.js +65 -0
  498. package/dist/router/__tests__/handler-context.test.js.map +1 -0
  499. package/dist/router/__tests__/loader-cycle-detection.test.d.ts +2 -0
  500. package/dist/router/__tests__/loader-cycle-detection.test.d.ts.map +1 -0
  501. package/dist/router/__tests__/loader-cycle-detection.test.js +221 -0
  502. package/dist/router/__tests__/loader-cycle-detection.test.js.map +1 -0
  503. package/dist/router/__tests__/match-context.test.d.ts +2 -0
  504. package/dist/router/__tests__/match-context.test.d.ts.map +1 -0
  505. package/dist/router/__tests__/match-context.test.js +92 -0
  506. package/dist/router/__tests__/match-context.test.js.map +1 -0
  507. package/dist/router/__tests__/match-pipelines.test.d.ts +2 -0
  508. package/dist/router/__tests__/match-pipelines.test.d.ts.map +1 -0
  509. package/dist/router/__tests__/match-pipelines.test.js +417 -0
  510. package/dist/router/__tests__/match-pipelines.test.js.map +1 -0
  511. package/dist/router/__tests__/match-result.test.d.ts +2 -0
  512. package/dist/router/__tests__/match-result.test.d.ts.map +1 -0
  513. package/dist/router/__tests__/match-result.test.js +457 -0
  514. package/dist/router/__tests__/match-result.test.js.map +1 -0
  515. package/dist/router/__tests__/on-error.test.d.ts +2 -0
  516. package/dist/router/__tests__/on-error.test.d.ts.map +1 -0
  517. package/dist/router/__tests__/on-error.test.js +678 -0
  518. package/dist/router/__tests__/on-error.test.js.map +1 -0
  519. package/dist/router/__tests__/pattern-matching.test.d.ts +2 -0
  520. package/dist/router/__tests__/pattern-matching.test.d.ts.map +1 -0
  521. package/dist/router/__tests__/pattern-matching.test.js +629 -0
  522. package/dist/router/__tests__/pattern-matching.test.js.map +1 -0
  523. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts +2 -0
  524. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts.map +1 -0
  525. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js +155 -0
  526. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js.map +1 -0
  527. package/dist/router/error-handling.d.ts +77 -0
  528. package/dist/router/error-handling.d.ts.map +1 -0
  529. package/dist/router/error-handling.js +202 -0
  530. package/dist/router/error-handling.js.map +1 -0
  531. package/dist/router/handler-context.d.ts +20 -0
  532. package/dist/router/handler-context.d.ts.map +1 -0
  533. package/dist/router/handler-context.js +198 -0
  534. package/dist/router/handler-context.js.map +1 -0
  535. package/dist/router/intercept-resolution.d.ts +66 -0
  536. package/dist/router/intercept-resolution.d.ts.map +1 -0
  537. package/dist/router/intercept-resolution.js +246 -0
  538. package/dist/router/intercept-resolution.js.map +1 -0
  539. package/dist/router/loader-resolution.d.ts +64 -0
  540. package/dist/router/loader-resolution.d.ts.map +1 -0
  541. package/dist/router/loader-resolution.js +284 -0
  542. package/dist/router/loader-resolution.js.map +1 -0
  543. package/dist/router/logging.d.ts +15 -0
  544. package/dist/router/logging.d.ts.map +1 -0
  545. package/dist/router/logging.js +99 -0
  546. package/dist/router/logging.js.map +1 -0
  547. package/dist/router/manifest.d.ts +22 -0
  548. package/dist/router/manifest.d.ts.map +1 -0
  549. package/dist/router/manifest.js +181 -0
  550. package/dist/router/manifest.js.map +1 -0
  551. package/dist/router/match-api.d.ts +35 -0
  552. package/dist/router/match-api.d.ts.map +1 -0
  553. package/dist/router/match-api.js +406 -0
  554. package/dist/router/match-api.js.map +1 -0
  555. package/dist/router/match-context.d.ts +206 -0
  556. package/dist/router/match-context.d.ts.map +1 -0
  557. package/dist/router/match-context.js +17 -0
  558. package/dist/router/match-context.js.map +1 -0
  559. package/dist/router/match-middleware/background-revalidation.d.ts +127 -0
  560. package/dist/router/match-middleware/background-revalidation.d.ts.map +1 -0
  561. package/dist/router/match-middleware/background-revalidation.js +75 -0
  562. package/dist/router/match-middleware/background-revalidation.js.map +1 -0
  563. package/dist/router/match-middleware/cache-lookup.d.ts +112 -0
  564. package/dist/router/match-middleware/cache-lookup.d.ts.map +1 -0
  565. package/dist/router/match-middleware/cache-lookup.js +257 -0
  566. package/dist/router/match-middleware/cache-lookup.js.map +1 -0
  567. package/dist/router/match-middleware/cache-store.d.ts +113 -0
  568. package/dist/router/match-middleware/cache-store.d.ts.map +1 -0
  569. package/dist/router/match-middleware/cache-store.js +108 -0
  570. package/dist/router/match-middleware/cache-store.js.map +1 -0
  571. package/dist/router/match-middleware/index.d.ts +81 -0
  572. package/dist/router/match-middleware/index.d.ts.map +1 -0
  573. package/dist/router/match-middleware/index.js +80 -0
  574. package/dist/router/match-middleware/index.js.map +1 -0
  575. package/dist/router/match-middleware/intercept-resolution.d.ts +117 -0
  576. package/dist/router/match-middleware/intercept-resolution.d.ts.map +1 -0
  577. package/dist/router/match-middleware/intercept-resolution.js +134 -0
  578. package/dist/router/match-middleware/intercept-resolution.js.map +1 -0
  579. package/dist/router/match-middleware/segment-resolution.d.ts +99 -0
  580. package/dist/router/match-middleware/segment-resolution.d.ts.map +1 -0
  581. package/dist/router/match-middleware/segment-resolution.js +53 -0
  582. package/dist/router/match-middleware/segment-resolution.js.map +1 -0
  583. package/dist/router/match-pipelines.d.ts +147 -0
  584. package/dist/router/match-pipelines.d.ts.map +1 -0
  585. package/dist/router/match-pipelines.js +82 -0
  586. package/dist/router/match-pipelines.js.map +1 -0
  587. package/dist/router/match-result.d.ts +126 -0
  588. package/dist/router/match-result.d.ts.map +1 -0
  589. package/dist/router/match-result.js +93 -0
  590. package/dist/router/match-result.js.map +1 -0
  591. package/dist/router/metrics.d.ts +20 -0
  592. package/dist/router/metrics.d.ts.map +1 -0
  593. package/dist/router/metrics.js +47 -0
  594. package/dist/router/metrics.js.map +1 -0
  595. package/dist/router/middleware.d.ts +249 -0
  596. package/dist/router/middleware.d.ts.map +1 -0
  597. package/dist/router/middleware.js +434 -0
  598. package/dist/router/middleware.js.map +1 -0
  599. package/dist/router/middleware.test.d.ts +2 -0
  600. package/dist/router/middleware.test.d.ts.map +1 -0
  601. package/dist/router/middleware.test.js +816 -0
  602. package/dist/router/middleware.test.js.map +1 -0
  603. package/dist/router/pattern-matching.d.ts +149 -0
  604. package/dist/router/pattern-matching.d.ts.map +1 -0
  605. package/dist/router/pattern-matching.js +349 -0
  606. package/dist/router/pattern-matching.js.map +1 -0
  607. package/dist/router/revalidation.d.ts +44 -0
  608. package/dist/router/revalidation.d.ts.map +1 -0
  609. package/dist/router/revalidation.js +147 -0
  610. package/dist/router/revalidation.js.map +1 -0
  611. package/dist/router/router-context.d.ts +135 -0
  612. package/dist/router/router-context.d.ts.map +1 -0
  613. package/dist/router/router-context.js +36 -0
  614. package/dist/router/router-context.js.map +1 -0
  615. package/dist/router/segment-resolution.d.ts +127 -0
  616. package/dist/router/segment-resolution.d.ts.map +1 -0
  617. package/dist/router/segment-resolution.js +919 -0
  618. package/dist/router/segment-resolution.js.map +1 -0
  619. package/dist/router/trie-matching.d.ts +40 -0
  620. package/dist/router/trie-matching.d.ts.map +1 -0
  621. package/dist/router/trie-matching.js +127 -0
  622. package/dist/router/trie-matching.js.map +1 -0
  623. package/dist/router/types.d.ts +136 -0
  624. package/dist/router/types.d.ts.map +1 -0
  625. package/dist/router/types.js +7 -0
  626. package/dist/router/types.js.map +1 -0
  627. package/dist/router.d.ts +753 -0
  628. package/dist/router.d.ts.map +1 -0
  629. package/dist/router.gen.d.ts +6 -0
  630. package/dist/router.gen.d.ts.map +1 -0
  631. package/dist/router.gen.js +6 -0
  632. package/dist/router.gen.js.map +1 -0
  633. package/dist/router.js +1304 -0
  634. package/dist/router.js.map +1 -0
  635. package/dist/rsc/__tests__/helpers.test.d.ts +2 -0
  636. package/dist/rsc/__tests__/helpers.test.d.ts.map +1 -0
  637. package/dist/rsc/__tests__/helpers.test.js +140 -0
  638. package/dist/rsc/__tests__/helpers.test.js.map +1 -0
  639. package/dist/rsc/handler.d.ts +45 -0
  640. package/dist/rsc/handler.d.ts.map +1 -0
  641. package/dist/rsc/handler.js +1172 -0
  642. package/dist/rsc/handler.js.map +1 -0
  643. package/dist/rsc/helpers.d.ts +16 -0
  644. package/dist/rsc/helpers.d.ts.map +1 -0
  645. package/dist/rsc/helpers.js +55 -0
  646. package/dist/rsc/helpers.js.map +1 -0
  647. package/dist/rsc/index.d.ts +22 -0
  648. package/dist/rsc/index.d.ts.map +1 -0
  649. package/dist/rsc/index.js +23 -0
  650. package/dist/rsc/index.js.map +1 -0
  651. package/dist/rsc/nonce.d.ts +9 -0
  652. package/dist/rsc/nonce.d.ts.map +1 -0
  653. package/dist/rsc/nonce.js +18 -0
  654. package/dist/rsc/nonce.js.map +1 -0
  655. package/dist/rsc/types.d.ts +206 -0
  656. package/dist/rsc/types.d.ts.map +1 -0
  657. package/dist/rsc/types.js +8 -0
  658. package/dist/rsc/types.js.map +1 -0
  659. package/dist/search-params.d.ts +103 -0
  660. package/dist/search-params.d.ts.map +1 -0
  661. package/dist/search-params.js +74 -0
  662. package/dist/search-params.js.map +1 -0
  663. package/dist/segment-system.d.ts +75 -0
  664. package/dist/segment-system.d.ts.map +1 -0
  665. package/dist/segment-system.js +336 -0
  666. package/dist/segment-system.js.map +1 -0
  667. package/dist/server/context.d.ts +245 -0
  668. package/dist/server/context.d.ts.map +1 -0
  669. package/dist/server/context.js +197 -0
  670. package/dist/server/context.js.map +1 -0
  671. package/dist/server/fetchable-loader-store.d.ts +18 -0
  672. package/dist/server/fetchable-loader-store.d.ts.map +1 -0
  673. package/dist/server/fetchable-loader-store.js +18 -0
  674. package/dist/server/fetchable-loader-store.js.map +1 -0
  675. package/dist/server/handle-store.d.ts +85 -0
  676. package/dist/server/handle-store.d.ts.map +1 -0
  677. package/dist/server/handle-store.js +142 -0
  678. package/dist/server/handle-store.js.map +1 -0
  679. package/dist/server/loader-registry.d.ts +55 -0
  680. package/dist/server/loader-registry.d.ts.map +1 -0
  681. package/dist/server/loader-registry.js +132 -0
  682. package/dist/server/loader-registry.js.map +1 -0
  683. package/dist/server/request-context.d.ts +226 -0
  684. package/dist/server/request-context.d.ts.map +1 -0
  685. package/dist/server/request-context.js +290 -0
  686. package/dist/server/request-context.js.map +1 -0
  687. package/dist/server/root-layout.d.ts +4 -0
  688. package/dist/server/root-layout.d.ts.map +1 -0
  689. package/dist/server/root-layout.js +5 -0
  690. package/dist/server/root-layout.js.map +1 -0
  691. package/dist/server.d.ts +15 -0
  692. package/dist/server.d.ts.map +1 -0
  693. package/dist/server.js +20 -0
  694. package/dist/server.js.map +1 -0
  695. package/dist/ssr/__tests__/ssr-handler.test.d.ts +2 -0
  696. package/dist/ssr/__tests__/ssr-handler.test.d.ts.map +1 -0
  697. package/dist/ssr/__tests__/ssr-handler.test.js +132 -0
  698. package/dist/ssr/__tests__/ssr-handler.test.js.map +1 -0
  699. package/dist/ssr/index.d.ts +98 -0
  700. package/dist/ssr/index.d.ts.map +1 -0
  701. package/dist/ssr/index.js +158 -0
  702. package/dist/ssr/index.js.map +1 -0
  703. package/dist/static-handler.d.ts +50 -0
  704. package/dist/static-handler.d.ts.map +1 -0
  705. package/dist/static-handler.gen.d.ts +5 -0
  706. package/dist/static-handler.gen.d.ts.map +1 -0
  707. package/dist/static-handler.gen.js +5 -0
  708. package/dist/static-handler.gen.js.map +1 -0
  709. package/dist/static-handler.js +29 -0
  710. package/dist/static-handler.js.map +1 -0
  711. package/dist/testing/vitest.js +82 -0
  712. package/dist/theme/ThemeProvider.d.ts +20 -0
  713. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  714. package/dist/theme/ThemeProvider.js +240 -0
  715. package/dist/theme/ThemeProvider.js.map +1 -0
  716. package/dist/theme/ThemeScript.d.ts +48 -0
  717. package/dist/theme/ThemeScript.d.ts.map +1 -0
  718. package/dist/theme/ThemeScript.js +13 -0
  719. package/dist/theme/ThemeScript.js.map +1 -0
  720. package/dist/theme/__tests__/theme.test.d.ts +2 -0
  721. package/dist/theme/__tests__/theme.test.d.ts.map +1 -0
  722. package/dist/theme/__tests__/theme.test.js +103 -0
  723. package/dist/theme/__tests__/theme.test.js.map +1 -0
  724. package/dist/theme/constants.d.ts +29 -0
  725. package/dist/theme/constants.d.ts.map +1 -0
  726. package/dist/theme/constants.js +48 -0
  727. package/dist/theme/constants.js.map +1 -0
  728. package/dist/theme/index.d.ts +31 -0
  729. package/dist/theme/index.d.ts.map +1 -0
  730. package/dist/theme/index.js +36 -0
  731. package/dist/theme/index.js.map +1 -0
  732. package/dist/theme/theme-context.d.ts +40 -0
  733. package/dist/theme/theme-context.d.ts.map +1 -0
  734. package/dist/theme/theme-context.js +60 -0
  735. package/dist/theme/theme-context.js.map +1 -0
  736. package/dist/theme/theme-script.d.ts +27 -0
  737. package/dist/theme/theme-script.d.ts.map +1 -0
  738. package/dist/theme/theme-script.js +147 -0
  739. package/dist/theme/theme-script.js.map +1 -0
  740. package/dist/theme/types.d.ts +163 -0
  741. package/dist/theme/types.d.ts.map +1 -0
  742. package/dist/theme/types.js +11 -0
  743. package/dist/theme/types.js.map +1 -0
  744. package/dist/theme/use-theme.d.ts +12 -0
  745. package/dist/theme/use-theme.d.ts.map +1 -0
  746. package/dist/theme/use-theme.js +40 -0
  747. package/dist/theme/use-theme.js.map +1 -0
  748. package/dist/types.d.ts +1479 -0
  749. package/dist/types.d.ts.map +1 -0
  750. package/dist/types.js +10 -0
  751. package/dist/types.js.map +1 -0
  752. package/dist/urls.d.ts +441 -0
  753. package/dist/urls.d.ts.map +1 -0
  754. package/dist/urls.gen.d.ts +8 -0
  755. package/dist/urls.gen.d.ts.map +1 -0
  756. package/dist/urls.gen.js +8 -0
  757. package/dist/urls.gen.js.map +1 -0
  758. package/dist/urls.js +443 -0
  759. package/dist/urls.js.map +1 -0
  760. package/dist/use-loader.d.ts +127 -0
  761. package/dist/use-loader.d.ts.map +1 -0
  762. package/dist/use-loader.js +237 -0
  763. package/dist/use-loader.js.map +1 -0
  764. package/dist/vite/__tests__/ast-handler-extract.test.d.ts +2 -0
  765. package/dist/vite/__tests__/ast-handler-extract.test.d.ts.map +1 -0
  766. package/dist/vite/__tests__/ast-handler-extract.test.js +294 -0
  767. package/dist/vite/__tests__/ast-handler-extract.test.js.map +1 -0
  768. package/dist/vite/__tests__/expose-id-utils.test.d.ts +2 -0
  769. package/dist/vite/__tests__/expose-id-utils.test.d.ts.map +1 -0
  770. package/dist/vite/__tests__/expose-id-utils.test.js +224 -0
  771. package/dist/vite/__tests__/expose-id-utils.test.js.map +1 -0
  772. package/dist/vite/__tests__/expose-internal-ids.test.d.ts +2 -0
  773. package/dist/vite/__tests__/expose-internal-ids.test.d.ts.map +1 -0
  774. package/dist/vite/__tests__/expose-internal-ids.test.js +647 -0
  775. package/dist/vite/__tests__/expose-internal-ids.test.js.map +1 -0
  776. package/dist/vite/__tests__/expose-router-id.test.d.ts +2 -0
  777. package/dist/vite/__tests__/expose-router-id.test.d.ts.map +1 -0
  778. package/dist/vite/__tests__/expose-router-id.test.js +39 -0
  779. package/dist/vite/__tests__/expose-router-id.test.js.map +1 -0
  780. package/dist/vite/ast-handler-extract.d.ts +49 -0
  781. package/dist/vite/ast-handler-extract.d.ts.map +1 -0
  782. package/dist/vite/ast-handler-extract.js +249 -0
  783. package/dist/vite/ast-handler-extract.js.map +1 -0
  784. package/dist/vite/expose-action-id.d.ts +19 -0
  785. package/dist/vite/expose-action-id.d.ts.map +1 -0
  786. package/dist/vite/expose-action-id.js +250 -0
  787. package/dist/vite/expose-action-id.js.map +1 -0
  788. package/dist/vite/expose-id-utils.d.ts +69 -0
  789. package/dist/vite/expose-id-utils.d.ts.map +1 -0
  790. package/dist/vite/expose-id-utils.js +289 -0
  791. package/dist/vite/expose-id-utils.js.map +1 -0
  792. package/dist/vite/expose-internal-ids.d.ts +22 -0
  793. package/dist/vite/expose-internal-ids.d.ts.map +1 -0
  794. package/dist/vite/expose-internal-ids.js +886 -0
  795. package/dist/vite/expose-internal-ids.js.map +1 -0
  796. package/dist/vite/index.d.ts +149 -0
  797. package/dist/vite/index.d.ts.map +1 -0
  798. package/dist/vite/index.js +7471 -2057
  799. package/dist/vite/index.js.bak +5448 -0
  800. package/dist/vite/index.js.map +1 -0
  801. package/dist/vite/package-resolution.d.ts +43 -0
  802. package/dist/vite/package-resolution.d.ts.map +1 -0
  803. package/{src/vite/package-resolution.ts → dist/vite/package-resolution.js} +53 -66
  804. package/dist/vite/package-resolution.js.map +1 -0
  805. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  806. package/dist/vite/virtual-entries.d.ts +25 -0
  807. package/dist/vite/virtual-entries.d.ts.map +1 -0
  808. package/{src/vite/virtual-entries.ts → dist/vite/virtual-entries.js} +12 -16
  809. package/dist/vite/virtual-entries.js.map +1 -0
  810. package/package.json +142 -61
  811. package/skills/api-client/SKILL.md +211 -0
  812. package/skills/breadcrumbs/SKILL.md +329 -0
  813. package/skills/bundle-analysis/SKILL.md +159 -0
  814. package/skills/cache-guide/SKILL.md +489 -0
  815. package/skills/caching/SKILL.md +413 -26
  816. package/skills/catalog.json +271 -0
  817. package/skills/comparison/SKILL.md +50 -0
  818. package/skills/comparison/agents/openai.yaml +4 -0
  819. package/skills/comparison/references/framework-comparison.md +837 -0
  820. package/skills/composability/SKILL.md +278 -0
  821. package/skills/css/SKILL.md +76 -0
  822. package/skills/debug-manifest/SKILL.md +17 -11
  823. package/skills/defer-hydration/SKILL.md +235 -0
  824. package/skills/document-cache/SKILL.md +96 -63
  825. package/skills/fonts/SKILL.md +7 -5
  826. package/skills/handler-use/SKILL.md +364 -0
  827. package/skills/hooks/SKILL.md +76 -436
  828. package/skills/hooks/data.md +273 -0
  829. package/skills/hooks/handle-and-actions.md +103 -0
  830. package/skills/hooks/navigation.md +110 -0
  831. package/skills/hooks/outlets.md +41 -0
  832. package/skills/hooks/state.md +228 -0
  833. package/skills/hooks/urls.md +135 -0
  834. package/skills/host-router/SKILL.md +320 -0
  835. package/skills/i18n/SKILL.md +276 -0
  836. package/skills/intercept/SKILL.md +215 -16
  837. package/skills/layout/SKILL.md +147 -7
  838. package/skills/links/SKILL.md +304 -25
  839. package/skills/loader/SKILL.md +629 -55
  840. package/skills/middleware/SKILL.md +218 -38
  841. package/skills/migrate-nextjs/SKILL.md +745 -0
  842. package/skills/migrate-react-router/SKILL.md +153 -0
  843. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  844. package/skills/migrate-react-router/component-migration.md +196 -0
  845. package/skills/migrate-react-router/data-and-actions.md +225 -0
  846. package/skills/migrate-react-router/route-mapping.md +271 -0
  847. package/skills/mime-routes/SKILL.md +60 -21
  848. package/skills/observability/SKILL.md +202 -0
  849. package/skills/parallel/SKILL.md +296 -4
  850. package/skills/ppr/SKILL.md +643 -0
  851. package/skills/prerender/SKILL.md +444 -52
  852. package/skills/rango/SKILL.md +324 -29
  853. package/skills/react-compiler/SKILL.md +168 -0
  854. package/skills/response-routes/SKILL.md +264 -122
  855. package/skills/route/SKILL.md +359 -22
  856. package/skills/router-setup/SKILL.md +247 -34
  857. package/skills/scripts/SKILL.md +179 -0
  858. package/skills/server-actions/SKILL.md +776 -0
  859. package/skills/shell-manifest/SKILL.md +185 -0
  860. package/skills/streams-and-websockets/SKILL.md +283 -0
  861. package/skills/tailwind/SKILL.md +28 -4
  862. package/skills/testing/SKILL.md +126 -222
  863. package/skills/testing/bindings.md +103 -0
  864. package/skills/testing/cache-prerender.md +127 -0
  865. package/skills/testing/client-components.md +124 -0
  866. package/skills/testing/e2e-parity.md +125 -0
  867. package/skills/testing/flight.md +91 -0
  868. package/skills/testing/handles.md +131 -0
  869. package/skills/testing/loader.md +128 -0
  870. package/skills/testing/middleware.md +99 -0
  871. package/skills/testing/render-handler.md +122 -0
  872. package/skills/testing/response-routes.md +95 -0
  873. package/skills/testing/reverse-and-types.md +85 -0
  874. package/skills/testing/server-actions.md +107 -0
  875. package/skills/testing/server-tree.md +128 -0
  876. package/skills/testing/setup.md +123 -0
  877. package/skills/theme/SKILL.md +10 -9
  878. package/skills/typesafety/SKILL.md +45 -496
  879. package/skills/typesafety/env-and-bindings.md +254 -0
  880. package/skills/typesafety/generated-files-and-cli.md +335 -0
  881. package/skills/typesafety/params-and-search.md +153 -0
  882. package/skills/typesafety/route-types.md +209 -0
  883. package/skills/use-cache/SKILL.md +383 -0
  884. package/skills/vercel/SKILL.md +128 -0
  885. package/skills/view-transitions/SKILL.md +337 -0
  886. package/src/__augment-tests__/augment.ts +81 -0
  887. package/src/__augment-tests__/augmented.check.ts +116 -0
  888. package/src/__internal.ts +77 -44
  889. package/src/bin/rango.ts +275 -29
  890. package/src/browser/action-coordinator.ts +114 -0
  891. package/src/browser/action-fence.ts +47 -0
  892. package/src/browser/app-shell.ts +39 -0
  893. package/src/browser/app-version.ts +14 -0
  894. package/src/browser/connection-warmup.ts +134 -0
  895. package/src/browser/cookie-name.ts +140 -0
  896. package/src/browser/event-controller.ts +348 -212
  897. package/src/browser/history-state.ts +101 -0
  898. package/src/browser/index.ts +3 -3
  899. package/src/browser/intercept-utils.ts +52 -0
  900. package/src/browser/invalidate-client-cache.ts +52 -0
  901. package/src/browser/link-interceptor.ts +24 -4
  902. package/src/browser/logging.ts +39 -0
  903. package/src/browser/merge-segment-loaders.ts +23 -13
  904. package/src/browser/navigation-bridge.ts +385 -576
  905. package/src/browser/navigation-client.ts +249 -75
  906. package/src/browser/navigation-store-handle.ts +38 -0
  907. package/src/browser/navigation-store.ts +230 -124
  908. package/src/browser/navigation-transaction.ts +247 -0
  909. package/src/browser/network-error-handler.ts +88 -0
  910. package/src/browser/partial-update.ts +432 -365
  911. package/src/browser/prefetch/cache.ts +381 -0
  912. package/src/browser/prefetch/fetch.ts +462 -0
  913. package/src/browser/prefetch/observer.ts +65 -0
  914. package/src/browser/prefetch/policy.ts +48 -0
  915. package/src/browser/prefetch/queue.ts +209 -0
  916. package/src/browser/prefetch/resource-ready.ts +77 -0
  917. package/src/browser/rango-state.ts +194 -0
  918. package/src/browser/react/Link.tsx +284 -67
  919. package/src/browser/react/NavigationProvider.tsx +267 -109
  920. package/src/browser/react/ScrollRestoration.tsx +10 -6
  921. package/src/browser/react/context.ts +11 -0
  922. package/src/browser/react/filter-segment-order.ts +70 -0
  923. package/src/browser/react/index.ts +0 -48
  924. package/src/browser/react/location-state-shared.ts +272 -60
  925. package/src/browser/react/location-state.ts +90 -20
  926. package/src/browser/react/mount-context.ts +6 -1
  927. package/src/browser/react/nonce-context.ts +23 -0
  928. package/src/browser/react/shallow-equal.ts +27 -0
  929. package/src/browser/react/use-action.ts +35 -66
  930. package/src/browser/react/use-handle.ts +39 -126
  931. package/src/browser/react/use-href.tsx +8 -1
  932. package/src/browser/react/use-link-status.ts +39 -13
  933. package/src/browser/react/use-navigation.ts +53 -69
  934. package/src/browser/react/use-params.ts +75 -0
  935. package/src/browser/react/use-pathname.ts +47 -0
  936. package/src/browser/react/use-reverse.ts +106 -0
  937. package/src/browser/react/use-router.ts +98 -0
  938. package/src/browser/react/use-search-params.ts +51 -0
  939. package/src/browser/react/use-segments.ts +72 -99
  940. package/src/browser/response-adapter.ts +164 -0
  941. package/src/browser/rsc-router.tsx +355 -73
  942. package/src/browser/scroll-restoration.ts +140 -50
  943. package/src/browser/segment-reconciler.ts +253 -0
  944. package/src/browser/segment-structure-assert.ts +17 -1
  945. package/src/browser/server-action-bridge.ts +668 -613
  946. package/src/browser/types.ts +235 -51
  947. package/src/browser/validate-redirect-origin.ts +56 -0
  948. package/src/build/collect-fallback-refs.ts +107 -0
  949. package/src/build/generate-manifest.ts +252 -161
  950. package/src/build/generate-route-types.ts +41 -848
  951. package/src/build/index.ts +12 -7
  952. package/src/build/prefix-tree-utils.ts +123 -0
  953. package/src/build/route-trie.ts +225 -42
  954. package/src/build/route-types/ast-helpers.ts +25 -0
  955. package/src/build/route-types/ast-route-extraction.ts +105 -0
  956. package/src/build/route-types/codegen.ts +113 -0
  957. package/src/build/route-types/include-resolution.ts +812 -0
  958. package/src/build/route-types/param-extraction.ts +51 -0
  959. package/src/build/route-types/per-module-writer.ts +144 -0
  960. package/src/build/route-types/router-processing.ts +695 -0
  961. package/src/build/route-types/scan-filter.ts +85 -0
  962. package/src/build/route-types/source-scan.ts +216 -0
  963. package/src/build/runtime-discovery.ts +223 -0
  964. package/src/cache/background-task.ts +34 -0
  965. package/src/cache/cache-error.ts +104 -0
  966. package/src/cache/cache-key-utils.ts +89 -0
  967. package/src/cache/cache-policy.ts +199 -0
  968. package/src/cache/cache-runtime.ts +695 -0
  969. package/src/cache/cache-scope.ts +324 -335
  970. package/src/cache/cache-tag.ts +149 -0
  971. package/src/cache/cf/cf-base64.ts +33 -0
  972. package/src/cache/cf/cf-cache-constants.ts +127 -0
  973. package/src/cache/cf/cf-cache-store.ts +2602 -156
  974. package/src/cache/cf/cf-cache-types.ts +349 -0
  975. package/src/cache/cf/cf-kv-utils.ts +46 -0
  976. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  977. package/src/cache/cf/index.ts +17 -17
  978. package/src/cache/document-cache.ts +226 -102
  979. package/src/cache/handle-capture.ts +81 -0
  980. package/src/cache/handle-snapshot.ts +132 -0
  981. package/src/cache/index.ts +24 -35
  982. package/src/cache/memory-segment-store.ts +446 -30
  983. package/src/cache/profile-registry.ts +88 -0
  984. package/src/cache/read-through-swr.ts +178 -0
  985. package/src/cache/segment-codec.ts +295 -0
  986. package/src/cache/shell-snapshot.ts +464 -0
  987. package/src/cache/tag-invalidation.ts +230 -0
  988. package/src/cache/taint.ts +153 -0
  989. package/src/cache/types.ts +283 -211
  990. package/src/cache/vercel/index.ts +11 -0
  991. package/src/cache/vercel/vercel-cache-store.ts +1201 -0
  992. package/src/client.rsc.tsx +43 -21
  993. package/src/client.tsx +131 -347
  994. package/src/cloudflare/index.ts +11 -0
  995. package/src/cloudflare/tracing.ts +108 -0
  996. package/src/component-utils.ts +23 -4
  997. package/src/components/DefaultDocument.tsx +13 -3
  998. package/src/context-var.ts +168 -0
  999. package/src/debug.ts +19 -9
  1000. package/src/decode-loader-results.ts +52 -0
  1001. package/src/defer.ts +185 -0
  1002. package/src/deps/ssr.ts +4 -2
  1003. package/src/encode-kv.ts +49 -0
  1004. package/src/errors.ts +106 -10
  1005. package/src/escape-script.ts +52 -0
  1006. package/src/handle.ts +110 -35
  1007. package/src/handles/MetaTags.tsx +83 -59
  1008. package/src/handles/Scripts.tsx +183 -0
  1009. package/src/handles/breadcrumbs.ts +93 -0
  1010. package/src/handles/deferred-resolution.ts +127 -0
  1011. package/src/handles/is-thenable.ts +18 -0
  1012. package/src/handles/meta.ts +44 -53
  1013. package/src/handles/script.ts +244 -0
  1014. package/src/host/cookie-handler.ts +20 -65
  1015. package/src/host/errors.ts +21 -30
  1016. package/src/host/index.ts +13 -9
  1017. package/src/host/pattern-matcher.ts +50 -79
  1018. package/src/host/router.ts +151 -121
  1019. package/src/host/testing.ts +45 -32
  1020. package/src/host/types.ts +52 -11
  1021. package/src/host/utils.ts +2 -2
  1022. package/src/href-client.ts +192 -57
  1023. package/src/index.rsc.ts +180 -35
  1024. package/src/index.ts +245 -73
  1025. package/src/internal-debug.ts +9 -2
  1026. package/src/loader-store.ts +500 -0
  1027. package/src/loader.rsc.ts +31 -99
  1028. package/src/loader.ts +30 -12
  1029. package/src/missing-id-error.ts +68 -0
  1030. package/src/outlet-context.ts +1 -1
  1031. package/src/outlet-provider.tsx +41 -0
  1032. package/src/prerender/build-shell-capture.ts +253 -0
  1033. package/src/prerender/param-hash.ts +16 -14
  1034. package/src/prerender/shell-manifest-key.ts +20 -0
  1035. package/src/prerender/store.ts +131 -22
  1036. package/src/prerender.ts +460 -26
  1037. package/src/redirect-origin.ts +114 -0
  1038. package/src/regex-escape.ts +8 -0
  1039. package/src/render-error-thrower.tsx +20 -0
  1040. package/src/response-utils.ts +62 -0
  1041. package/src/reverse.ts +198 -128
  1042. package/src/root-error-boundary.tsx +42 -48
  1043. package/src/route-content-wrapper.tsx +22 -77
  1044. package/src/route-definition/dsl-helpers.ts +1116 -0
  1045. package/src/route-definition/helper-factories.ts +88 -0
  1046. package/src/route-definition/helpers-types.ts +506 -0
  1047. package/src/route-definition/index.ts +54 -0
  1048. package/src/route-definition/redirect.ts +134 -0
  1049. package/src/route-definition/resolve-handler-use.ts +160 -0
  1050. package/src/route-definition/use-item-types.ts +29 -0
  1051. package/src/route-definition.ts +1 -1481
  1052. package/src/route-map-builder.ts +120 -145
  1053. package/src/route-name.ts +53 -0
  1054. package/src/route-types.ts +71 -45
  1055. package/src/router/basename.ts +14 -0
  1056. package/src/router/content-negotiation.ts +305 -0
  1057. package/src/router/debug-manifest.ts +72 -0
  1058. package/src/router/error-handling.ts +54 -27
  1059. package/src/router/find-match.ts +259 -0
  1060. package/src/router/handler-context.ts +376 -125
  1061. package/src/router/instrument.ts +355 -0
  1062. package/src/router/intercept-resolution.ts +88 -28
  1063. package/src/router/lazy-includes.ts +260 -0
  1064. package/src/router/loader-resolution.ts +449 -157
  1065. package/src/router/logging.ts +106 -6
  1066. package/src/router/manifest.ts +144 -62
  1067. package/src/router/match-api.ts +224 -256
  1068. package/src/router/match-context.ts +4 -24
  1069. package/src/router/match-handlers.ts +499 -0
  1070. package/src/router/match-middleware/background-revalidation.ts +117 -93
  1071. package/src/router/match-middleware/cache-lookup.ts +308 -150
  1072. package/src/router/match-middleware/cache-store.ts +123 -51
  1073. package/src/router/match-middleware/intercept-resolution.ts +44 -43
  1074. package/src/router/match-middleware/segment-resolution.ts +64 -22
  1075. package/src/router/match-pipelines.ts +11 -87
  1076. package/src/router/match-result.ts +143 -52
  1077. package/src/router/metrics.ts +235 -29
  1078. package/src/router/middleware-types.ts +93 -0
  1079. package/src/router/middleware.ts +522 -441
  1080. package/src/router/navigation-snapshot.ts +133 -0
  1081. package/src/router/params-util.ts +23 -0
  1082. package/src/router/parse-pattern.ts +115 -0
  1083. package/src/router/pattern-matching.ts +311 -142
  1084. package/src/router/prefetch-cache-ttl.ts +51 -0
  1085. package/src/router/prefetch-limits.ts +37 -0
  1086. package/src/router/prerender-match.ts +568 -0
  1087. package/src/router/preview-match.ts +102 -0
  1088. package/src/router/request-classification.ts +291 -0
  1089. package/src/router/revalidation.ts +203 -62
  1090. package/src/router/route-snapshot.ts +256 -0
  1091. package/src/router/router-context.ts +45 -48
  1092. package/src/router/router-interfaces.ts +570 -0
  1093. package/src/router/router-options.ts +792 -0
  1094. package/src/router/router-registry.ts +21 -0
  1095. package/src/router/segment-resolution/fresh.ts +812 -0
  1096. package/src/router/segment-resolution/helpers.ts +348 -0
  1097. package/src/router/segment-resolution/loader-cache.ts +315 -0
  1098. package/src/router/segment-resolution/loader-mask.ts +60 -0
  1099. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  1100. package/src/router/segment-resolution/mask-nested.ts +99 -0
  1101. package/src/router/segment-resolution/revalidation.ts +1340 -0
  1102. package/src/router/segment-resolution/static-store.ts +81 -0
  1103. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  1104. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  1105. package/src/router/segment-resolution.ts +25 -1354
  1106. package/src/router/segment-wrappers.ts +292 -0
  1107. package/src/router/state-cookie-name.ts +33 -0
  1108. package/src/router/substitute-pattern-params.ts +75 -0
  1109. package/src/router/telemetry-otel.ts +259 -0
  1110. package/src/router/telemetry.ts +385 -0
  1111. package/src/router/timeout.ts +128 -0
  1112. package/src/router/tracing.ts +215 -0
  1113. package/src/router/trie-matching.ts +240 -61
  1114. package/src/router/types.ts +23 -70
  1115. package/src/router/url-params.ts +57 -0
  1116. package/src/router.ts +796 -2387
  1117. package/src/rsc/capture-queue.ts +67 -0
  1118. package/src/rsc/full-payload.ts +70 -0
  1119. package/src/rsc/handler-context.ts +46 -0
  1120. package/src/rsc/handler.ts +927 -1139
  1121. package/src/rsc/helpers.ts +275 -19
  1122. package/src/rsc/index.ts +2 -25
  1123. package/src/rsc/json-route-result.ts +38 -0
  1124. package/src/rsc/loader-fetch.ts +305 -0
  1125. package/src/rsc/manifest-init.ts +77 -0
  1126. package/src/rsc/nonce.ts +23 -0
  1127. package/src/rsc/origin-guard.ts +155 -0
  1128. package/src/rsc/progressive-enhancement.ts +502 -0
  1129. package/src/rsc/redirect-guard.ts +100 -0
  1130. package/src/rsc/response-cache-serve.ts +238 -0
  1131. package/src/rsc/response-error.ts +104 -0
  1132. package/src/rsc/response-route-handler.ts +257 -0
  1133. package/src/rsc/rsc-rendering.ts +822 -0
  1134. package/src/rsc/runtime-warnings.ts +55 -0
  1135. package/src/rsc/server-action.ts +522 -0
  1136. package/src/rsc/shell-build-manifest.ts +274 -0
  1137. package/src/rsc/shell-capture-constants.ts +27 -0
  1138. package/src/rsc/shell-capture.ts +1705 -0
  1139. package/src/rsc/shell-serve.ts +226 -0
  1140. package/src/rsc/ssr-setup.ts +176 -0
  1141. package/src/rsc/transition-gate.ts +89 -0
  1142. package/src/rsc/types.ts +95 -12
  1143. package/src/runtime-env.ts +18 -0
  1144. package/src/search-params.ts +99 -82
  1145. package/src/segment-content-promise.ts +67 -0
  1146. package/src/segment-fragments.ts +124 -0
  1147. package/src/segment-loader-promise.ts +167 -0
  1148. package/src/segment-system.tsx +464 -135
  1149. package/src/serialize.ts +243 -0
  1150. package/src/server/context.ts +616 -85
  1151. package/src/server/cookie-parse.ts +32 -0
  1152. package/src/server/cookie-store.ts +337 -0
  1153. package/src/server/fetchable-loader-store.ts +11 -6
  1154. package/src/server/handle-store.ts +123 -42
  1155. package/src/server/loader-registry.ts +51 -100
  1156. package/src/server/request-context.ts +1041 -155
  1157. package/src/server.ts +15 -8
  1158. package/src/ssr/index.tsx +661 -140
  1159. package/src/ssr/inject-rsc-eager.ts +167 -0
  1160. package/src/ssr/preinit-client-references.ts +106 -0
  1161. package/src/ssr/ssr-root.tsx +261 -0
  1162. package/src/static-handler.ts +45 -18
  1163. package/src/testing/cache-status.ts +162 -0
  1164. package/src/testing/collect-handle.ts +46 -0
  1165. package/src/testing/dispatch.ts +813 -0
  1166. package/src/testing/dom.entry.ts +22 -0
  1167. package/src/testing/e2e/fixture.ts +188 -0
  1168. package/src/testing/e2e/index.ts +128 -0
  1169. package/src/testing/e2e/matchers.ts +35 -0
  1170. package/src/testing/e2e/page-helpers.ts +272 -0
  1171. package/src/testing/e2e/parity.ts +387 -0
  1172. package/src/testing/e2e/server.ts +195 -0
  1173. package/src/testing/flight-matchers.ts +97 -0
  1174. package/src/testing/flight-normalize.ts +11 -0
  1175. package/src/testing/flight-runtime.d.ts +57 -0
  1176. package/src/testing/flight-tree.ts +682 -0
  1177. package/src/testing/flight.entry.ts +52 -0
  1178. package/src/testing/flight.ts +257 -0
  1179. package/src/testing/generated-routes.ts +199 -0
  1180. package/src/testing/index.ts +105 -0
  1181. package/src/testing/internal/context.ts +371 -0
  1182. package/src/testing/internal/flight-client-globals.ts +30 -0
  1183. package/src/testing/internal/seed-vars.ts +54 -0
  1184. package/src/testing/render-handler.ts +357 -0
  1185. package/src/testing/render-route.tsx +584 -0
  1186. package/src/testing/run-loader.ts +385 -0
  1187. package/src/testing/run-middleware.ts +205 -0
  1188. package/src/testing/run-transition-when.ts +164 -0
  1189. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  1190. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  1191. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  1192. package/src/testing/vitest-stubs/version.ts +5 -0
  1193. package/src/testing/vitest.ts +305 -0
  1194. package/src/theme/ThemeProvider.tsx +76 -98
  1195. package/src/theme/ThemeScript.tsx +12 -14
  1196. package/src/theme/constants.ts +57 -15
  1197. package/src/theme/index.ts +3 -20
  1198. package/src/theme/theme-context.ts +5 -35
  1199. package/src/theme/theme-script.ts +43 -39
  1200. package/src/theme/use-theme.ts +0 -3
  1201. package/src/types/boundaries.ts +123 -0
  1202. package/src/types/cache-types.ts +207 -0
  1203. package/src/types/error-types.ts +132 -0
  1204. package/src/types/global-namespace.ts +113 -0
  1205. package/src/types/handler-context.ts +839 -0
  1206. package/src/types/index.ts +81 -0
  1207. package/src/types/loader-types.ts +212 -0
  1208. package/src/types/request-scope.ts +112 -0
  1209. package/src/types/route-config.ts +138 -0
  1210. package/src/types/route-entry.ts +114 -0
  1211. package/src/types/segments.ts +271 -0
  1212. package/src/types.ts +1 -1795
  1213. package/src/urls/include-helper.ts +162 -0
  1214. package/src/urls/include-provider.ts +71 -0
  1215. package/src/urls/index.ts +44 -0
  1216. package/src/urls/path-helper-types.ts +418 -0
  1217. package/src/urls/path-helper.ts +280 -0
  1218. package/src/urls/pattern-types.ts +189 -0
  1219. package/src/urls/response-types.ts +109 -0
  1220. package/src/urls/type-extraction.ts +316 -0
  1221. package/src/urls/urls-function.ts +80 -0
  1222. package/src/urls.ts +1 -1352
  1223. package/src/use-loader.tsx +406 -141
  1224. package/src/vercel/index.ts +11 -0
  1225. package/src/vercel/tracing.ts +88 -0
  1226. package/src/vite/debug.ts +185 -0
  1227. package/src/vite/discovery/bundle-postprocess.ts +182 -0
  1228. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  1229. package/src/vite/discovery/discover-routers.ts +416 -0
  1230. package/src/vite/discovery/discovery-errors.ts +255 -0
  1231. package/src/vite/discovery/gate-state.ts +171 -0
  1232. package/src/vite/discovery/prerender-collection.ts +483 -0
  1233. package/src/vite/discovery/route-types-writer.ts +214 -0
  1234. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  1235. package/src/vite/discovery/shell-prerender-phase.ts +397 -0
  1236. package/src/vite/discovery/state.ts +205 -0
  1237. package/src/vite/discovery/virtual-module-codegen.ts +183 -0
  1238. package/src/vite/index.ts +31 -2255
  1239. package/src/vite/inject-client-debug.ts +88 -0
  1240. package/src/vite/plugin-types.ts +336 -0
  1241. package/src/vite/plugins/cjs-to-esm.ts +90 -0
  1242. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  1243. package/src/vite/plugins/client-ref-hashing.ts +118 -0
  1244. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  1245. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  1246. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  1247. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
  1248. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
  1249. package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
  1250. package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
  1251. package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
  1252. package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
  1253. package/src/vite/plugins/expose-ids/types.ts +45 -0
  1254. package/src/vite/plugins/expose-internal-ids.ts +805 -0
  1255. package/src/vite/plugins/performance-tracks.ts +89 -0
  1256. package/src/vite/plugins/refresh-cmd.ts +127 -0
  1257. package/src/vite/plugins/use-cache-transform.ts +313 -0
  1258. package/src/vite/plugins/vercel-output.ts +384 -0
  1259. package/src/vite/plugins/version-injector.ts +94 -0
  1260. package/src/vite/plugins/version-plugin.ts +271 -0
  1261. package/src/vite/plugins/virtual-entries.ts +267 -0
  1262. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  1263. package/src/vite/rango.ts +569 -0
  1264. package/src/vite/router-discovery.ts +2011 -0
  1265. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
  1266. package/src/vite/utils/banner.ts +36 -0
  1267. package/src/vite/utils/bundle-analysis.ts +132 -0
  1268. package/src/vite/utils/client-chunks.ts +184 -0
  1269. package/src/vite/utils/directive-prologue.ts +40 -0
  1270. package/src/vite/utils/forward-user-plugins.ts +171 -0
  1271. package/src/vite/utils/manifest-utils.ts +15 -0
  1272. package/src/vite/utils/package-resolution.ts +89 -0
  1273. package/src/vite/utils/prerender-utils.ts +268 -0
  1274. package/src/vite/utils/shared-utils.ts +271 -0
  1275. package/CLAUDE.md +0 -43
  1276. package/src/browser/lru-cache.ts +0 -69
  1277. package/src/browser/request-controller.ts +0 -164
  1278. package/src/browser/shallow.ts +0 -35
  1279. package/src/cache/memory-store.ts +0 -253
  1280. package/src/router.gen.ts +0 -6
  1281. package/src/static-handler.gen.ts +0 -5
  1282. package/src/urls.gen.ts +0 -8
  1283. package/src/vite/expose-internal-ids.ts +0 -1167
  1284. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  /// <reference types="@vitejs/plugin-rsc/types" />
2
- /// <reference path="../vite/version.d.ts" />
2
+ /// <reference path="../vite/plugins/version.d.ts" />
3
3
  /**
4
4
  * RSC Request Handler
5
5
  *
@@ -8,72 +8,97 @@
8
8
  */
9
9
 
10
10
  import { createElement } from "react";
11
- import { renderSegments } from "../segment-system.js";
12
- import { RouteNotFoundError, RouterError } from "../errors.js";
13
- import type { ResponseError } from "../urls.js";
14
- import { getLoaderLazy } from "../server/loader-registry.js";
15
- import {
16
- matchMiddleware,
17
- executeMiddleware,
18
- executeLoaderMiddleware,
19
- } from "../router/middleware.js";
11
+ import { isRouteNotFoundError } from "../errors.js";
12
+ import { matchMiddleware, executeMiddleware } from "../router/middleware.js";
20
13
  import {
21
14
  runWithRequestContext,
22
15
  setRequestContextParams,
23
- requireRequestContext,
16
+ getRequestContext,
17
+ _getRequestContext,
24
18
  createRequestContext,
25
- type ExecutionContext,
26
19
  } from "../server/request-context.js";
27
20
  import * as rscDeps from "@vitejs/plugin-rsc/rsc";
28
-
29
21
  import type {
30
22
  RscPayload,
31
- ReactFormState,
32
23
  CreateRSCHandlerOptions,
24
+ LoadSSRModule,
25
+ SSRModule,
33
26
  } from "./types.js";
34
- import { hasBodyContent, createResponseWithMergedHeaders } from "./helpers.js";
35
- import { generateNonce } from "./nonce.js";
27
+ import {
28
+ createResponseWithMergedHeaders,
29
+ finalizeResponse,
30
+ interceptRedirectForPartial,
31
+ buildRouteMiddlewareEntries,
32
+ } from "./helpers.js";
33
+ import { guardOutgoingRedirect } from "./redirect-guard.js";
34
+ import { resolvedHandleStream } from "../handles/deferred-resolution.js";
35
+ import {
36
+ isWebSocketUpgradeResponse,
37
+ appendVaryAccept,
38
+ } from "../response-utils.js";
39
+ import {
40
+ handleResponseRoute,
41
+ type ResponseRouteMatch,
42
+ } from "./response-route-handler.js";
43
+ import { generateNonce, nonce as nonceToken } from "./nonce.js";
36
44
  import { VERSION } from "@rangojs/router:version";
37
45
  import type { ErrorPhase } from "../types.js";
46
+ import type { RouterRequestInput } from "../router/router-interfaces.js";
38
47
  import { invokeOnError } from "../router/error-handling.js";
39
48
  import {
40
- getGlobalRouteMap,
49
+ createReverseFunction,
50
+ stripInternalParams,
51
+ } from "../router/handler-context.js";
52
+ import { contextSet } from "../context-var.js";
53
+ import {
41
54
  hasCachedManifest,
42
- setCachedManifest,
43
55
  getRouteTrie,
44
- setRouteTrie,
45
56
  getPrecomputedEntries,
46
57
  waitForManifestReady,
47
58
  getRouterManifest,
48
59
  getRouterTrie,
49
- setRouterManifest,
50
- setRouterTrie,
51
60
  } from "../route-map-builder.js";
52
-
53
- /**
54
- * Build a ResponseError payload from a caught error.
55
- * RouterError messages are always exposed (developer-crafted).
56
- * Standard Error messages are hidden in production.
57
- */
58
- function createResponseErrorPayload(error: unknown, isDev: boolean): ResponseError {
59
- if (error instanceof RouterError) {
60
- return {
61
- message: error.message,
62
- code: error.code,
63
- ...(error.type ? { type: error.type } : {}),
64
- ...(isDev && error.stack ? { stack: error.stack } : {}),
65
- };
66
- }
67
- if (error instanceof Error) {
68
- return {
69
- message: isDev ? error.message : "Internal Server Error",
70
- ...(isDev && error.stack ? { stack: error.stack } : {}),
71
- };
72
- }
73
- return {
74
- message: isDev ? String(error) : "Internal Server Error",
75
- };
76
- }
61
+ import type { HandlerContext } from "./handler-context.js";
62
+ import type { CacheErrorCategory } from "../cache/cache-error.js";
63
+ import type { SegmentCacheStore } from "../cache/types.js";
64
+ import { buildRouterTrieFromUrlpatterns } from "./manifest-init.js";
65
+ import { handleProgressiveEnhancement } from "./progressive-enhancement.js";
66
+ import {
67
+ executeServerAction,
68
+ revalidateAfterAction,
69
+ type ActionContinuation,
70
+ } from "./server-action.js";
71
+ import { handleLoaderFetch } from "./loader-fetch.js";
72
+ import {
73
+ checkRequestOrigin,
74
+ ORIGIN_CHECK_PHASE_BY_MODE,
75
+ } from "./origin-guard.js";
76
+ import { handleRscRendering } from "./rsc-rendering.js";
77
+ import {
78
+ withTimeout,
79
+ RouterTimeoutError,
80
+ createDefaultTimeoutResponse,
81
+ type TimeoutPhase,
82
+ } from "../router/timeout.js";
83
+ import {
84
+ createMetricsStore,
85
+ appendMetric,
86
+ buildMetricsTiming,
87
+ } from "../router/metrics.js";
88
+ import { observePhase, PHASES } from "../router/instrument.js";
89
+ import { safeEmit, resolveSink, getRequestId } from "../router/telemetry.js";
90
+ import {
91
+ startSSRSetup,
92
+ getSSRSetup,
93
+ mayNeedSSR,
94
+ isRscRequest,
95
+ SSR_SETUP_VAR,
96
+ } from "./ssr-setup.js";
97
+ import {
98
+ classifyRequest,
99
+ type RequestPlan,
100
+ type ExecutableRequestPlan,
101
+ } from "../router/request-classification.js";
77
102
 
78
103
  /**
79
104
  * Create an RSC request handler.
@@ -108,12 +133,35 @@ function createResponseErrorPayload(error: unknown, isDev: boolean): ResponseErr
108
133
  * });
109
134
  * ```
110
135
  */
136
+
137
+ /**
138
+ * Response that tells the client to do a full document navigation. Shared by
139
+ * the terminal reload plans (version-mismatch and app-switch): an empty 200
140
+ * carrying X-RSC-Reload, which the client turns into window.location.href.
141
+ */
142
+ function createReloadResponse(reloadUrl: string) {
143
+ return createResponseWithMergedHeaders(null, {
144
+ status: 200,
145
+ headers: {
146
+ "X-RSC-Reload": reloadUrl,
147
+ "content-type": "text/x-component;charset=utf-8",
148
+ },
149
+ });
150
+ }
151
+
111
152
  export function createRSCHandler<
112
153
  TEnv = unknown,
113
154
  TRoutes extends Record<string, string> = Record<string, string>,
114
155
  >(options: CreateRSCHandlerOptions<TEnv, TRoutes>) {
115
156
  const { router, version = VERSION, nonce: nonceProvider } = options;
116
157
 
158
+ // Handler-owned registry of explicit per-scope stores from cache({ store }).
159
+ // Lives in the closure so it is scoped per handler (multi-router deployments
160
+ // get separate registries) and accumulates every explicit store this handler
161
+ // resolves across requests. updateTag()/revalidateTag() iterate it to reach
162
+ // stores not covered by the app-level ctx._cacheStore.
163
+ const explicitTaggedStores = new Set<SegmentCacheStore>();
164
+
117
165
  // Use provided deps or default to @vitejs/plugin-rsc/rsc exports
118
166
  const deps = options.deps ?? rscDeps;
119
167
  const {
@@ -125,33 +173,188 @@ export function createRSCHandler<
125
173
  decodeFormState,
126
174
  } = deps;
127
175
 
128
- // Use provided loadSSRModule or default to vite RSC module loader
129
- const loadSSRModule =
176
+ // Use provided loadSSRModule or default to vite RSC module loader.
177
+ // In production the SSR module is stable across requests, so memoize
178
+ // the dynamic import to avoid repeated module resolution overhead.
179
+ // In dev mode Vite may hot-reload the module, so skip memoization.
180
+ const rawLoadSSRModule: LoadSSRModule =
130
181
  options.loadSSRModule ??
131
182
  (() => import.meta.viteRsc.loadModule("ssr", "index"));
183
+ let _ssrModulePromise: Promise<SSRModule> | undefined;
184
+ const loadSSRModule: LoadSSRModule =
185
+ process.env.NODE_ENV === "production"
186
+ ? () =>
187
+ (_ssrModulePromise ??= rawLoadSSRModule().catch((err) => {
188
+ _ssrModulePromise = undefined;
189
+ throw err;
190
+ }))
191
+ : rawLoadSSRModule;
132
192
 
133
193
  /**
134
- * Wrapper for invokeOnError that binds the router's onError callback.
135
- * Uses the shared utility from router/error-handling.ts for consistent behavior.
194
+ * Per-request error reporter that deduplicates via the ALS request context.
195
+ *
196
+ * Uses the same _reportedErrors WeakSet as the router layer so errors
197
+ * that propagate across layers are only reported once per request.
136
198
  */
137
199
  function callOnError(
138
200
  error: unknown,
139
201
  phase: ErrorPhase,
140
202
  context: Parameters<typeof invokeOnError<TEnv>>[3],
141
203
  ): void {
204
+ // Guard: abort signal handlers fire asynchronously outside the ALS
205
+ // request scope, so the context may be gone. Skip dedup in that
206
+ // case — the error is from a cancelled stream, not a real failure.
207
+ const reqCtx = _getRequestContext();
208
+ if (error != null && typeof error === "object" && reqCtx) {
209
+ if (reqCtx._reportedErrors.has(error)) return;
210
+ reqCtx._reportedErrors.add(error);
211
+ }
142
212
  invokeOnError(router.onError, error, phase, context, "RSC");
143
213
  }
144
214
 
145
- return async function handler(
215
+ function getRequiredRouteMap(): Record<string, string> {
216
+ const routeMap = getRouterManifest(router.id);
217
+ if (!routeMap) {
218
+ throw new Error(
219
+ `Route manifest for router "${router.id}" is not available.`,
220
+ );
221
+ }
222
+ return routeMap;
223
+ }
224
+
225
+ /**
226
+ * Handle a timeout by reporting the error, emitting telemetry,
227
+ * and returning either the custom onTimeout response or a default 504.
228
+ */
229
+ async function handleTimeoutResponse(
146
230
  request: Request,
147
- env: TEnv & { ctx?: ExecutionContext } = {} as TEnv & {
148
- ctx?: ExecutionContext;
231
+ env: TEnv,
232
+ url: URL,
233
+ phase: TimeoutPhase,
234
+ durationMs: number,
235
+ routeKey?: string,
236
+ actionId?: string,
237
+ ): Promise<Response> {
238
+ const timeoutError = new RouterTimeoutError(phase, durationMs);
239
+
240
+ callOnError(timeoutError, phase === "action" ? "action" : "handler", {
241
+ request,
242
+ url,
243
+ env,
244
+ routeKey,
245
+ actionId,
246
+ handledByBoundary: false,
247
+ metadata: { timeout: true, phase, durationMs },
248
+ });
249
+
250
+ if (router.telemetry) {
251
+ safeEmit(resolveSink(router.telemetry), {
252
+ type: "request.timeout",
253
+ timestamp: performance.now(),
254
+ requestId: getRequestId(request),
255
+ phase,
256
+ pathname: url.pathname,
257
+ routeKey,
258
+ actionId,
259
+ durationMs,
260
+ customHandler: !!router.onTimeout,
261
+ });
262
+ }
263
+
264
+ if (router.onTimeout) {
265
+ try {
266
+ return await router.onTimeout({
267
+ phase,
268
+ request,
269
+ url,
270
+ env,
271
+ routeKey,
272
+ actionId,
273
+ durationMs,
274
+ });
275
+ } catch (e) {
276
+ if (process.env.NODE_ENV !== "production") {
277
+ console.error("[RSC] onTimeout callback error:", e);
278
+ }
279
+ return createDefaultTimeoutResponse(phase);
280
+ }
281
+ }
282
+
283
+ return createDefaultTimeoutResponse(phase);
284
+ }
285
+
286
+ /**
287
+ * Build a 200 Flight response that carries a redirect URL and optional state.
288
+ * Used when a partial/action request results in a redirect -- fetch
289
+ * auto-follows 3xx so we send the redirect as payload metadata instead.
290
+ */
291
+ function createRedirectFlightResponse(
292
+ redirectUrl: string,
293
+ locationState?: Record<string, unknown>,
294
+ external?: boolean,
295
+ ): Response {
296
+ const redirectPayload: RscPayload = {
297
+ metadata: {
298
+ pathname: redirectUrl,
299
+ segments: [],
300
+ redirect: { url: redirectUrl, ...(external && { external: true }) },
301
+ ...(locationState && { locationState }),
302
+ },
303
+ };
304
+ const rscStream = renderToReadableStream<RscPayload>(redirectPayload, {
305
+ onError: (error: unknown) => {
306
+ const reqCtx = _getRequestContext<TEnv>();
307
+ if (!reqCtx) return;
308
+ callOnError(error, "rendering", {
309
+ request: reqCtx.request,
310
+ url: reqCtx.url,
311
+ env: reqCtx.env,
312
+ });
313
+ },
314
+ });
315
+ return createResponseWithMergedHeaders(rscStream, {
316
+ status: 200,
317
+ headers: { "content-type": "text/x-component;charset=utf-8" },
318
+ });
319
+ }
320
+
321
+ // Bundle shared dependencies for extracted handler functions.
322
+ // callOnError reads from ALS so it's inherently per-request scoped.
323
+ const handlerCtx: HandlerContext<TEnv> = {
324
+ router,
325
+ version,
326
+ renderToReadableStream,
327
+ decodeReply,
328
+ createTemporaryReferenceSet,
329
+ loadServerAction,
330
+ decodeAction,
331
+ decodeFormState,
332
+ loadSSRModule,
333
+ callOnError,
334
+ getRequiredRouteMap,
335
+ createRedirectFlightResponse,
336
+ resolveStreamMode: async (request, env, url) => {
337
+ const resolver = router.ssr?.resolveStreaming;
338
+ if (!resolver) return "stream";
339
+ return resolver({ request, env, url });
149
340
  },
341
+ };
342
+
343
+ return async function handler(
344
+ request: Request,
345
+ input: RouterRequestInput<TEnv> = {},
150
346
  ): Promise<Response> {
151
347
  const handlerStart = performance.now();
348
+ // Create the metrics store at handler start so handler:total has startTime=0
349
+ // and all metrics are relative to the request entry point.
350
+ const earlyMetricsStore = router.debugPerformance
351
+ ? createMetricsStore(true, handlerStart)
352
+ : undefined;
353
+
354
+ const { env = {} as TEnv, vars: initialVars, ctx: executionCtx } = input;
152
355
 
153
356
  // Connection warmup: return 204 immediately before any processing
154
- if (router.warmupEnabled && request.method === "HEAD") {
357
+ if (router?.warmupEnabled && request.method === "HEAD") {
155
358
  const warmupUrl = new URL(request.url);
156
359
  if (warmupUrl.searchParams.has("_rsc_warmup")) {
157
360
  return new Response(null, { status: 204 });
@@ -175,24 +378,27 @@ export function createRSCHandler<
175
378
  const mwMatchDur = performance.now() - mwMatchStart;
176
379
 
177
380
  // Shared variables between middleware and route handlers
178
- // Initialize from env.Variables if provided (allows pre-seeding from worker entry)
179
- const variables: Record<string, any> = {
180
- ...((env as any)?.Variables ?? {}),
181
- };
381
+ // Initialize from input.vars if provided (allows pre-seeding from worker entry)
382
+ const variables: Record<string, any> = initialVars
383
+ ? { ...initialVars }
384
+ : {};
182
385
 
183
- // Store nonce in variables so middleware can access via ctx.get('nonce')
386
+ // Store nonce via ContextVar token and string key for backward compat
184
387
  if (nonce) {
388
+ contextSet(variables, nonceToken, nonce);
185
389
  variables.nonce = nonce;
186
390
  }
187
391
 
188
392
  // Resolve cache store configuration
189
393
  // Priority: options.cache (handler override) > router.cache (router default)
190
394
  // Store is enabled only if: config provided, enabled, and no ?__no_cache query param
191
- let cacheStore = undefined;
395
+ let cacheStore: SegmentCacheStore | undefined;
192
396
  const cacheOption = options.cache ?? router.cache;
193
397
  if (cacheOption && !url.searchParams.has("__no_cache")) {
194
398
  const cacheConfig =
195
- typeof cacheOption === "function" ? cacheOption(env) : cacheOption;
399
+ typeof cacheOption === "function"
400
+ ? cacheOption(env, executionCtx)
401
+ : cacheOption;
196
402
 
197
403
  if (cacheConfig.enabled !== false) {
198
404
  cacheStore = cacheConfig.store;
@@ -223,56 +429,7 @@ export function createRSCHandler<
223
429
  // Cloudflare dev: generate manifest inline for this router.
224
430
  // Each router generates its own manifest independently so
225
431
  // multi-router setups (host routing) work correctly.
226
- const { generateManifest } =
227
- await import("../build/generate-manifest.js");
228
- const generated = generateManifest(router.urlpatterns);
229
- if (
230
- generated._routeAncestry &&
231
- Object.keys(generated._routeAncestry).length > 0
232
- ) {
233
- const { buildRouteTrie } = await import("../build/route-trie.js");
234
- // Map each route to its include() staticPrefix so the trie
235
- // returns the correct sp for lazy entry lookup in findMatch.
236
- const routeToStaticPrefix: Record<string, string> = {};
237
- for (const name of Object.keys(generated.routeManifest)) {
238
- routeToStaticPrefix[name] = "";
239
- }
240
- // Override with prefix from include() entries so the trie
241
- // returns the correct sp for lazy entry lookup in findMatch.
242
- // Walk recursively to include routes in nested includes.
243
- if (generated.prefixTree) {
244
- const visitPrefixNode = (node: any): void => {
245
- const sp = node.staticPrefix || "";
246
- for (const route of (node.routes || [])) {
247
- routeToStaticPrefix[route] = sp;
248
- }
249
- for (const child of Object.values(node.children || {})) {
250
- visitPrefixNode(child);
251
- }
252
- };
253
- for (const node of Object.values(generated.prefixTree)) {
254
- visitPrefixNode(node);
255
- }
256
- }
257
- const trie = buildRouteTrie(
258
- generated.routeManifest,
259
- generated._routeAncestry,
260
- routeToStaticPrefix,
261
- generated.routeTrailingSlash,
262
- generated.prerenderRoutes ? new Set(generated.prerenderRoutes) : undefined,
263
- generated.passthroughRoutes ? new Set(generated.passthroughRoutes) : undefined,
264
- generated.responseTypeRoutes,
265
- );
266
- setRouterTrie(router.id, trie);
267
- // Set global trie only if not already set by another router
268
- if (!getRouteTrie()) {
269
- setRouteTrie(trie);
270
- }
271
- }
272
- setRouterManifest(router.id, generated.routeManifest);
273
- // Merge into global manifest (needed for reverse/href across routers)
274
- const existing = hasCachedManifest() ? getGlobalRouteMap() : {};
275
- setCachedManifest({ ...existing, ...generated.routeManifest });
432
+ await buildRouterTrieFromUrlpatterns(router);
276
433
  }
277
434
  if (!getRouterManifest(router.id) && !hasCachedManifest()) {
278
435
  throw new Error(
@@ -280,10 +437,17 @@ export function createRSCHandler<
280
437
  );
281
438
  }
282
439
  }
283
- const manifestCacheDur = performance.now() - manifestCacheStart;
284
440
 
285
- // Note: Route map for useHref() is loaded lazily via getGlobalRouteMap()
286
- // This allows it to include all routes from lazy includes after manifest loading
441
+ // Rebuild the trie when the manifest exists but the per-router trie is
442
+ // missing. This happens in dev mode after HMR: the virtual module sets
443
+ // the manifest (from fresh gen files) but the trie is intentionally not
444
+ // injected to avoid stale discovery-time data. Without the trie, route
445
+ // matching falls back to regex iteration which does not handle wildcard
446
+ // priority correctly (catch-all patterns match before specific routes).
447
+ if (!getRouterTrie(router.id) && router.urlpatterns) {
448
+ await buildRouterTrieFromUrlpatterns(router);
449
+ }
450
+ const manifestCacheDur = performance.now() - manifestCacheStart;
287
451
 
288
452
  // Create unified request context with all methods
289
453
  // Includes: stub response, handle store, loader memoization, use(), cookies, headers, cache store
@@ -295,9 +459,35 @@ export function createRSCHandler<
295
459
  url,
296
460
  variables,
297
461
  cacheStore,
298
- executionContext: env.ctx,
462
+ explicitTaggedStores,
463
+ cacheProfiles: router.cacheProfiles,
464
+ executionContext: executionCtx,
299
465
  themeConfig: router.themeConfig,
466
+ stateCookieName: router.resolvedStateCookieName,
467
+ version,
300
468
  });
469
+ // Thread the true request entry timestamp onto the context so a metrics
470
+ // store created MID-request (ctx.debugPerformance() / getMetricsStore) anchors
471
+ // to the real start, not the opt-in moment — phases that began earlier then
472
+ // report non-negative offsets. Set unconditionally: debug may be enabled later.
473
+ requestContext._handlerStart = handlerStart;
474
+ if (earlyMetricsStore) {
475
+ requestContext._debugPerformance = true;
476
+ requestContext._metricsStore = earlyMetricsStore;
477
+ }
478
+ // Wire background error reporting so "use cache" and other subsystems
479
+ // can surface non-fatal errors through the router's onError callback.
480
+ requestContext._reportBackgroundError = (
481
+ error: unknown,
482
+ category: CacheErrorCategory,
483
+ ) => {
484
+ callOnError(error, "cache", {
485
+ request,
486
+ url,
487
+ metadata: { category },
488
+ });
489
+ };
490
+
301
491
  const ctxCreateDur = performance.now() - ctxCreateStart;
302
492
 
303
493
  // Accumulate handler-level timing for Server-Timing header
@@ -319,296 +509,140 @@ export function createRSCHandler<
319
509
  // - Server components during rendering
320
510
  // - Error boundaries
321
511
  // - Streaming
322
- return runWithRequestContext(requestContext, async () => {
323
- // Core handler logic (wrapped by middleware)
324
- const coreHandler = async (): Promise<Response> => {
325
- return coreRequestHandler(request, env, url, variables, nonce);
326
- };
327
-
328
- // Execute middleware chain if any, otherwise call core handler directly
329
- if (matchedMiddleware.length > 0) {
330
- return executeMiddleware(
331
- matchedMiddleware,
332
- request,
333
- env,
334
- variables,
335
- coreHandler,
336
- );
337
- }
338
-
339
- return coreHandler();
340
- });
341
- };
342
-
343
- // Core request handling logic (separated for middleware wrapping)
344
- async function coreRequestHandler(
345
- request: Request,
346
- env: TEnv,
347
- url: URL,
348
- variables: Record<string, any>,
349
- nonce: string | undefined,
350
- ): Promise<Response> {
351
- // First, check for route-level middleware
352
- const previewStart = performance.now();
353
- const preview = await router.previewMatch(request, env);
354
- const previewDur = performance.now() - previewStart;
355
- const handlerTiming: string[] = variables.__handlerTiming || [];
356
- handlerTiming.push(`handler-preview-match;dur=${previewDur.toFixed(2)}`);
357
- // Response route short-circuit: skip entire RSC pipeline
358
- if (preview?.responseType && preview.handler) {
359
- const isPartial = url.searchParams.has("_rsc_partial");
360
-
361
- // Partial requests (client-side navigation) to response routes
362
- // get X-RSC-Reload to trigger hard navigation in the browser
363
- if (isPartial) {
364
- const cleanUrl = new URL(url);
365
- cleanUrl.searchParams.delete("_rsc_partial");
366
- cleanUrl.searchParams.delete("_rsc_segments");
367
- cleanUrl.searchParams.delete("_rsc_v");
368
- cleanUrl.searchParams.delete("_rsc_stale");
369
- cleanUrl.searchParams.delete("_rsc_action");
370
- cleanUrl.searchParams.delete("_rsc_prev");
371
-
372
- return createResponseWithMergedHeaders(null, {
373
- status: 200,
374
- headers: {
375
- "X-RSC-Reload": cleanUrl.toString(),
376
- "content-type": "text/x-component;charset=utf-8",
377
- },
378
- });
379
- }
512
+ // Store basename on request context (scoped per-request via existing ALS)
513
+ requestContext._basename = router.basename;
514
+
515
+ // Resolved span tracing for this request (read at each traced phase).
516
+ requestContext._tracing = router.tracing;
517
+
518
+ // The "rango.request" span is opened inside the request context so the
519
+ // Cloudflare runner can read executionContext.tracing, and so every nested
520
+ // phase span (and the platform's automatic KV/D1/fetch spans) nests under
521
+ // it. Construction-bound: the span ends when the Response is built, never
522
+ // wrapping the streamed body. metric:false — handler:total is metered
523
+ // directly below (a grand total incl. the pre-context bootstrap timings).
524
+ // When tracing is off this is a direct pass-through.
525
+ return runWithRequestContext(requestContext, () =>
526
+ observePhase(PHASES.request, async (span) => {
527
+ span.setAttribute("http.method", request.method);
528
+ // The matched route template is not known until match() runs later, so
529
+ // emit the concrete path as url.path (low-level), NOT http.route — the
530
+ // latter is reserved for the low-cardinality template (OTel convention).
531
+ span.setAttribute("url.path", url.pathname);
532
+
533
+ // Core handler logic (wrapped by middleware)
534
+ const coreHandler = async (): Promise<Response> => {
535
+ return coreRequestHandler(request, env, url, variables, nonce);
536
+ };
380
537
 
381
- // Build lightweight context for response handler
382
- const bindings = (env as any)?.Bindings ?? env;
383
- const reqCtx = requireRequestContext();
384
- const responseHandlerCtx = {
385
- request,
386
- params: preview.params || {},
387
- env: bindings,
388
- searchParams: url.searchParams,
389
- url,
390
- pathname: url.pathname,
391
- href: (name: string, hrefParams?: Record<string, string>) => {
392
- if (name.startsWith("/")) {
393
- if (!hrefParams) return name;
394
- return name.replace(/:([^/]+)/g, (_, key) => {
395
- const value = hrefParams[key];
396
- if (value === undefined) throw new Error(`Missing param "${key}" for path "${name}"`);
397
- return encodeURIComponent(value);
398
- });
399
- }
400
- return name;
401
- },
402
- get: (key: string) => variables[key],
403
- header: (name: string, value: string) => reqCtx.header(name, value),
404
- setCookie: (name: string, value: string, options?: any) => reqCtx.setCookie(name, value, options),
405
- };
538
+ // Execute middleware chain if any, otherwise call core handler directly
539
+ let response: Response;
540
+ if (matchedMiddleware.length > 0) {
541
+ const mwResponse = await executeMiddleware(
542
+ matchedMiddleware,
543
+ request,
544
+ env,
545
+ variables,
546
+ coreHandler,
547
+ createReverseFunction(getRequiredRouteMap()),
548
+ );
406
549
 
407
- // Call handler directly, wrapped by route middleware if present
408
- const callHandler = async () => {
409
- // JSON response routes: wrap in { data } / { error } envelope
410
- if (preview.responseType === "json") {
411
- const errorCtx = { request, url, env };
412
- try {
413
- const result = await (preview.handler as Function)(responseHandlerCtx);
414
- if (result instanceof Response) {
415
- const mergedHeaders: Record<string, string> = {};
416
- result.headers.forEach((value, key) => {
417
- mergedHeaders[key] = value;
418
- });
419
- return createResponseWithMergedHeaders(result.body, {
420
- status: result.status,
421
- headers: mergedHeaders,
422
- });
423
- }
424
- return createResponseWithMergedHeaders(
425
- JSON.stringify({ data: result }),
426
- { status: 200, headers: { "content-type": "application/json;charset=utf-8" } },
427
- );
428
- } catch (error) {
429
- callOnError(error, "handler", errorCtx);
430
- const isDev = process.env.NODE_ENV !== "production";
431
- const status = error instanceof RouterError ? error.status : 500;
432
- return createResponseWithMergedHeaders(
433
- JSON.stringify({ error: createResponseErrorPayload(error, isDev) }),
434
- { status, headers: { "content-type": "application/json;charset=utf-8" } },
550
+ if (
551
+ url.searchParams.has("_rsc_partial") ||
552
+ url.searchParams.has("_rsc_action")
553
+ ) {
554
+ const intercepted = interceptRedirectForPartial(
555
+ mwResponse,
556
+ createRedirectFlightResponse,
435
557
  );
558
+ response = intercepted ?? finalizeResponse(mwResponse);
559
+ } else {
560
+ response = finalizeResponse(mwResponse);
436
561
  }
562
+ } else {
563
+ response = await coreHandler();
437
564
  }
438
565
 
439
- // Non-JSON response routes: catch errors and return plain Response
440
- const errorCtx = { request, url, env };
441
- try {
442
- const result = await (preview.handler as Function)(responseHandlerCtx);
443
-
444
- if (result instanceof Response) {
445
- // Handler returned a Response directly -- pass through
446
- const mergedHeaders: Record<string, string> = {};
447
- result.headers.forEach((value, key) => {
448
- mergedHeaders[key] = value;
449
- });
450
- return createResponseWithMergedHeaders(result.body, {
451
- status: result.status,
452
- headers: mergedHeaders,
453
- });
454
- }
455
-
456
- // Auto-wrap based on response type tag
457
- switch (preview.responseType) {
458
- case "text":
459
- return createResponseWithMergedHeaders(
460
- String(result),
461
- { status: 200, headers: { "content-type": "text/plain;charset=utf-8" } },
462
- );
463
- case "html":
464
- return createResponseWithMergedHeaders(
465
- String(result),
466
- { status: 200, headers: { "content-type": "text/html;charset=utf-8" } },
467
- );
468
- case "xml":
469
- return createResponseWithMergedHeaders(
470
- String(result),
471
- { status: 200, headers: { "content-type": "application/xml;charset=utf-8" } },
472
- );
473
- case "md":
474
- return createResponseWithMergedHeaders(
475
- String(result),
476
- { status: 200, headers: { "content-type": "text/markdown;charset=utf-8" } },
477
- );
478
- default:
479
- // image, stream, any -- must return Response
480
- throw new Error(
481
- `Response route handler for "${preview.responseType}" must return a Response object, got ${typeof result}`
482
- );
483
- }
484
- } catch (error) {
485
- callOnError(error, "handler", errorCtx);
486
- const isDev = process.env.NODE_ENV !== "production";
487
- const status = error instanceof RouterError ? error.status : 500;
488
- const message = error instanceof RouterError
489
- ? error.message
490
- : isDev && error instanceof Error
491
- ? error.message
492
- : "Internal Server Error";
493
- return createResponseWithMergedHeaders(message, {
494
- status,
495
- headers: { "content-type": "text/plain;charset=utf-8" },
496
- });
566
+ // Finalize metrics after all middleware (including post-next work)
567
+ // has completed so :post spans are captured in the timeline.
568
+ // Handler timing parts are always emitted (even without debug metrics)
569
+ // so non-debug requests still get bootstrap Server-Timing entries.
570
+ const handlerTimingArr: string[] = variables.__handlerTiming || [];
571
+ // Preserve any existing Server-Timing set by response routes or middleware
572
+ const existingTiming = response.headers.get("Server-Timing");
573
+ const timingParts = existingTiming
574
+ ? [existingTiming, ...handlerTimingArr]
575
+ : [...handlerTimingArr];
576
+
577
+ const metricsStore = requestContext._metricsStore;
578
+ if (metricsStore) {
579
+ // When the store was created at handler start (earlyMetricsStore),
580
+ // handler:total covers the full request. When ctx.debugPerformance()
581
+ // created the store mid-request its requestStart is now the threaded
582
+ // _handlerStart (== handlerStart), so both branches yield the true
583
+ // request entry; reading the store's own anchor keeps this correct even
584
+ // if a store ever lands without the threading (falls back to its start).
585
+ const totalStart = earlyMetricsStore
586
+ ? handlerStart
587
+ : metricsStore.requestStart;
588
+ appendMetric(
589
+ metricsStore,
590
+ "handler:total",
591
+ totalStart,
592
+ performance.now() - totalStart,
593
+ );
594
+ const metricsTiming = buildMetricsTiming(
595
+ request.method,
596
+ url.pathname,
597
+ metricsStore,
598
+ );
599
+ if (metricsTiming) timingParts.push(metricsTiming);
497
600
  }
498
- };
499
601
 
500
- // Wrap callHandler to append Vary: Accept on content-negotiated responses
501
- const callHandlerWithVary = async () => {
502
- const response = await callHandler();
503
- if (preview.negotiated) {
504
- response.headers.append("Vary", "Accept");
602
+ const fullTiming = timingParts.join(", ");
603
+ if (fullTiming && !isWebSocketUpgradeResponse(response)) {
604
+ try {
605
+ response.headers.set("Server-Timing", fullTiming);
606
+ } catch {
607
+ // Immutable headers (e.g. a passed-through platform Response) — drop
608
+ // the timing header, never the response. Instrumentation must not
609
+ // 500 a request.
610
+ }
505
611
  }
506
- return response;
507
- };
508
-
509
- if (preview.routeMiddleware && preview.routeMiddleware.length > 0) {
510
- const middlewareEntries = preview.routeMiddleware.map((mw) => ({
511
- entry: {
512
- pattern: null,
513
- regex: null,
514
- paramNames: [],
515
- handler: mw.handler,
516
- mountPrefix: null,
517
- },
518
- params: mw.params,
519
- }));
520
- return executeMiddleware(middlewareEntries, request, env, variables, callHandlerWithVary);
521
- }
522
-
523
- return callHandlerWithVary();
524
- }
525
-
526
- // Wrap RSC handler to append Vary: Accept on content-negotiated routes
527
- const rscHandler = async () => {
528
- const response = await coreRequestHandlerInner(request, env, url, variables, nonce);
529
- if (preview?.negotiated) {
530
- response.headers.append("Vary", "Accept");
531
- }
532
- return response;
533
- };
534
-
535
- if (preview?.routeMiddleware && preview.routeMiddleware.length > 0) {
536
- // Convert route middleware to app middleware format for execution
537
- const middlewareEntries = preview.routeMiddleware.map((mw) => ({
538
- entry: {
539
- pattern: null,
540
- regex: null,
541
- paramNames: [],
542
- handler: mw.handler,
543
- mountPrefix: null,
544
- },
545
- params: mw.params,
546
- }));
547
612
 
548
- // Execute route middleware wrapping the actual request handling
549
- return executeMiddleware(middlewareEntries, request, env, variables, rscHandler);
550
- }
551
-
552
- // No route middleware, proceed directly
553
- return rscHandler();
554
- }
613
+ // Single open-redirect chokepoint: every response (PE, full-page,
614
+ // middleware short-circuit, response-route) funnels through here, so
615
+ // guarding browser-followed (3xx) redirects once covers them all and any
616
+ // future redirect exit. Soft SPA/Flight redirects are 200/204 and pass
617
+ // through untouched (validated client-side instead).
618
+ return guardOutgoingRedirect(response, url.origin, router.basename);
619
+ }),
620
+ );
621
+ };
555
622
 
556
- // Inner request handler (actual RSC logic, wrapped by route middleware if any)
557
- async function coreRequestHandlerInner(
623
+ // Core request handling logic (separated for middleware wrapping).
624
+ // Uses the classify → execute model: classifyRequest produces a RequestPlan,
625
+ // then execution dispatches on the plan mode.
626
+ async function coreRequestHandler(
558
627
  request: Request,
559
628
  env: TEnv,
560
629
  url: URL,
561
630
  variables: Record<string, any>,
562
631
  nonce: string | undefined,
563
632
  ): Promise<Response> {
564
- const isPartial = url.searchParams.has("_rsc_partial");
565
- const isAction =
566
- request.headers.has("rsc-action") || url.searchParams.has("_rsc_action");
567
- const actionId =
568
- request.headers.get("rsc-action") || url.searchParams.get("_rsc_action");
569
-
570
- // Version mismatch detection - client may have stale code after HMR/deployment
571
- // If versions don't match, tell the client to reload
572
- const clientVersion = url.searchParams.get("_rsc_v");
573
- if (version && clientVersion && clientVersion !== version) {
574
- console.log(
575
- `[RSC] Version mismatch: client=${clientVersion}, server=${version}. Forcing reload.`,
576
- );
633
+ const handlerTiming: string[] = variables.__handlerTiming || [];
577
634
 
578
- // Clean URL by removing RSC params
579
- const cleanUrl = new URL(url);
580
- cleanUrl.searchParams.delete("_rsc_partial");
581
- cleanUrl.searchParams.delete("_rsc_segments");
582
- cleanUrl.searchParams.delete("_rsc_v");
583
- cleanUrl.searchParams.delete("_rsc_stale");
584
- cleanUrl.searchParams.delete("_rsc_action");
585
- cleanUrl.searchParams.delete("_rsc_prev");
586
-
587
- // For actions, reload current page (referer)
588
- // For navigation, load the target URL
589
- const reloadUrl = isAction
590
- ? request.headers.get("referer") || cleanUrl.toString()
591
- : cleanUrl.toString();
592
-
593
- // Return special response that tells client to reload
594
- return createResponseWithMergedHeaders(null, {
595
- status: 200,
596
- headers: {
597
- "X-RSC-Reload": reloadUrl,
598
- "content-type": "text/x-component;charset=utf-8",
599
- },
600
- });
601
- }
602
- // Debug manifest endpoint: ?__debug_manifest on any route.
603
- // Always available in dev, requires allowDebugManifest option in production.
635
+ // Debug manifest endpoint: handled before classification since it
636
+ // doesn't need a route match and needs trie access from the closure.
604
637
  const isDev = process.env.NODE_ENV !== "production";
605
638
  if (
606
639
  url.searchParams.has("__debug_manifest") &&
607
640
  (isDev || router.allowDebugManifest)
608
641
  ) {
609
642
  const trie = getRouterTrie(router.id) ?? getRouteTrie();
610
- const routeManifest = getRouterManifest(router.id) ?? getGlobalRouteMap();
611
- const { extractAncestryFromTrie } = await import("../build/route-trie.js");
643
+ const routeManifest = getRequiredRouteMap();
644
+ const { extractAncestryFromTrie } =
645
+ await import("../build/route-trie.js");
612
646
  return new Response(
613
647
  JSON.stringify(
614
648
  {
@@ -627,851 +661,605 @@ export function createRSCHandler<
627
661
  );
628
662
  }
629
663
 
630
- // Get handle store from request context (created at start of request)
631
- const handleStore = requireRequestContext()._handleStore;
632
-
664
+ // ---- 1. Classify ----
665
+ // classifyRequest may throw RouteNotFoundError for unknown routes.
666
+ // In that case, fall through to a full-render plan so the pipeline
667
+ // can render the 404 page via the existing error handling path.
668
+ const classifyStart = performance.now();
669
+ let plan: RequestPlan<TEnv>;
633
670
  try {
634
- // ============================================================================
635
- // PROGRESSIVE ENHANCEMENT: No-JS Form Submissions
636
- // ============================================================================
637
- const progressiveResult = await handleProgressiveEnhancement(
638
- request,
639
- env,
640
- url,
641
- isAction,
642
- handleStore,
643
- nonce,
644
- );
645
- if (progressiveResult) {
646
- return progressiveResult;
671
+ plan = await classifyRequest<TEnv>(request, url, {
672
+ findMatch: router.findMatch,
673
+ routerVersion: version,
674
+ routerId: router.id,
675
+ });
676
+ } catch (error) {
677
+ if (isRouteNotFoundError(error)) {
678
+ // Let the render path handle 404 — match()/matchPartial() will
679
+ // re-throw RouteNotFoundError and the catch block in
680
+ // executeRenderWithMiddleware renders the not-found page.
681
+ plan = {
682
+ mode: "full-render",
683
+ route: {
684
+ matched: null as any,
685
+ manifestEntry: null as any,
686
+ entries: [],
687
+ routeKey: "",
688
+ localRouteName: "",
689
+ params: {},
690
+ routeMiddleware: [],
691
+ cacheScope: null,
692
+ isPassthrough: false,
693
+ },
694
+ negotiated: false,
695
+ };
696
+ } else {
697
+ throw error;
647
698
  }
648
-
649
- // ============================================================================
650
- // SERVER ACTION EXECUTION (JavaScript-enabled client)
651
- // ============================================================================
652
- if (isAction && actionId) {
653
- return handleServerAction(request, env, url, actionId, handleStore);
699
+ }
700
+ const classifyDur = performance.now() - classifyStart;
701
+ handlerTiming.push(`handler-classify;dur=${classifyDur.toFixed(2)}`);
702
+
703
+ // ---- 2. Terminal plans (no execution needed) ----
704
+ if (plan.mode === "redirect") {
705
+ // Redirects are handled by the pipeline (match/matchPartial),
706
+ // but for partial requests we short-circuit with a Flight redirect.
707
+ if (url.searchParams.has("_rsc_partial")) {
708
+ return createRedirectFlightResponse(plan.redirectUrl);
654
709
  }
710
+ // Full requests: let the pipeline handle the redirect via match()
711
+ // which returns { redirect: url }. Fall through to full-render.
712
+ }
655
713
 
656
- // ============================================================================
657
- // LOADER FETCH EXECUTION (data fetching with RSC serialization)
658
- // ============================================================================
659
- const isLoaderRequest = url.searchParams.has("_rsc_loader");
660
- if (isLoaderRequest) {
661
- return handleLoaderFetch(request, env, url, variables);
662
- }
714
+ if (plan.mode === "version-mismatch") {
715
+ console.log(
716
+ `[RSC] Version mismatch: client=${url.searchParams.get("_rsc_v")}, server=${version}. Forcing reload.`,
717
+ );
718
+ return createReloadResponse(plan.reloadUrl);
719
+ }
663
720
 
664
- // ============================================================================
665
- // REGULAR RSC RENDERING (Navigation)
666
- // ============================================================================
667
- // Note: Must use "return await" for try/catch to catch async rejections
668
- return await handleRscRendering(
721
+ if (plan.mode === "app-switch") {
722
+ // Cross-app SPA navigation crossed a host-router app boundary. Force a
723
+ // real document navigation so the target app's document is re-established
724
+ // (stylesheets, theme, warmup, prefetch-TTL). See request-classification.
725
+ return createReloadResponse(plan.reloadUrl);
726
+ }
727
+
728
+ // ---- 3. Origin guard (gate for action/loader/PE modes) ----
729
+ const originPhase = ORIGIN_CHECK_PHASE_BY_MODE[plan.mode];
730
+ if (originPhase) {
731
+ const originResult = await checkRequestOrigin(
669
732
  request,
670
- env,
671
733
  url,
672
- isPartial,
673
- handleStore,
674
- nonce,
734
+ router.originCheck,
735
+ env,
736
+ router.id,
737
+ originPhase,
675
738
  );
676
- } catch (error) {
677
- // Check if middleware/handler returned Response
678
- if (error instanceof Response) {
679
- // During partial (client-side navigation), a 200 Response from a handler
680
- // means the route serves raw content (JSON, text, etc.), not JSX.
681
- // Signal the browser to hard-navigate so it renders the raw response.
682
- // Only for 200 — redirects (3xx) work already because the browser follows
683
- // them automatically to a URL that serves Flight data.
684
- if (isPartial && error.status === 200) {
685
- console.warn(
686
- `[RSC] Route handler at ${url.pathname} returned a Response during client-side navigation. ` +
687
- `Falling back to hard navigation. Use data-external on the <Link> to avoid the extra round-trip.`,
688
- );
689
- const cleanUrl = new URL(url);
690
- cleanUrl.searchParams.delete("_rsc_partial");
691
- cleanUrl.searchParams.delete("_rsc_segments");
692
- cleanUrl.searchParams.delete("_rsc_v");
693
- cleanUrl.searchParams.delete("_rsc_stale");
694
- cleanUrl.searchParams.delete("_rsc_action");
695
- cleanUrl.searchParams.delete("_rsc_prev");
696
- return createResponseWithMergedHeaders(null, {
697
- status: 200,
698
- headers: {
699
- "X-RSC-Reload": cleanUrl.toString(),
700
- "content-type": "text/x-component;charset=utf-8",
701
- },
702
- });
703
- }
704
- return error;
705
- }
739
+ if (originResult) {
740
+ const originError = new Error(
741
+ `Origin check rejected: ${request.headers.get("origin") ?? "none"} vs ${request.headers.get("host") ?? "none"}`,
742
+ );
743
+ originError.name = "OriginCheckError";
706
744
 
707
- // Render 404 page for unmatched routes
708
- // Check both instanceof and error.name for cross-bundle compatibility
709
- const isRouteNotFound =
710
- error instanceof RouteNotFoundError ||
711
- (error instanceof Error && error.name === "RouteNotFoundError");
712
- if (isRouteNotFound) {
713
- callOnError(error, "routing", {
745
+ callOnError(originError, "origin", {
714
746
  request,
715
747
  url,
716
748
  env,
717
- handledByBoundary: true, // Handled by notFound component
718
- });
719
-
720
- // Get notFound component from router options or use default
721
- const notFoundOption = router.notFound;
722
- const notFoundComponent =
723
- typeof notFoundOption === "function"
724
- ? notFoundOption({ pathname: url.pathname })
725
- : (notFoundOption ?? createElement("h1", null, "Not Found"));
726
-
727
- // Create a simple segment for the 404 page
728
- const notFoundSegment = {
729
- id: "notFound",
730
- namespace: "notFound",
731
- type: "route" as const,
732
- index: 0,
733
- component: notFoundComponent,
734
- params: {},
735
- };
736
-
737
- // Render with rootLayout to maintain app shell
738
- const root = await renderSegments([notFoundSegment], {
739
- rootLayout: router.rootLayout,
740
- // No routeName for not-found routes
741
- });
742
-
743
- const payload: RscPayload = {
744
- root,
749
+ handledByBoundary: false,
745
750
  metadata: {
746
- pathname: url.pathname,
747
- segments: [notFoundSegment],
748
- matched: [],
749
- diff: [],
750
- isPartial: false,
751
- handles: handleStore.stream(),
752
- version,
753
- themeConfig: router.themeConfig,
754
- warmupEnabled: router.warmupEnabled,
755
- initialTheme: requireRequestContext().theme,
756
- // No routeName for not-found routes
751
+ phase: originPhase,
752
+ origin: request.headers.get("origin"),
753
+ host: request.headers.get("host"),
757
754
  },
758
- };
759
-
760
- const rscStream = renderToReadableStream(payload);
761
-
762
- // Determine if this is an RSC request or HTML request
763
- const isRscRequest =
764
- (!request.headers.get("accept")?.includes("text/html") &&
765
- !url.searchParams.has("__html")) ||
766
- url.searchParams.has("__rsc");
755
+ });
767
756
 
768
- if (isRscRequest) {
769
- return createResponseWithMergedHeaders(rscStream, {
770
- status: 404,
771
- headers: { "content-type": "text/x-component;charset=utf-8" },
757
+ if (router.telemetry) {
758
+ safeEmit(resolveSink(router.telemetry), {
759
+ type: "request.origin-rejected",
760
+ timestamp: performance.now(),
761
+ requestId: getRequestId(request),
762
+ method: request.method,
763
+ pathname: url.pathname,
764
+ phase: originPhase,
765
+ origin: request.headers.get("origin"),
766
+ host: request.headers.get("host"),
772
767
  });
773
768
  }
774
769
 
775
- // Delegate to SSR for HTML response
776
- const ssrModule = await loadSSRModule();
777
- const htmlStream = await ssrModule.renderHTML(rscStream, { nonce });
778
-
779
- return createResponseWithMergedHeaders(htmlStream, {
780
- status: 404,
781
- headers: { "content-type": "text/html;charset=utf-8" },
782
- });
770
+ return originResult;
783
771
  }
784
-
785
- // Report unhandled errors
786
- callOnError(error, "routing", {
787
- request,
788
- url,
789
- env,
790
- handledByBoundary: false,
791
- });
792
- console.error(`[RSC] Error:`, error);
793
- throw error;
794
772
  }
773
+
774
+ // ---- 4. Execute ----
775
+ return executeRequest(
776
+ plan as ExecutableRequestPlan<TEnv>,
777
+ request,
778
+ env,
779
+ url,
780
+ variables,
781
+ nonce,
782
+ );
795
783
  }
796
784
 
797
- // ============================================================================
798
- // PROGRESSIVE ENHANCEMENT HANDLER
799
- // When JavaScript is disabled, React renders forms with hidden fields
800
- // ($ACTION_REF_*, $ACTION_KEY) containing the action reference.
801
- // We detect these and return HTML instead of RSC stream.
802
- // ============================================================================
803
- async function handleProgressiveEnhancement(
785
+ // Execute a classified request plan. Dispatches to the appropriate handler
786
+ // based on plan.mode. Lives in the createRSCHandler closure for access to
787
+ // handlerCtx, router, callOnError, etc.
788
+ // Only receives executable plans (version-mismatch is handled above).
789
+ async function executeRequest(
790
+ plan: ExecutableRequestPlan<TEnv>,
804
791
  request: Request,
805
792
  env: TEnv,
806
793
  url: URL,
807
- isAction: boolean,
808
- handleStore: ReturnType<typeof requireRequestContext>["_handleStore"],
794
+ variables: Record<string, any>,
809
795
  nonce: string | undefined,
810
- ): Promise<Response | null> {
811
- const contentType = request.headers.get("content-type") || "";
812
- const isFormSubmission =
813
- contentType.includes("multipart/form-data") ||
814
- contentType.includes("application/x-www-form-urlencoded");
815
-
816
- if (request.method !== "POST" || isAction || !isFormSubmission) {
817
- return null;
818
- }
819
-
820
- // Clone the request to read FormData without consuming it
821
- const formData = await request.clone().formData();
822
-
823
- // Look for React's progressive enhancement hidden fields
824
- let isDirectAction = false;
825
- let isUseActionState = false;
826
- let directActionId: string | null = null;
796
+ ): Promise<Response> {
797
+ // Common setup
798
+ const handleStore = getRequestContext()._handleStore;
827
799
 
828
- formData.forEach((_value, key) => {
829
- if (key.startsWith("$ACTION_ID_")) {
830
- isDirectAction = true;
831
- directActionId = key.slice("$ACTION_ID_".length);
832
- } else if (key.startsWith("$ACTION_REF_")) {
833
- isUseActionState = true;
800
+ // Wire up error reporting for late streaming-handle failures
801
+ handleStore.onError = (error: Error) => {
802
+ const reqCtx = getRequestContext();
803
+ callOnError(error, "handler", {
804
+ request,
805
+ url,
806
+ routeKey: reqCtx._routeName,
807
+ params: reqCtx.params as Record<string, string>,
808
+ handledByBoundary: true,
809
+ });
810
+ if (router.telemetry) {
811
+ safeEmit(resolveSink(router.telemetry), {
812
+ type: "handler.error",
813
+ timestamp: performance.now(),
814
+ requestId: getRequestId(request),
815
+ error,
816
+ handledByBoundary: true,
817
+ pathname: url.pathname,
818
+ routeKey: reqCtx._routeName,
819
+ params: reqCtx.params as Record<string, string>,
820
+ });
834
821
  }
835
- });
822
+ };
836
823
 
837
- if (!isDirectAction && !isUseActionState) {
838
- return null;
824
+ // Set route params early so all execution paths can access ctx.params.
825
+ // Also store the classified snapshot so match/matchPartial can reuse it
826
+ // instead of calling resolveRoute again.
827
+ if (plan.mode !== "redirect") {
828
+ setRequestContextParams(plan.route.params, plan.route.routeKey);
829
+ getRequestContext()._classifiedRoute = plan.route;
839
830
  }
840
831
 
841
- // Execute action and return HTML
842
- let actionResult: unknown = undefined;
843
- let reactFormState: ReactFormState | null = null;
844
-
845
- if (isUseActionState) {
846
- try {
847
- const boundAction = await decodeAction(formData);
848
- actionResult = await boundAction();
849
- } catch (error) {
850
- callOnError(error, "action", {
832
+ const routeReverse = createReverseFunction(getRequiredRouteMap());
833
+
834
+ // ---- Response route: skip entire RSC pipeline ----
835
+ if (plan.mode === "response") {
836
+ // Build ResponseRouteMatch from plan fields. handleResponseRoute
837
+ // expects a flat object with params at the top level.
838
+ const responseMatch: ResponseRouteMatch = {
839
+ responseType: plan.responseType,
840
+ handler: plan.handler,
841
+ params: plan.route.params,
842
+ negotiated: plan.negotiated,
843
+ manifestEntry: plan.manifestEntry,
844
+ routeMiddleware: plan.routeMiddleware,
845
+ };
846
+ const responseOutcome = await withTimeout(
847
+ handleResponseRoute(
848
+ handlerCtx,
849
+ responseMatch,
851
850
  request,
852
- url,
853
851
  env,
854
- handledByBoundary: false,
855
- });
856
- console.error("[RSC] Progressive enhancement action error:", error);
857
- }
858
- } else if (isDirectAction && directActionId) {
859
- const temporaryReferences = createTemporaryReferenceSet();
860
-
861
- let args: unknown[] = [];
862
- try {
863
- args = await decodeReply(formData, { temporaryReferences });
864
- } catch {
865
- args = [formData];
866
- }
867
-
868
- try {
869
- const loadedAction = await loadServerAction(directActionId);
870
- actionResult = await loadedAction.apply(null, args);
871
- } catch (error) {
872
- callOnError(error, "action", {
873
- request,
874
852
  url,
853
+ variables,
854
+ ),
855
+ router.timeouts.renderStartMs,
856
+ "render-start",
857
+ );
858
+ if (responseOutcome.timedOut) {
859
+ return handleTimeoutResponse(
860
+ request,
875
861
  env,
876
- actionId: directActionId,
877
- handledByBoundary: false,
878
- });
879
- console.error("[RSC] Progressive enhancement action error:", error);
862
+ url,
863
+ "render-start",
864
+ responseOutcome.durationMs,
865
+ plan.route.routeKey,
866
+ );
867
+ }
868
+ const response = responseOutcome.result;
869
+ if (plan.negotiated && !isWebSocketUpgradeResponse(response)) {
870
+ // handleResponseRoute (callHandlerWithVary) already appends Vary: Accept
871
+ // for negotiated responses; dedup so we don't emit Vary: Accept, Accept.
872
+ appendVaryAccept(response);
880
873
  }
874
+ return response;
881
875
  }
882
876
 
883
- // Decode form state for useActionState progressive enhancement
884
- try {
885
- reactFormState = await decodeFormState(actionResult, formData);
886
- } catch (error) {
887
- callOnError(error, "action", {
877
+ // SSR setup: kick off in parallel for modes that need HTML rendering.
878
+ // Placed after response-route short-circuit so response/mime routes
879
+ // never pay for SSR work.
880
+ //
881
+ // Only kick off when the request will actually render HTML, so the
882
+ // eager loadSSRModule() + user resolveStreaming() are not started (and
883
+ // never consumed) for a request that returns an RSC stream — that wasted
884
+ // work also leaves an orphaned Promise.all that can reject (D7). PE form
885
+ // submissions always render HTML (handleProgressiveEnhancement renders via
886
+ // getSSRSetup regardless of Accept). For full/partial-render and action,
887
+ // the render-time HTML decision is exactly !isRscRequest — mayNeedSSR is
888
+ // the coarse transport pre-filter, isRscRequest adds the partial/__rsc
889
+ // flags; both share the same Accept rule (acceptsFlightExplicitly in
890
+ // ssr-setup.ts), so the Accept call cannot drift between them. Both must
891
+ // pass.
892
+ const willRenderHtml =
893
+ plan.mode === "pe-render" ||
894
+ (mayNeedSSR(request, url) &&
895
+ !isRscRequest(request, url, plan.mode === "partial-render"));
896
+ if (plan.mode !== "loader" && willRenderHtml) {
897
+ variables[SSR_SETUP_VAR] = startSSRSetup(
898
+ handlerCtx,
888
899
  request,
889
- url,
890
900
  env,
891
- handledByBoundary: false,
892
- });
893
- console.error("[RSC] Failed to decode form state:", error);
894
- }
895
-
896
- // Re-render the page and return HTML
897
- const renderRequest = new Request(url.toString(), {
898
- method: "GET",
899
- headers: new Headers({ accept: "text/html" }),
900
- });
901
-
902
- const match = await router.match(renderRequest, env);
903
-
904
- if (match.redirect) {
905
- return new Response(null, {
906
- status: 308,
907
- headers: { Location: match.redirect },
908
- });
901
+ url,
902
+ router.debugPerformance
903
+ ? () => getRequestContext()._metricsStore
904
+ : undefined,
905
+ );
909
906
  }
910
907
 
911
- const root = renderSegments(match.segments, {
912
- rootLayout: router.rootLayout,
913
- });
914
-
915
- const payload: RscPayload = {
916
- root,
917
- metadata: {
918
- pathname: url.pathname,
919
- segments: match.segments,
920
- matched: match.matched,
921
- diff: match.diff,
922
- isPartial: false,
923
- rootLayout: router.rootLayout,
924
- handles: handleStore.stream(),
925
- version,
926
- themeConfig: router.themeConfig,
927
- warmupEnabled: router.warmupEnabled,
928
- initialTheme: requireRequestContext().theme,
929
- },
930
- formState: actionResult,
931
- };
932
-
933
- const rscStream = renderToReadableStream<RscPayload>(payload);
934
- const ssrModule = await loadSSRModule();
935
- const htmlStream = await ssrModule.renderHTML(rscStream, {
936
- formState: reactFormState,
937
- nonce,
938
- });
939
-
940
- return new Response(htmlStream, {
941
- headers: { "content-type": "text/html;charset=utf-8" },
942
- });
943
- }
944
-
945
- // ============================================================================
946
- // SERVER ACTION HANDLER
947
- // ============================================================================
948
- async function handleServerAction(
949
- request: Request,
950
- env: TEnv,
951
- url: URL,
952
- actionId: string,
953
- handleStore: ReturnType<typeof requireRequestContext>["_handleStore"],
954
- ): Promise<Response> {
955
- const temporaryReferences = createTemporaryReferenceSet();
956
-
957
- // Decode action arguments from request body
958
- const contentType = request.headers.get("content-type") || "";
959
- let args: unknown[] = [];
960
- let actionFormData: FormData | undefined;
961
-
962
- try {
963
- const body = contentType.includes("multipart/form-data")
964
- ? await request.formData()
965
- : await request.text();
966
-
967
- if (body instanceof FormData) {
968
- actionFormData = body;
969
- }
970
-
971
- if (hasBodyContent(body)) {
972
- args = await decodeReply(body, { temporaryReferences });
973
- }
974
- } catch (error) {
975
- callOnError(error, "action", {
908
+ // ---- Loader fetch ----
909
+ if (plan.mode === "loader") {
910
+ return handleLoaderFetch(
911
+ handlerCtx,
976
912
  request,
977
- url,
978
913
  env,
979
- actionId,
980
- handledByBoundary: false,
981
- });
982
- throw new Error(`Failed to decode action arguments: ${error}`, {
983
- cause: error,
984
- });
914
+ url,
915
+ variables,
916
+ plan.route.params,
917
+ );
985
918
  }
986
919
 
987
- // Execute the server action
988
- let returnValue: { ok: boolean; data: unknown };
989
- let actionStatus = 200;
990
- let loadedAction: Function | undefined;
991
-
992
- try {
993
- loadedAction = await loadServerAction(actionId);
994
- const data = await loadedAction!.apply(null, args);
995
- returnValue = { ok: true, data };
996
- } catch (error) {
997
- returnValue = { ok: false, data: error };
998
- actionStatus = 500;
999
-
1000
- // Try to render error boundary
1001
- const errorResult = await router.matchError(request, env, error, "route");
1002
-
1003
- // Report the action error (handledByBoundary indicates if error boundary will render)
1004
- callOnError(error, "action", {
920
+ // ---- Progressive enhancement ----
921
+ if (plan.mode === "pe-render") {
922
+ const peResult = await handleProgressiveEnhancement(
923
+ handlerCtx,
1005
924
  request,
1006
- url,
1007
925
  env,
1008
- actionId,
1009
- handledByBoundary: !!errorResult,
1010
- });
1011
-
1012
- if (errorResult) {
1013
- setRequestContextParams(errorResult.params);
1014
-
1015
- const payload: RscPayload = {
1016
- root: null,
1017
- metadata: {
1018
- pathname: url.pathname,
1019
- segments: errorResult.segments,
1020
- isPartial: true,
1021
- matched: errorResult.matched,
1022
- diff: errorResult.diff,
1023
- isError: true,
1024
- handles: handleStore.stream(),
1025
- version,
1026
- },
1027
- returnValue,
1028
- };
1029
-
1030
- const rscStream = renderToReadableStream<RscPayload>(payload, {
1031
- temporaryReferences,
1032
- });
1033
-
1034
- return createResponseWithMergedHeaders(rscStream, {
1035
- status: actionStatus,
1036
- headers: { "content-type": "text/x-component;charset=utf-8" },
1037
- });
1038
- }
926
+ url,
927
+ false, // isAction = false for PE
928
+ handleStore,
929
+ nonce,
930
+ {
931
+ routeMiddleware: plan.route.routeMiddleware,
932
+ variables,
933
+ routeReverse,
934
+ },
935
+ );
936
+ if (peResult) return peResult;
937
+ // PE handler returned null (not a PE form) — fall through to render
1039
938
  }
1040
939
 
1041
- // Revalidate after action
1042
- const resolvedActionId =
1043
- (loadedAction as { $id?: string; $$id?: string } | undefined)?.$id ??
1044
- (loadedAction as { $$id?: string } | undefined)?.$$id ??
1045
- actionId;
1046
- const actionContext = {
1047
- actionId: resolvedActionId,
1048
- actionUrl: new URL(request.url),
1049
- actionResult: returnValue.data,
1050
- formData: actionFormData,
1051
- };
1052
-
1053
- const matchResult = await router.matchPartial(request, env, actionContext);
1054
-
1055
- if (!matchResult) {
1056
- // Fall back to full render
1057
- const fullMatch = await router.match(request, env);
1058
- setRequestContextParams(fullMatch.params);
1059
-
1060
- if (fullMatch.redirect) {
1061
- return createResponseWithMergedHeaders(null, {
1062
- status: 308,
1063
- headers: { Location: fullMatch.redirect },
940
+ // ---- Action: execute action, then revalidate wrapped in route middleware ----
941
+ if (plan.mode === "action") {
942
+ let actionContinuation: ActionContinuation | undefined;
943
+ try {
944
+ // Instrument the action execution as its own phase (action:<actionId> +
945
+ // rango.action), so a POST shows the mutation time AND which action ran,
946
+ // not just the downstream revalidation render. The action's own
947
+ // loaders/fetches nest under rango.action.
948
+ const actionOutcome = await withTimeout(
949
+ observePhase(PHASES.action(plan.actionId), () =>
950
+ executeServerAction(
951
+ handlerCtx,
952
+ request,
953
+ env,
954
+ url,
955
+ plan.actionId,
956
+ handleStore,
957
+ ),
958
+ ),
959
+ router.timeouts.actionMs,
960
+ "action",
961
+ );
962
+ if (actionOutcome.timedOut) {
963
+ return handleTimeoutResponse(
964
+ request,
965
+ env,
966
+ url,
967
+ "action",
968
+ actionOutcome.durationMs,
969
+ plan.route.routeKey,
970
+ plan.actionId,
971
+ );
972
+ }
973
+ const result = actionOutcome.result;
974
+ // Response means redirect or error boundary — done.
975
+ if (result instanceof Response) return result;
976
+ actionContinuation = result;
977
+ } catch (error) {
978
+ callOnError(error, "action", {
979
+ request,
980
+ url,
981
+ env,
982
+ actionId: plan.actionId,
983
+ handledByBoundary: false,
1064
984
  });
985
+ console.error(`[RSC] Action error:`, error);
986
+ throw error;
1065
987
  }
1066
988
 
1067
- const renderStart = performance.now();
1068
- const root = renderSegments(fullMatch.segments, {
1069
- rootLayout: router.rootLayout,
1070
- isAction: true,
1071
- });
1072
- const renderDuration = performance.now() - renderStart;
1073
- const serverTiming = fullMatch.serverTiming
1074
- ? `${fullMatch.serverTiming}, rendering;dur=${renderDuration.toFixed(2)}`
1075
- : `rendering;dur=${renderDuration.toFixed(2)}`;
1076
-
1077
- const payload: RscPayload = {
1078
- root,
1079
- metadata: {
1080
- pathname: url.pathname,
1081
- segments: fullMatch.segments,
1082
- matched: fullMatch.matched,
1083
- diff: fullMatch.diff,
1084
- handles: handleStore.stream(),
1085
- version,
1086
- },
1087
- returnValue,
1088
- };
1089
-
1090
- const rscStream = renderToReadableStream<RscPayload>(payload, {
1091
- temporaryReferences,
1092
- });
1093
-
1094
- const headers: Record<string, string> = {
1095
- "content-type": "text/x-component;charset=utf-8",
1096
- };
1097
- if (serverTiming) {
1098
- headers["Server-Timing"] = serverTiming;
1099
- }
1100
-
1101
- return createResponseWithMergedHeaders(rscStream, {
1102
- status: actionStatus,
1103
- headers,
1104
- });
1105
- }
1106
-
1107
- // Return updated segments
1108
- setRequestContextParams(matchResult.params);
1109
-
1110
- const renderStart = performance.now();
1111
-
1112
- const renderDuration = performance.now() - renderStart;
1113
- const serverTiming = matchResult.serverTiming
1114
- ? `${matchResult.serverTiming}, rendering;dur=${renderDuration.toFixed(2)}`
1115
- : `rendering;dur=${renderDuration.toFixed(2)}`;
1116
-
1117
- const payload: RscPayload = {
1118
- root: null,
1119
- metadata: {
1120
- pathname: url.pathname,
1121
- segments: matchResult.segments,
1122
- isPartial: true,
1123
- matched: matchResult.matched,
1124
- diff: matchResult.diff,
1125
- slots: matchResult.slots,
1126
- handles: handleStore.stream(),
1127
- version,
1128
- },
1129
- returnValue,
1130
- };
1131
-
1132
- const rscStream = renderToReadableStream<RscPayload>(payload, {
1133
- temporaryReferences,
1134
- });
1135
-
1136
- const actionHeaders: Record<string, string> = {
1137
- "content-type": "text/x-component;charset=utf-8",
1138
- };
1139
- if (serverTiming) {
1140
- actionHeaders["Server-Timing"] = serverTiming;
989
+ // Revalidation render wrapped in route middleware.
990
+ // Actions from client-side navigation include _rsc_partial — preserve
991
+ // the partial flag so the revalidation returns a Flight stream, not HTML.
992
+ // App-switch is already excluded by classifyRequest (would be full-render).
993
+ const isPartialAction = url.searchParams.has("_rsc_partial");
994
+ return executeRenderWithMiddleware(
995
+ plan.route.routeMiddleware,
996
+ plan.negotiated,
997
+ plan.route.routeKey,
998
+ routeReverse,
999
+ request,
1000
+ env,
1001
+ url,
1002
+ variables,
1003
+ nonce,
1004
+ handleStore,
1005
+ isPartialAction,
1006
+ actionContinuation,
1007
+ );
1141
1008
  }
1142
1009
 
1143
- return createResponseWithMergedHeaders(rscStream, {
1144
- status: actionStatus,
1145
- headers: actionHeaders,
1146
- });
1010
+ // Full render, partial render, fallen-through PE, and full-page redirect all
1011
+ // render through the same middleware-wrapped path. Only full/partial-render
1012
+ // carry negotiation + the partial flag; pe/redirect render plainly.
1013
+ const isPartial = plan.mode === "partial-render";
1014
+ const negotiated =
1015
+ plan.mode === "full-render" || plan.mode === "partial-render"
1016
+ ? plan.negotiated
1017
+ : false;
1018
+ return executeRenderWithMiddleware(
1019
+ plan.route.routeMiddleware,
1020
+ negotiated,
1021
+ plan.route.routeKey,
1022
+ routeReverse,
1023
+ request,
1024
+ env,
1025
+ url,
1026
+ variables,
1027
+ nonce,
1028
+ handleStore,
1029
+ isPartial,
1030
+ );
1147
1031
  }
1148
1032
 
1149
- // ============================================================================
1150
- // LOADER FETCH HANDLER
1151
- // Supports GET (params in query string) and POST/PUT/PATCH/DELETE (JSON body)
1152
- // ============================================================================
1153
- async function handleLoaderFetch(
1033
+ // Shared render execution: wraps handleRscRendering (or revalidateAfterAction)
1034
+ // in route middleware and timeout handling. Consolidates the pattern used by
1035
+ // action-revalidate, full-render, and partial-render modes.
1036
+ async function executeRenderWithMiddleware(
1037
+ routeMiddleware: import("../router/middleware-types.js").CollectedMiddleware[],
1038
+ negotiated: boolean,
1039
+ routeKey: string,
1040
+ routeReverse: ReturnType<typeof createReverseFunction>,
1154
1041
  request: Request,
1155
1042
  env: TEnv,
1156
1043
  url: URL,
1157
1044
  variables: Record<string, any>,
1045
+ nonce: string | undefined,
1046
+ handleStore: ReturnType<typeof getRequestContext>["_handleStore"],
1047
+ isPartial: boolean,
1048
+ actionContinuation?: ActionContinuation,
1158
1049
  ): Promise<Response> {
1159
- const loaderId = url.searchParams.get("_rsc_loader");
1050
+ const renderHandler = async (): Promise<Response> => {
1051
+ try {
1052
+ let response: Response;
1053
+ if (actionContinuation) {
1054
+ response = await revalidateAfterAction(
1055
+ handlerCtx,
1056
+ request,
1057
+ env,
1058
+ url,
1059
+ handleStore,
1060
+ actionContinuation,
1061
+ );
1062
+ } else {
1063
+ response = await handleRscRendering(
1064
+ handlerCtx,
1065
+ request,
1066
+ env,
1067
+ url,
1068
+ isPartial,
1069
+ handleStore,
1070
+ nonce,
1071
+ );
1072
+ }
1073
+ if (negotiated && !isWebSocketUpgradeResponse(response)) {
1074
+ // handleRscRendering bakes `accept` into the RSC response's Vary list;
1075
+ // dedup so the negotiated append does not list accept twice.
1076
+ appendVaryAccept(response);
1077
+ }
1078
+ return response;
1079
+ } catch (error) {
1080
+ // Check if middleware/handler returned Response
1081
+ if (error instanceof Response) {
1082
+ // An action revalidation render is delivered to the client over the
1083
+ // same Flight-parsing path as a partial navigation, so a Response
1084
+ // thrown during it must be converted exactly like a partial one
1085
+ // (raw 200 -> hard-nav hint, 3xx -> Flight redirect). Without this,
1086
+ // the no-middleware path returns the raw Response (the with-middleware
1087
+ // path is already covered by the isPartial || actionContinuation
1088
+ // guard below).
1089
+ const treatAsPartial = isPartial || actionContinuation != null;
1090
+
1091
+ // During partial (client-side navigation), a 200 Response from a handler
1092
+ // means the route serves raw content (JSON, text, etc.), not JSX.
1093
+ // Signal the browser to hard-navigate so it renders the raw response.
1094
+ if (treatAsPartial && error.status === 200) {
1095
+ console.warn(
1096
+ `[RSC] Route handler at ${url.pathname} returned a Response during client-side navigation. ` +
1097
+ `Falling back to hard navigation. Use data-external on the <Link> to avoid the extra round-trip.`,
1098
+ );
1099
+ return createResponseWithMergedHeaders(null, {
1100
+ status: 200,
1101
+ headers: {
1102
+ "X-RSC-Reload": stripInternalParams(url).toString(),
1103
+ "content-type": "text/x-component;charset=utf-8",
1104
+ },
1105
+ });
1106
+ }
1160
1107
 
1161
- if (!loaderId) {
1162
- return createResponseWithMergedHeaders("Missing _rsc_loader parameter", {
1163
- status: 400,
1164
- });
1165
- }
1108
+ if (treatAsPartial) {
1109
+ const intercepted = interceptRedirectForPartial(
1110
+ error,
1111
+ createRedirectFlightResponse,
1112
+ );
1113
+ if (intercepted) return intercepted;
1114
+ }
1166
1115
 
1167
- // Look up loader lazily
1168
- const registeredLoader = await getLoaderLazy(loaderId);
1169
- if (!registeredLoader) {
1170
- return createResponseWithMergedHeaders(
1171
- `Loader "${loaderId}" not found in registry`,
1172
- { status: 404 },
1173
- );
1174
- }
1116
+ return error;
1117
+ }
1175
1118
 
1176
- // Parse params and body based on request method
1177
- let loaderParams: Record<string, string> = {};
1178
- let loaderBody: unknown = undefined;
1179
- const isBodyMethod = request.method !== "GET" && request.method !== "HEAD";
1119
+ // Render 404 page for unmatched routes
1120
+ if (isRouteNotFoundError(error)) {
1121
+ callOnError(error, "routing", {
1122
+ request,
1123
+ url,
1124
+ env,
1125
+ handledByBoundary: true,
1126
+ });
1180
1127
 
1181
- if (isBodyMethod) {
1182
- try {
1183
- const contentType = request.headers.get("content-type") || "";
1184
- if (contentType.includes("application/json")) {
1185
- const jsonBody = (await request.json()) as {
1186
- params?: Record<string, string>;
1187
- body?: unknown;
1128
+ const notFoundOption = router.notFound;
1129
+ const notFoundComponent =
1130
+ typeof notFoundOption === "function"
1131
+ ? notFoundOption({ pathname: url.pathname })
1132
+ : (notFoundOption ?? createElement("h1", null, "Not Found"));
1133
+
1134
+ const notFoundSegment = {
1135
+ id: "notFound",
1136
+ namespace: "notFound",
1137
+ type: "route" as const,
1138
+ index: 0,
1139
+ component: notFoundComponent,
1140
+ params: {},
1188
1141
  };
1189
- loaderParams = jsonBody.params ?? {};
1190
- loaderBody = jsonBody.body;
1191
- }
1192
- } catch {
1193
- return createResponseWithMergedHeaders("Invalid JSON body", {
1194
- status: 400,
1195
- });
1196
- }
1197
- } else {
1198
- const loaderParamsJson = url.searchParams.get("_rsc_loader_params");
1199
- if (loaderParamsJson) {
1200
- try {
1201
- loaderParams = JSON.parse(loaderParamsJson);
1202
- } catch {
1203
- return createResponseWithMergedHeaders(
1204
- "Invalid _rsc_loader_params JSON",
1205
- { status: 400 },
1206
- );
1207
- }
1208
- }
1209
- }
1210
1142
 
1211
- // Execute the loader with middleware
1212
- try {
1213
- const { fn, middleware } = registeredLoader;
1214
-
1215
- return await executeLoaderMiddleware(
1216
- middleware,
1217
- request,
1218
- env,
1219
- loaderParams,
1220
- variables,
1221
- async () => {
1222
- const ctx = requireRequestContext();
1223
- const loaderCtx: any = {
1224
- ...ctx,
1225
- params: loaderParams,
1226
- body: loaderBody,
1143
+ const payload: RscPayload = {
1144
+ metadata: {
1145
+ pathname: url.pathname,
1146
+ routerId: router.id,
1147
+ basename: router.basename,
1148
+ segments: [notFoundSegment],
1149
+ matched: [],
1150
+ diff: [],
1151
+ // Shape parity with buildFullPayload (rsc-rendering.ts): the 404
1152
+ // payload carries params/resolvedIds/prefetchCacheTTL the same way a
1153
+ // matched full render does. resolvedIds mirrors the rendered segment
1154
+ // list (the single notFound segment) like the error-boundary path
1155
+ // (match-api.ts) does for its boundary segment.
1156
+ resolvedIds: [notFoundSegment.id],
1157
+ params: {},
1158
+ isPartial: false,
1159
+ rootLayout: router.rootLayout,
1160
+ // Full (404) render: resolve deferred handle values server-side.
1161
+ handles: resolvedHandleStream(handleStore),
1162
+ version,
1163
+ prefetchCacheTTL: router.prefetchCacheTTL,
1164
+ prefetchCacheSize: router.prefetchCacheSize,
1165
+ prefetchConcurrency: router.prefetchConcurrency,
1166
+ stateCookieName: router.resolvedStateCookieName,
1167
+ themeConfig: router.themeConfig,
1168
+ warmupEnabled: router.warmupEnabled,
1169
+ strictMode: router.strictMode,
1170
+ initialTheme: getRequestContext().theme,
1171
+ },
1227
1172
  };
1228
1173
 
1229
- const result = await fn(loaderCtx);
1174
+ const rscStream = renderToReadableStream(payload, {
1175
+ onError: (error: unknown) => {
1176
+ callOnError(error, "rendering", { request, url, env });
1177
+ },
1178
+ });
1230
1179
 
1231
- interface LoaderPayload {
1232
- loaderResult: unknown;
1180
+ if (isRscRequest(request, url, isPartial)) {
1181
+ return createResponseWithMergedHeaders(rscStream, {
1182
+ status: 404,
1183
+ headers: {
1184
+ "content-type": "text/x-component;charset=utf-8",
1185
+ // Router identity for the client's pre-decode integrity check; a
1186
+ // same-app 404 matches and applies in place. See response-adapter.
1187
+ "X-RSC-Router-Id": router.id,
1188
+ },
1189
+ });
1233
1190
  }
1234
- const loaderPayload: LoaderPayload = { loaderResult: result };
1235
- const rscStream =
1236
- renderToReadableStream<LoaderPayload>(loaderPayload);
1237
1191
 
1238
- return createResponseWithMergedHeaders(rscStream, {
1239
- headers: { "content-type": "text/x-component;charset=utf-8" },
1192
+ const [ssrModule, streamMode] = await getSSRSetup(
1193
+ handlerCtx,
1194
+ request,
1195
+ env,
1196
+ url,
1197
+ getRequestContext()._metricsStore,
1198
+ );
1199
+ const htmlStream = await ssrModule.renderHTML(rscStream, {
1200
+ nonce,
1201
+ streamMode,
1240
1202
  });
1241
- },
1242
- );
1243
- } catch (error) {
1244
- const err = error instanceof Error ? error : new Error(String(error));
1245
- const isDev = process.env.NODE_ENV !== "production";
1246
-
1247
- console.error("[RSC] Loader error:", error);
1248
-
1249
- callOnError(error, "loader", {
1250
- request,
1251
- url,
1252
- env,
1253
- loaderName: loaderId,
1254
- handledByBoundary: false,
1255
- });
1256
1203
 
1257
- const errorPayload = {
1258
- loaderResult: null,
1259
- loaderError: {
1260
- message: isDev ? err.message : "An error occurred",
1261
- name: err.name,
1262
- },
1263
- };
1264
- const rscStream = renderToReadableStream(errorPayload);
1265
-
1266
- return createResponseWithMergedHeaders(rscStream, {
1267
- status: 500,
1268
- headers: { "content-type": "text/x-component;charset=utf-8" },
1269
- });
1270
- }
1271
- }
1272
-
1273
- // ============================================================================
1274
- // RSC RENDERING HANDLER (Navigation)
1275
- // ============================================================================
1276
- async function handleRscRendering(
1277
- request: Request,
1278
- env: TEnv,
1279
- url: URL,
1280
- isPartial: boolean,
1281
- handleStore: ReturnType<typeof requireRequestContext>["_handleStore"],
1282
- nonce: string | undefined,
1283
- ): Promise<Response> {
1284
- // Retrieve handler-level timing from variables
1285
- const reqCtx = requireRequestContext();
1286
- const handlerTimingArr: string[] = reqCtx.var.__handlerTiming || [];
1287
- const handlerStart: number = reqCtx.var.__handlerStart || 0;
1288
-
1289
- let payload: RscPayload;
1290
- let serverTiming: string | undefined;
1291
-
1292
- if (isPartial) {
1293
- // Partial render (navigation)
1294
- const result = await router.matchPartial(request, env);
1295
-
1296
- if (!result) {
1297
- // Fall back to full render
1298
- const match = await router.match(request, env);
1299
- setRequestContextParams(match.params);
1300
-
1301
- if (match.redirect) {
1302
- return createResponseWithMergedHeaders(null, {
1303
- status: 308,
1304
- headers: { Location: match.redirect },
1204
+ return createResponseWithMergedHeaders(htmlStream, {
1205
+ status: 404,
1206
+ headers: { "content-type": "text/html;charset=utf-8" },
1305
1207
  });
1306
1208
  }
1307
1209
 
1308
- const renderStart = performance.now();
1309
- const root = renderSegments(match.segments, {
1310
- rootLayout: router.rootLayout,
1311
- });
1312
- const renderDuration = performance.now() - renderStart;
1313
- serverTiming = match.serverTiming
1314
- ? `${match.serverTiming}, rendering;dur=${renderDuration.toFixed(2)}`
1315
- : `rendering;dur=${renderDuration.toFixed(2)}`;
1316
-
1317
- payload = {
1318
- root,
1319
- metadata: {
1320
- pathname: url.pathname,
1321
- segments: match.segments,
1322
- matched: match.matched,
1323
- diff: match.diff,
1324
- isPartial: false,
1325
- handles: handleStore.stream(),
1326
- version,
1327
- themeConfig: router.themeConfig,
1328
- initialTheme: reqCtx.theme,
1329
- },
1330
- };
1331
- } else {
1332
- setRequestContextParams(result.params);
1333
- serverTiming = result.serverTiming;
1334
-
1335
- payload = {
1336
- root: null,
1337
- metadata: {
1338
- pathname: url.pathname,
1339
- segments: result.segments,
1340
- matched: result.matched,
1341
- diff: result.diff,
1342
- isPartial: true,
1343
- slots: result.slots,
1344
- handles: handleStore.stream(),
1345
- version,
1346
- },
1347
- };
1348
- }
1349
- } else {
1350
- // Full render (initial page load)
1351
- const match = await router.match(request, env);
1352
- setRequestContextParams(match.params);
1353
-
1354
- if (match.redirect) {
1355
- return createResponseWithMergedHeaders(null, {
1356
- status: 308,
1357
- headers: { Location: match.redirect },
1210
+ // Report unhandled errors
1211
+ callOnError(error, "routing", {
1212
+ request,
1213
+ url,
1214
+ env,
1215
+ handledByBoundary: false,
1358
1216
  });
1217
+ console.error(`[RSC] Error:`, error);
1218
+ throw error;
1359
1219
  }
1220
+ };
1360
1221
 
1361
- // Caching is now handled in router.match() via cache provider in request context
1362
- // match.segments already contains cached or fresh segments as appropriate
1363
-
1364
- if (url.searchParams.has("__prerender_collect")) {
1365
- // Build-time prerender collection: serialize segments and handle data
1366
- // to JSON for storage as build artifacts. At runtime the worker
1367
- // deserializes these and feeds them through the normal segment pipeline.
1368
- const nonLoaderSegments = match.segments.filter((s) => s.type !== "loader");
1369
- await handleStore.settled;
1370
- const { serializeSegments } = await import("../cache/cache-scope.js");
1371
- const serializedSegments = await serializeSegments(nonLoaderSegments);
1372
- const handles: Record<string, Record<string, unknown[]>> = {};
1373
- for (const seg of nonLoaderSegments) {
1374
- const segHandles = handleStore.getDataForSegment(seg.id);
1375
- if (Object.keys(segHandles).length > 0) {
1376
- handles[seg.id] = segHandles;
1377
- }
1378
- }
1379
- return new Response(
1380
- JSON.stringify({
1381
- segments: serializedSegments,
1382
- handles,
1383
- routeName: match.routeName,
1384
- params: match.params,
1385
- }),
1386
- { headers: { "Content-Type": "application/json" } },
1222
+ // Wrap the render path in a renderStartMs timeout
1223
+ const executeRender = async (): Promise<Response> => {
1224
+ if (routeMiddleware.length > 0) {
1225
+ const mwResponse = await executeMiddleware(
1226
+ buildRouteMiddlewareEntries<TEnv>(routeMiddleware),
1227
+ request,
1228
+ env,
1229
+ variables,
1230
+ renderHandler,
1231
+ routeReverse,
1387
1232
  );
1388
- } else {
1389
- const renderStart = performance.now();
1390
- const root = renderSegments(match.segments, {
1391
- rootLayout: router.rootLayout,
1392
- });
1393
- const renderDuration = performance.now() - renderStart;
1394
- serverTiming = match.serverTiming
1395
- ? `${match.serverTiming}, rendering;dur=${renderDuration.toFixed(2)}`
1396
- : `rendering;dur=${renderDuration.toFixed(2)}`;
1397
-
1398
- payload = {
1399
- root,
1400
- metadata: {
1401
- pathname: url.pathname,
1402
- segments: match.segments,
1403
- matched: match.matched,
1404
- diff: match.diff,
1405
- isPartial: false,
1406
- rootLayout: router.rootLayout,
1407
- handles: handleStore.stream(),
1408
- version,
1409
- themeConfig: router.themeConfig,
1410
- initialTheme: reqCtx.theme,
1411
- },
1412
- };
1413
- }
1414
- }
1415
1233
 
1416
- // Serialize to RSC stream
1417
- const rscSerializeStart = performance.now();
1418
- const rscStream = renderToReadableStream<RscPayload>(payload);
1419
- const rscSerializeDur = performance.now() - rscSerializeStart;
1420
-
1421
- // Determine if this is an RSC request or HTML request
1422
- const isRscRequest =
1423
- (!request.headers.get("accept")?.includes("text/html") &&
1424
- !url.searchParams.has("__html")) ||
1425
- url.searchParams.has("__rsc");
1426
-
1427
- // Build complete Server-Timing: handler phases + match/manifest + rendering + RSC serialize
1428
- const timingParts: string[] = [...handlerTimingArr];
1429
- if (serverTiming) {
1430
- timingParts.push(serverTiming);
1431
- }
1432
- timingParts.push(`rsc-serialize;dur=${rscSerializeDur.toFixed(2)}`);
1234
+ if (isPartial || actionContinuation) {
1235
+ const intercepted = interceptRedirectForPartial(
1236
+ mwResponse,
1237
+ createRedirectFlightResponse,
1238
+ );
1239
+ if (intercepted) return intercepted;
1240
+ }
1433
1241
 
1434
- if (isRscRequest) {
1435
- const fullTiming = timingParts.join(", ");
1436
- const rscHeaders: Record<string, string> = {
1437
- "content-type": "text/x-component;charset=utf-8",
1438
- vary: "accept",
1439
- };
1440
- if (fullTiming) {
1441
- rscHeaders["Server-Timing"] = fullTiming;
1242
+ return finalizeResponse(mwResponse);
1442
1243
  }
1443
- return createResponseWithMergedHeaders(rscStream, {
1444
- headers: rscHeaders,
1445
- });
1446
- }
1447
-
1448
- // Delegate to SSR for HTML response
1449
- const ssrModuleStart = performance.now();
1450
- const ssrModule = await loadSSRModule();
1451
- const ssrModuleDur = performance.now() - ssrModuleStart;
1452
- timingParts.push(`ssr-module-load;dur=${ssrModuleDur.toFixed(2)}`);
1453
-
1454
- const ssrRenderStart = performance.now();
1455
- const htmlStream = await ssrModule.renderHTML(rscStream, { nonce });
1456
- const ssrRenderDur = performance.now() - ssrRenderStart;
1457
- timingParts.push(`ssr-render-html;dur=${ssrRenderDur.toFixed(2)}`);
1458
-
1459
- // Add total handler duration
1460
- if (handlerStart) {
1461
- const totalHandler = performance.now() - handlerStart;
1462
- timingParts.push(`handler-total;dur=${totalHandler.toFixed(2)}`);
1463
- }
1464
1244
 
1465
- const fullTiming = timingParts.join(", ");
1466
- const htmlHeaders: Record<string, string> = {
1467
- "content-type": "text/html;charset=utf-8",
1245
+ return renderHandler();
1468
1246
  };
1469
- if (fullTiming) {
1470
- htmlHeaders["Server-Timing"] = fullTiming;
1471
- }
1472
1247
 
1473
- return createResponseWithMergedHeaders(htmlStream, {
1474
- headers: htmlHeaders,
1475
- });
1248
+ const renderOutcome = await withTimeout(
1249
+ executeRender(),
1250
+ router.timeouts.renderStartMs,
1251
+ "render-start",
1252
+ );
1253
+ if (renderOutcome.timedOut) {
1254
+ return handleTimeoutResponse(
1255
+ request,
1256
+ env,
1257
+ url,
1258
+ "render-start",
1259
+ renderOutcome.durationMs,
1260
+ routeKey,
1261
+ );
1262
+ }
1263
+ return renderOutcome.result;
1476
1264
  }
1477
1265
  }