@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1c0bdfad

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 (406) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +291 -61
  3. package/dist/bin/rango.js +544 -143
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3744 -1329
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +67 -13
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +312 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +247 -23
  12. package/skills/caching/SKILL.md +322 -19
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/debug-manifest/SKILL.md +4 -2
  16. package/skills/document-cache/SKILL.md +78 -55
  17. package/skills/handler-use/SKILL.md +364 -0
  18. package/skills/hooks/SKILL.md +282 -60
  19. package/skills/host-router/SKILL.md +278 -0
  20. package/skills/i18n/SKILL.md +276 -0
  21. package/skills/intercept/SKILL.md +50 -6
  22. package/skills/layout/SKILL.md +35 -9
  23. package/skills/links/SKILL.md +249 -17
  24. package/skills/loader/SKILL.md +297 -31
  25. package/skills/middleware/SKILL.md +52 -13
  26. package/skills/migrate-nextjs/SKILL.md +584 -0
  27. package/skills/migrate-react-router/SKILL.md +771 -0
  28. package/skills/mime-routes/SKILL.md +28 -1
  29. package/skills/observability/SKILL.md +172 -0
  30. package/skills/parallel/SKILL.md +203 -7
  31. package/skills/prerender/SKILL.md +155 -111
  32. package/skills/rango/SKILL.md +251 -23
  33. package/skills/react-compiler/SKILL.md +168 -0
  34. package/skills/response-routes/SKILL.md +123 -48
  35. package/skills/route/SKILL.md +104 -9
  36. package/skills/router-setup/SKILL.md +124 -11
  37. package/skills/scripts/SKILL.md +179 -0
  38. package/skills/server-actions/SKILL.md +775 -0
  39. package/skills/streams-and-websockets/SKILL.md +283 -0
  40. package/skills/tailwind/SKILL.md +27 -3
  41. package/skills/testing/SKILL.md +125 -222
  42. package/skills/testing/bindings.md +103 -0
  43. package/skills/testing/cache-prerender.md +127 -0
  44. package/skills/testing/client-components.md +124 -0
  45. package/skills/testing/e2e-parity.md +125 -0
  46. package/skills/testing/flight.md +91 -0
  47. package/skills/testing/handles.md +129 -0
  48. package/skills/testing/loader.md +128 -0
  49. package/skills/testing/middleware.md +99 -0
  50. package/skills/testing/render-handler.md +121 -0
  51. package/skills/testing/response-routes.md +95 -0
  52. package/skills/testing/reverse-and-types.md +84 -0
  53. package/skills/testing/server-actions.md +107 -0
  54. package/skills/testing/server-tree.md +128 -0
  55. package/skills/testing/setup.md +123 -0
  56. package/skills/typesafety/SKILL.md +357 -52
  57. package/skills/use-cache/SKILL.md +46 -14
  58. package/skills/view-transitions/SKILL.md +294 -0
  59. package/src/__augment-tests__/augment.ts +81 -0
  60. package/src/__augment-tests__/augmented.check.ts +116 -0
  61. package/src/__internal.ts +67 -40
  62. package/src/bin/rango.ts +18 -0
  63. package/src/browser/action-coordinator.ts +53 -36
  64. package/src/browser/action-fence.ts +47 -0
  65. package/src/browser/app-shell.ts +39 -0
  66. package/src/browser/app-version.ts +14 -0
  67. package/src/browser/connection-warmup.ts +134 -0
  68. package/src/browser/cookie-name.ts +140 -0
  69. package/src/browser/event-controller.ts +197 -150
  70. package/src/browser/history-state.ts +21 -0
  71. package/src/browser/index.ts +3 -3
  72. package/src/browser/invalidate-client-cache.ts +52 -0
  73. package/src/browser/link-interceptor.ts +4 -0
  74. package/src/browser/navigation-bridge.ts +200 -30
  75. package/src/browser/navigation-client.ts +217 -58
  76. package/src/browser/navigation-store-handle.ts +38 -0
  77. package/src/browser/navigation-store.ts +76 -67
  78. package/src/browser/navigation-transaction.ts +18 -66
  79. package/src/browser/network-error-handler.ts +34 -7
  80. package/src/browser/partial-update.ts +187 -112
  81. package/src/browser/prefetch/cache.ts +312 -30
  82. package/src/browser/prefetch/fetch.ts +344 -47
  83. package/src/browser/prefetch/policy.ts +6 -0
  84. package/src/browser/prefetch/queue.ts +126 -20
  85. package/src/browser/prefetch/resource-ready.ts +77 -0
  86. package/src/browser/rango-state.ts +158 -76
  87. package/src/browser/react/Link.tsx +125 -18
  88. package/src/browser/react/NavigationProvider.tsx +135 -120
  89. package/src/browser/react/ScrollRestoration.tsx +10 -6
  90. package/src/browser/react/context.ts +7 -2
  91. package/src/browser/react/filter-segment-order.ts +66 -7
  92. package/src/browser/react/index.ts +0 -48
  93. package/src/browser/react/location-state-shared.ts +178 -8
  94. package/src/browser/react/location-state.ts +39 -14
  95. package/src/browser/react/use-action.ts +6 -15
  96. package/src/browser/react/use-handle.ts +23 -69
  97. package/src/browser/react/use-href.tsx +8 -1
  98. package/src/browser/react/use-link-status.ts +33 -8
  99. package/src/browser/react/use-navigation.ts +32 -7
  100. package/src/browser/react/use-params.ts +20 -10
  101. package/src/browser/react/use-reverse.ts +106 -0
  102. package/src/browser/react/use-router.ts +46 -11
  103. package/src/browser/react/use-search-params.ts +0 -5
  104. package/src/browser/react/use-segments.ts +11 -21
  105. package/src/browser/response-adapter.ts +80 -5
  106. package/src/browser/rsc-router.tsx +226 -75
  107. package/src/browser/scroll-restoration.ts +54 -42
  108. package/src/browser/segment-reconciler.ts +36 -9
  109. package/src/browser/segment-structure-assert.ts +2 -2
  110. package/src/browser/server-action-bridge.ts +619 -442
  111. package/src/browser/types.ts +115 -11
  112. package/src/browser/validate-redirect-origin.ts +43 -16
  113. package/src/build/collect-fallback-refs.ts +107 -0
  114. package/src/build/generate-manifest.ts +65 -40
  115. package/src/build/generate-route-types.ts +7 -1
  116. package/src/build/index.ts +8 -2
  117. package/src/build/prefix-tree-utils.ts +123 -0
  118. package/src/build/route-trie.ts +182 -37
  119. package/src/build/route-types/ast-route-extraction.ts +15 -8
  120. package/src/build/route-types/codegen.ts +16 -5
  121. package/src/build/route-types/include-resolution.ts +125 -24
  122. package/src/build/route-types/param-extraction.ts +6 -3
  123. package/src/build/route-types/per-module-writer.ts +22 -6
  124. package/src/build/route-types/router-processing.ts +392 -106
  125. package/src/build/route-types/scan-filter.ts +9 -2
  126. package/src/build/route-types/source-scan.ts +216 -0
  127. package/src/build/runtime-discovery.ts +9 -20
  128. package/src/cache/cache-error.ts +104 -0
  129. package/src/cache/cache-key-utils.ts +29 -13
  130. package/src/cache/cache-policy.ts +108 -34
  131. package/src/cache/cache-runtime.ts +214 -48
  132. package/src/cache/cache-scope.ts +236 -89
  133. package/src/cache/cache-tag.ts +103 -0
  134. package/src/cache/cf/cf-base64.ts +33 -0
  135. package/src/cache/cf/cf-cache-constants.ts +127 -0
  136. package/src/cache/cf/cf-cache-store.ts +2224 -171
  137. package/src/cache/cf/cf-cache-types.ts +349 -0
  138. package/src/cache/cf/cf-kv-utils.ts +46 -0
  139. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  140. package/src/cache/cf/index.ts +11 -17
  141. package/src/cache/document-cache.ts +89 -27
  142. package/src/cache/handle-snapshot.ts +70 -0
  143. package/src/cache/index.ts +11 -20
  144. package/src/cache/memory-segment-store.ts +136 -37
  145. package/src/cache/profile-registry.ts +31 -31
  146. package/src/cache/read-through-swr.ts +41 -11
  147. package/src/cache/segment-codec.ts +9 -17
  148. package/src/cache/tag-invalidation.ts +230 -0
  149. package/src/cache/taint.ts +55 -0
  150. package/src/cache/types.ts +37 -100
  151. package/src/client.rsc.tsx +45 -21
  152. package/src/client.tsx +120 -336
  153. package/src/cloudflare/index.ts +11 -0
  154. package/src/cloudflare/tracing.ts +109 -0
  155. package/src/component-utils.ts +19 -0
  156. package/src/components/DefaultDocument.tsx +8 -2
  157. package/src/context-var.ts +84 -2
  158. package/src/debug.ts +2 -2
  159. package/src/decode-loader-results.ts +52 -0
  160. package/src/defer.ts +196 -0
  161. package/src/deps/ssr.ts +0 -1
  162. package/src/encode-kv.ts +49 -0
  163. package/src/errors.ts +30 -4
  164. package/src/escape-script.ts +52 -0
  165. package/src/handle.ts +70 -22
  166. package/src/handles/MetaTags.tsx +56 -19
  167. package/src/handles/Scripts.tsx +183 -0
  168. package/src/handles/breadcrumbs.ts +95 -0
  169. package/src/handles/is-thenable.ts +19 -0
  170. package/src/handles/meta.ts +51 -40
  171. package/src/handles/script.ts +244 -0
  172. package/src/host/cookie-handler.ts +9 -60
  173. package/src/host/errors.ts +0 -24
  174. package/src/host/index.ts +8 -5
  175. package/src/host/pattern-matcher.ts +23 -52
  176. package/src/host/router.ts +107 -99
  177. package/src/host/testing.ts +40 -27
  178. package/src/host/types.ts +37 -4
  179. package/src/host/utils.ts +1 -1
  180. package/src/href-client.ts +137 -22
  181. package/src/index.rsc.ts +79 -29
  182. package/src/index.ts +149 -65
  183. package/src/internal-debug.ts +11 -10
  184. package/src/loader-store.ts +500 -0
  185. package/src/loader.rsc.ts +20 -13
  186. package/src/loader.ts +12 -11
  187. package/src/missing-id-error.ts +68 -0
  188. package/src/outlet-context.ts +1 -1
  189. package/src/outlet-provider.tsx +1 -5
  190. package/src/prerender/param-hash.ts +16 -16
  191. package/src/prerender/store.ts +63 -26
  192. package/src/prerender.ts +198 -82
  193. package/src/redirect-origin.ts +100 -0
  194. package/src/regex-escape.ts +8 -0
  195. package/src/render-error-thrower.tsx +20 -0
  196. package/src/response-utils.ts +62 -0
  197. package/src/reverse.ts +65 -15
  198. package/src/root-error-boundary.tsx +1 -19
  199. package/src/route-content-wrapper.tsx +7 -72
  200. package/src/route-definition/dsl-helpers.ts +469 -276
  201. package/src/route-definition/helper-factories.ts +29 -139
  202. package/src/route-definition/helpers-types.ts +113 -37
  203. package/src/route-definition/index.ts +3 -3
  204. package/src/route-definition/redirect.ts +53 -12
  205. package/src/route-definition/resolve-handler-use.ts +161 -0
  206. package/src/route-definition/use-item-types.ts +32 -0
  207. package/src/route-map-builder.ts +7 -17
  208. package/src/route-types.ts +37 -41
  209. package/src/router/basename.ts +14 -0
  210. package/src/router/content-negotiation.ts +164 -17
  211. package/src/router/error-handling.ts +45 -18
  212. package/src/router/find-match.ts +45 -22
  213. package/src/router/handler-context.ts +110 -39
  214. package/src/router/instrument.ts +350 -0
  215. package/src/router/intercept-resolution.ts +50 -24
  216. package/src/router/lazy-includes.ts +19 -53
  217. package/src/router/loader-resolution.ts +274 -56
  218. package/src/router/logging.ts +5 -8
  219. package/src/router/manifest.ts +49 -45
  220. package/src/router/match-api.ts +121 -205
  221. package/src/router/match-context.ts +0 -22
  222. package/src/router/match-handlers.ts +58 -58
  223. package/src/router/match-middleware/background-revalidation.ts +33 -6
  224. package/src/router/match-middleware/cache-lookup.ts +214 -263
  225. package/src/router/match-middleware/cache-store.ts +73 -33
  226. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  227. package/src/router/match-middleware/segment-resolution.ts +52 -18
  228. package/src/router/match-pipelines.ts +1 -42
  229. package/src/router/match-result.ts +104 -49
  230. package/src/router/metrics.ts +217 -26
  231. package/src/router/middleware-types.ts +24 -110
  232. package/src/router/middleware.ts +384 -197
  233. package/src/router/navigation-snapshot.ts +131 -0
  234. package/src/router/params-util.ts +23 -0
  235. package/src/router/pattern-matching.ts +148 -91
  236. package/src/router/prefetch-cache-ttl.ts +51 -0
  237. package/src/router/prerender-match.ts +199 -56
  238. package/src/router/preview-match.ts +32 -102
  239. package/src/router/request-classification.ts +276 -0
  240. package/src/router/revalidation.ts +144 -74
  241. package/src/router/route-snapshot.ts +244 -0
  242. package/src/router/router-context.ts +8 -28
  243. package/src/router/router-interfaces.ts +129 -36
  244. package/src/router/router-options.ts +185 -23
  245. package/src/router/router-registry.ts +2 -5
  246. package/src/router/segment-resolution/fresh.ts +281 -76
  247. package/src/router/segment-resolution/helpers.ts +116 -31
  248. package/src/router/segment-resolution/loader-cache.ts +63 -37
  249. package/src/router/segment-resolution/revalidation.ts +493 -391
  250. package/src/router/segment-resolution/static-store.ts +19 -5
  251. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  252. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  253. package/src/router/segment-resolution.ts +5 -1
  254. package/src/router/segment-wrappers.ts +8 -5
  255. package/src/router/state-cookie-name.ts +33 -0
  256. package/src/router/substitute-pattern-params.ts +56 -0
  257. package/src/router/telemetry-otel.ts +161 -199
  258. package/src/router/telemetry.ts +96 -19
  259. package/src/router/timeout.ts +0 -20
  260. package/src/router/tracing.ts +206 -0
  261. package/src/router/trie-matching.ts +180 -58
  262. package/src/router/types.ts +10 -63
  263. package/src/router/url-params.ts +44 -0
  264. package/src/router.ts +182 -54
  265. package/src/rsc/handler-context.ts +3 -2
  266. package/src/rsc/handler.ts +702 -460
  267. package/src/rsc/helpers.ts +168 -46
  268. package/src/rsc/index.ts +2 -25
  269. package/src/rsc/json-route-result.ts +38 -0
  270. package/src/rsc/loader-fetch.ts +127 -31
  271. package/src/rsc/manifest-init.ts +33 -42
  272. package/src/rsc/origin-guard.ts +39 -25
  273. package/src/rsc/progressive-enhancement.ts +98 -19
  274. package/src/rsc/redirect-guard.ts +99 -0
  275. package/src/rsc/response-cache-serve.ts +238 -0
  276. package/src/rsc/response-error.ts +79 -12
  277. package/src/rsc/response-route-handler.ts +99 -189
  278. package/src/rsc/rsc-rendering.ts +126 -106
  279. package/src/rsc/runtime-warnings.ts +23 -10
  280. package/src/rsc/server-action.ts +269 -114
  281. package/src/rsc/ssr-setup.ts +144 -0
  282. package/src/rsc/types.ts +34 -6
  283. package/src/runtime-env.ts +18 -0
  284. package/src/search-params.ts +49 -41
  285. package/src/segment-content-promise.ts +67 -0
  286. package/src/segment-loader-promise.ts +149 -0
  287. package/src/segment-system.tsx +281 -129
  288. package/src/serialize.ts +243 -0
  289. package/src/server/context.ts +317 -63
  290. package/src/server/cookie-parse.ts +32 -0
  291. package/src/server/cookie-store.ts +80 -5
  292. package/src/server/handle-store.ts +40 -38
  293. package/src/server/loader-registry.ts +26 -46
  294. package/src/server/request-context.ts +425 -177
  295. package/src/server.ts +6 -0
  296. package/src/ssr/index.tsx +25 -16
  297. package/src/static-handler.ts +27 -18
  298. package/src/testing/cache-status.ts +162 -0
  299. package/src/testing/collect-handle.ts +40 -0
  300. package/src/testing/dispatch.ts +701 -0
  301. package/src/testing/dom.entry.ts +22 -0
  302. package/src/testing/e2e/fixture.ts +188 -0
  303. package/src/testing/e2e/index.ts +128 -0
  304. package/src/testing/e2e/matchers.ts +35 -0
  305. package/src/testing/e2e/page-helpers.ts +272 -0
  306. package/src/testing/e2e/parity.ts +387 -0
  307. package/src/testing/e2e/server.ts +195 -0
  308. package/src/testing/flight-matchers.ts +97 -0
  309. package/src/testing/flight-normalize.ts +11 -0
  310. package/src/testing/flight-runtime.d.ts +57 -0
  311. package/src/testing/flight-tree.ts +682 -0
  312. package/src/testing/flight.entry.ts +52 -0
  313. package/src/testing/flight.ts +257 -0
  314. package/src/testing/generated-routes.ts +183 -0
  315. package/src/testing/index.ts +99 -0
  316. package/src/testing/internal/context.ts +371 -0
  317. package/src/testing/internal/flight-client-globals.ts +30 -0
  318. package/src/testing/internal/seed-vars.ts +54 -0
  319. package/src/testing/render-handler.ts +343 -0
  320. package/src/testing/render-route.tsx +581 -0
  321. package/src/testing/run-loader.ts +385 -0
  322. package/src/testing/run-middleware.ts +205 -0
  323. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  324. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  325. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  326. package/src/testing/vitest-stubs/version.ts +5 -0
  327. package/src/testing/vitest.ts +305 -0
  328. package/src/theme/ThemeProvider.tsx +20 -58
  329. package/src/theme/ThemeScript.tsx +7 -9
  330. package/src/theme/constants.ts +52 -13
  331. package/src/theme/index.ts +3 -19
  332. package/src/theme/theme-context.ts +1 -5
  333. package/src/theme/theme-script.ts +22 -21
  334. package/src/theme/use-theme.ts +0 -3
  335. package/src/types/boundaries.ts +0 -35
  336. package/src/types/cache-types.ts +17 -8
  337. package/src/types/error-types.ts +30 -90
  338. package/src/types/global-namespace.ts +54 -41
  339. package/src/types/handler-context.ts +236 -88
  340. package/src/types/index.ts +1 -10
  341. package/src/types/loader-types.ts +44 -15
  342. package/src/types/request-scope.ts +112 -0
  343. package/src/types/route-config.ts +10 -45
  344. package/src/types/route-entry.ts +19 -7
  345. package/src/types/segments.ts +37 -19
  346. package/src/urls/include-helper.ts +33 -70
  347. package/src/urls/index.ts +1 -11
  348. package/src/urls/path-helper-types.ts +58 -11
  349. package/src/urls/path-helper.ts +57 -111
  350. package/src/urls/pattern-types.ts +48 -19
  351. package/src/urls/response-types.ts +25 -22
  352. package/src/urls/type-extraction.ts +58 -139
  353. package/src/urls/urls-function.ts +1 -18
  354. package/src/use-loader.tsx +346 -89
  355. package/src/vite/debug.ts +185 -0
  356. package/src/vite/discovery/bundle-postprocess.ts +64 -91
  357. package/src/vite/discovery/discover-routers.ts +147 -88
  358. package/src/vite/discovery/discovery-errors.ts +194 -0
  359. package/src/vite/discovery/gate-state.ts +171 -0
  360. package/src/vite/discovery/prerender-collection.ts +247 -145
  361. package/src/vite/discovery/route-types-writer.ts +40 -84
  362. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  363. package/src/vite/discovery/state.ts +61 -13
  364. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  365. package/src/vite/index.ts +10 -3
  366. package/src/vite/inject-client-debug.ts +36 -0
  367. package/src/vite/plugin-types.ts +155 -65
  368. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  369. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  370. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  371. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  372. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  373. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  374. package/src/vite/plugins/expose-action-id.ts +49 -98
  375. package/src/vite/plugins/expose-id-utils.ts +96 -51
  376. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  377. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  378. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  379. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  380. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  381. package/src/vite/plugins/performance-tracks.ts +89 -0
  382. package/src/vite/plugins/refresh-cmd.ts +127 -0
  383. package/src/vite/plugins/use-cache-transform.ts +73 -83
  384. package/src/vite/plugins/version-injector.ts +21 -25
  385. package/src/vite/plugins/version-plugin.ts +46 -37
  386. package/src/vite/plugins/virtual-entries.ts +13 -18
  387. package/src/vite/rango.ts +241 -287
  388. package/src/vite/router-discovery.ts +956 -149
  389. package/src/vite/utils/ast-handler-extract.ts +26 -35
  390. package/src/vite/utils/banner.ts +4 -4
  391. package/src/vite/utils/bundle-analysis.ts +10 -15
  392. package/src/vite/utils/client-chunks.ts +184 -0
  393. package/src/vite/utils/directive-prologue.ts +40 -0
  394. package/src/vite/utils/forward-user-plugins.ts +171 -0
  395. package/src/vite/utils/manifest-utils.ts +4 -59
  396. package/src/vite/utils/package-resolution.ts +20 -52
  397. package/src/vite/utils/prerender-utils.ts +141 -34
  398. package/src/vite/utils/shared-utils.ts +92 -42
  399. package/CLAUDE.md +0 -5
  400. package/src/browser/action-response-classifier.ts +0 -99
  401. package/src/browser/react/use-client-cache.ts +0 -58
  402. package/src/browser/shallow.ts +0 -40
  403. package/src/handles/index.ts +0 -6
  404. package/src/network-error-thrower.tsx +0 -23
  405. package/src/route-definition/route-function.ts +0 -119
  406. package/src/router/middleware-cookies.ts +0 -55
