@rangojs/router 0.0.0-experimental.9 → 0.0.0-experimental.90

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 (1125) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +972 -4
  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 +1619 -155
  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/dist/browser/react/use-client-cache.js +39 -0
  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/dist/handles/index.js +6 -0
  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/dist/network-error-thrower.d.ts +17 -0
  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/theme/ThemeProvider.d.ts +20 -0
  712. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  713. package/dist/theme/ThemeProvider.js +240 -0
  714. package/dist/theme/ThemeProvider.js.map +1 -0
  715. package/dist/theme/ThemeScript.d.ts +48 -0
  716. package/dist/theme/ThemeScript.d.ts.map +1 -0
  717. package/dist/theme/ThemeScript.js +13 -0
  718. package/dist/theme/ThemeScript.js.map +1 -0
  719. package/dist/theme/__tests__/theme.test.d.ts +2 -0
  720. package/dist/theme/__tests__/theme.test.d.ts.map +1 -0
  721. package/dist/theme/__tests__/theme.test.js +103 -0
  722. package/dist/theme/__tests__/theme.test.js.map +1 -0
  723. package/dist/theme/constants.d.ts +29 -0
  724. package/dist/theme/constants.d.ts.map +1 -0
  725. package/dist/theme/constants.js +48 -0
  726. package/dist/theme/constants.js.map +1 -0
  727. package/dist/theme/index.d.ts +31 -0
  728. package/dist/theme/index.d.ts.map +1 -0
  729. package/dist/theme/index.js +36 -0
  730. package/dist/theme/index.js.map +1 -0
  731. package/dist/theme/theme-context.d.ts +40 -0
  732. package/dist/theme/theme-context.d.ts.map +1 -0
  733. package/dist/theme/theme-context.js +60 -0
  734. package/dist/theme/theme-context.js.map +1 -0
  735. package/dist/theme/theme-script.d.ts +27 -0
  736. package/dist/theme/theme-script.d.ts.map +1 -0
  737. package/dist/theme/theme-script.js +147 -0
  738. package/dist/theme/theme-script.js.map +1 -0
  739. package/dist/theme/types.d.ts +163 -0
  740. package/dist/theme/types.d.ts.map +1 -0
  741. package/dist/theme/types.js +11 -0
  742. package/dist/theme/types.js.map +1 -0
  743. package/dist/theme/use-theme.d.ts +12 -0
  744. package/dist/theme/use-theme.d.ts.map +1 -0
  745. package/dist/theme/use-theme.js +40 -0
  746. package/dist/theme/use-theme.js.map +1 -0
  747. package/dist/types.d.ts +1479 -0
  748. package/dist/types.d.ts.map +1 -0
  749. package/dist/types.js +10 -0
  750. package/dist/types.js.map +1 -0
  751. package/dist/urls.d.ts +441 -0
  752. package/dist/urls.d.ts.map +1 -0
  753. package/dist/urls.gen.d.ts +8 -0
  754. package/dist/urls.gen.d.ts.map +1 -0
  755. package/dist/urls.gen.js +8 -0
  756. package/dist/urls.gen.js.map +1 -0
  757. package/dist/urls.js +443 -0
  758. package/dist/urls.js.map +1 -0
  759. package/dist/use-loader.d.ts +127 -0
  760. package/dist/use-loader.d.ts.map +1 -0
  761. package/dist/use-loader.js +237 -0
  762. package/dist/use-loader.js.map +1 -0
  763. package/dist/vite/__tests__/ast-handler-extract.test.d.ts +2 -0
  764. package/dist/vite/__tests__/ast-handler-extract.test.d.ts.map +1 -0
  765. package/dist/vite/__tests__/ast-handler-extract.test.js +294 -0
  766. package/dist/vite/__tests__/ast-handler-extract.test.js.map +1 -0
  767. package/dist/vite/__tests__/expose-id-utils.test.d.ts +2 -0
  768. package/dist/vite/__tests__/expose-id-utils.test.d.ts.map +1 -0
  769. package/dist/vite/__tests__/expose-id-utils.test.js +224 -0
  770. package/dist/vite/__tests__/expose-id-utils.test.js.map +1 -0
  771. package/dist/vite/__tests__/expose-internal-ids.test.d.ts +2 -0
  772. package/dist/vite/__tests__/expose-internal-ids.test.d.ts.map +1 -0
  773. package/dist/vite/__tests__/expose-internal-ids.test.js +647 -0
  774. package/dist/vite/__tests__/expose-internal-ids.test.js.map +1 -0
  775. package/dist/vite/__tests__/expose-router-id.test.d.ts +2 -0
  776. package/dist/vite/__tests__/expose-router-id.test.d.ts.map +1 -0
  777. package/dist/vite/__tests__/expose-router-id.test.js +39 -0
  778. package/dist/vite/__tests__/expose-router-id.test.js.map +1 -0
  779. package/dist/vite/ast-handler-extract.d.ts +49 -0
  780. package/dist/vite/ast-handler-extract.d.ts.map +1 -0
  781. package/dist/vite/ast-handler-extract.js +249 -0
  782. package/dist/vite/ast-handler-extract.js.map +1 -0
  783. package/dist/vite/expose-action-id.d.ts +19 -0
  784. package/dist/vite/expose-action-id.d.ts.map +1 -0
  785. package/dist/vite/expose-action-id.js +250 -0
  786. package/dist/vite/expose-action-id.js.map +1 -0
  787. package/dist/vite/expose-id-utils.d.ts +69 -0
  788. package/dist/vite/expose-id-utils.d.ts.map +1 -0
  789. package/dist/vite/expose-id-utils.js +289 -0
  790. package/dist/vite/expose-id-utils.js.map +1 -0
  791. package/dist/vite/expose-internal-ids.d.ts +22 -0
  792. package/dist/vite/expose-internal-ids.d.ts.map +1 -0
  793. package/dist/vite/expose-internal-ids.js +886 -0
  794. package/dist/vite/expose-internal-ids.js.map +1 -0
  795. package/dist/vite/index.d.ts +149 -0
  796. package/dist/vite/index.d.ts.map +1 -0
  797. package/dist/vite/index.js +5565 -2291
  798. package/dist/vite/index.js.bak +5448 -0
  799. package/dist/vite/index.js.map +1 -0
  800. package/dist/vite/index.named-routes.gen.ts +103 -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 +64 -56
  811. package/skills/breadcrumbs/SKILL.md +252 -0
  812. package/skills/cache-guide/SKILL.md +294 -0
  813. package/skills/caching/SKILL.md +93 -23
  814. package/skills/composability/SKILL.md +172 -0
  815. package/skills/debug-manifest/SKILL.md +12 -8
  816. package/skills/document-cache/SKILL.md +18 -16
  817. package/skills/fonts/SKILL.md +6 -4
  818. package/skills/handler-use/SKILL.md +362 -0
  819. package/skills/hooks/SKILL.md +341 -71
  820. package/skills/host-router/SKILL.md +218 -0
  821. package/skills/intercept/SKILL.md +151 -8
  822. package/skills/layout/SKILL.md +122 -3
  823. package/skills/links/SKILL.md +158 -25
  824. package/skills/loader/SKILL.md +439 -46
  825. package/skills/middleware/SKILL.md +205 -37
  826. package/skills/migrate-nextjs/SKILL.md +560 -0
  827. package/skills/migrate-react-router/SKILL.md +765 -0
  828. package/skills/mime-routes/SKILL.md +15 -11
  829. package/skills/parallel/SKILL.md +263 -1
  830. package/skills/prerender/SKILL.md +467 -65
  831. package/skills/rango/SKILL.md +87 -21
  832. package/skills/response-routes/SKILL.md +152 -91
  833. package/skills/route/SKILL.md +281 -14
  834. package/skills/router-setup/SKILL.md +210 -32
  835. package/skills/streams-and-websockets/SKILL.md +283 -0
  836. package/skills/theme/SKILL.md +9 -8
  837. package/skills/typesafety/SKILL.md +327 -86
  838. package/skills/use-cache/SKILL.md +324 -0
  839. package/src/__internal.ts +102 -4
  840. package/src/bin/rango.ts +312 -15
  841. package/src/browser/action-coordinator.ts +97 -0
  842. package/src/browser/action-response-classifier.ts +99 -0
  843. package/src/browser/app-shell.ts +52 -0
  844. package/src/browser/app-version.ts +14 -0
  845. package/src/browser/event-controller.ts +92 -64
  846. package/src/browser/history-state.ts +80 -0
  847. package/src/browser/intercept-utils.ts +52 -0
  848. package/src/browser/link-interceptor.ts +24 -4
  849. package/src/browser/logging.ts +55 -0
  850. package/src/browser/merge-segment-loaders.ts +20 -12
  851. package/src/browser/navigation-bridge.ts +367 -561
  852. package/src/browser/navigation-client.ts +228 -70
  853. package/src/browser/navigation-store.ts +97 -55
  854. package/src/browser/navigation-transaction.ts +297 -0
  855. package/src/browser/network-error-handler.ts +61 -0
  856. package/src/browser/partial-update.ts +362 -316
  857. package/src/browser/prefetch/cache.ts +314 -0
  858. package/src/browser/prefetch/fetch.ts +282 -0
  859. package/src/browser/prefetch/observer.ts +65 -0
  860. package/src/browser/prefetch/policy.ts +48 -0
  861. package/src/browser/prefetch/queue.ts +191 -0
  862. package/src/browser/prefetch/resource-ready.ts +77 -0
  863. package/src/browser/rango-state.ts +152 -0
  864. package/src/browser/react/Link.tsx +255 -71
  865. package/src/browser/react/NavigationProvider.tsx +132 -17
  866. package/src/browser/react/context.ts +11 -0
  867. package/src/browser/react/filter-segment-order.ts +11 -0
  868. package/src/browser/react/index.ts +12 -12
  869. package/src/browser/react/location-state-shared.ts +95 -53
  870. package/src/browser/react/location-state.ts +60 -15
  871. package/src/browser/react/mount-context.ts +6 -1
  872. package/src/browser/react/nonce-context.ts +23 -0
  873. package/src/browser/react/shallow-equal.ts +27 -0
  874. package/src/browser/react/use-action.ts +29 -51
  875. package/src/browser/react/use-client-cache.ts +5 -3
  876. package/src/browser/react/use-handle.ts +30 -120
  877. package/src/browser/react/use-link-status.ts +6 -5
  878. package/src/browser/react/use-navigation.ts +44 -65
  879. package/src/browser/react/use-params.ts +75 -0
  880. package/src/browser/react/use-pathname.ts +47 -0
  881. package/src/browser/react/use-router.ts +83 -0
  882. package/src/browser/react/use-search-params.ts +56 -0
  883. package/src/browser/react/use-segments.ts +80 -97
  884. package/src/browser/response-adapter.ts +73 -0
  885. package/src/browser/rsc-router.tsx +246 -64
  886. package/src/browser/scroll-restoration.ts +127 -52
  887. package/src/browser/segment-reconciler.ts +243 -0
  888. package/src/browser/segment-structure-assert.ts +16 -0
  889. package/src/browser/server-action-bridge.ts +510 -603
  890. package/src/browser/shallow.ts +6 -1
  891. package/src/browser/types.ts +152 -48
  892. package/src/browser/validate-redirect-origin.ts +29 -0
  893. package/src/build/generate-manifest.ts +84 -23
  894. package/src/build/generate-route-types.ts +39 -752
  895. package/src/build/index.ts +6 -5
  896. package/src/build/route-trie.ts +83 -31
  897. package/src/build/route-types/ast-helpers.ts +25 -0
  898. package/src/build/route-types/ast-route-extraction.ts +98 -0
  899. package/src/build/route-types/codegen.ts +102 -0
  900. package/src/build/route-types/include-resolution.ts +418 -0
  901. package/src/build/route-types/param-extraction.ts +48 -0
  902. package/src/build/route-types/per-module-writer.ts +128 -0
  903. package/src/build/route-types/router-processing.ts +618 -0
  904. package/src/build/route-types/scan-filter.ts +85 -0
  905. package/src/build/runtime-discovery.ts +231 -0
  906. package/src/cache/background-task.ts +34 -0
  907. package/src/cache/cache-key-utils.ts +44 -0
  908. package/src/cache/cache-policy.ts +125 -0
  909. package/src/cache/cache-runtime.ts +342 -0
  910. package/src/cache/cache-scope.ts +167 -307
  911. package/src/cache/cf/cf-cache-store.ts +573 -21
  912. package/src/cache/cf/index.ts +13 -3
  913. package/src/cache/document-cache.ts +116 -77
  914. package/src/cache/handle-capture.ts +81 -0
  915. package/src/cache/handle-snapshot.ts +41 -0
  916. package/src/cache/index.ts +1 -15
  917. package/src/cache/memory-segment-store.ts +191 -13
  918. package/src/cache/profile-registry.ts +73 -0
  919. package/src/cache/read-through-swr.ts +134 -0
  920. package/src/cache/segment-codec.ts +256 -0
  921. package/src/cache/taint.ts +153 -0
  922. package/src/cache/types.ts +72 -122
  923. package/src/client.rsc.tsx +3 -1
  924. package/src/client.tsx +113 -301
  925. package/src/component-utils.ts +4 -4
  926. package/src/components/DefaultDocument.tsx +5 -1
  927. package/src/context-var.ts +156 -0
  928. package/src/debug.ts +19 -9
  929. package/src/errors.ts +77 -7
  930. package/src/handle.ts +55 -10
  931. package/src/handles/MetaTags.tsx +73 -20
  932. package/src/handles/breadcrumbs.ts +66 -0
  933. package/src/handles/index.ts +1 -0
  934. package/src/handles/meta.ts +30 -13
  935. package/src/host/cookie-handler.ts +21 -15
  936. package/src/host/errors.ts +8 -8
  937. package/src/host/index.ts +4 -7
  938. package/src/host/pattern-matcher.ts +27 -27
  939. package/src/host/router.ts +61 -39
  940. package/src/host/testing.ts +8 -8
  941. package/src/host/types.ts +15 -7
  942. package/src/host/utils.ts +1 -1
  943. package/src/href-client.ts +65 -45
  944. package/src/index.rsc.ts +138 -21
  945. package/src/index.ts +206 -51
  946. package/src/internal-debug.ts +11 -0
  947. package/src/loader.rsc.ts +25 -143
  948. package/src/loader.ts +27 -10
  949. package/src/network-error-thrower.tsx +3 -1
  950. package/src/outlet-context.ts +1 -1
  951. package/src/outlet-provider.tsx +45 -0
  952. package/src/prerender/param-hash.ts +4 -2
  953. package/src/prerender/store.ts +159 -13
  954. package/src/prerender.ts +397 -29
  955. package/src/response-utils.ts +28 -0
  956. package/src/reverse.ts +209 -121
  957. package/src/root-error-boundary.tsx +41 -29
  958. package/src/route-content-wrapper.tsx +7 -4
  959. package/src/route-definition/dsl-helpers.ts +1134 -0
  960. package/src/route-definition/helper-factories.ts +200 -0
  961. package/src/route-definition/helpers-types.ts +478 -0
  962. package/src/route-definition/index.ts +55 -0
  963. package/src/route-definition/redirect.ts +101 -0
  964. package/src/route-definition/resolve-handler-use.ts +155 -0
  965. package/src/route-definition.ts +1 -1431
  966. package/src/route-map-builder.ts +162 -123
  967. package/src/route-name.ts +53 -0
  968. package/src/route-types.ts +66 -9
  969. package/src/router/content-negotiation.ts +215 -0
  970. package/src/router/debug-manifest.ts +72 -0
  971. package/src/router/error-handling.ts +9 -9
  972. package/src/router/find-match.ts +160 -0
  973. package/src/router/handler-context.ts +455 -86
  974. package/src/router/intercept-resolution.ts +35 -20
  975. package/src/router/lazy-includes.ts +237 -0
  976. package/src/router/loader-resolution.ts +359 -128
  977. package/src/router/logging.ts +251 -0
  978. package/src/router/manifest.ts +98 -32
  979. package/src/router/match-api.ts +195 -261
  980. package/src/router/match-context.ts +4 -2
  981. package/src/router/match-handlers.ts +440 -0
  982. package/src/router/match-middleware/background-revalidation.ts +108 -93
  983. package/src/router/match-middleware/cache-lookup.ts +415 -86
  984. package/src/router/match-middleware/cache-store.ts +91 -29
  985. package/src/router/match-middleware/intercept-resolution.ts +48 -21
  986. package/src/router/match-middleware/segment-resolution.ts +73 -9
  987. package/src/router/match-pipelines.ts +10 -45
  988. package/src/router/match-result.ts +135 -35
  989. package/src/router/metrics.ts +240 -15
  990. package/src/router/middleware-cookies.ts +55 -0
  991. package/src/router/middleware-types.ts +200 -0
  992. package/src/router/middleware.ts +373 -371
  993. package/src/router/navigation-snapshot.ts +182 -0
  994. package/src/router/pattern-matching.ts +251 -44
  995. package/src/router/prerender-match.ts +502 -0
  996. package/src/router/preview-match.ts +98 -0
  997. package/src/router/request-classification.ts +310 -0
  998. package/src/router/revalidation.ts +137 -38
  999. package/src/router/route-snapshot.ts +245 -0
  1000. package/src/router/router-context.ts +41 -21
  1001. package/src/router/router-interfaces.ts +484 -0
  1002. package/src/router/router-options.ts +618 -0
  1003. package/src/router/router-registry.ts +24 -0
  1004. package/src/router/segment-resolution/fresh.ts +748 -0
  1005. package/src/router/segment-resolution/helpers.ts +268 -0
  1006. package/src/router/segment-resolution/loader-cache.ts +199 -0
  1007. package/src/router/segment-resolution/revalidation.ts +1379 -0
  1008. package/src/router/segment-resolution/static-store.ts +67 -0
  1009. package/src/router/segment-resolution.ts +21 -1315
  1010. package/src/router/segment-wrappers.ts +291 -0
  1011. package/src/router/telemetry-otel.ts +299 -0
  1012. package/src/router/telemetry.ts +300 -0
  1013. package/src/router/timeout.ts +148 -0
  1014. package/src/router/trie-matching.ts +103 -30
  1015. package/src/router/types.ts +17 -9
  1016. package/src/router/url-params.ts +49 -0
  1017. package/src/router.ts +647 -1988
  1018. package/src/rsc/handler-context.ts +45 -0
  1019. package/src/rsc/handler.ts +864 -1114
  1020. package/src/rsc/helpers.ts +181 -19
  1021. package/src/rsc/index.ts +0 -20
  1022. package/src/rsc/loader-fetch.ts +229 -0
  1023. package/src/rsc/manifest-init.ts +90 -0
  1024. package/src/rsc/nonce.ts +14 -0
  1025. package/src/rsc/origin-guard.ts +141 -0
  1026. package/src/rsc/progressive-enhancement.ts +393 -0
  1027. package/src/rsc/response-error.ts +37 -0
  1028. package/src/rsc/response-route-handler.ts +360 -0
  1029. package/src/rsc/rsc-rendering.ts +253 -0
  1030. package/src/rsc/runtime-warnings.ts +42 -0
  1031. package/src/rsc/server-action.ts +358 -0
  1032. package/src/rsc/ssr-setup.ts +128 -0
  1033. package/src/rsc/types.ts +46 -11
  1034. package/src/search-params.ts +230 -0
  1035. package/src/segment-content-promise.ts +67 -0
  1036. package/src/segment-loader-promise.ts +122 -0
  1037. package/src/segment-system.tsx +134 -36
  1038. package/src/server/context.ts +333 -59
  1039. package/src/server/cookie-store.ts +190 -0
  1040. package/src/server/fetchable-loader-store.ts +37 -0
  1041. package/src/server/handle-store.ts +113 -15
  1042. package/src/server/loader-registry.ts +24 -64
  1043. package/src/server/request-context.ts +603 -109
  1044. package/src/server.ts +35 -155
  1045. package/src/ssr/index.tsx +103 -30
  1046. package/src/static-handler.ts +126 -0
  1047. package/src/theme/ThemeProvider.tsx +21 -15
  1048. package/src/theme/ThemeScript.tsx +5 -5
  1049. package/src/theme/constants.ts +5 -2
  1050. package/src/theme/index.ts +4 -14
  1051. package/src/theme/theme-context.ts +4 -30
  1052. package/src/theme/theme-script.ts +21 -18
  1053. package/src/types/boundaries.ts +158 -0
  1054. package/src/types/cache-types.ts +198 -0
  1055. package/src/types/error-types.ts +192 -0
  1056. package/src/types/global-namespace.ts +100 -0
  1057. package/src/types/handler-context.ts +759 -0
  1058. package/src/types/index.ts +88 -0
  1059. package/src/types/loader-types.ts +209 -0
  1060. package/src/types/request-scope.ts +126 -0
  1061. package/src/types/route-config.ts +170 -0
  1062. package/src/types/route-entry.ts +120 -0
  1063. package/src/types/segments.ts +150 -0
  1064. package/src/types.ts +1 -1757
  1065. package/src/urls/include-helper.ts +207 -0
  1066. package/src/urls/index.ts +53 -0
  1067. package/src/urls/path-helper-types.ts +372 -0
  1068. package/src/urls/path-helper.ts +364 -0
  1069. package/src/urls/pattern-types.ts +107 -0
  1070. package/src/urls/response-types.ts +108 -0
  1071. package/src/urls/type-extraction.ts +372 -0
  1072. package/src/urls/urls-function.ts +98 -0
  1073. package/src/urls.ts +1 -1282
  1074. package/src/use-loader.tsx +161 -81
  1075. package/src/vite/debug.ts +184 -0
  1076. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  1077. package/src/vite/discovery/discover-routers.ts +376 -0
  1078. package/src/vite/discovery/prerender-collection.ts +486 -0
  1079. package/src/vite/discovery/route-types-writer.ts +258 -0
  1080. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  1081. package/src/vite/discovery/state.ts +117 -0
  1082. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  1083. package/src/vite/index.ts +15 -1963
  1084. package/src/vite/plugin-types.ts +103 -0
  1085. package/src/vite/plugins/cjs-to-esm.ts +98 -0
  1086. package/src/vite/plugins/client-ref-dedup.ts +131 -0
  1087. package/src/vite/plugins/client-ref-hashing.ts +117 -0
  1088. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  1089. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  1090. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  1091. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
  1092. package/src/vite/plugins/expose-id-utils.ts +299 -0
  1093. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  1094. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  1095. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  1096. package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
  1097. package/src/vite/plugins/expose-ids/types.ts +45 -0
  1098. package/src/vite/plugins/expose-internal-ids.ts +816 -0
  1099. package/src/vite/plugins/performance-tracks.ts +96 -0
  1100. package/src/vite/plugins/refresh-cmd.ts +127 -0
  1101. package/src/vite/plugins/use-cache-transform.ts +336 -0
  1102. package/src/vite/plugins/version-injector.ts +83 -0
  1103. package/src/vite/plugins/version-plugin.ts +266 -0
  1104. package/src/vite/plugins/virtual-entries.ts +123 -0
  1105. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  1106. package/src/vite/rango.ts +497 -0
  1107. package/src/vite/router-discovery.ts +1111 -0
  1108. package/src/vite/utils/ast-handler-extract.ts +517 -0
  1109. package/src/vite/utils/banner.ts +36 -0
  1110. package/src/vite/utils/bundle-analysis.ts +137 -0
  1111. package/src/vite/utils/manifest-utils.ts +70 -0
  1112. package/src/vite/utils/package-resolution.ts +161 -0
  1113. package/src/vite/utils/prerender-utils.ts +221 -0
  1114. package/src/vite/utils/shared-utils.ts +170 -0
  1115. package/CLAUDE.md +0 -43
  1116. package/src/browser/lru-cache.ts +0 -69
  1117. package/src/browser/request-controller.ts +0 -164
  1118. package/src/cache/memory-store.ts +0 -253
  1119. package/src/router.gen.ts +0 -6
  1120. package/src/urls.gen.ts +0 -8
  1121. package/src/vite/expose-handle-id.ts +0 -209
  1122. package/src/vite/expose-loader-id.ts +0 -426
  1123. package/src/vite/expose-location-state-id.ts +0 -177
  1124. package/src/vite/expose-prerender-handler-id.ts +0 -429
  1125. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/router.ts CHANGED