@@ -0,0 +1,44 @@
1
+ /**
2
+ * URL param encode/decode at the route boundary.
3
+ *
4
+ * Extraction (decode): regex/trie matchers keep param values URL-encoded;
5
+ * `safeDecodeURIComponent` turns them back into raw strings so `ctx.params`
6
+ * matches the contract apps expect (Express/React Router/Fastify/Koa) and
7
+ * round-trips through reverse stay stable. Malformed %-encoding is
8
+ * preserved as-is so a broken URL doesn't crash matching.
9
+ *
10
+ * Reversal (encode): `encodePathSegment` escapes only what RFC 3986
11
+ * requires for a path segment — `/`, `?`, `#`, space, control chars,
12
+ * non-ASCII — and leaves pchar sub-delims (`@ : $ & + , ; =` and friends)
13
+ * readable. `encodeURIComponent` over-encodes for path segments, which
14
+ * makes generated URLs harder for humans to read in the address bar
15
+ * (e.g. mailbox IDs like `ivo@example.com` would become
16
+ * `ivo%40example.com` even though `@` is path-legal).
17
+ */
18
+
19
+ export function safeDecodeURIComponent(raw: string): string {
20
+ if (raw === "" || raw.indexOf("%") === -1) return raw;
21
+ try {
22
+ return decodeURIComponent(raw);
23
+ } catch {
24
+ return raw;
25
+ }
26
+ }
27
+
28
+ const PATH_SAFE_ESCAPES: Record<string, string> = {
29
+ "%3A": ":",
30
+ "%40": "@",
31
+ "%24": "$",
32
+ "%26": "&",
33
+ "%2B": "+",
34
+ "%2C": ",",
35
+ "%3B": ";",
36
+ "%3D": "=",
37
+ };
38
+
39
+ export function encodePathSegment(value: string): string {
40
+ return encodeURIComponent(value).replace(
41
+ /%(?:3A|40|24|26|2B|2C|3B|3D)/gi,
42
+ (match) => PATH_SAFE_ESCAPES[match.toUpperCase()] ?? match,
43
+ );
44
+ }
package/src/router.ts CHANGED
@@ -1,9 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { createCacheScope } from "./cache/cache-scope.js";
3
- import {
4
- setCacheProfiles,
5
- resolveCacheProfiles,
6
- } from "./cache/profile-registry.js";
3
+ import { resolveCacheProfiles } from "./cache/profile-registry.js";
7
4
  import { isCachedFunction } from "./cache/taint.js";