@@ -1,60 +1,45 @@
1
- import type { ComponentType } from "react";
2
1
  import { type ReactNode } from "react";
3
2
  import { createCacheScope } from "./cache/cache-scope.js";
4
- import type { SegmentCacheStore } from "./cache/types.js";
3
+ import {
4
+ setCacheProfiles,
5
+ resolveCacheProfiles,
6
+ } from "./cache/profile-registry.js";
7
+ import { isCachedFunction } from "./cache/taint.js";
5
8
  import { assertClientComponent } from "./component-utils.js";
6
9
  import { DefaultDocument } from "./components/DefaultDocument.js";
7
- import {
8
- sanitizeError,
9
- } from "./errors";
10
- import { serializeManifest, type SerializedManifest } from "./debug.js";
11
- import {
12
- createReverse,
13
- type ReverseFunction,
14
- type PrefixRoutePatterns,
15
- } from "./reverse.js";
10
+ import type { SerializedManifest } from "./debug.js";
11
+ import { createReverse, type ReverseFunction } from "./reverse.js";
16
12
  import {
17
13
  registerRouteMap,
18
14
  getPrecomputedEntries,
19
- getRouteTrie,
15
+ getRouterManifest,
16
+ getRouterPrecomputedEntries,
17
+ ensureRouterManifest,
20
18
  } from "./route-map-builder.js";
21
- import { tryTrieMatch } from "./router/trie-matching.js";
22
- import {
23
- createRouteHelpers,
24
- type RouteHandlers,
25
- } from "./route-definition.js";
26
19
  import MapRootLayout from "./server/root-layout.js";
27
- import type { AllUseItems, IncludeItem } from "./route-types.js";
20
+ import type { AllUseItems } from "./route-types.js";
28
21
  import type { UrlPatterns } from "./urls.js";
22
+ import type { UrlBuilder } from "./urls/pattern-types.js";
23
+ import { urls } from "./urls.js";
29
24
  import {
30
- EntryData,
31
- InterceptEntry,
32
- InterceptSelectorContext,
25
+ type EntryData,
33
26
  getContext,
34
27
  RSCRouterContext,
35
- runWithPrefixes,
36
28
  type MetricsStore,
37
29
  } from "./server/context";
38
30
  import { createHandleStore, type HandleStore } from "./server/handle-store.js";
39
- import { getRequestContext } from "./server/request-context.js";
31
+ import {
32
+ getRequestContext,
33
+ _getRequestContext,
34
+ } from "./server/request-context.js";
40
35
  import type {
41
- ErrorBoundaryHandler,
42
- ErrorInfo,
43
36
  ErrorPhase,
44
37
  HandlerContext,
45
38
  LoaderDataResult,
46
- MatchResult,
47
- NotFoundBoundaryHandler,
48
- OnErrorCallback,
49
39
  ResolvedRouteMap,
50
- RouteDefinition,
51
40
  RouteEntry,
52
41
  TrailingSlashMode,
53
42
  } from "./types";
54
- import type {
55
- NonceProvider,
56
- } from "./rsc/types.js";
57
- import type { ExecutionContext } from "./server/request-context.js";
58
43
 
59
44
  // Extracted router utilities
60
45
  import {
@@ -64,28 +49,10 @@ import {
64
49
  invokeOnError,
65
50
  } from "./router/error-handling.js";
66
51
 
67
- // Extracted segment resolution functions
68
- import {
69
- resolveAllSegments as _resolveAllSegments,
70
- resolveLoadersOnly as _resolveLoadersOnly,
71
- resolveLoadersOnlyWithRevalidation as _resolveLoadersOnlyWithRevalidation,
72
- buildEntryRevalidateMap as _buildEntryRevalidateMap,
73
- resolveAllSegmentsWithRevalidation as _resolveAllSegmentsWithRevalidation,
74
- } from "./router/segment-resolution.js";
75
-
76
- // Extracted intercept resolution functions
77
- import {
78
- findInterceptForRoute as _findInterceptForRoute,
79
- resolveInterceptEntry as _resolveInterceptEntry,
80
- resolveInterceptLoadersOnly as _resolveInterceptLoadersOnly,
81
- } from "./router/intercept-resolution.js";
82
-
83
- // Extracted match API functions
84
- import {
85
- createMatchContextForFull as _createMatchContextForFull,
86
- createMatchContextForPartial as _createMatchContextForPartial,
87
- matchError as _matchError,
88
- } from "./router/match-api.js";
52
+ // Extracted module factories
53
+ import { createSegmentWrappers } from "./router/segment-wrappers.js";
54
+ import { createMatchHandlers } from "./router/match-handlers.js";
55
+ import { buildDebugManifest } from "./router/debug-manifest.js";
89
56
 
90
57
  import type { SegmentResolutionDeps, MatchApiDeps } from "./router/types.js";
91
58
  import { createHandlerContext } from "./router/handler-context.js";
@@ -95,993 +62,79 @@ import {
95
62
  wrapLoaderWithErrorHandling,
96
63
  } from "./router/loader-resolution.js";
97
64
  import { loadManifest } from "./router/manifest.js";
65
+ import { createMetricsStore } from "./router/metrics.js";
98
66
  import {
99
- createMetricsStore,
100
- } from "./router/metrics.js";
101
- import {
102
- collectRouteMiddleware,
103
67
  parsePattern,
104
68
  type MiddlewareEntry,
105
69
  type MiddlewareFn,
106
70
  } from "./router/middleware.js";
107
71
  import {
108
72
  extractStaticPrefix,
109
- findMatch as findRouteMatch,
110
- isLazyEvaluationNeeded,
111
73
  traverseBack,
112
- type RouteMatchResult,
113
74
  } from "./router/pattern-matching.js";
75
+ import { resolveSink, safeEmit, getRequestId } from "./router/telemetry.js";
114
76
  import { evaluateRevalidation } from "./router/revalidation.js";
115
77
  import {
116
78
  type RouterContext,
117
79
  runWithRouterContext,
118
80
  } from "./router/router-context.js";
119
- import {
120
- type ActionContext,
121
- type MatchContext,
122
- createPipelineState,
123
- } from "./router/match-context.js";
124
- import { createMatchPartialPipeline } from "./router/match-pipelines.js";
125
- import { collectMatchResult } from "./router/match-result.js";
126
81
  import { resolveThemeConfig } from "./theme/constants.js";
82
+ import { resolveTimeouts } from "./router/timeout.js";
127
83
 
128
- // Response type -> MIME type used for Accept header matching
129
- const RESPONSE_TYPE_MIME: Record<string, string> = {
130
- json: "application/json",
131
- text: "text/plain",
132
- xml: "application/xml",
133
- html: "text/html",
134
- md: "text/markdown",
135
- };
136
-
137
- // Reverse lookup: MIME type -> response type tag (e.g. "text/html" -> "html")
138
- const MIME_RESPONSE_TYPE: Record<string, string> = Object.fromEntries(
139
- Object.entries(RESPONSE_TYPE_MIME).map(([tag, mime]) => [mime, tag]),
140
- );
141
-
142
- interface AcceptEntry {
143
- mime: string;
144
- q: number;
145
- order: number;
146
- }
147
-
148
- /**
149
- * Parse an Accept header into a sorted array of MIME entries.
150
- * Respects q-values (default 1.0) and uses client order as tiebreaker
151
- * when q-values are equal (matching Express/Hono behavior).
152
- */
153
- function parseAcceptTypes(accept: string): AcceptEntry[] {
154
- const entries: AcceptEntry[] = [];
155
- const parts = accept.split(",");
156
- for (let i = 0; i < parts.length; i++) {
157
- const part = parts[i]!;
158
- const segments = part.split(";");
159
- const mime = segments[0]!.trim();
160
- if (!mime) continue;
161
- let q = 1.0;
162
- for (let j = 1; j < segments.length; j++) {
163
- const param = segments[j]!.trim();
164
- if (param.startsWith("q=")) {
165
- q = Math.max(0, Math.min(1, Number(param.slice(2)) || 0));
166
- }
167
- }
168
- entries.push({ mime, q, order: i });
169
- }
170
- // Sort: highest q first, then lowest client order first (stable)
171
- entries.sort((a, b) => b.q - a.q || a.order - b.order);
172
- return entries;
173
- }
174
-
175
- // Sentinel response type for RSC routes in negotiation candidates
176
- const RSC_RESPONSE_TYPE = "__rsc__";
177
-
178
- /**
179
- * Pick the best negotiate variant by walking the client's sorted Accept list.
180
- * For each accepted MIME type (in q-value/order priority), check if any
181
- * candidate serves that type. Wildcards (*\/*) match the first candidate.
182
- * Falls back to the first candidate if nothing matches.
183
- */
184
- function pickNegotiateVariant(
185
- acceptEntries: AcceptEntry[],
186
- candidates: Array<{ routeKey: string; responseType: string }>,
187
- ): { routeKey: string; responseType: string } {
188
- // Build a MIME -> candidate lookup for O(1) matching
189
- const byCandidateMime = new Map<string, { routeKey: string; responseType: string }>();
190
- for (const c of candidates) {
191
- const mime = c.responseType === RSC_RESPONSE_TYPE ? "text/html" : RESPONSE_TYPE_MIME[c.responseType];
192
- if (mime && !byCandidateMime.has(mime)) {
193
- byCandidateMime.set(mime, c);
194
- }
195
- }
196
-
197
- for (const entry of acceptEntries) {
198
- if (entry.q === 0) continue;
199
- // Wildcard matches first candidate
200
- if (entry.mime === "*/*") return candidates[0]!;
201
- // Type wildcard (e.g. "text/*") — match first candidate with that type
202
- if (entry.mime.endsWith("/*")) {
203
- const typePrefix = entry.mime.slice(0, entry.mime.indexOf("/"));
204
- for (const [mime, candidate] of byCandidateMime) {
205
- if (mime.startsWith(typePrefix + "/")) return candidate;
206
- }
207
- continue;
208
- }
209
- const match = byCandidateMime.get(entry.mime);
210
- if (match) return match;
211
- }
212
- // No match — use first candidate as default
213
- return candidates[0]!;
214
- }
215
-
216
- /**
217
- * Props passed to the root layout component
218
- */
219
- export interface RootLayoutProps {
220
- children: ReactNode;
221
- }
222
-
223
- /**
224
- * Router configuration options
225
- */
226
- /**
227
- * Brand marker for identifying router instances at build time.
228
- * Used by the Vite plugin to auto-discover routers from module exports.
229
- */
230
- export const RSC_ROUTER_BRAND: "__rsc_router__" = "__rsc_router__";
231
-
232
- /**
233
- * Global registry of all router instances created via createRouter().
234
- * Each router is keyed by its id (auto-generated or user-provided).
235
- * Used by the Vite plugin at build time to discover routers and extract
236
- * manifests, prefix trees, and pre-render candidates.
237
- */
238
- export const RouterRegistry: Map<string, RSCRouter<any, any>> = new Map();
239
-
240
- let routerAutoId = 0;
241
-
242
- export interface RSCRouterOptions<TEnv = any> {
243
- /**
244
- * Unique identifier for this router instance.
245
- * Used to namespace static output files and route maps.
246
- * Auto-generated if not provided.
247
- */
248
- id?: string;
249
-
250
- /**
251
- * Enable performance metrics collection
252
- * When enabled, metrics are output to console and available via Server-Timing header
253
- */
254
- debugPerformance?: boolean;
255
-
256
- /**
257
- * Allow the `?__debug_manifest` query parameter to return route manifest data as JSON.
258
- * In development mode this is always enabled regardless of this setting.
259
- * Defaults to true. Set to false to disable in production.
260
- * @internal
261
- */
262
- allowDebugManifest?: boolean;
263
-
264
- /**
265
- * Document component that wraps the entire application.
266
- *
267
- * This component provides the HTML structure for your app and wraps
268
- * both normal route content AND error states, preventing the app shell
269
- * from unmounting during errors (avoids FOUC).
270
- *
271
- * Must be a client component ("use client") that accepts { children }.
272
- *
273
- * If not provided, a default document with basic HTML structure is used:
274
- * `<html><head><meta charset/viewport></head><body>{children}</body></html>`
275
- *
276
- * @example
277
- * ```typescript
278
- * // components/Document.tsx
279
- * "use client";
280
- * export function Document({ children }: { children: ReactNode }) {
281
- * return (
282
- * <html lang="en">
283
- * <head>
284
- * <link rel="stylesheet" href="/styles.css" />
285
- * </head>
286
- * <body>
287
- * <nav>...</nav>
288
- * {children}
289
- * </body>
290
- * </html>
291
- * );
292
- * }
293
- *
294
- * // router.tsx
295
- * const router = createRouter<AppEnv>({
296
- * document: Document,
297
- * });
298
- * ```
299
- */
300
- document?: ComponentType<RootLayoutProps>;
301
-
302
- /**
303
- * Default error boundary fallback used when no error boundary is defined in the route tree
304
- * If not provided, errors will propagate and crash the request
305
- */
306
- defaultErrorBoundary?: ReactNode | ErrorBoundaryHandler;
307
-
308
- /**
309
- * Default not-found boundary fallback used when no notFoundBoundary is defined in the route tree
310
- * If not provided, DataNotFoundError will be treated as a regular error
311
- */
312
- defaultNotFoundBoundary?: ReactNode | NotFoundBoundaryHandler;
313
-
314
- /**
315
- * Component to render when no route matches the requested URL.
316
- *
317
- * This is rendered within your document/app shell with a 404 status code.
318
- * Use this for a custom 404 page that maintains your app's look and feel.
319
- *
320
- * If not provided, a default "Page not found" component is rendered.
321
- *
322
- * Can be a static ReactNode or a function receiving the pathname.
323
- *
324
- * @example
325
- * ```typescript
326
- * // Simple static component
327
- * const router = createRouter<AppEnv>({
328
- * document: Document,
329
- * notFound: <NotFound404 />,
330
- * });
331
- *
332
- * // Dynamic component with pathname
333
- * const router = createRouter<AppEnv>({
334
- * document: Document,
335
- * notFound: ({ pathname }) => (
336
- * <div>
337
- * <h1>404 - Not Found</h1>
338
- * <p>No page exists at {pathname}</p>
339
- * <a href="/">Go home</a>
340
- * </div>
341
- * ),
342
- * });
343
- * ```
344
- */
345
- notFound?: ReactNode | ((props: { pathname: string }) => ReactNode);
346
-
347
- /**
348
- * Callback invoked when an error occurs during request handling.
349
- *
350
- * This callback is for notification/logging purposes - it cannot modify
351
- * the error handling flow. Use errorBoundary() in route definitions to
352
- * customize error UI.
353
- *
354
- * The callback receives comprehensive context about the error including:
355
- * - The error itself
356
- * - Phase where it occurred (routing, middleware, loader, handler, etc.)
357
- * - Request info (URL, method, params)
358
- * - Route info (routeKey, segmentId)
359
- * - Environment/bindings
360
- * - Duration from request start
361
- *
362
- * @example
363
- * ```typescript
364
- * const router = createRouter<AppEnv>({
365
- * onError: (context) => {
366
- * // Send to error tracking service
367
- * Sentry.captureException(context.error, {
368
- * tags: {
369
- * phase: context.phase,
370
- * route: context.routeKey,
371
- * },
372
- * extra: {
373
- * url: context.url.toString(),
374
- * params: context.params,
375
- * duration: context.duration,
376
- * },
377
- * });
378
- * },
379
- * });
380
- * ```
381
- */
382
- onError?: OnErrorCallback<TEnv>;
383
-
384
- /**
385
- * Cache store for segment caching.
386
- *
387
- * When provided, enables route-level caching via cache() boundaries.
388
- * The store handles persistence (memory, KV, Redis, etc.).
389
- *
390
- * Can be a static config or a function receiving env for runtime bindings.
391
- *
392
- * @example Static config
393
- * ```typescript
394
- * import { MemorySegmentCacheStore } from "rsc-router/rsc";
395
- *
396
- * const router = createRouter({
397
- * cache: {
398
- * store: new MemorySegmentCacheStore({ defaults: { ttl: 60 } }),
399
- * },
400
- * });
401
- * ```
402
- *
403
- * @example Dynamic config with env (e.g., Cloudflare Workers with ExecutionContext)
404
- * ```typescript
405
- * const router = createRouter<AppEnv>({
406
- * cache: (env) => ({
407
- * store: new CFCacheStore({
408
- * defaults: { ttl: 60 },
409
- * ctx: env.ctx, // ExecutionContext for non-blocking writes
410
- * }),
411
- * }),
412
- * });
413
- * ```
414
- */
415
- cache?:
416
- | { store: SegmentCacheStore; enabled?: boolean }
417
- | ((env: TEnv & { ctx?: ExecutionContext }) => {
418
- store: SegmentCacheStore;
419
- enabled?: boolean;
420
- });
421
-
422
- /**
423
- * Theme configuration for automatic theme management.
424
- *
425
- * When provided, enables:
426
- * - ctx.theme and ctx.setTheme() in route handlers
427
- * - useTheme() hook for client components
428
- * - FOUC prevention via inline script in MetaTags
429
- * - Automatic ThemeProvider wrapping in NavigationProvider
430
- *
431
- * @example
432
- * ```typescript
433
- * const router = createRouter<AppEnv>({
434
- * theme: {
435
- * defaultTheme: "system",
436
- * themes: ["light", "dark"],
437
- * }
438
- * });
439
- *
440
- * // In route handler:
441
- * route("settings", (ctx) => {
442
- * const theme = ctx.theme; // "light" | "dark" | "system"
443
- * ctx.setTheme("dark"); // Sets cookie
444
- * return <SettingsPage />;
445
- * });
446
- *
447
- * // In client component:
448
- * import { useTheme } from "@rangojs/router/theme";
449
- *
450
- * function ThemeToggle() {
451
- * const { theme, setTheme, themes } = useTheme();
452
- * return <select value={theme} onChange={e => setTheme(e.target.value)}>
453
- * {themes.map(t => <option key={t}>{t}</option>)}
454
- * </select>;
455
- * }
456
- * ```
457
- *
458
- * Use `theme: true` to enable with all defaults.
459
- */
460
- theme?: import("./theme/types.js").ThemeConfig | true;
461
-
462
- /**
463
- * URL patterns to register with the router.
464
- *
465
- * Alternative to calling `.routes()` method - allows passing patterns
466
- * directly in the config for a more concise setup.
467
- *
468
- * @example
469
- * ```typescript
470
- * import { urls } from "@rangojs/router/server";
471
- *
472
- * const urlpatterns = urls(({ path, layout }) => [
473
- * path("/", HomePage, { name: "home" }),
474
- * path("/about", AboutPage, { name: "about" }),
475
- * ]);
476
- *
477
- * const router = createRouter<AppEnv>({
478
- * document: Document,
479
- * urls: urlpatterns,
480
- * });
481
- * ```
482
- */
483
- urls?: UrlPatterns<TEnv, any>;
484
-
485
- /**
486
- * Nonce provider for Content Security Policy (CSP).
487
- *
488
- * Can be:
489
- * - A function that returns a nonce string
490
- * - A function that returns `true` to auto-generate a nonce
491
- * - Undefined to disable nonce (default)
492
- *
493
- * The nonce will be applied to inline scripts injected by the RSC payload.
494
- * It's also available to middleware via `ctx.get('nonce')`.
495
- *
496
- * @example Auto-generate nonce
497
- * ```tsx
498
- * createRouter({
499
- * nonce: () => true,
500
- * });
501
- * ```
502
- *
503
- * @example Custom nonce from request context
504
- * ```tsx
505
- * createRouter({
506
- * nonce: (request, env) => env.nonce,
507
- * });
508
- * ```
509
- */
510
- nonce?: NonceProvider<TEnv>;
511
-
512
- /**
513
- * RSC version string included in metadata.
514
- * The browser sends this back on partial requests to detect version mismatches.
515
- *
516
- * Defaults to the auto-generated VERSION from `@rangojs/router:version` virtual module.
517
- * Only set this if you need a custom versioning strategy.
518
- *
519
- * @default VERSION from @rangojs/router:version
520
- */
521
- version?: string;
522
-
523
- /**
524
- * Enable connection warmup to keep TCP+TLS alive after idle periods.
525
- *
526
- * When enabled, the client sends a HEAD request after the user returns
527
- * from an idle period (60s+), prewarming the TLS connection before
528
- * the next navigation.
529
- *
530
- * @default true
531
- */
532
- warmup?: boolean;
533
- }
534
-
535
- /**
536
- * Merge route patterns with response types into a single route map.
537
- * Routes with response types get { path, response } objects; others stay as strings.
538
- */
539
- type MergeRoutesWithResponses<
540
- TRoutes extends Record<string, string>,
541
- TResponses,
542
- > = {
543
- [K in keyof TRoutes]: K extends keyof NonNullable<TResponses>
544
- ? unknown extends NonNullable<TResponses>[K]
545
- ? TRoutes[K] // RSC route — TData defaults to unknown, keep as plain string
546
- : { readonly path: TRoutes[K]; readonly response: NonNullable<TResponses>[K] }
547
- : TRoutes[K]
548
- };
549
-
550
- /**
551
- * Extract the URL pattern from a route entry (string or { path, response } object)
552
- */
553
- type PatternOfEntry<V> =
554
- V extends string ? V
555
- : V extends { readonly path: infer P extends string } ? P
556
- : never;
557
-
558
- /**
559
- * Type-level detection of conflicting route keys.
560
- * Extracts keys that exist in both TExisting and TNew but with different URL patterns.
561
- * Returns `never` if no conflicts exist.
562
- * Compares patterns (not full entries) to handle both string and { path, response } values.
563
- *
564
- * @example
565
- * ```typescript
566
- * ConflictingKeys<{ a: "/a" }, { a: "/b" }> // "a" (conflict - same key, different URLs)
567
- * ConflictingKeys<{ a: "/a" }, { a: "/a" }> // never (no conflict - same key and URL)
568
- * ConflictingKeys<{ a: "/a" }, { b: "/b" }> // never (no conflict - different keys)
569
- * ```
570
- */
571
- type ConflictingKeys<
572
- TExisting extends Record<string, unknown>,
573
- TNew extends Record<string, unknown>,
574
- > = {
575
- [K in keyof TExisting & keyof TNew]: PatternOfEntry<TExisting[K]> extends PatternOfEntry<TNew[K]>
576
- ? PatternOfEntry<TNew[K]> extends PatternOfEntry<TExisting[K]>
577
- ? never // Same pattern, no conflict
578
- : K // Different patterns, conflict
579
- : K; // Different patterns, conflict
580
- }[keyof TExisting & keyof TNew];
581
-
582
- /**
583
- * Error type returned when route keys conflict.
584
- * Methods require an impossible `never` parameter so TypeScript errors at the call site.
585
- */
586
- type RouteConflictError<TConflicts extends string> = {
587
- __error: `Route key conflict! Key "${TConflicts}" already exists with a different URL pattern.`;
588
- hint: "Route keys must be globally unique. Use prefixed names like 'blog.index' instead of 'index'.";
589
- conflictingKeys: TConflicts;
590
- // These methods require `never` so calling them produces an error at the call site
591
- routes: (
592
- __conflict: `Fix route key conflict: "${TConflicts}" is already defined with a different URL pattern`,
593
- ) => never;
594
- map: (
595
- __conflict: `Fix route key conflict: "${TConflicts}" is already defined with a different URL pattern`,
596
- ) => never;
597
- };
598
-
599
- /**
600
- * Simplified route helpers for inline route definitions.
601
- * Uses TRoutes (Record<string, string>) instead of RouteDefinition.
602
- *
603
- * Note: Some helpers use `any` for context types as a trade-off for simpler usage.
604
- * The main type safety is in the `route` helper which enforces valid route names.
605
- * For full type safety, use the standard map() API with separate handler files.
606
- */
607
- type InlineRouteHelpers<TRoutes extends Record<string, string>, TEnv> = {
608
- /**
609
- * Define a route handler for a specific route pattern
610
- */
611
- route: <K extends keyof TRoutes & string>(
612
- name: K,
613
- handler:
614
- | ((ctx: HandlerContext<{}, TEnv>) => ReactNode | Promise<ReactNode>)
615
- | ReactNode,
616
- ) => AllUseItems;
617
-
618
- /**
619
- * Define a layout that wraps child routes
620
- */
621
- layout: (
622
- component:
623
- | ReactNode
624
- | ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>),
625
- use?: () => AllUseItems[],
626
- ) => AllUseItems;
627
-
628
- /**
629
- * Define parallel routes
630
- */
631
- parallel: (
632
- slots: Record<
633
- `@${string}`,
634
- | ReactNode
635
- | ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
636
- >,
637
- use?: () => AllUseItems[],
638
- ) => AllUseItems;
639
-
640
- /**
641
- * Define route middleware
642
- */
643
- middleware: (
644
- fn: (ctx: any, next: () => Promise<void>) => Promise<void>,
645
- ) => AllUseItems;
84
+ // Extracted content negotiation utilities
85
+ import { flattenNamedRoutes } from "./router/content-negotiation.js";
646
86
 
647
- /**
648
- * Define revalidation handlers
649
- */
650
- revalidate: (fn: (ctx: any) => boolean | Promise<boolean>) => AllUseItems;
651
-
652
- /**
653
- * Define data loaders
654
- */
655
- loader: (loader: any, use?: () => AllUseItems[]) => AllUseItems;
656
-
657
- /**
658
- * Define loading states
659
- */
660
- loading: (component: ReactNode) => AllUseItems;
661
-
662
- /**
663
- * Define error boundaries
664
- */
665
- errorBoundary: (
666
- handler: ReactNode | ((props: { error: Error }) => ReactNode),
667
- ) => AllUseItems;
668
-
669
- /**
670
- * Define not found boundaries
671
- */
672
- notFoundBoundary: (
673
- handler: ReactNode | ((props: { pathname: string }) => ReactNode),
674
- ) => AllUseItems;
675
-
676
- /**
677
- * Define intercept routes
678
- */
679
- intercept: (
680
- name: string,
681
- handler:
682
- | ReactNode
683
- | ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>),
684
- use?: () => AllUseItems[],
685
- ) => AllUseItems;
686
-
687
- /**
688
- * Define when conditions for intercepts
689
- */
690
- when: (condition: (ctx: any) => boolean | Promise<boolean>) => AllUseItems;
691
-
692
- /**
693
- * Define cache configuration
694
- */
695
- cache: (
696
- config: { ttl?: number; swr?: number } | false,
697
- use?: () => AllUseItems[],
698
- ) => AllUseItems;
699
- };
700
-
701
- /**
702
- * Router builder for chaining .use() and .map()
703
- * TRoutes accumulates all registered route types through the chain
704
- * TLocalRoutes contains the routes for the current .routes() call (for inline handler typing)
705
- */
706
- interface RouteBuilder<
707
- T extends RouteDefinition,
708
- TEnv,
709
- TRoutes extends Record<string, unknown>,
710
- TLocalRoutes extends Record<string, string> = Record<string, string>,
711
- > {
712
- /**
713
- * Add middleware scoped to this mount
714
- * Called between .routes() and .map()
715
- *
716
- * @example
717
- * ```typescript
718
- * .routes("/admin", adminRoutes)
719
- * .use(authMiddleware) // All of /admin/*
720
- * .use("/danger/*", superAuth) // Only /admin/danger/*
721
- * .map(() => import("./admin"))
722
- * ```
723
- */
724
- use(
725
- patternOrMiddleware: string | MiddlewareFn<TEnv>,
726
- middleware?: MiddlewareFn<TEnv>,
727
- ): RouteBuilder<T, TEnv, TRoutes, TLocalRoutes>;
728
-
729
- /**
730
- * Map routes to handlers
731
- *
732
- * Supports two patterns:
733
- *
734
- * 1. Lazy loading (code-split):
735
- * ```typescript
736
- * .routes(homeRoutes)
737
- * .map(() => import("./handlers/home"))
738
- * ```
739
- *
740
- * 2. Inline definition:
741
- * ```typescript
742
- * .routes({ index: "/", about: "/about" })
743
- * .map(({ route }) => [
744
- * route("index", () => <HomePage />),
745
- * route("about", () => <AboutPage />),
746
- * ])
747
- * ```
748
- */
749
- // Inline definition overload - handler receives helpers (must be first for correct inference)
750
- // Uses TLocalRoutes so route names don't need the prefix
751
- map<
752
- H extends (
753
- helpers: InlineRouteHelpers<TLocalRoutes, TEnv>,
754
- ) => Array<AllUseItems>,
755
- >(
756
- handler: H,
757
- ): RSCRouter<TEnv, TRoutes>;
758
- // Lazy loading overload - verifies imported handlers match route definition
759
- map(
760
- handler: () =>
761
- | Array<AllUseItems>
762
- | Promise<{ default: RouteHandlers<TLocalRoutes> }>
763
- | Promise<RouteHandlers<TLocalRoutes>>,
764
- ): RSCRouter<TEnv, TRoutes>;
765
-
766
- /**
767
- * Accumulated route map for typeof extraction
768
- * Used for module augmentation: `type AppRoutes = typeof _router.routeMap`
769
- */
770
- readonly routeMap: TRoutes;
771
- }
772
-
773
- /**
774
- * RSC Router interface
775
- * TRoutes accumulates all registered route types through the builder chain
776
- */
777
- export interface RSCRouter<
778
- TEnv = any,
779
- TRoutes extends Record<string, unknown> = Record<string, string>,
780
- > {
781
- /**
782
- * Brand marker for build-time discovery.
783
- * The Vite plugin uses this to identify router instances in module exports.
784
- */
785
- readonly __brand: typeof RSC_ROUTER_BRAND;
786
-
787
- /**
788
- * Unique identifier for this router instance.
789
- * Used to namespace static output and isolate route maps between routers.
790
- */
791
- readonly id: string;
792
-
793
- /**
794
- * Register routes with a prefix
795
- * Route keys stay unchanged, only URL patterns get the prefix applied.
796
- * This enables composable route modules that work regardless of mount point.
797
- *
798
- * @throws Compile-time error if route keys conflict with previously registered routes
799
- */
800
- routes<const TPrefix extends string, const T extends Record<string, string>>(
801
- prefix: TPrefix,
802
- routes: T,
803
- ): ConflictingKeys<TRoutes, PrefixRoutePatterns<T, TPrefix>> extends never
804
- ? RouteBuilder<
805
- RouteDefinition,
806
- TEnv,
807
- TRoutes & PrefixRoutePatterns<T, TPrefix>,
808
- T
809
- >
810
- : RouteConflictError<
811
- ConflictingKeys<TRoutes, PrefixRoutePatterns<T, TPrefix>> & string
812
- >;
813
-
814
- /**
815
- * Register routes without a prefix
816
- * Route types are accumulated through the chain
817
- *
818
- * @throws Compile-time error if route keys conflict with previously registered routes
819
- */
820
- routes<const T extends Record<string, string>>(
821
- routes: T,
822
- ): ConflictingKeys<TRoutes, T> extends never
823
- ? RouteBuilder<RouteDefinition, TEnv, TRoutes & T, T>
824
- : RouteConflictError<ConflictingKeys<TRoutes, T> & string>;
825
-
826
- /**
827
- * Register routes using Django-style URL patterns
828
- * This is the new API for @rangojs/router - call once with urls() result
829
- *
830
- * @example
831
- * ```typescript
832
- * createRouter({})
833
- * .routes(urlpatterns) // Single call with urls() result
834
- * ```
835
- */
836
- routes<T extends UrlPatterns<TEnv, any>>(
837
- patterns: T,
838
- ): RSCRouter<
839
- TEnv,
840
- TRoutes &
841
- (NonNullable<T["_routes"]> extends Record<string, string>
842
- ? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
843
- : Record<string, string>)
844
- >;
845
-
846
- /**
847
- * Add global middleware that runs on all routes
848
- * Position matters: middleware before any .routes() is global
849
- *
850
- * @example
851
- * ```typescript
852
- * createRouter({ document: RootLayout })
853
- * .use(loggerMiddleware) // All routes
854
- * .use("/api/*", rateLimiter) // Pattern match
855
- * .routes(homeRoutes)
856
- * .map(() => import("./home"))
857
- * ```
858
- */
859
- use(
860
- patternOrMiddleware: string | MiddlewareFn<TEnv>,
861
- middleware?: MiddlewareFn<TEnv>,
862
- ): RSCRouter<TEnv, TRoutes>;
863
-
864
- /**
865
- * Type-safe URL builder for registered routes
866
- * Types are inferred from the accumulated route registrations
867
- * Route keys stay unchanged regardless of mount prefix.
868
- *
869
- * @example
870
- * ```typescript
871
- * // Given: .routes("/shop", { cart: "/cart", detail: "/product/:slug" })
872
- * router.reverse("cart"); // "/shop/cart"
873
- * router.reverse("detail", { slug: "widget" }); // "/shop/product/widget"
874
- * ```
875
- */
876
- reverse: ReverseFunction<TRoutes>;
877
-
878
- /**
879
- * Accumulated route map for typeof extraction
880
- * Used for module augmentation: `type AppRoutes = typeof _router.routeMap`
881
- *
882
- * @example
883
- * ```typescript
884
- * const _router = createRouter<AppEnv>()
885
- * .routes(homeRoutes).map(() => import('./home'))
886
- * .routes('/shop', shopRoutes).map(() => import('./shop'));
887
- *
888
- * type AppRoutes = typeof _router.routeMap;
889
- *
890
- * declare global {
891
- * namespace RSCRouter {
892
- * interface RegisteredRoutes extends AppRoutes {}
893
- * }
894
- * }
895
- * ```
896
- */
897
- readonly routeMap: TRoutes;
898
-
899
- /**
900
- * Root layout component that wraps the entire application
901
- * Access this to pass to renderSegments
902
- */
903
- readonly rootLayout?: ComponentType<RootLayoutProps>;
904
-
905
- /**
906
- * Error callback for monitoring/alerting
907
- * Called when errors occur in loaders, actions, or routes
908
- */
909
- readonly onError?: RSCRouterOptions<TEnv>["onError"];
910
-
911
- /**
912
- * Cache configuration (for internal use by RSC handler)
913
- */
914
- readonly cache?: RSCRouterOptions<TEnv>["cache"];
915
-
916
- /**
917
- * Not found component to render when no route matches (for internal use by RSC handler)
918
- */
919
- readonly notFound?: RSCRouterOptions<TEnv>["notFound"];
920
-
921
- /**
922
- * Resolved theme configuration (null if theme not enabled)
923
- * Used by NavigationProvider to include ThemeProvider and by MetaTags to render theme script
924
- */
925
- readonly themeConfig: import("./theme/types.js").ResolvedThemeConfig | null;
926
-
927
- /**
928
- * Whether connection warmup is enabled.
929
- * When true, the client sends HEAD /?_rsc_warmup after idle periods
930
- * and the server responds with 204 No Content.
931
- */
932
- readonly warmupEnabled: boolean;
933
-
934
- /**
935
- * Whether ?__debug_manifest is allowed in production.
936
- * Always enabled in development.
937
- * @internal
938
- */
939
- readonly allowDebugManifest: boolean;
940
-
941
- /**
942
- * App-level middleware entries (for internal use by RSC handler)
943
- * These wrap the entire request/response cycle
944
- */
945
- readonly middleware: MiddlewareEntry<TEnv>[];
946
-
947
- /**
948
- * Nonce provider for CSP (for internal use by createHandler)
949
- */
950
- readonly nonce?: NonceProvider<TEnv>;
951
-
952
- /**
953
- * RSC version string (for internal use by createHandler)
954
- */
955
- readonly version?: string;
956
-
957
- /**
958
- * URL patterns reference for build-time manifest generation
959
- * @internal
960
- */
961
- readonly urlpatterns?: UrlPatterns<TEnv, any>;
962
-
963
- match(request: Request, context: TEnv): Promise<MatchResult>;
964
-
965
- /**
966
- * Preview match - returns route middleware without segment resolution.
967
- * Also returns responseType and handler for response routes (non-RSC short-circuit).
968
- */
969
- previewMatch(
970
- request: Request,
971
- context: TEnv,
972
- ): Promise<{
973
- routeMiddleware?: Array<{
974
- handler: import("./router/middleware.js").MiddlewareFn;
975
- params: Record<string, string>;
976
- }>;
977
- responseType?: string;
978
- handler?: Function;
979
- params?: Record<string, string>;
980
- negotiated?: boolean;
981
- } | null>;
982
-
983
- matchPartial(
984
- request: Request,
985
- context: TEnv,
986
- actionContext?: {
987
- actionId?: string;
988
- actionUrl?: URL;
989
- actionResult?: any;
990
- formData?: FormData;
991
- },
992
- ): Promise<MatchResult | null>;
993
-
994
- /**
995
- * Match an error to the nearest error boundary and return error segments
996
- *
997
- * Used when an action or other operation fails and we need to render
998
- * the error boundary UI. Finds the nearest errorBoundary in the route tree
999
- * for the current URL and renders it with the error info.
1000
- *
1001
- * @param request - The current request (used to match the route)
1002
- * @param context - Environment context
1003
- * @param error - The error that occurred
1004
- * @param segmentType - Type of segment where error occurred (default: "route")
1005
- * @returns MatchResult with error segment, or null if no error boundary found
1006
- */
1007
- matchError(
1008
- request: Request,
1009
- context: TEnv,
1010
- error: unknown,
1011
- segmentType?: ErrorInfo["segmentType"],
1012
- ): Promise<MatchResult | null>;
1013
-
1014
- /**
1015
- * @internal
1016
- * Debug utility to serialize the manifest for inspection
1017
- * Returns a JSON-friendly representation of all routes and layouts
1018
- */
1019
- debugManifest(): Promise<SerializedManifest>;
1020
-
1021
- /**
1022
- * Handle an RSC request.
1023
- *
1024
- * Uses the router's configuration (nonce, version, cache) automatically.
1025
- * The handler is lazily created on first call.
1026
- *
1027
- * @example Cloudflare Workers
1028
- * ```tsx
1029
- * import { router } from "./router";
1030
- *
1031
- * export default { fetch: router.fetch };
1032
- * ```
1033
- *
1034
- * @example Direct export
1035
- * ```tsx
1036
- * const router = createRouter({
1037
- * document: Document,
1038
- * urls: urlpatterns,
1039
- * nonce: () => true,
1040
- * });
1041
- *
1042
- * export const fetch = router.fetch;
1043
- * ```
1044
- */
1045
- fetch(
1046
- request: Request,
1047
- env: TEnv & { ctx?: ExecutionContext },
1048
- ): Promise<Response>;
1049
- }
87
+ // Extracted router types and registry
88
+ import {
89
+ RSC_ROUTER_BRAND,
90
+ RouterRegistry,
91
+ nextRouterAutoId,
92
+ } from "./router/router-registry.js";
93
+ import type {
94
+ RSCRouterOptions,
95
+ RootLayoutProps,
96
+ } from "./router/router-options.js";
97
+ import type {
98
+ RSCRouter,
99
+ RSCRouterInternal,
100
+ RouterRequestInput,
101
+ } from "./router/router-interfaces.js";
1050
102
 