8
5
  import { assertClientComponent } from "./component-utils.js";
9
6
  import { DefaultDocument } from "./components/DefaultDocument.js";
@@ -19,11 +16,13 @@ import {
19
16
  import MapRootLayout from "./server/root-layout.js";
20
17
  import type { AllUseItems } from "./route-types.js";
21
18
  import type { UrlPatterns } from "./urls.js";
19
+ import type { UrlBuilder } from "./urls/pattern-types.js";
20
+ import { urls } from "./urls.js";
21
+ import { buildPrecomputedByPrefix } from "./build/prefix-tree-utils.js";
22
22
  import {
23
- EntryData,
24
- InterceptSelectorContext,
23
+ type EntryData,
25
24
  getContext,
26
- RSCRouterContext,
25
+ RangoContext,
27
26
  type MetricsStore,
28
27
  } from "./server/context";
29
28
  import { createHandleStore, type HandleStore } from "./server/handle-store.js";
@@ -55,6 +54,7 @@ import { buildDebugManifest } from "./router/debug-manifest.js";
55
54
 
56
55
  import type { SegmentResolutionDeps, MatchApiDeps } from "./router/types.js";
57
56
  import { createHandlerContext } from "./router/handler-context.js";
57
+ import { normalizeBasename } from "./router/basename.js";
58
58
  import {
59
59
  setupLoaderAccess,
60
60
  setupLoaderAccessSilent,
@@ -63,15 +63,17 @@ import {
63
63
  import { loadManifest } from "./router/manifest.js";
64
64
  import { createMetricsStore } from "./router/metrics.js";
65
65
  import {
66
- parsePattern,
66
+ compileMiddlewarePattern,
67
67
  type MiddlewareEntry,
68
68
  type MiddlewareFn,
69
69
  } from "./router/middleware.js";
70
70
  import {
71
71
  extractStaticPrefix,
72
+ joinPrefix,
72
73
  traverseBack,
73
74
  } from "./router/pattern-matching.js";
74
75
  import { resolveSink, safeEmit, getRequestId } from "./router/telemetry.js";
76
+ import { resolveTracing } from "./router/tracing.js";
75
77
  import { evaluateRevalidation } from "./router/revalidation.js";
76
78
  import {
77
79
  type RouterContext,
@@ -89,13 +91,10 @@ import {
89
91
  RouterRegistry,
90
92
  nextRouterAutoId,
91
93
  } from "./router/router-registry.js";
94
+ import type { RangoOptions, RootLayoutProps } from "./router/router-options.js";
92
95
  import type {
93
- RSCRouterOptions,
94
- RootLayoutProps,
95
- } from "./router/router-options.js";
96
- import type {
97
- RSCRouter,
98
- RSCRouterInternal,
96
+ Rango,
97
+ RangoInternal,
99
98
  RouterRequestInput,
100
99
  } from "./router/router-interfaces.js";
101
100
 
@@ -110,29 +109,32 @@ import {
110
109
  matchForPrerender as _matchForPrerender,
111
110
  renderStaticSegment as _renderStaticSegment,
112
111
  } from "./router/prerender-match.js";
112
+ import { resolveStateCookieName } from "./router/state-cookie-name.js";
113
+ import { resolvePrefetchCacheTTL } from "./router/prefetch-cache-ttl.js";
113
114
 
114
115
  // Re-export public types and values from extracted modules
115
116
  export { RSC_ROUTER_BRAND, RouterRegistry } from "./router/router-registry.js";
116
117
  export type {
117
- RSCRouterOptions,
118
+ RangoOptions,
118
119
  RootLayoutProps,
119
120
  SSRStreamMode,
120
121
  SSROptions,
121
122
  ResolveStreamingContext,
122
123
  } from "./router/router-options.js";
123
124
  export type {
124
- RSCRouter,
125
- RSCRouterInternal,
125
+ Rango,
126
+ RangoInternal,
126
127
  RouterRequestInput,
127
128
  } from "./router/router-interfaces.js";
128
129
  export { toInternal } from "./router/router-interfaces.js";
129
130
 
130
131
  export function createRouter<TEnv = any>(
131
- options: RSCRouterOptions<TEnv> = {},
132
- ): RSCRouter<TEnv, {}> {
132
+ options: RangoOptions<TEnv> = {},
133
+ ): Rango<TEnv, {}> {
133
134
  const {
134
135
  id: userProvidedId,
135
136
  $$id: injectedId,
137
+ basename: basenameOption,
136
138
  debugPerformance = false,
137
139
  document: documentOption,
138
140
  defaultErrorBoundary,
@@ -147,25 +149,47 @@ export function createRouter<TEnv = any>(
147
149
  $$sourceFile: injectedSourceFile,
148
150
  nonce,
149
151
  version,
150
- prefetchCacheControl: prefetchCacheControlOption,
152
+ prefetchCacheTTL: prefetchCacheTTLOption,
153
+ stateCookiePrefix: stateCookiePrefixOption,
151
154
  warmup: warmupOption,
152
155
  allowDebugManifest: allowDebugManifestOption = false,
153
156
  telemetry: telemetrySink,
157
+ tracing: tracingOption,
154
158
  ssr: ssrOption,
155
159
  timeout: timeoutShorthand,
156
160
  timeouts: timeoutsOption,
157
161
  onTimeout,
158
162
  originCheck: originCheckOption,
163
+ viewTransition: viewTransitionOption = "auto",
164
+ debugCacheSignal: debugCacheSignalOption = false,
165
+ strictMode: strictModeOption = true,
159
166
  } = options;
160
167
 
168
+ // Debug cache signal gate (DEVELOPMENT/TEST ONLY). Enabled by the
169
+ // debugCacheSignal option OR the RANGO_TEST_SIGNALS=1 env flag. When off,
170
+ // no X-Rango-Cache header is emitted and output is byte-identical.
171
+ const cacheSignalEnabled =
172
+ debugCacheSignalOption ||
173
+ (typeof process !== "undefined" &&
174
+ (process as { env?: Record<string, string | undefined> }).env
175
+ ?.RANGO_TEST_SIGNALS === "1");
176
+
177
+ // Normalize basename: ensure leading slash, strip trailing slash.
178
+ // A bare "/" is equivalent to no basename. Shared with the testing
179
+ // primitives via normalizeBasename so they can never drift.
180
+ const basename = normalizeBasename(basenameOption);
181
+
161
182
  // Resolve telemetry sink (no-op when not configured)
162
183
  const telemetry = resolveSink(telemetrySink);
163
184
 
164
- // Resolve cache profiles: merge user config with guaranteed default profile.
165
- // This resolved map is both stored on the router (for per-request context)
166
- // and written to the global registry (for DSL-time cache("profileName")).
185
+ // Resolve span tracing (undefined when not configured; every traceSpan() call
186
+ // is then a direct pass-through with zero behavior change).
187
+ const resolvedTracing = resolveTracing(tracingOption);
188
+
189
+ // Resolve cache profiles: merge user config with the guaranteed default
190
+ // profile. This resolved map is threaded onto each request context; the
191
+ // "use cache: <profile>" runtime path reads it request-scoped.
167
192
  const resolvedCacheProfiles = resolveCacheProfiles(cacheProfilesOption);
168
- setCacheProfiles(resolvedCacheProfiles);
169
193
 
170
194
  // Source file: prefer Vite-injected path (zero cost), fall back to
171
195
  // stack trace parsing for non-Vite environments (e.g. tests).
@@ -200,15 +224,32 @@ export function createRouter<TEnv = any>(
200
224
  const routerId =
201
225
  userProvidedId ?? injectedId ?? `router_${nextRouterAutoId()}`;
202
226
 
203
- // Resolve prefetch cache control (default: 'private, max-age=300')
204
- const prefetchCacheControl =
205
- prefetchCacheControlOption !== undefined
206
- ? prefetchCacheControlOption
207
- : "private, max-age=300";
227
+ // Resolve the rango state cookie name once, here, so the two cookie writers
228
+ // (the client document.cookie writer and the server Set-Cookie writer)
229
+ // consume one pre-composed name and cannot drift.
230
+ const resolvedStateCookieName = resolveStateCookieName(
231
+ stateCookiePrefixOption,
232
+ routerId,
233
+ );
234
+
235
+ // Resolve prefetch cache TTL (default: 300 seconds / 5 minutes). Clamps to a
236
+ // non-negative integer and guards non-finite (NaN/Infinity) inputs so a
237
+ // malformed `Cache-Control: max-age=NaN` can never reach the wire.
238
+ const resolvedPrefetchCacheTTL = resolvePrefetchCacheTTL(
239
+ prefetchCacheTTLOption,
240
+ );
241
+ const prefetchCacheTTL = resolvedPrefetchCacheTTL.ms;
242
+ const prefetchCacheControl: string | false =
243
+ resolvedPrefetchCacheTTL.cacheControl;
208
244
 
209
245
  // Resolve warmup enabled flag (default: true)
210
246
  const warmupEnabled = warmupOption !== false;
211
247
 
248
+ // Resolve StrictMode flag (default: true). Shipped to the client in payload
249
+ // metadata; the browser entry reads it once to decide whether to wrap the
250
+ // hydrated tree in React.StrictMode.
251
+ const strictMode = strictModeOption !== false;
252
+
212
253
  // Resolve theme config (null if theme not enabled)
213
254
  const resolvedThemeConfig = themeOption
214
255
  ? resolveThemeConfig(themeOption)
@@ -240,9 +281,14 @@ export function createRouter<TEnv = any>(
240
281
  invokeOnError(onError, error, phase, context, "Router");
241
282
  }
242
283
 
243
- // Validate document is a client component
284
+ // Validate document is a client component. Under a test runner the "use
285
+ // client" transform has not run, so a real exported document has no marker;
286
+ // allowServerInTest lets the router construct in a bare unit test (for
287
+ // dispatch / assertGeneratedRoutesMatch) while a real build still throws.
244
288
  if (documentOption !== undefined) {
245
- assertClientComponent(documentOption, "document");
289
+ assertClientComponent(documentOption, "document", {
290
+ allowServerInTest: true,
291
+ });
246
292
  }
247
293
 
248
294
  // Use default document if none provided (keeps internal name as rootLayout)
@@ -306,7 +352,7 @@ export function createRouter<TEnv = any>(
306
352
  let regex: RegExp | null = null;
307
353
  let paramNames: string[] = [];
308
354
  if (fullPattern) {
309
- const parsed = parsePattern(fullPattern);
355
+ const parsed = compileMiddlewarePattern(fullPattern);
310
356
  regex = parsed.regex;
311
357
  paramNames = parsed.paramNames;
312
358
  }
@@ -316,7 +362,6 @@ export function createRouter<TEnv = any>(
316
362
  regex,
317
363
  paramNames,
318
364
  handler,
319
- mountPrefix,
320
365
  });
321
366
  }
322
367
 
@@ -350,15 +395,27 @@ export function createRouter<TEnv = any>(
350
395
  getRouterPrecomputedEntries(routerId) ?? getPrecomputedEntries();
351
396
  if (current !== precomputedSource) {
352
397
  precomputedSource = current;
353
- precomputedByPrefix = current
354
- ? new Map(current.map((e) => [e.staticPrefix, e.routes]))
355
- : null;
398
+ // buildPrecomputedByPrefix drops any staticPrefix owned by more than one
399
+ // leaf include instead of collapsing it last-wins (which would mis-assign
400
+ // one include's routes to another's entry and 500 a valid sibling route).
401
+ // Such shared-prefix includes resolve via the handler path instead.
402
+ precomputedByPrefix = current ? buildPrecomputedByPrefix(current) : null;
356
403
  }
357
404
  return precomputedByPrefix;
358
405
  }
359
406
 
360
- // Wrapper to pass debugPerformance to external createMetricsStore
361
- const getMetricsStore = () => createMetricsStore(debugPerformance);
407
+ // Wrapper to pass debugPerformance to external createMetricsStore.
408
+ // Also checks per-request flag set by ctx.debugPerformance() in middleware.
409
+ const getMetricsStore = () => {
410
+ const reqCtx = _getRequestContext();
411
+ const enabled = debugPerformance || !!reqCtx?._debugPerformance;
412
+ if (!enabled) return undefined;
413
+ if (!reqCtx) {
414
+ return createMetricsStore(true);
415
+ }
416
+ reqCtx._metricsStore ??= createMetricsStore(true);
417
+ return reqCtx._metricsStore;
418
+ };
362
419
 
363
420
  // Wrapper to pass defaults to error/notFound boundary finders
364
421
  const findNearestErrorBoundary = (entry: EntryData | null) =>
@@ -510,7 +567,9 @@ export function createRouter<TEnv = any>(
510
567
  trackHandler,
511
568
  findNearestErrorBoundary,
512
569
  findNearestNotFoundBoundary,
570
+ notFoundComponent: notFound,
513
571
  callOnError,
572
+ viewTransitionDefault: viewTransitionOption,
514
573
  };
515
574
 
516
575
  // Match API dependencies
@@ -544,6 +603,7 @@ export function createRouter<TEnv = any>(
544
603
  mergedRouteMap,
545
604
  nextMountIndex: () => mountIndex++,
546
605
  getPrecomputedByPrefix,
606
+ routerId,
547
607
  };
548
608
 
549
609
  function evaluateLazyEntry(entry: RouteEntry<TEnv>): void {
@@ -597,6 +657,8 @@ export function createRouter<TEnv = any>(
597
657
  params: Record<string, string>,
598
658
  buildVars?: Record<string, any>,
599
659
  isPassthroughRoute?: boolean,
660
+ buildEnv?: TEnv,
661
+ devMode?: boolean,
600
662
  ) {
601
663
  return _matchForPrerender(
602
664
  pathname,
@@ -604,6 +666,8 @@ export function createRouter<TEnv = any>(
604
666
  prerenderDeps,
605
667
  buildVars,
606
668
  isPassthroughRoute,
669
+ buildEnv,
670
+ devMode,
607
671
  );
608
672
  }
609
673
 
@@ -611,12 +675,16 @@ export function createRouter<TEnv = any>(
611
675
  handler: Function,
612
676
  handlerId: string,
613
677
  routeName?: string,
678
+ buildEnv?: TEnv,
679
+ devMode?: boolean,
614
680
  ) {
615
681
  return _renderStaticSegment<TEnv>(
616
682
  handler,
617
683
  handlerId,
618
684
  mergedRouteMap,
619
685
  routeName,
686
+ buildEnv,
687
+ devMode,
620
688
  );
621
689
  }
622
690
 
@@ -629,6 +697,7 @@ export function createRouter<TEnv = any>(
629
697
  findMatch,
630
698
  findInterceptForRoute,
631
699
  telemetry: telemetrySink,
700
+ cacheSignalEnabled,
632
701
  });
633
702
 
634
703
  const { match, matchPartial, matchError, previewMatch } = matchHandlers;
@@ -638,11 +707,18 @@ export function createRouter<TEnv = any>(
638
707
  * The type system tracks accumulated routes through the builder chain
639
708
  * Initial TRoutes is {} (empty) to avoid poisoning accumulated types with Record<string, string>
640
709
  */
641
- const router: RSCRouterInternal<TEnv, {}> = {
710
+ const router: RangoInternal<TEnv, {}> = {
642
711
  __brand: RSC_ROUTER_BRAND,
643
712
  id: routerId,
713
+ basename,
714
+
715
+ routes(patternsOrBuilder: UrlPatterns<TEnv> | UrlBuilder<TEnv>): any {
716
+ // Wrap builder functions in urls() automatically
717
+ const urlPatterns: UrlPatterns<TEnv> =
718
+ typeof patternsOrBuilder === "function"
719
+ ? (urls(patternsOrBuilder) as UrlPatterns<TEnv>)
720
+ : patternsOrBuilder;
644
721
 
645
- routes(urlPatterns: UrlPatterns<TEnv>): any {
646
722
  // Store reference for runtime manifest generation
647
723
  storedUrlPatterns = urlPatterns;
648
724
  const currentMountIndex = mountIndex++;
@@ -673,13 +749,13 @@ export function createRouter<TEnv = any>(
673
749
  errorBoundary: [],
674
750
  notFoundBoundary: [],
675
751
  layout: [],
676
- parallel: [],
752
+ parallel: {},
677
753
  intercept: [],
678
754
  loader: [],
679
755
  };
680
756
 
681
757
  let handlerResult: AllUseItems[] = [];
682
- RSCRouterContext.run(
758
+ RangoContext.run(
683
759
  {
684
760
  manifest,
685
761
  patterns: routePatterns,
@@ -690,6 +766,10 @@ export function createRouter<TEnv = any>(
690
766
  counters: {},
691
767
  mountIndex: currentMountIndex,
692
768
  cacheProfiles: resolvedCacheProfiles,
769
+ // basename sets the initial URL prefix so all path() patterns
770
+ // are registered with the prefix (e.g. "/admin" + "/users" = "/admin/users").
771
+ // No namePrefix — route names stay unprefixed.
772
+ ...(basename ? { urlPrefix: basename } : {}),
693
773
  },
694
774
  () => {
695
775
  handlerResult = urlPatterns.handler() as AllUseItems[];
@@ -709,7 +789,7 @@ export function createRouter<TEnv = any>(
709
789
  if (entry.type === "route" && entry.isPrerender) {
710
790
  if (!prerenderRouteKeys) prerenderRouteKeys = new Set();
711
791
  prerenderRouteKeys.add(name);
712
- if (entry.prerenderDef?.options?.passthrough === true) {
792
+ if (entry.isPassthrough === true) {
713
793
  if (!passthroughRouteKeys) passthroughRouteKeys = new Set();
714
794
  passthroughRouteKeys.add(name);
715
795
  }
@@ -735,6 +815,7 @@ export function createRouter<TEnv = any>(
735
815
  trailingSlash: trailingSlashConfig,
736
816
  handler: urlPatterns.handler,
737
817
  mountIndex: currentMountIndex,
818
+ routerId,
738
819
  cacheProfiles: resolvedCacheProfiles,
739
820
  ...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
740
821
  ...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
@@ -754,6 +835,7 @@ export function createRouter<TEnv = any>(
754
835
  trailingSlash: trailingSlashConfig,
755
836
  handler: urlPatterns.handler,
756
837
  mountIndex: currentMountIndex,
838
+ routerId,
757
839
  cacheProfiles: resolvedCacheProfiles,
758
840
  ...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
759
841
  ...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
@@ -785,10 +867,13 @@ export function createRouter<TEnv = any>(
785
867
 
786
868
  // Create placeholder RouteEntry for each lazy include
787
869
  for (const lazyInclude of lazyIncludes) {
788
- // Compute the full URL prefix (combining parent prefix if any)
789
- const fullPrefix = lazyInclude.context.urlPrefix
790
- ? lazyInclude.context.urlPrefix + lazyInclude.prefix
791
- : lazyInclude.prefix;
870
+ // Compute the full URL prefix (combining parent prefix if any). Use the
871
+ // slash-collapsing join so a trailing-slash parent prefix does not
872
+ // produce a double-slash staticPrefix the trie's sp can never match.
873
+ const fullPrefix = joinPrefix(
874
+ lazyInclude.context.urlPrefix,
875
+ lazyInclude.prefix,
876
+ );
792
877
 
793
878
  const lazyEntry: RouteEntry<TEnv> & { _lazyPrefix?: string } = {
794
879
  prefix: "",
@@ -797,6 +882,7 @@ export function createRouter<TEnv = any>(
797
882
  trailingSlash: trailingSlashConfig,
798
883
  handler: urlPatterns.handler,
799
884
  mountIndex: mountIndex++,
885
+ routerId,
800
886
  // Lazy evaluation fields
801
887
  lazy: true,
802
888
  lazyPatterns: lazyInclude.patterns,
@@ -835,8 +921,18 @@ export function createRouter<TEnv = any>(
835
921
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
836
922
  middleware?: MiddlewareFn<TEnv>,
837
923
  ): any {
838
- // Global middleware - no mount prefix
839
- addMiddleware(patternOrMiddleware, middleware, null);
924
+ // Auto-prefix pattern with basename so router-level middleware
925
+ // patterns are router-relative (e.g. "/users/*" matches "/app/users/*").
926
+ if (basename && typeof patternOrMiddleware === "string") {
927
+ const pattern = patternOrMiddleware;
928
+ const prefixed =
929
+ pattern === "/*" || pattern === "*"
930
+ ? `${basename}/*`
931
+ : `${basename}${pattern}`;
932
+ addMiddleware(prefixed, middleware, null);
933
+ } else {
934
+ addMiddleware(patternOrMiddleware, middleware, null);
935
+ }
840
936
  return router;
841
937
  },
842
938
 
@@ -869,12 +965,26 @@ export function createRouter<TEnv = any>(
869
965
  // Expose resolved cache profiles for per-request resolution
870
966
  cacheProfiles: resolvedCacheProfiles,
871
967
 
872
- // Expose prefetch cache control for RSC handler
968
+ // Expose prefetch cache settings
873
969
  prefetchCacheControl,
970
+ prefetchCacheTTL,
971
+
972
+ // Expose the resolved rango state cookie name for the server-side writer
973
+ // (invalidateClientCache) and for shipping to the client in metadata.
974
+ resolvedStateCookieName,
874
975
 
875
976
  // Expose warmup enabled flag for handler and client
876
977
  warmupEnabled,
877
978
 
979
+ // Expose StrictMode flag for the initial-render payload metadata
980
+ strictMode,
981
+
982
+ // Expose router-wide performance debugging for request-level metrics setup
983
+ debugPerformance,
984
+
985
+ // Expose resolved span tracing for the handler (Cloudflare custom spans)
986
+ tracing: resolvedTracing,
987
+
878
988
  // Expose debug manifest flag for handler
879
989
  allowDebugManifest: allowDebugManifestOption,
880
990
 
@@ -933,6 +1043,16 @@ export function createRouter<TEnv = any>(
933
1043
  // Expose source file for per-router type generation
934
1044
  __sourceFile,
935
1045
 
1046
+ // Expose basename for runtime manifest generation
1047
+ __basename: basename,
1048
+
1049
+ // Expose router-level boundary defaults for build-time clientChunks
1050
+ // discovery (so a "use client" default boundary lands in app-fallback).
1051
+ // These are createRouter options, never pushed onto EntryData.
1052
+ __defaultErrorBoundary: defaultErrorBoundary,
1053
+ __defaultNotFoundBoundary: defaultNotFoundBoundary,
1054
+ __notFound: notFound,
1055
+
936
1056
  // RSC request handler (lazily created on first call)
937
1057
  fetch: (() => {
938
1058
  // Handler is created on first call and reused
@@ -950,8 +1070,10 @@ export function createRouter<TEnv = any>(
950
1070
  if (!handler) {
951
1071
  // Lazy import deferred to first request to avoid dev mode issues
952
1072
  const { createRSCHandler } = await import("./rsc/handler.js");
953
- // Cast: handler.ts still accepts (request, env) — will be updated
954
- // separately to accept RouterRequestInput.
1073
+ // Cast: createRSCHandler receives `router as any`, which erases TEnv
1074
+ // and infers its handler as RouterRequestInput<unknown>. Re-narrow the
1075
+ // returned handler to RouterRequestInput<TEnv> so the call below stays
1076
+ // typed. (The handler already accepts (request, RouterRequestInput).)
955
1077
  handler = createRSCHandler({
956
1078
  router: router as any,
957
1079
  cache,
@@ -966,6 +1088,10 @@ export function createRouter<TEnv = any>(
966
1088
  };
967
1089
  })(),
968
1090
 
1091
+ // Low-level route matching for request classification
1092
+ findMatch: (pathname: string, metricsStore?: any) =>
1093
+ findMatch(pathname, metricsStore),
1094
+
969
1095
  // Debug utility for manifest inspection
970
1096
  debugManifest: () => buildDebugManifest<TEnv>(routesEntries),
971
1097
  };
@@ -974,8 +1100,10 @@ export function createRouter<TEnv = any>(
974
1100
  RouterRegistry.set(routerId, router);
975
1101
 
976
1102
  // If urls option was provided, auto-register them
977
- if (urlsOption) {
978
- return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
1103
+ if (typeof urlsOption === "function") {
1104
+ return router.routes(urlsOption) as Rango<TEnv, {}>;
1105
+ } else if (urlsOption) {
1106
+ return router.routes(urlsOption) as Rango<TEnv, {}>;
979
1107
  }
980
1108
 
981
1109
  return router;
@@ -6,14 +6,14 @@
6
6
  * RSC rendering) so they can be standalone modules without closure coupling.
7
7
  */
8
8
 
9
- import type { RSCRouterInternal } from "../router/router-interfaces.js";
9
+ import type { RangoInternal } from "../router/router-interfaces.js";
10
10
  import type { ErrorPhase } from "../types.js";
11
11
  import type { InvokeOnErrorContext } from "../router/error-handling.js";
12
12
  import type { RSCDependencies, LoadSSRModule } from "./types.js";
13
13
  import type { SSRStreamMode } from "../router/router-options.js";
14
14
 
15
15
  export interface HandlerContext<TEnv = unknown> {
16
- router: RSCRouterInternal<TEnv, any>;
16
+ router: RangoInternal<TEnv, any>;
17
17
  version: string;
18
18
  renderToReadableStream: RSCDependencies["renderToReadableStream"];
19
19
  decodeReply: RSCDependencies["decodeReply"];
@@ -31,6 +31,7 @@ export interface HandlerContext<TEnv = unknown> {
31
31
  createRedirectFlightResponse: (
32
32
  redirectUrl: string,
33
33
  locationState?: Record<string, unknown>,
34
+ external?: boolean,
34
35
  ) => Response;
35
36
 
36
37
  /**