1051
- /**
1052
- * Create an RSC router with generic context type
1053
- * Route types are accumulated automatically through the builder chain
1054
- *
1055
- * @example
1056
- * ```typescript
1057
- * interface AppContext {
1058
- * db: Database;
1059
- * user?: User;
1060
- * }
1061
- *
1062
- * const router = createRouter<AppContext>({
1063
- * debugPerformance: true // Enable metrics
1064
- * });
1065
- *
1066
- * // Route types accumulate through the chain - no module augmentation needed!
1067
- * // Keys stay unchanged, only URL patterns get the prefix
1068
- * router
1069
- * .routes(homeRoutes) // accumulates homeRoutes
1070
- * .map(() => import('./home'))
1071
- * .routes('/shop', shopRoutes) // accumulates shopRoutes with prefixed URLs
1072
- * .map(() => import('./shop'));
1073
- *
1074
- * // router.reverse now has type-safe autocomplete for all registered routes
1075
- * // Given shopRoutes = { cart: "/cart" }, reverse uses original key:
1076
- * router.reverse("cart"); // "/shop/cart"
1077
- * ```
1078
- */
103
+ // Extracted closure functions
104
+ import {
105
+ findLazyIncludes,
106
+ evaluateLazyEntry as _evaluateLazyEntry,
107
+ type LazyEvalDeps,
108
+ } from "./router/lazy-includes.js";
109
+ import { createFindMatch } from "./router/find-match.js";
110
+ import {
111
+ matchForPrerender as _matchForPrerender,
112
+ renderStaticSegment as _renderStaticSegment,
113
+ } from "./router/prerender-match.js";
114
+
115
+ // Re-export public types and values from extracted modules
116
+ export { RSC_ROUTER_BRAND, RouterRegistry } from "./router/router-registry.js";
117
+ export type {
118
+ RSCRouterOptions,
119
+ RootLayoutProps,
120
+ SSRStreamMode,
121
+ SSROptions,
122
+ ResolveStreamingContext,
123
+ } from "./router/router-options.js";
124
+ export type {
125
+ RSCRouter,
126
+ RSCRouterInternal,
127
+ RouterRequestInput,
128
+ } from "./router/router-interfaces.js";
129
+ export { toInternal } from "./router/router-interfaces.js";
1079
130
 
1080
131
  export function createRouter<TEnv = any>(
1081
132
  options: RSCRouterOptions<TEnv> = {},
1082
133
  ): RSCRouter<TEnv, {}> {
1083
134
  const {
1084
135
  id: userProvidedId,
136
+ $$id: injectedId,
137
+ basename: basenameOption,
1085
138
  debugPerformance = false,
1086
139
  document: documentOption,
1087
140
  defaultErrorBoundary,
@@ -1089,15 +142,84 @@ export function createRouter<TEnv = any>(
1089
142
  notFound,
1090
143
  onError,
1091
144
  cache,
145
+ cacheProfiles: cacheProfilesOption,
1092
146
  theme: themeOption,
1093
147
  urls: urlsOption,
148
+ $$routeNames: staticRouteNames,
149
+ $$sourceFile: injectedSourceFile,
1094
150
  nonce,
1095
151
  version,
152
+ prefetchCacheTTL: prefetchCacheTTLOption,
1096
153
  warmup: warmupOption,
1097
- allowDebugManifest: allowDebugManifestOption = true,
154
+ allowDebugManifest: allowDebugManifestOption = false,
155
+ telemetry: telemetrySink,
156
+ ssr: ssrOption,
157
+ timeout: timeoutShorthand,
158
+ timeouts: timeoutsOption,
159
+ onTimeout,
160
+ originCheck: originCheckOption,
1098
161
  } = options;
1099
162
 
1100
- const routerId = userProvidedId ?? `router_${routerAutoId++}`;
163
+ // Normalize basename: ensure leading slash, strip trailing slash.
164
+ // A bare "/" is equivalent to no basename.
165
+ const basename =
166
+ basenameOption && basenameOption.replace(/^\/+|\/+$/g, "")
167
+ ? "/" + basenameOption.replace(/^\/+|\/+$/g, "")
168
+ : undefined;
169
+
170
+ // Resolve telemetry sink (no-op when not configured)
171
+ const telemetry = resolveSink(telemetrySink);
172
+
173
+ // Resolve cache profiles: merge user config with guaranteed default profile.
174
+ // This resolved map is both stored on the router (for per-request context)
175
+ // and written to the global registry (for DSL-time cache("profileName")).
176
+ const resolvedCacheProfiles = resolveCacheProfiles(cacheProfilesOption);
177
+ setCacheProfiles(resolvedCacheProfiles);
178
+
179
+ // Source file: prefer Vite-injected path (zero cost), fall back to
180
+ // stack trace parsing for non-Vite environments (e.g. tests).
181
+ let __sourceFile: string | undefined = injectedSourceFile;
182
+ if (!__sourceFile) {
183
+ try {
184
+ const stack = new Error().stack;
185
+ if (stack) {
186
+ const lines = stack.split("\n");
187
+ for (const line of lines) {
188
+ const match = line.match(/\((.+?\.(ts|tsx|js|jsx)):\d+:\d+\)/);
189
+ if (
190
+ match &&
191
+ !match[1].endsWith("/router.ts") &&
192
+ !match[1].includes("@rangojs/router") &&
193
+ !match[1].includes("node_modules")
194
+ ) {
195
+ __sourceFile = match[1].startsWith("file:")
196
+ ? match[1].slice(5)
197
+ : match[1];
198
+ break;
199
+ }
200
+ }
201
+ }
202
+ } catch {}
203
+ }
204
+
205
+ // Router ID priority: explicit id > Vite-injected $$id > counter fallback.
206
+ // $$id is a hash of filename+line injected by the Vite transform at compile
207
+ // time, so it's stable across build/runtime regardless of module evaluation
208
+ // order (unlike the counter which depends on import order).
209
+ const routerId =
210
+ userProvidedId ?? injectedId ?? `router_${nextRouterAutoId()}`;
211
+
212
+ // Resolve prefetch cache TTL (default: 300 seconds / 5 minutes)
213
+ // Clamp to a non-negative integer for valid Cache-Control max-age.
214
+ const rawTTL =
215
+ prefetchCacheTTLOption !== undefined ? prefetchCacheTTLOption : 300;
216
+ const prefetchCacheTTLSeconds =
217
+ rawTTL === false ? 0 : Math.max(0, Math.floor(rawTTL));
218
+ const prefetchCacheTTL = prefetchCacheTTLSeconds * 1000;
219
+ const prefetchCacheControl: string | false =
220
+ prefetchCacheTTLSeconds === 0
221
+ ? false
222
+ : `private, max-age=${prefetchCacheTTLSeconds}`;
1101
223
 
1102
224
  // Resolve warmup enabled flag (default: true)
1103
225
  const warmupEnabled = warmupOption !== false;
@@ -1107,15 +229,29 @@ export function createRouter<TEnv = any>(
1107
229
  ? resolveThemeConfig(themeOption)
1108
230
  : null;
1109
231
 
232
+ // Resolve timeout config (merge shorthand + structured)
233
+ const resolvedTimeouts = resolveTimeouts(timeoutShorthand, timeoutsOption);
234
+
1110
235
  /**
1111
236
  * Wrapper for invokeOnError that binds the router's onError callback.
1112
237
  * Uses the shared utility from router/error-handling.ts for consistent behavior.
238
+ *
239
+ * Deduplicates via per-request WeakSet stored on the ALS request context.
240
+ * A closure-level WeakSet would silently swallow errors if the same object
241
+ * instance is thrown across separate requests (e.g. a singleton error).
1113
242
  */
1114
243
  function callOnError(
1115
244
  error: unknown,
1116
245
  phase: ErrorPhase,
1117
246
  context: Parameters<typeof invokeOnError<TEnv>>[3],
1118
247
  ): void {
248
+ if (error != null && typeof error === "object") {
249
+ const reportedErrors = _getRequestContext()?._reportedErrors;
250
+ if (reportedErrors) {
251
+ if (reportedErrors.has(error)) return;
252
+ reportedErrors.add(error);
253
+ }
254
+ }
1119
255
  invokeOnError(onError, error, phase, context, "Router");
1120
256
  }
1121
257
 
@@ -1158,6 +294,18 @@ export function createRouter<TEnv = any>(
1158
294
  handler = patternOrMiddleware;
1159
295
  }
1160
296
 
297
+ // Prevent "use cache" functions from being used as middleware.
298
+ // They return data/JSX and do not call next() — silently accepting
299
+ // them would be a confusing no-op.
300
+ if (isCachedFunction(handler)) {
301
+ throw new Error(
302
+ `A "use cache" function cannot be used as middleware. ` +
303
+ `Cached functions return data and do not participate in the ` +
304
+ `middleware chain. Remove the "use cache" directive or use a ` +
305
+ `regular middleware function instead.`,
306
+ );
307
+ }
308
+
1161
309
  // If mount-scoped, prepend mount prefix to pattern
1162
310
  let fullPattern = pattern;
1163
311
  if (mountPrefix && pattern) {
@@ -1187,20 +335,55 @@ export function createRouter<TEnv = any>(
1187
335
  });
1188
336
  }
1189
337
 
1190
- // Track all registered routes with their prefixes for reverse()
1191
- const mergedRouteMap: Record<string, string> = {};
1192
-
1193
- // Build a Map from precomputed entries for O(1) lookup by staticPrefix.
1194
- // The array is set at import time (from the virtual module) before createRouter runs.
1195
- const precomputedEntriesRaw = getPrecomputedEntries();
1196
- const precomputedByPrefix: Map<string, Record<string, string>> | null =
1197
- precomputedEntriesRaw
1198
- ? new Map(precomputedEntriesRaw.map((e) => [e.staticPrefix, e.routes]))
1199
- : null;
1200
-
338
+ // Track all registered routes with their prefixes for reverse().
339
+ // Seed from injected NamedRoutes so reverse() works at module load time
340
+ // for routes that come from lazy includes.
341
+ const mergedRouteMap: Record<string, string> =
342
+ flattenNamedRoutes(staticRouteNames);
343
+
344
+ // Track names that came from the static seed so we can silently overwrite
345
+ // them during routes() registration. The gen file may be stale during HMR,
346
+ // so conflicts between seeded and runtime-registered values are expected.
347
+ const seededNames = new Set(Object.keys(mergedRouteMap));
348
+
349
+ // Lazy precomputed entries lookup: rebuilt when per-router data arrives.
350
+ // In production multi-router setups, per-router data is loaded lazily via
351
+ // ensureRouterManifest(). At createRouter() time the data isn't available yet,
352
+ // so we defer building the Map until first use and invalidate when the
353
+ // per-router source changes.
354
+ let precomputedByPrefix: Map<string, Record<string, string>> | null = null;
355
+ let precomputedSource:
356
+ | Array<{ staticPrefix: string; routes: Record<string, string> }>
357
+ | null
358
+ | undefined;
359
+
360
+ function getPrecomputedByPrefix(): Map<
361
+ string,
362
+ Record<string, string>
363
+ > | null {
364
+ const current =
365
+ getRouterPrecomputedEntries(routerId) ?? getPrecomputedEntries();
366
+ if (current !== precomputedSource) {
367
+ precomputedSource = current;
368
+ precomputedByPrefix = current
369
+ ? new Map(current.map((e) => [e.staticPrefix, e.routes]))
370
+ : null;
371
+ }
372
+ return precomputedByPrefix;
373
+ }
1201
374
 
1202
- // Wrapper to pass debugPerformance to external createMetricsStore
1203
- const getMetricsStore = () => createMetricsStore(debugPerformance);
375
+ // Wrapper to pass debugPerformance to external createMetricsStore.
376
+ // Also checks per-request flag set by ctx.debugPerformance() in middleware.
377
+ const getMetricsStore = () => {
378
+ const reqCtx = _getRequestContext();
379
+ const enabled = debugPerformance || !!reqCtx?._debugPerformance;
380
+ if (!enabled) return undefined;
381
+ if (!reqCtx) {
382
+ return createMetricsStore(true);
383
+ }
384
+ reqCtx._metricsStore ??= createMetricsStore(true);
385
+ return reqCtx._metricsStore;
386
+ };
1204
387
 
1205
388
  // Wrapper to pass defaults to error/notFound boundary finders
1206
389
  const findNearestErrorBoundary = (entry: EntryData | null) =>
@@ -1211,17 +394,46 @@ export function createRouter<TEnv = any>(
1211
394
 
1212
395
  // Helper to get handleStore from request context
1213
396
  const getHandleStore = (): HandleStore | undefined => {
1214
- return getRequestContext()?._handleStore;
397
+ return _getRequestContext()?._handleStore;
1215
398
  };
1216
399
 
1217
- // Track a pending handler promise (non-blocking)
1218
- const trackHandler = <T>(promise: Promise<T>): Promise<T> => {
400
+ // Track a pending handler promise (non-blocking).
401
+ // Attaches a side-effect .catch() to report streaming handler errors to onError
402
+ // without altering the rejection chain (React's streaming error boundary still handles it).
403
+ const trackHandler = <T>(
404
+ promise: Promise<T>,
405
+ errorContext?: {
406
+ segmentId?: string;
407
+ segmentType?: string;
408
+ },
409
+ ): Promise<T> => {
1219
410
  const store = getHandleStore();
1220
- return store ? store.track(promise) : promise;
411
+ const tracked = store ? store.track(promise) : promise;
412
+
413
+ // Report streaming handler errors to onError as a side-effect.
414
+ // The rejection still propagates to the RSC stream for client error boundaries.
415
+ // Captures request context eagerly (closure) so the catch handler has full context.
416
+ const reqCtx = _getRequestContext();
417
+ if (reqCtx && onError) {
418
+ tracked.catch((error) => {
419
+ callOnError(error, "handler", {
420
+ request: reqCtx.request,
421
+ url: reqCtx.url,
422
+ routeKey: reqCtx._routeName,
423
+ params: reqCtx.params as Record<string, string>,
424
+ env: reqCtx.env as TEnv,
425
+ segmentId: errorContext?.segmentId,
426
+ segmentType: errorContext?.segmentType as any,
427
+ handledByBoundary: true,
428
+ });
429
+ });
430
+ }
431
+
432
+ return tracked;
1221
433
  };
1222
434
 
1223
435
  // Wrapper for wrapLoaderWithErrorHandling that uses router's error boundary finder
1224
- // Includes onError callback for loader error notification
436
+ // Includes onError callback for loader error notification and telemetry emission.
1225
437
  function wrapLoaderPromise<T>(
1226
438
  promise: Promise<T>,
1227
439
  entry: EntryData,
@@ -1237,7 +449,25 @@ export function createRouter<TEnv = any>(
1237
449
  requestStartTime?: number;
1238
450
  },
1239
451
  ): Promise<LoaderDataResult<T>> {
1240
- return wrapLoaderWithErrorHandling(
452
+ const loaderStart = telemetrySink ? performance.now() : 0;
453
+ const loaderRequestId = telemetrySink
454
+ ? errorContext?.request
455
+ ? getRequestId(errorContext.request)
456
+ : undefined
457
+ : undefined;
458
+ if (telemetrySink) {
459
+ const loaderName = segmentId.split(".").pop() || "unknown";
460
+ safeEmit(telemetry, {
461
+ type: "loader.start",
462
+ timestamp: loaderStart,
463
+ requestId: loaderRequestId,
464
+ segmentId,
465
+ loaderName,
466
+ pathname,
467
+ });
468
+ }
469
+
470
+ const result = wrapLoaderWithErrorHandling(
1241
471
  promise,
1242
472
  entry,
1243
473
  segmentId,
@@ -1260,9 +490,42 @@ export function createRouter<TEnv = any>(
1260
490
  handledByBoundary: ctx.handledByBoundary,
1261
491
  requestStartTime: errorContext.requestStartTime,
1262
492
  });
493
+ if (telemetrySink) {
494
+ const errorObj =
495
+ error instanceof Error ? error : new Error(String(error));
496
+ safeEmit(telemetry, {
497
+ type: "loader.error",
498
+ timestamp: performance.now(),
499
+ requestId: loaderRequestId,
500
+ segmentId: ctx.segmentId,
501
+ loaderName: ctx.loaderName,
502
+ pathname,
503
+ error: errorObj,
504
+ handledByBoundary: ctx.handledByBoundary,
505
+ });
506
+ }
1263
507
  }
1264
508
  : undefined,
1265
509
  );
510
+
511
+ // Emit loader.end after the promise settles (fire-and-forget)
512
+ if (telemetrySink) {
513
+ const loaderName = segmentId.split(".").pop() || "unknown";
514
+ result.then((r) => {
515
+ safeEmit(telemetry, {
516
+ type: "loader.end",
517
+ timestamp: performance.now(),
518
+ requestId: loaderRequestId,
519
+ segmentId,
520
+ loaderName,
521
+ pathname,
522
+ durationMs: performance.now() - loaderStart,
523
+ ok: r.ok,
524
+ });
525
+ });
526
+ }
527
+
528
+ return result;
1266
529
  }
1267
530
 
1268
531
  // Dependencies object for extracted segment resolution functions.
@@ -1272,6 +535,7 @@ export function createRouter<TEnv = any>(
1272
535
  trackHandler,
1273
536
  findNearestErrorBoundary,
1274
537
  findNearestNotFoundBoundary,
538
+ notFoundComponent: notFound,
1275
539
  callOnError,
1276
540
  };
1277
541
 
@@ -1283,438 +547,46 @@ export function createRouter<TEnv = any>(
1283
547
  findInterceptForRoute(routeKey, parentEntry, selectorContext, isAction),
1284
548
  callOnError,
1285
549
  findNearestErrorBoundary,
550
+ // Use per-router manifest when available, otherwise the static named map
551
+ // seeded into mergedRouteMap at router creation.
552
+ getRouteMap: () => getRouterManifest(routerId) ?? mergedRouteMap,
1286
553
  };
1287
554
 
1288
- // Thin wrappers that bind the deps to extracted functions.
1289
- // These maintain the same signatures as the original inline functions
1290
- // so that RouterContext and call sites don't need to change.
1291
-
1292
- function resolveAllSegments(
1293
- entries: EntryData[],
1294
- routeKey: string,
1295
- params: Record<string, string>,
1296
- context: HandlerContext<any, TEnv>,
1297
- loaderPromises: Map<string, Promise<any>>,
1298
- ) {
1299
- return _resolveAllSegments(entries, routeKey, params, context, loaderPromises, segmentDeps);
1300
- }
1301
-
1302
- function resolveLoadersOnly(
1303
- entries: EntryData[],
1304
- context: HandlerContext<any, TEnv>,
1305
- ) {
1306
- return _resolveLoadersOnly(entries, context, segmentDeps);
1307
- }
1308
-
1309
- function resolveLoadersOnlyWithRevalidation(
1310
- entries: EntryData[],
1311
- context: HandlerContext<any, TEnv>,
1312
- clientSegmentIds: Set<string>,
1313
- prevParams: Record<string, string>,
1314
- request: Request,
1315
- prevUrl: URL,
1316
- nextUrl: URL,
1317
- routeKey: string,
1318
- actionContext?: { actionId?: string; actionUrl?: URL; actionResult?: any; formData?: FormData },
1319
- ) {
1320
- return _resolveLoadersOnlyWithRevalidation(
1321
- entries, context, clientSegmentIds, prevParams, request,
1322
- prevUrl, nextUrl, routeKey, segmentDeps, actionContext,
1323
- );
1324
- }
1325
-
1326
- function buildEntryRevalidateMap(entries: EntryData[]) {
1327
- return _buildEntryRevalidateMap(entries);
1328
- }
1329
-
1330
- function resolveAllSegmentsWithRevalidation(
1331
- entries: EntryData[],
1332
- routeKey: string,
1333
- params: Record<string, string>,
1334
- context: HandlerContext<any, TEnv>,
1335
- clientSegmentSet: Set<string>,
1336
- prevParams: Record<string, string>,
1337
- request: Request,
1338
- prevUrl: URL,
1339
- nextUrl: URL,
1340
- loaderPromises: Map<string, Promise<any>>,
1341
- actionContext: { actionId?: string; actionUrl?: URL; actionResult?: any; formData?: FormData } | undefined,
1342
- interceptResult: { intercept: InterceptEntry; entry: EntryData } | null,
1343
- localRouteName: string,
1344
- pathname: string,
1345
- ) {
1346
- return _resolveAllSegmentsWithRevalidation(
1347
- entries, routeKey, params, context, clientSegmentSet, prevParams, request,
1348
- prevUrl, nextUrl, loaderPromises, actionContext, interceptResult,
1349
- localRouteName, pathname, segmentDeps,
1350
- );
1351
- }
1352
-
1353
- function findInterceptForRoute(
1354
- targetRouteKey: string,
1355
- fromEntry: EntryData | null,
1356
- selectorContext: InterceptSelectorContext | null = null,
1357
- isAction: boolean = false,
1358
- ) {
1359
- return _findInterceptForRoute(targetRouteKey, fromEntry, selectorContext, isAction);
1360
- }
1361
-
1362
- function resolveInterceptEntry(
1363
- interceptEntry: InterceptEntry,
1364
- parentEntry: EntryData,
1365
- params: Record<string, string>,
1366
- context: HandlerContext<any, TEnv>,
1367
- belongsToRoute: boolean = true,
1368
- revalidationContext?: any,
1369
- ) {
1370
- return _resolveInterceptEntry(
1371
- interceptEntry, parentEntry, params, context, belongsToRoute,
1372
- segmentDeps, revalidationContext,
1373
- );
1374
- }
1375
-
1376
- function resolveInterceptLoadersOnly(
1377
- interceptEntry: InterceptEntry,
1378
- parentEntry: EntryData,
1379
- params: Record<string, string>,
1380
- context: HandlerContext<any, TEnv>,
1381
- belongsToRoute: boolean = true,
1382
- revalidationContext: any,
1383
- ) {
1384
- return _resolveInterceptLoadersOnly(
1385
- interceptEntry, parentEntry, params, context, belongsToRoute,
1386
- segmentDeps, revalidationContext,
1387
- );
1388
- }
1389
-
1390
- // Detect lazy includes in handler result and create placeholder entries
1391
- // Lazy includes are IncludeItem with lazy: true and _lazyContext
1392
- // Moved to outer scope so it can be reused by evaluateLazyEntry for nested includes
1393
- function findLazyIncludes(items: AllUseItems[]): Array<{
1394
- prefix: string;
1395
- patterns: UrlPatterns<TEnv>;
1396
- context: {
1397
- urlPrefix: string;
1398
- namePrefix: string | undefined;
1399
- parent: unknown;
1400
- };
1401
- }> {
1402
- const lazyItems: Array<{
1403
- prefix: string;
1404
- patterns: UrlPatterns<TEnv>;
1405
- context: {
1406
- urlPrefix: string;
1407
- namePrefix: string | undefined;
1408
- parent: unknown;
1409
- };
1410
- }> = [];
1411
-
1412
- for (const item of items) {
1413
- if (!item) continue;
1414
- if (item.type === "include") {
1415
- const includeItem = item as IncludeItem;
1416
- if (includeItem.lazy === true && includeItem._lazyContext) {
1417
- lazyItems.push({
1418
- prefix: includeItem.prefix,
1419
- patterns: includeItem.patterns as UrlPatterns<TEnv>,
1420
- context: includeItem._lazyContext,
1421
- });
1422
- }
1423
- }
1424
- // Recursively check nested items (in layouts, etc.)
1425
- if ((item as any).uses && Array.isArray((item as any).uses)) {
1426
- lazyItems.push(...findLazyIncludes((item as any).uses));
1427
- }
1428
- }
1429
-
1430
- return lazyItems;
1431
- }
555
+ // Create segment resolution wrappers bound to segmentDeps
556
+ const {
557
+ resolveAllSegments,
558
+ resolveLoadersOnly,
559
+ resolveLoadersOnlyWithRevalidation,
560
+ buildEntryRevalidateMap,
561
+ resolveAllSegmentsWithRevalidation,
562
+ findInterceptForRoute,
563
+ resolveInterceptEntry,
564
+ resolveInterceptLoadersOnly,
565
+ } = createSegmentWrappers<TEnv>(segmentDeps);
566
+
567
+ // Lazy evaluation deps — captures closure state for extracted evaluateLazyEntry
568
+ const lazyEvalDeps: LazyEvalDeps<TEnv> = {
569
+ routesEntries,
570
+ mergedRouteMap,
571
+ nextMountIndex: () => mountIndex++,
572
+ getPrecomputedByPrefix,
573
+ routerId,
574
+ };
1432
575
 
1433
- /**
1434
- * Evaluate a lazy entry's patterns and populate its routes
1435
- * This runs the lazy patterns handler and updates the entry in-place
1436
- * Also detects nested lazy includes and registers them as new entries
1437
- */
1438
576
  function evaluateLazyEntry(entry: RouteEntry<TEnv>): void {
1439
- if (!entry.lazy || entry.lazyEvaluated || !entry.lazyPatterns) {
1440
- return;
1441
- }
1442
-
1443
- // Check for pre-computed routes from build-time data.
1444
- // Only leaf nodes (no nested includes) are precomputed, so entries with
1445
- // nested lazy includes fall through to the handler below.
1446
- if (precomputedByPrefix) {
1447
- const routes = precomputedByPrefix.get(entry.staticPrefix);
1448
- if (routes) {
1449
- entry.lazyEvaluated = true;
1450
- entry.routes = routes as ResolvedRouteMap<any>;
1451
- for (const [name, pattern] of Object.entries(routes)) {
1452
- mergedRouteMap[name] = pattern;
1453
- }
1454
- registerRouteMap(mergedRouteMap);
1455
- return;
1456
- }
1457
- }
1458
-
1459
- // Mark as evaluated immediately to prevent concurrent evaluation.
1460
- // JS is single-threaded but handlers.handler() could theoretically yield,
1461
- // and the while-loop in findMatch retries after evaluation.
1462
- entry.lazyEvaluated = true;
1463
-
1464
- const lazyPatterns = entry.lazyPatterns as UrlPatterns<TEnv>;
1465
- const lazyContext = entry.lazyContext;
1466
-
1467
- // Create a new context for evaluating the lazy patterns
1468
- const manifest = new Map<string, EntryData>();
1469
- const patterns = new Map<string, string>();
1470
- const patternsByPrefix = new Map<string, Map<string, string>>();
1471
- const trailingSlashMap = new Map<string, TrailingSlashMode>();
1472
-
1473
- // Capture the handler result to detect nested lazy includes
1474
- let handlerResult: AllUseItems[] = [];
1475
-
1476
- RSCRouterContext.run(
1477
- {
1478
- manifest,
1479
- patterns,
1480
- patternsByPrefix,
1481
- trailingSlash: trailingSlashMap,
1482
- namespace: "lazy",
1483
- parent: (lazyContext?.parent as EntryData | null) ?? null,
1484
- counters: {},
1485
- },
1486
- () => {
1487
- // Run the lazy patterns handler with the original context prefixes
1488
- // The prefix comes from the IncludeItem stored in lazyPatterns
1489
- const includePrefix = (entry as any)._lazyPrefix || "";
1490
- const fullPrefix = (lazyContext?.urlPrefix || "") + includePrefix;
1491
-
1492
- if (fullPrefix || lazyContext?.namePrefix) {
1493
- runWithPrefixes(fullPrefix, lazyContext?.namePrefix, () => {
1494
- handlerResult = lazyPatterns.handler() as AllUseItems[];
1495
- });
1496
- } else {
1497
- handlerResult = lazyPatterns.handler() as AllUseItems[];
1498
- }
1499
- },
1500
- );
1501
-
1502
- // Populate the entry's routes from the patterns
1503
- const routesObject: Record<string, string> = {};
1504
- for (const [name, pattern] of patterns.entries()) {
1505
- routesObject[name] = pattern;
1506
- // Also add to merged route map for reverse() support
1507
- const existingPattern = mergedRouteMap[name];
1508
- if (existingPattern !== undefined && existingPattern !== pattern) {
1509
- console.warn(
1510
- `[@rangojs/router] Route name conflict: "${name}" already maps to "${existingPattern}", ` +
1511
- `overwriting with "${pattern}" (from lazy include). Use unique route names to avoid this.`,
1512
- );
1513
- }
1514
- mergedRouteMap[name] = pattern;
1515
- }
1516
-
1517
- // Update the entry in-place
1518
- entry.routes = routesObject as ResolvedRouteMap<any>;
1519
-
1520
- // Note: Do NOT clear lazyPatterns/lazyContext here.
1521
- // loadManifest() needs them on every request to re-run the handler
1522
- // in the correct AsyncLocalStorage context (Store.manifest).
1523
-
1524
- // Update trailing slash config if available
1525
- if (trailingSlashMap.size > 0) {
1526
- entry.trailingSlash = Object.fromEntries(trailingSlashMap);
1527
- }
1528
-
1529
- // Detect nested lazy includes and register them as new entries
1530
- const nestedLazyIncludes = findLazyIncludes(handlerResult);
1531
- for (const lazyInclude of nestedLazyIncludes) {
1532
- // Compute the full URL prefix (combining parent prefix if any)
1533
- const fullPrefix = lazyInclude.context.urlPrefix
1534
- ? lazyInclude.context.urlPrefix + lazyInclude.prefix
1535
- : lazyInclude.prefix;
1536
-
1537
- const nestedEntry: RouteEntry<TEnv> & { _lazyPrefix?: string } = {
1538
- prefix: "",
1539
- staticPrefix: extractStaticPrefix(fullPrefix),
1540
- routes: {} as ResolvedRouteMap<any>, // Empty until first match
1541
- trailingSlash: entry.trailingSlash,
1542
- handler: (lazyInclude.patterns as UrlPatterns<TEnv>).handler,
1543
- mountIndex: entry.mountIndex,
1544
- // Lazy evaluation fields
1545
- lazy: true,
1546
- lazyPatterns: lazyInclude.patterns,
1547
- lazyContext: lazyInclude.context,
1548
- lazyEvaluated: false,
1549
- // Store the include prefix for evaluation
1550
- _lazyPrefix: lazyInclude.prefix,
1551
- };
1552
- // Insert nested lazy entry before any entry whose staticPrefix is a
1553
- // prefix of (but shorter than) this lazy entry's staticPrefix.
1554
- // This ensures more specific lazy includes are matched before
1555
- // less specific eager entries (e.g., "/href/nested" before "/href/:id").
1556
- const nestedPrefix = nestedEntry.staticPrefix;
1557
- let insertIndex = routesEntries.length;
1558
- if (nestedPrefix) {
1559
- for (let i = 0; i < routesEntries.length; i++) {
1560
- const existing = routesEntries[i]!;
1561
- if (
1562
- nestedPrefix.startsWith(existing.staticPrefix) &&
1563
- nestedPrefix.length > existing.staticPrefix.length
1564
- ) {
1565
- insertIndex = i;
1566
- break;
1567
- }
1568
- }
1569
- }
1570
- routesEntries.splice(insertIndex, 0, nestedEntry);
1571
- }
1572
-
1573
- // Re-register route map for runtime reverse() usage
1574
- registerRouteMap(mergedRouteMap);
1575
- }
1576
-
1577
- // Single-entry cache for findMatch to avoid redundant matching within the same request.
1578
- // previewMatch and match both call findMatch with the same pathname — this ensures
1579
- // the route matching work (which may check thousands of routes) only happens once.
1580
- let lastFindMatchPathname: string | null = null;
1581
- let lastFindMatchResult: RouteMatchResult<TEnv> | null = null;
1582
-
1583
- // Wrapper for findMatch that uses routesEntries
1584
- // Handles lazy evaluation by evaluating lazy entries on first match.
1585
- // Phase 1: try O(path_length) trie match.
1586
- // Phase 2: fall back to regex iteration.
1587
- function findMatch(
1588
- pathname: string,
1589
- ms?: MetricsStore,
1590
- ): RouteMatchResult<TEnv> | null {
1591
- // Return cached result if same pathname (avoids double-match per request)
1592
- if (lastFindMatchPathname === pathname) {
1593
- return lastFindMatchResult;
1594
- }
1595
-
1596
- // Helper to push sub-metrics
1597
- const pushMetric = ms
1598
- ? (label: string, start: number) => {
1599
- ms.metrics.push({
1600
- label,
1601
- duration: performance.now() - start,
1602
- startTime: start - ms.requestStart,
1603
- });
1604
- }
1605
- : undefined;
1606
-
1607
- // Phase 1: Try trie match (O(path_length))
1608
- const routeTrie = getRouteTrie();
1609
- if (routeTrie) {
1610
- const trieStart = performance.now();
1611
- const trieResult = tryTrieMatch(routeTrie, pathname);
1612
- pushMetric?.("match:trie", trieStart);
1613
-
1614
- if (trieResult) {
1615
- // Find the RouteEntry that contains this route.
1616
- // Multiple entries can share the same staticPrefix (e.g., several
1617
- // include("/", patterns) calls all produce staticPrefix=""). Evaluate
1618
- // each candidate and pick the one whose routes include the matched key.
1619
- const entryStart = performance.now();
1620
- let entry: RouteEntry<TEnv> | undefined;
1621
- let fallbackEntry: RouteEntry<TEnv> | undefined;
1622
-
1623
- for (const e of routesEntries) {
1624
- if (e.staticPrefix !== trieResult.sp) continue;
1625
- if (!fallbackEntry) fallbackEntry = e;
1626
- evaluateLazyEntry(e);
1627
- if (
1628
- e.routes &&
1629
- trieResult.routeKey in (e.routes as Record<string, unknown>)
1630
- ) {
1631
- entry = e;
1632
- break;
1633
- }
1634
- }
1635
-
1636
- // If no entry had the route in its routes map, use the first matching
1637
- // entry as fallback (handles main entry with inline routes not yet
1638
- // reflected in its routes object).
1639
- if (!entry) entry = fallbackEntry;
1640
-
1641
- // If entry not found (nested include not yet discovered), evaluate parent
1642
- if (!entry) {
1643
- const parent = routesEntries.find(
1644
- (e) =>
1645
- trieResult.sp.startsWith(e.staticPrefix) &&
1646
- e.staticPrefix !== trieResult.sp,
1647
- );
1648
- if (parent) {
1649
- const lazyStart = performance.now();
1650
- evaluateLazyEntry(parent);
1651
- pushMetric?.("match:lazy-eval", lazyStart);
1652
- }
1653
- entry = routesEntries.find((e) => e.staticPrefix === trieResult.sp);
1654
- }
1655
- pushMetric?.("match:entry-resolve", entryStart);
1656
-
1657
- if (entry) {
1658
- lastFindMatchPathname = pathname;
1659
- lastFindMatchResult = {
1660
- entry,
1661
- routeKey: trieResult.routeKey,
1662
- params: trieResult.params,
1663
- optionalParams: new Set(trieResult.optionalParams || []),
1664
- redirectTo: trieResult.redirectTo,
1665
- ancestry: trieResult.ancestry,
1666
- ...(trieResult.pr ? { pr: true } : {}),
1667
- ...(trieResult.pt ? { pt: true } : {}),
1668
- ...(trieResult.responseType ? { responseType: trieResult.responseType } : {}),
1669
- ...(trieResult.negotiateVariants ? { negotiateVariants: trieResult.negotiateVariants } : {}),
1670
- ...(trieResult.rscFirst ? { rscFirst: true } : {}),
1671
- };
1672
- return lastFindMatchResult;
1673
- }
1674
- }
1675
- }
1676
-
1677
- // Phase 2: Fall back to existing matching (regex iteration)
1678
- const regexStart = performance.now();
1679
- let result = findRouteMatch(pathname, routesEntries);
1680
-
1681
- // If we hit a lazy entry that needs evaluation, evaluate and retry.
1682
- // Cap iterations to prevent infinite loops from pathological nesting.
1683
- const MAX_LAZY_ITERATIONS = 100;
1684
- let iterations = 0;
1685
- while (isLazyEvaluationNeeded(result)) {
1686
- if (++iterations > MAX_LAZY_ITERATIONS) {
1687
- console.error(
1688
- `[@rangojs/router] Exceeded ${MAX_LAZY_ITERATIONS} lazy evaluation iterations ` +
1689
- `for pathname "${pathname}". This likely indicates circular lazy includes.`,
1690
- );
1691
- lastFindMatchPathname = pathname;
1692
- lastFindMatchResult = null;
1693
- return null;
1694
- }
1695
- evaluateLazyEntry(result.lazyEntry);
1696
- result = findRouteMatch(pathname, routesEntries);
1697
- }
1698
- pushMetric?.("match:regex-fallback", regexStart);
1699
-
1700
- lastFindMatchPathname = pathname;
1701
- lastFindMatchResult = result;
1702
- return result;
577
+ _evaluateLazyEntry(entry, lazyEvalDeps);
1703
578
  }
1704
579
 
580
+ // Create findMatch with single-entry cache, bound to router state
581
+ const findMatch = createFindMatch<TEnv>({
582
+ routesEntries,
583
+ evaluateLazyEntry,
584
+ routerId,
585
+ });
1705
586
 
1706
- /**
1707
- * Match request and return segments (document/SSR requests)
1708
- *
1709
- * Uses generator middleware pipeline for clean separation of concerns:
1710
- * - cache-lookup: Check cache first
1711
- * - segment-resolution: Resolve segments on cache miss
1712
- * - cache-store: Store results in cache
1713
- * - background-revalidation: SWR revalidation
1714
- */
1715
- async function match(request: Request, env: TEnv): Promise<MatchResult> {
1716
- // Build RouterContext with all closure functions needed by middleware
1717
- const routerCtx: RouterContext<TEnv> = {
587
+ // Build a RouterContext once — shared by match, matchPartial, matchForPrerender
588
+ function buildRouterContext(): RouterContext<TEnv> {
589
+ return {
1718
590
  findMatch,
1719
591
  loadManifest,
1720
592
  traverseBack,
@@ -1735,528 +607,301 @@ export function createRouter<TEnv = any>(
1735
607
  resolveLoadersOnlyWithRevalidation,
1736
608
  resolveInterceptLoadersOnly,
1737
609
  resolveLoadersOnly,
610
+ telemetry: telemetrySink,
1738
611
  };
1739
-
1740
- return runWithRouterContext(routerCtx, async () => {
1741
- const result = await createMatchContextForFull(request, env);
1742
-
1743
- // Handle redirect case
1744
- if ("type" in result && result.type === "redirect") {
1745
- return {
1746
- segments: [],
1747
- matched: [],
1748
- diff: [],
1749
- params: {},
1750
- redirect: result.redirectUrl,
1751
- };
1752
- }
1753
-
1754
- const ctx = result as MatchContext<TEnv>;
1755
-
1756
- try {
1757
- const state = createPipelineState();
1758
- const pipeline = createMatchPartialPipeline(ctx, state);
1759
- return await collectMatchResult(pipeline, ctx, state);
1760
- } catch (error) {
1761
- if (error instanceof Response) throw error;
1762
- // Report unhandled errors during full match pipeline
1763
- callOnError(error, "routing", {
1764
- request,
1765
- url: ctx.url,
1766
- env,
1767
- isPartial: false,
1768
- handledByBoundary: false,
1769
- });
1770
- throw sanitizeError(error);
1771
- }
1772
- });
1773
- }
1774
-
1775
- async function matchError(
1776
- request: Request,
1777
- _context: TEnv,
1778
- error: unknown,
1779
- segmentType: ErrorInfo["segmentType"] = "route",
1780
- ): Promise<MatchResult | null> {
1781
- return _matchError(request, _context, error, matchApiDeps, defaultErrorBoundary, segmentType);
1782
612
  }
1783
613
 
614
+ // Prerender/static match deps (bind closure state for extracted functions)
615
+ const prerenderDeps = {
616
+ findMatch,
617
+ buildRouterContext,
618
+ mergedRouteMap,
619
+ resolveAllSegments,
620
+ };
1784
621
 
1785
- async function createMatchContextForFull(
1786
- request: Request,
1787
- env: TEnv,
1788
- ) {
1789
- return _createMatchContextForFull(request, env, matchApiDeps, findInterceptForRoute);
1790
- }
1791
-
1792
- async function createMatchContextForPartial(
1793
- request: Request,
1794
- env: TEnv,
1795
- actionContext?: { actionId?: string; actionUrl?: URL; actionResult?: any; formData?: FormData },
622
+ async function matchForPrerender(
623
+ pathname: string,
624
+ params: Record<string, string>,
625
+ buildVars?: Record<string, any>,
626
+ isPassthroughRoute?: boolean,
627
+ buildEnv?: TEnv,
628
+ devMode?: boolean,
1796
629
  ) {
1797
- return _createMatchContextForPartial(request, env, matchApiDeps, findInterceptForRoute, actionContext);
1798
- }
1799
-
1800
- /**
1801
- * Match partial request with revalidation
1802
- *
1803
- * Uses generator middleware pipeline for clean separation of concerns:
1804
- * - cache-lookup: Check cache first
1805
- * - segment-resolution: Resolve segments on cache miss
1806
- * - intercept-resolution: Handle intercept routes
1807
- * - cache-store: Store results in cache
1808
- * - background-revalidation: SWR revalidation
1809
- */
1810
- async function matchPartial(
1811
- request: Request,
1812
- context: TEnv,
1813
- actionContext?: ActionContext,
1814
- ): Promise<MatchResult | null> {
1815
- // Build RouterContext with all closure functions needed by middleware
1816
- const routerCtx: RouterContext<TEnv> = {
1817
- findMatch,
1818
- loadManifest,
1819
- traverseBack,
1820
- createHandlerContext,
1821
- setupLoaderAccess,
1822
- setupLoaderAccessSilent,
1823
- getContext,
1824
- getMetricsStore,
1825
- createCacheScope,
1826
- findInterceptForRoute,
1827
- resolveAllSegmentsWithRevalidation,
1828
- resolveInterceptEntry,
1829
- evaluateRevalidation,
1830
- getRequestContext,
1831
- resolveAllSegments,
1832
- createHandleStore,
1833
- buildEntryRevalidateMap,
1834
- resolveLoadersOnlyWithRevalidation,
1835
- resolveInterceptLoadersOnly,
1836
- };
1837
-
1838
- return runWithRouterContext(routerCtx, async () => {
1839
- const ctx = await createMatchContextForPartial(
1840
- request,
1841
- context,
1842
- actionContext,
1843
- );
1844
- if (!ctx) return null;
1845
-
1846
- try {
1847
- const state = createPipelineState();
1848
- const pipeline = createMatchPartialPipeline(ctx, state);
1849
- return await collectMatchResult(pipeline, ctx, state);
1850
- } catch (error) {
1851
- if (error instanceof Response) throw error;
1852
- // Report unhandled errors during partial match pipeline
1853
- callOnError(error, actionContext ? "action" : "revalidation", {
1854
- request,
1855
- url: ctx.url,
1856
- env: context,
1857
- actionId: actionContext?.actionId,
1858
- isPartial: true,
1859
- handledByBoundary: false,
1860
- });
1861
- throw sanitizeError(error);
1862
- }
1863
- });
1864
- }
1865
-
1866
- /**
1867
- * Preview match - returns route middleware without segment resolution.
1868
- * Also returns responseType and handler for response routes (non-RSC short-circuit).
1869
- */
1870
- async function previewMatch(
1871
- request: Request,
1872
- _context: TEnv,
1873
- ): Promise<{
1874
- routeMiddleware?: Array<{
1875
- handler: import("./router/middleware.js").MiddlewareFn;
1876
- params: Record<string, string>;
1877
- }>;
1878
- responseType?: string;
1879
- handler?: Function;
1880
- params?: Record<string, string>;
1881
- negotiated?: boolean;
1882
- } | null> {
1883
- const url = new URL(request.url);
1884
- const pathname = url.pathname;
1885
-
1886
- // Quick route matching
1887
- const matched = findMatch(pathname);
1888
- if (!matched) {
1889
- return null;
1890
- }
1891
-
1892
- // Skip redirect check - will be handled in full match
1893
- if (matched.redirectTo) {
1894
- return { routeMiddleware: undefined };
1895
- }
1896
-
1897
- // Load manifest (without segment resolution)
1898
- const manifestEntry = await loadManifest(
1899
- matched.entry,
1900
- matched.routeKey,
630
+ return _matchForPrerender(
1901
631
  pathname,
1902
- undefined, // No metrics store for preview
1903
- false, // isSSR - doesn't matter for preview
632
+ params,
633
+ prerenderDeps,
634
+ buildVars,
635
+ isPassthroughRoute,
636
+ buildEnv,
637
+ devMode,
1904
638
  );
639
+ }
1905
640
 
1906
- // Collect route-level middleware from entry tree
1907
- // Includes middleware from orphan layouts (inline layouts within routes)
1908
- const routeMiddleware = collectRouteMiddleware(
1909
- traverseBack(manifestEntry),
1910
- matched.params,
641
+ async function renderStaticSegment(
642
+ handler: Function,
643
+ handlerId: string,
644
+ routeName?: string,
645
+ buildEnv?: TEnv,
646
+ devMode?: boolean,
647
+ ) {
648
+ return _renderStaticSegment<TEnv>(
649
+ handler,
650
+ handlerId,
651
+ mergedRouteMap,
652
+ routeName,
653
+ buildEnv,
654
+ devMode,
1911
655
  );
1912
-
1913
- // Check for response type (from trie match or manifest entry)
1914
- const responseType = matched.responseType ||
1915
- (manifestEntry.type === "route" ? manifestEntry.responseType : undefined);
1916
-
1917
- // Content negotiation: when negotiate variants exist, pick the best
1918
- // handler based on the Accept header. Uses q-values and client order
1919
- // as tiebreaker (matching Express/Hono behavior). RSC routes participate
1920
- // as text/html candidates so browsers naturally get HTML without
1921
- // special-casing.
1922
- if (matched.negotiateVariants && matched.negotiateVariants.length > 0) {
1923
- const acceptEntries = parseAcceptTypes(request.headers.get("accept") || "");
1924
-
1925
- // Build candidate list preserving definition order.
1926
- // For wildcard (*/*) and no-Accept fallback, the first candidate wins.
1927
- const variants = matched.negotiateVariants;
1928
- let candidates: Array<{ routeKey: string; responseType: string }>;
1929
- if (responseType) {
1930
- // Primary is response-type — include it as a candidate
1931
- candidates = [...variants, { routeKey: matched.routeKey, responseType }];
1932
- } else {
1933
- // Primary is RSC — insert as text/html candidate in definition order
1934
- const rscCandidate = { routeKey: matched.routeKey, responseType: RSC_RESPONSE_TYPE };
1935
- candidates = matched.rscFirst
1936
- ? [rscCandidate, ...variants]
1937
- : [...variants, rscCandidate];
1938
- }
1939
-
1940
- const variant = pickNegotiateVariant(acceptEntries, candidates);
1941
-
1942
- // If the winner is RSC, fall through to default RSC handling
1943
- if (variant.responseType === RSC_RESPONSE_TYPE) {
1944
- // Fall through — RSC won negotiation
1945
- } else if (responseType && variant.routeKey === matched.routeKey) {
1946
- // Fall through — response-type primary won, already set
1947
- } else {
1948
- const negotiateEntry = await loadManifest(
1949
- matched.entry,
1950
- variant.routeKey,
1951
- pathname,
1952
- undefined,
1953
- false,
1954
- );
1955
- return {
1956
- routeMiddleware: routeMiddleware.length > 0 ? routeMiddleware : undefined,
1957
- responseType: variant.responseType,
1958
- handler: negotiateEntry.type === "route" ? negotiateEntry.handler : undefined,
1959
- params: matched.params,
1960
- negotiated: true,
1961
- };
1962
- }
1963
- }
1964
-
1965
- // If we passed through the negotiation block (variants exist), mark as
1966
- // negotiated so the handler sets Vary: Accept on the response.
1967
- const hasVariants = matched.negotiateVariants && matched.negotiateVariants.length > 0;
1968
- return {
1969
- routeMiddleware: routeMiddleware.length > 0 ? routeMiddleware : undefined,
1970
- ...(responseType ? {
1971
- responseType,
1972
- handler: manifestEntry.type === "route" ? manifestEntry.handler : undefined,
1973
- params: matched.params,
1974
- } : {}),
1975
- ...(hasVariants ? { negotiated: true } : {}),
1976
- };
1977
656
  }
1978
657
 
1979
- /**
1980
- * Create route builder with accumulated route types
1981
- * The TNewRoutes type parameter captures the new routes being added
1982
- */
1983
- function createRouteBuilder<TNewRoutes extends Record<string, string>>(
1984
- prefix: string,
1985
- routes: TNewRoutes,
1986
- ): RouteBuilder<RouteDefinition, TEnv, any, TNewRoutes> {
1987
- const currentMountIndex = mountIndex++;
1988
-
1989
- // Merge routes into the reverse map
1990
- // Keys stay unchanged for composability - only URL patterns get prefixed
1991
- const routeEntries = routes as Record<string, string>;
1992
- for (const [key, pattern] of Object.entries(routeEntries)) {
1993
- // Build prefixed pattern: "/shop" + "/cart" -> "/shop/cart"
1994
- const prefixedPattern =
1995
- prefix && pattern !== "/"
1996
- ? `${prefix}${pattern}`
1997
- : prefix && pattern === "/"
1998
- ? prefix
1999
- : pattern;
2000
-
2001
- // Runtime validation: warn if key already exists with different pattern
2002
- const existingPattern = mergedRouteMap[key];
2003
- if (
2004
- existingPattern !== undefined &&
2005
- existingPattern !== prefixedPattern
2006
- ) {
2007
- console.warn(
2008
- `[rsc-router] Route key conflict: "${key}" already maps to "${existingPattern}", ` +
2009
- `overwriting with "${prefixedPattern}". Use unique key names to avoid this.`,
2010
- );
2011
- }
2012
-
2013
- // Use original key - enables reusable route modules
2014
- mergedRouteMap[key] = prefixedPattern;
2015
- }
2016
-
2017
- // Auto-register route map for runtime reverse() usage
2018
- registerRouteMap(mergedRouteMap);
2019
-
2020
- // Extract trailing slash config if present (attached by route())
2021
- const trailingSlashConfig = (routes as any).__trailingSlash as
2022
- | Record<string, TrailingSlashMode>
2023
- | undefined;
2024
-
2025
- // Create builder object so .use() can return it
2026
- const builder: RouteBuilder<RouteDefinition, TEnv, any, TNewRoutes> = {
2027
- use(
2028
- patternOrMiddleware: string | MiddlewareFn<TEnv>,
2029
- middleware?: MiddlewareFn<TEnv>,
2030
- ) {
2031
- // Mount-scoped middleware - prefix is the mount prefix
2032
- addMiddleware(patternOrMiddleware, middleware, prefix || null);
2033
- return builder;
2034
- },
2035
-
2036
- map(
2037
- handler:
2038
- | ((
2039
- helpers: InlineRouteHelpers<TNewRoutes, TEnv>,
2040
- ) => Array<AllUseItems>)
2041
- | (() =>
2042
- | Array<AllUseItems>
2043
- | Promise<{ default: () => Array<AllUseItems> }>
2044
- | Promise<() => Array<AllUseItems>>),
2045
- ) {
2046
- // Store handler as-is - detection happens at call time based on return type
2047
- // Both patterns use the same signature:
2048
- // - Inline: ({ route }) => [...] - receives helpers, returns Array
2049
- // - Lazy: () => import(...) - ignores helpers, returns Promise
2050
- routesEntries.push({
2051
- prefix,
2052
- staticPrefix: extractStaticPrefix(prefix),
2053
- routes: routes as ResolvedRouteMap<any>,
2054
- trailingSlash: trailingSlashConfig,
2055
- handler: handler as any,
2056
- mountIndex: currentMountIndex,
2057
- });
2058
- // Return router with accumulated types
2059
- // At runtime this is the same object, but TypeScript tracks the accumulated route types
2060
- return router as any;
2061
- },
2062
-
2063
- // Expose accumulated route map for typeof extraction
2064
- get routeMap() {
2065
- return mergedRouteMap as TNewRoutes;
2066
- },
2067
- };
658
+ // Create match handler functions bound to router state
659
+ const matchHandlers = createMatchHandlers<TEnv>({
660
+ buildRouterContext,
661
+ callOnError,
662
+ matchApiDeps,
663
+ defaultErrorBoundary,
664
+ findMatch,
665
+ findInterceptForRoute,
666
+ telemetry: telemetrySink,
667
+ });
2068
668
 
2069
- return builder;
2070
- }
669
+ const { match, matchPartial, matchError, previewMatch } = matchHandlers;
2071
670
 
2072
671
  /**
2073
672
  * Router instance
2074
673
  * The type system tracks accumulated routes through the builder chain
2075
674
  * Initial TRoutes is {} (empty) to avoid poisoning accumulated types with Record<string, string>
2076
675
  */
2077
- const router: RSCRouter<TEnv, {}> = {
676
+ const router: RSCRouterInternal<TEnv, {}> = {
2078
677
  __brand: RSC_ROUTER_BRAND,
2079
678
  id: routerId,
679
+ basename,
2080
680
 
2081
- routes(
2082
- prefixOrRoutes: string | Record<string, string> | UrlPatterns<TEnv>,
2083
- maybeRoutes?: Record<string, string>,
2084
- ): any {
2085
- // Note: Multiple .routes() calls are allowed for backwards compatibility
2086
- // with the old map() pattern. For new code, prefer urls() with include().
2087
-
2088
- // Check if argument is UrlPatterns (new Django-style API)
2089
- // Detect by checking for handler and definitions properties
2090
- if (
2091
- typeof prefixOrRoutes === "object" &&
2092
- prefixOrRoutes !== null &&
2093
- "handler" in prefixOrRoutes &&
2094
- "definitions" in prefixOrRoutes &&
2095
- typeof (prefixOrRoutes as UrlPatterns<TEnv>).handler === "function"
2096
- ) {
2097
- const urlPatterns = prefixOrRoutes as UrlPatterns<TEnv>;
2098
- // Store reference for runtime manifest generation
2099
- storedUrlPatterns = urlPatterns;
2100
- const currentMountIndex = mountIndex++;
2101
-
2102
- // Create manifest and patterns maps for route registration
2103
- const manifest = new Map<string, EntryData>();
2104
- const patterns = new Map<string, string>();
2105
- const patternsByPrefix = new Map<string, Map<string, string>>();
2106
- const trailingSlashMap = new Map<string, TrailingSlashMode>();
2107
-
2108
- // Run the handler once to extract patterns for route matching
2109
- // Note: loadManifest will re-run the handler to register entries in its context
2110
- // Lazy includes are detected in the return value and handled separately
2111
- let handlerResult: AllUseItems[] = [];
2112
- RSCRouterContext.run(
2113
- {
2114
- manifest,
2115
- patterns,
2116
- patternsByPrefix,
2117
- trailingSlash: trailingSlashMap,
2118
- namespace: "root",
2119
- parent: null,
2120
- counters: {},
2121
- },
2122
- () => {
2123
- // Execute the handler to collect patterns
2124
- handlerResult = urlPatterns.handler() as AllUseItems[];
2125
- },
2126
- );
681
+ routes(patternsOrBuilder: UrlPatterns<TEnv> | UrlBuilder<TEnv>): any {
682
+ // Wrap builder functions in urls() automatically
683
+ const urlPatterns: UrlPatterns<TEnv> =
684
+ typeof patternsOrBuilder === "function"
685
+ ? (urls(patternsOrBuilder) as UrlPatterns<TEnv>)
686
+ : patternsOrBuilder;
2127
687
 
2128
- // Store the ORIGINAL handler - loadManifest will re-run it to register manifest entries
2129
- // Convert trailingSlash map to object for the router
2130
- const trailingSlashConfig =
2131
- trailingSlashMap.size > 0
2132
- ? Object.fromEntries(trailingSlashMap)
2133
- : undefined;
2134
-
2135
- // Create separate RouteEntry for each URL prefix group
2136
- // This enables prefix-based short-circuit optimization
2137
- if (patternsByPrefix.size > 0) {
2138
- for (const [prefix, prefixPatterns] of patternsByPrefix.entries()) {
2139
- const routesObject: Record<string, string> = {};
2140
- for (const [name, pattern] of prefixPatterns.entries()) {
2141
- routesObject[name] = pattern;
2142
- }
688
+ // Store reference for runtime manifest generation
689
+ storedUrlPatterns = urlPatterns;
690
+ const currentMountIndex = mountIndex++;
2143
691
 
2144
- routesEntries.push({
2145
- // prefix is "" because patterns already include the URL prefix
2146
- // (e.g., "/site/:locale/user1/:id" not just "/user1/:id")
2147
- prefix: "",
2148
- // staticPrefix is the actual prefix for short-circuit optimization
2149
- staticPrefix: extractStaticPrefix(prefix),
2150
- routes: routesObject as ResolvedRouteMap<any>,
2151
- trailingSlash: trailingSlashConfig,
2152
- handler: urlPatterns.handler,
2153
- mountIndex: currentMountIndex,
2154
- });
692
+ // Create manifest and patterns maps for route registration
693
+ const manifest = new Map<string, EntryData>();
694
+ const routePatterns = new Map<string, string>();
695
+ const patternsByPrefix = new Map<string, Map<string, string>>();
696
+ const trailingSlashMap = new Map<string, TrailingSlashMode>();
697
+
698
+ // Run the handler once to extract patterns for route matching.
699
+ // Note: loadManifest will re-run the handler to register entries in its context.
700
+ // Lazy includes are detected in the return value and handled separately.
701
+ //
702
+ // Pattern extraction must use the same mountIndex and MapRootLayout root
703
+ // parent as loadManifest so that shortCodes produced here match those at
704
+ // runtime. include() captures the current parent and counters; if those
705
+ // shortCodes diverge from the runtime tree the segment reconciliation on
706
+ // the client will see a full mismatch and remount the entire page.
707
+ const syntheticMapRoot: EntryData = {
708
+ type: "layout",
709
+ id: `#synthetic-maproot-M${currentMountIndex}`,
710
+ shortCode: `M${currentMountIndex}L0`,
711
+ parent: null,
712
+ handler: MapRootLayout,
713
+ middleware: [],
714
+ revalidate: [],
715
+ errorBoundary: [],
716
+ notFoundBoundary: [],
717
+ layout: [],
718
+ parallel: {},
719
+ intercept: [],
720
+ loader: [],
721
+ };
722
+
723
+ let handlerResult: AllUseItems[] = [];
724
+ RSCRouterContext.run(
725
+ {
726
+ manifest,
727
+ patterns: routePatterns,
728
+ patternsByPrefix,
729
+ trailingSlash: trailingSlashMap,
730
+ namespace: "root",
731
+ parent: syntheticMapRoot,
732
+ counters: {},
733
+ mountIndex: currentMountIndex,
734
+ cacheProfiles: resolvedCacheProfiles,
735
+ // basename sets the initial URL prefix so all path() patterns
736
+ // are registered with the prefix (e.g. "/admin" + "/users" = "/admin/users").
737
+ // No namePrefix — route names stay unprefixed.
738
+ ...(basename ? { urlPrefix: basename } : {}),
739
+ },
740
+ () => {
741
+ handlerResult = urlPatterns.handler() as AllUseItems[];
742
+ },
743
+ );
744
+
745
+ // Convert trailingSlash map to object for the router
746
+ const trailingSlashConfig =
747
+ trailingSlashMap.size > 0
748
+ ? Object.fromEntries(trailingSlashMap)
749
+ : undefined;
750
+
751
+ // Collect route keys that have prerender handlers (for non-trie match path)
752
+ let prerenderRouteKeys: Set<string> | undefined;
753
+ let passthroughRouteKeys: Set<string> | undefined;
754
+ for (const [name, entry] of manifest.entries()) {
755
+ if (entry.type === "route" && entry.isPrerender) {
756
+ if (!prerenderRouteKeys) prerenderRouteKeys = new Set();
757
+ prerenderRouteKeys.add(name);
758
+ if (entry.isPassthrough === true) {
759
+ if (!passthroughRouteKeys) passthroughRouteKeys = new Set();
760
+ passthroughRouteKeys.add(name);
2155
761
  }
2156
- } else {
2157
- // Fallback: no prefix grouping, use flat patterns map
762
+ }
763
+ }
764
+
765
+ // Create separate RouteEntry for each URL prefix group
766
+ // This enables prefix-based short-circuit optimization
767
+ if (patternsByPrefix.size > 0) {
768
+ for (const [prefix, prefixPatterns] of patternsByPrefix.entries()) {
2158
769
  const routesObject: Record<string, string> = {};
2159
- for (const [name, pattern] of patterns.entries()) {
770
+ for (const [name, pattern] of prefixPatterns.entries()) {
2160
771
  routesObject[name] = pattern;
2161
772
  }
2162
773
 
2163
774
  routesEntries.push({
775
+ // prefix is "" because patterns already include the URL prefix
776
+ // (e.g., "/site/:locale/user1/:id" not just "/user1/:id")
2164
777
  prefix: "",
2165
- staticPrefix: "",
778
+ // staticPrefix is the actual prefix for short-circuit optimization
779
+ staticPrefix: extractStaticPrefix(prefix),
2166
780
  routes: routesObject as ResolvedRouteMap<any>,
2167
781
  trailingSlash: trailingSlashConfig,
2168
782
  handler: urlPatterns.handler,
2169
783
  mountIndex: currentMountIndex,
784
+ routerId,
785
+ cacheProfiles: resolvedCacheProfiles,
786
+ ...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
787
+ ...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
2170
788
  });
2171
789
  }
790
+ } else {
791
+ // Fallback: no prefix grouping, use flat patterns map
792
+ const routesObject: Record<string, string> = {};
793
+ for (const [name, pattern] of routePatterns.entries()) {
794
+ routesObject[name] = pattern;
795
+ }
2172
796
 
2173
- // Build route map from registered patterns
2174
- for (const [name, pattern] of patterns.entries()) {
2175
- // Runtime validation: warn if key already exists with different pattern
2176
- const existingPattern = mergedRouteMap[name];
2177
- if (existingPattern !== undefined && existingPattern !== pattern) {
2178
- console.warn(
2179
- `[@rangojs/router] Route name conflict: "${name}" already maps to "${existingPattern}", ` +
2180
- `overwriting with "${pattern}". Use unique route names to avoid this.`,
2181
- );
2182
- }
2183
- mergedRouteMap[name] = pattern;
797
+ routesEntries.push({
798
+ prefix: "",
799
+ staticPrefix: "",
800
+ routes: routesObject as ResolvedRouteMap<any>,
801
+ trailingSlash: trailingSlashConfig,
802
+ handler: urlPatterns.handler,
803
+ mountIndex: currentMountIndex,
804
+ routerId,
805
+ cacheProfiles: resolvedCacheProfiles,
806
+ ...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
807
+ ...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
808
+ });
809
+ }
810
+
811
+ // Build route map from registered patterns
812
+ for (const [name, pattern] of routePatterns.entries()) {
813
+ // Runtime validation: warn if key already exists with different pattern.
814
+ // Skip warning for entries that came from the static seed — the gen file
815
+ // can be stale during HMR, so runtime registration is authoritative.
816
+ const existingPattern = mergedRouteMap[name];
817
+ if (
818
+ existingPattern !== undefined &&
819
+ existingPattern !== pattern &&
820
+ !seededNames.has(name)
821
+ ) {
822
+ console.warn(
823
+ `[@rangojs/router] Route name conflict: "${name}" already maps to "${existingPattern}", ` +
824
+ `overwriting with "${pattern}". Use unique route names to avoid this.`,
825
+ );
2184
826
  }
827
+ mergedRouteMap[name] = pattern;
828
+ seededNames.delete(name);
829
+ }
2185
830
 
2186
- // Detect lazy includes in handler result and create placeholder entries
2187
- // Uses findLazyIncludes from outer scope (shared with evaluateLazyEntry)
2188
- const lazyIncludes = findLazyIncludes(handlerResult);
831
+ // Detect lazy includes in handler result and create placeholder entries
832
+ const lazyIncludes = findLazyIncludes(handlerResult);
2189
833
 
2190
- // Create placeholder RouteEntry for each lazy include
2191
- for (const lazyInclude of lazyIncludes) {
2192
- // Compute the full URL prefix (combining parent prefix if any)
2193
- const fullPrefix = lazyInclude.context.urlPrefix
2194
- ? lazyInclude.context.urlPrefix + lazyInclude.prefix
2195
- : lazyInclude.prefix;
834
+ // Create placeholder RouteEntry for each lazy include
835
+ for (const lazyInclude of lazyIncludes) {
836
+ // Compute the full URL prefix (combining parent prefix if any)
837
+ const fullPrefix = lazyInclude.context.urlPrefix
838
+ ? lazyInclude.context.urlPrefix + lazyInclude.prefix
839
+ : lazyInclude.prefix;
2196
840
 
2197
- const lazyEntry: RouteEntry<TEnv> & { _lazyPrefix?: string } = {
2198
- prefix: "",
2199
- staticPrefix: extractStaticPrefix(fullPrefix),
2200
- routes: {} as ResolvedRouteMap<any>, // Empty until first match
2201
- trailingSlash: trailingSlashConfig,
2202
- handler: urlPatterns.handler,
2203
- mountIndex: currentMountIndex,
2204
- // Lazy evaluation fields
2205
- lazy: true,
2206
- lazyPatterns: lazyInclude.patterns,
2207
- lazyContext: lazyInclude.context,
2208
- lazyEvaluated: false,
2209
- // Store the include prefix for evaluation
2210
- _lazyPrefix: lazyInclude.prefix,
2211
- };
2212
- // Insert lazy entry before any entry whose staticPrefix is a
2213
- // prefix of (but shorter than) this lazy entry's staticPrefix.
2214
- // This ensures more specific lazy includes are matched before
2215
- // less specific eager entries (e.g., "/href/nested" before "/href/:id").
2216
- const lazyPrefix = lazyEntry.staticPrefix;
2217
- let insertIndex = routesEntries.length;
2218
- if (lazyPrefix) {
2219
- for (let i = 0; i < routesEntries.length; i++) {
2220
- const existing = routesEntries[i]!;
2221
- if (
2222
- lazyPrefix.startsWith(existing.staticPrefix) &&
2223
- lazyPrefix.length > existing.staticPrefix.length
2224
- ) {
2225
- insertIndex = i;
2226
- break;
2227
- }
841
+ const lazyEntry: RouteEntry<TEnv> & { _lazyPrefix?: string } = {
842
+ prefix: "",
843
+ staticPrefix: extractStaticPrefix(fullPrefix),
844
+ routes: {} as ResolvedRouteMap<any>, // Empty until first match
845
+ trailingSlash: trailingSlashConfig,
846
+ handler: urlPatterns.handler,
847
+ mountIndex: mountIndex++,
848
+ routerId,
849
+ // Lazy evaluation fields
850
+ lazy: true,
851
+ lazyPatterns: lazyInclude.patterns,
852
+ lazyContext: lazyInclude.context,
853
+ lazyEvaluated: false,
854
+ _lazyPrefix: lazyInclude.prefix,
855
+ };
856
+ // Insert lazy entry before any entry whose staticPrefix is a
857
+ // prefix of (but shorter than) this lazy entry's staticPrefix.
858
+ // This ensures more specific lazy includes are matched before
859
+ // less specific eager entries (e.g., "/href/nested" before "/href/:id").
860
+ const lazyPrefix = lazyEntry.staticPrefix;
861
+ let insertIndex = routesEntries.length;
862
+ if (lazyPrefix) {
863
+ for (let i = 0; i < routesEntries.length; i++) {
864
+ const existing = routesEntries[i]!;
865
+ if (
866
+ lazyPrefix.startsWith(existing.staticPrefix) &&
867
+ lazyPrefix.length > existing.staticPrefix.length
868
+ ) {
869
+ insertIndex = i;
870
+ break;
2228
871
  }
2229
872
  }
2230
- routesEntries.splice(insertIndex, 0, lazyEntry);
2231
873
  }
2232
-
2233
- // Auto-register route map for runtime reverse() usage
2234
- registerRouteMap(mergedRouteMap);
2235
-
2236
- // Return the router (no .map() needed for UrlPatterns)
2237
- return router;
874
+ routesEntries.splice(insertIndex, 0, lazyEntry);
2238
875
  }
2239
876
 
2240
- // Legacy API: route() + map() pattern
2241
- // If second argument exists, first is prefix
2242
- if (maybeRoutes !== undefined) {
2243
- return createRouteBuilder(prefixOrRoutes as string, maybeRoutes);
2244
- }
2245
- // Otherwise, first argument is routes with empty prefix
2246
- return createRouteBuilder("", prefixOrRoutes as Record<string, string>);
877
+ // Auto-register route map for runtime reverse() usage
878
+ registerRouteMap(mergedRouteMap);
879
+
880
+ return router;
2247
881
  },
2248
882
 
2249
883
  use(
2250
884
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
2251
885
  middleware?: MiddlewareFn<TEnv>,
2252
886
  ): any {
2253
- // Global middleware - no mount prefix
2254
- addMiddleware(patternOrMiddleware, middleware, null);
887
+ // Auto-prefix pattern with basename so router-level middleware
888
+ // patterns are router-relative (e.g. "/users/*" matches "/app/users/*").
889
+ if (basename && typeof patternOrMiddleware === "string") {
890
+ const pattern = patternOrMiddleware;
891
+ const prefixed =
892
+ pattern === "/*" || pattern === "*"
893
+ ? `${basename}/*`
894
+ : `${basename}${pattern}`;
895
+ addMiddleware(prefixed, middleware, null);
896
+ } else {
897
+ addMiddleware(patternOrMiddleware, middleware, null);
898
+ }
2255
899
  return router;
2256
900
  },
2257
901
 
2258
902
  // Type-safe URL builder using merged route map
2259
903
  // Types are tracked through the builder chain via TRoutes parameter
904
+ // Seeded with static route names from the generated file (injected by Vite)
2260
905
  reverse: createReverse(mergedRouteMap),
2261
906
 
2262
907
  // Expose accumulated route map for typeof extraction
@@ -2280,19 +925,62 @@ export function createRouter<TEnv = any>(
2280
925
  // Expose resolved theme configuration for NavigationProvider and MetaTags
2281
926
  themeConfig: resolvedThemeConfig,
2282
927
 
928
+ // Expose resolved cache profiles for per-request resolution
929
+ cacheProfiles: resolvedCacheProfiles,
930
+
931
+ // Expose prefetch cache settings
932
+ prefetchCacheControl,
933
+ prefetchCacheTTL,
934
+
2283
935
  // Expose warmup enabled flag for handler and client
2284
936
  warmupEnabled,
2285
937
 
938
+ // Expose router-wide performance debugging for request-level metrics setup
939
+ debugPerformance,
940
+
2286
941
  // Expose debug manifest flag for handler
2287
942
  allowDebugManifest: allowDebugManifestOption,
2288
943
 
944
+ // Expose origin check configuration for handler (default: enabled)
945
+ originCheck: originCheckOption ?? true,
946
+
947
+ // Expose SSR configuration for handler
948
+ ssr: ssrOption,
949
+
950
+ // Expose resolved timeouts for RSC handler
951
+ timeouts: resolvedTimeouts,
952
+ onTimeout,
953
+
2289
954
  // Expose global middleware for RSC handler
2290
955
  middleware: globalMiddleware,
2291
956
 
2292
- match,
2293
- matchPartial,
2294
- matchError,
2295
- previewMatch,
957
+ match: (request: Request, input: RouterRequestInput<TEnv> = {}) => {
958
+ const env = input.env ?? ({} as TEnv);
959
+ return match(request, env);
960
+ },
961
+ matchForPrerender,
962
+ renderStaticSegment,
963
+ matchPartial: (
964
+ request: Request,
965
+ input: RouterRequestInput<TEnv> = {},
966
+ actionContext?: Parameters<typeof matchPartial>[2],
967
+ ) => {
968
+ const env = input.env ?? ({} as TEnv);
969
+ return matchPartial(request, env, actionContext);
970
+ },
971
+ matchError: (
972
+ request: Request,
973
+ input: RouterRequestInput<TEnv> | undefined,
974
+ error: unknown,
975
+ segmentType?: Parameters<typeof matchError>[3],
976
+ ) => {
977
+ const env = input?.env ?? ({} as TEnv);
978
+ return matchError(request, env, error, segmentType);
979
+ },
980
+ previewMatch: (request: Request, input: RouterRequestInput<TEnv> = {}) => {
981
+ const env = input.env ?? ({} as TEnv);
982
+ return previewMatch(request, env);
983
+ },
2296
984
 
2297
985
  // Expose nonce provider for fetch
2298
986
  nonce,
@@ -2305,89 +993,60 @@ export function createRouter<TEnv = any>(
2305
993
  return storedUrlPatterns ?? undefined;
2306
994
  },
2307
995
 
996
+ // Expose source file for per-router type generation
997
+ __sourceFile,
998
+
999
+ // Expose basename for runtime manifest generation
1000
+ __basename: basename,
1001
+
2308
1002
  // RSC request handler (lazily created on first call)
2309
1003
  fetch: (() => {
2310
1004
  // Handler is created on first call and reused
2311
1005
  let handler:
2312
1006
  | ((
2313
1007
  request: Request,
2314
- env: TEnv & { ctx?: ExecutionContext },
1008
+ input: RouterRequestInput<TEnv>,
2315
1009
  ) => Promise<Response>)
2316
1010
  | null = null;
2317
1011
 
2318
- return async (
2319
- request: Request,
2320
- env: TEnv & { ctx?: ExecutionContext },
2321
- ) => {
1012
+ return async (request: Request, input: RouterRequestInput<TEnv> = {}) => {
1013
+ // Trigger lazy import of per-router manifest data before route matching.
1014
+ // No-op if data is already loaded or no loader is registered.
1015
+ await ensureRouterManifest(routerId);
2322
1016
  if (!handler) {
2323
1017
  // Lazy import deferred to first request to avoid dev mode issues
2324
1018
  const { createRSCHandler } = await import("./rsc/handler.js");
1019
+ // Cast: handler.ts still accepts (request, env) — will be updated
1020
+ // separately to accept RouterRequestInput.
2325
1021
  handler = createRSCHandler({
2326
1022
  router: router as any,
2327
1023
  cache,
2328
1024
  nonce,
2329
1025
  version,
2330
- });
1026
+ }) as (
1027
+ request: Request,
1028
+ input: RouterRequestInput<TEnv>,
1029
+ ) => Promise<Response>;
2331
1030
  }
2332
- return handler(request, env);
1031
+ return handler!(request, input);
2333
1032
  };
2334
1033
  })(),
2335
1034
 
2336
- // Debug utility for manifest inspection
2337
- async debugManifest(): Promise<SerializedManifest> {
2338
- const manifest = new Map<string, EntryData>();
2339
-
2340
- for (const entry of routesEntries) {
2341
- const Store = {
2342
- manifest,
2343
- namespace: `debug.M${entry.mountIndex}`,
2344
- parent: null as EntryData | null,
2345
- counters: {} as Record<string, number>,
2346
- mountIndex: entry.mountIndex,
2347
- patterns: new Map<string, string>(),
2348
- trailingSlash: new Map<string, TrailingSlashMode>(),
2349
- };
2350
-
2351
- await getContext().runWithStore(
2352
- Store,
2353
- `debug.M${entry.mountIndex}`,
2354
- null,
2355
- async () => {
2356
- const helpers = createRouteHelpers();
2357
-
2358
- // Wrap handler execution in root layout (same as loadManifest)
2359
- let promiseResult: Promise<any> | null = null;
2360
- helpers.layout(MapRootLayout, () => {
2361
- const result = entry.handler();
2362
- if (result instanceof Promise) {
2363
- promiseResult = result;
2364
- return [];
2365
- }
2366
- return result;
2367
- });
2368
-
2369
- if (promiseResult !== null) {
2370
- const load = await (promiseResult as Promise<any>);
2371
- if (load && typeof load === "object" && "default" in load) {
2372
- const useItems = load.default;
2373
- if (typeof useItems === "function") {
2374
- useItems(helpers);
2375
- }
2376
- }
2377
- }
2378
- },
2379
- );
2380
- }
1035
+ // Low-level route matching for request classification
1036
+ findMatch: (pathname: string, metricsStore?: any) =>
1037
+ findMatch(pathname, metricsStore),
2381
1038
 
2382
- return serializeManifest(manifest);
2383
- },
1039
+ // Debug utility for manifest inspection
1040
+ debugManifest: () => buildDebugManifest<TEnv>(routesEntries),
2384
1041
  };
2385
1042
 
2386
1043
  // Register router in the global registry for build-time discovery
2387
1044
  RouterRegistry.set(routerId, router);
2388
1045
 
2389
1046
  // If urls option was provided, auto-register them
2390
- if (urlsOption) {
1047
+ if (typeof urlsOption === "function") {
1048
+ return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
1049
+ } else if (urlsOption) {
2391
1050
  return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
2392
1051
  }
2393
1052