@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa

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 (411) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +296 -887
  3. package/dist/bin/rango.js +459 -91
  4. package/dist/testing/vitest.js +36 -2
  5. package/dist/vite/index.js +1708 -414
  6. package/package.json +35 -10
  7. package/skills/api-client/SKILL.md +211 -0
  8. package/skills/breadcrumbs/SKILL.md +82 -5
  9. package/skills/bundle-analysis/SKILL.md +2 -2
  10. package/skills/cache-guide/SKILL.md +14 -9
  11. package/skills/caching/SKILL.md +221 -12
  12. package/skills/catalog.json +271 -0
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +83 -2
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +5 -3
  19. package/skills/defer-hydration/SKILL.md +235 -0
  20. package/skills/document-cache/SKILL.md +11 -3
  21. package/skills/fonts/SKILL.md +1 -1
  22. package/skills/handler-use/SKILL.md +9 -9
  23. package/skills/hooks/SKILL.md +73 -900
  24. package/skills/hooks/data.md +273 -0
  25. package/skills/hooks/handle-and-actions.md +103 -0
  26. package/skills/hooks/navigation.md +110 -0
  27. package/skills/hooks/outlets.md +41 -0
  28. package/skills/hooks/state.md +228 -0
  29. package/skills/hooks/urls.md +135 -0
  30. package/skills/host-router/SKILL.md +84 -7
  31. package/skills/i18n/SKILL.md +1 -1
  32. package/skills/intercept/SKILL.md +51 -17
  33. package/skills/layout/SKILL.md +38 -16
  34. package/skills/links/SKILL.md +1 -1
  35. package/skills/loader/SKILL.md +48 -20
  36. package/skills/middleware/SKILL.md +11 -5
  37. package/skills/migrate-nextjs/SKILL.md +203 -20
  38. package/skills/migrate-react-router/SKILL.md +59 -675
  39. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  40. package/skills/migrate-react-router/component-migration.md +196 -0
  41. package/skills/migrate-react-router/data-and-actions.md +225 -0
  42. package/skills/migrate-react-router/route-mapping.md +271 -0
  43. package/skills/mime-routes/SKILL.md +3 -3
  44. package/skills/observability/SKILL.md +70 -5
  45. package/skills/parallel/SKILL.md +32 -8
  46. package/skills/ppr/SKILL.md +622 -0
  47. package/skills/prerender/SKILL.md +59 -28
  48. package/skills/rango/SKILL.md +124 -50
  49. package/skills/response-routes/SKILL.md +78 -46
  50. package/skills/route/SKILL.md +85 -6
  51. package/skills/router-setup/SKILL.md +41 -6
  52. package/skills/scripts/SKILL.md +179 -0
  53. package/skills/server-actions/SKILL.md +28 -3
  54. package/skills/shell-manifest/SKILL.md +185 -0
  55. package/skills/streams-and-websockets/SKILL.md +1 -1
  56. package/skills/tailwind/SKILL.md +28 -4
  57. package/skills/testing/SKILL.md +68 -654
  58. package/skills/testing/bindings.md +103 -0
  59. package/skills/testing/cache-prerender.md +127 -0
  60. package/skills/testing/client-components.md +124 -0
  61. package/skills/testing/e2e-parity.md +125 -0
  62. package/skills/testing/flight.md +91 -0
  63. package/skills/testing/handles.md +131 -0
  64. package/skills/testing/loader.md +128 -0
  65. package/skills/testing/middleware.md +99 -0
  66. package/skills/testing/render-handler.md +122 -0
  67. package/skills/testing/response-routes.md +95 -0
  68. package/skills/testing/reverse-and-types.md +85 -0
  69. package/skills/testing/server-actions.md +107 -0
  70. package/skills/testing/server-tree.md +128 -0
  71. package/skills/testing/setup.md +123 -0
  72. package/skills/theme/SKILL.md +1 -1
  73. package/skills/typesafety/SKILL.md +45 -918
  74. package/skills/typesafety/env-and-bindings.md +254 -0
  75. package/skills/typesafety/generated-files-and-cli.md +335 -0
  76. package/skills/typesafety/params-and-search.md +153 -0
  77. package/skills/typesafety/route-types.md +209 -0
  78. package/skills/use-cache/SKILL.md +47 -17
  79. package/skills/vercel/SKILL.md +128 -0
  80. package/skills/view-transitions/SKILL.md +44 -1
  81. package/src/__augment-tests__/augmented.check.ts +2 -3
  82. package/src/__internal.ts +0 -65
  83. package/src/browser/action-coordinator.ts +1 -1
  84. package/src/browser/action-fence.ts +47 -0
  85. package/src/browser/app-shell.ts +14 -27
  86. package/src/browser/connection-warmup.ts +134 -0
  87. package/src/browser/cookie-name.ts +140 -0
  88. package/src/browser/event-controller.ts +178 -100
  89. package/src/browser/invalidate-client-cache.ts +52 -0
  90. package/src/browser/logging.ts +28 -0
  91. package/src/browser/merge-segment-loaders.ts +6 -4
  92. package/src/browser/navigation-bridge.ts +81 -68
  93. package/src/browser/navigation-client.ts +115 -70
  94. package/src/browser/navigation-store-handle.ts +38 -0
  95. package/src/browser/navigation-store.ts +153 -88
  96. package/src/browser/navigation-transaction.ts +0 -32
  97. package/src/browser/network-error-handler.ts +34 -7
  98. package/src/browser/partial-update.ts +157 -144
  99. package/src/browser/prefetch/cache.ts +148 -81
  100. package/src/browser/prefetch/fetch.ts +231 -51
  101. package/src/browser/prefetch/queue.ts +25 -7
  102. package/src/browser/rango-state.ts +157 -115
  103. package/src/browser/react/Link.tsx +40 -7
  104. package/src/browser/react/NavigationProvider.tsx +140 -99
  105. package/src/browser/react/ScrollRestoration.tsx +10 -6
  106. package/src/browser/react/filter-segment-order.ts +17 -2
  107. package/src/browser/react/index.ts +0 -51
  108. package/src/browser/react/location-state-shared.ts +14 -15
  109. package/src/browser/react/location-state.ts +0 -1
  110. package/src/browser/react/use-action.ts +6 -15
  111. package/src/browser/react/use-handle.ts +0 -5
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +33 -8
  114. package/src/browser/react/use-navigation.ts +10 -5
  115. package/src/browser/react/use-params.ts +0 -2
  116. package/src/browser/react/use-router.ts +6 -4
  117. package/src/browser/react/use-search-params.ts +0 -5
  118. package/src/browser/react/use-segments.ts +0 -13
  119. package/src/browser/response-adapter.ts +74 -8
  120. package/src/browser/rsc-router.tsx +97 -22
  121. package/src/browser/scroll-restoration.ts +15 -8
  122. package/src/browser/segment-reconciler.ts +31 -21
  123. package/src/browser/server-action-bridge.ts +216 -38
  124. package/src/browser/types.ts +94 -22
  125. package/src/browser/validate-redirect-origin.ts +43 -16
  126. package/src/build/generate-manifest.ts +155 -131
  127. package/src/build/generate-route-types.ts +1 -1
  128. package/src/build/index.ts +11 -5
  129. package/src/build/prefix-tree-utils.ts +123 -0
  130. package/src/build/route-trie.ts +152 -22
  131. package/src/build/route-types/ast-route-extraction.ts +15 -8
  132. package/src/build/route-types/codegen.ts +12 -1
  133. package/src/build/route-types/include-resolution.ts +455 -61
  134. package/src/build/route-types/param-extraction.ts +6 -3
  135. package/src/build/route-types/per-module-writer.ts +15 -2
  136. package/src/build/route-types/router-processing.ts +77 -41
  137. package/src/build/route-types/source-scan.ts +105 -7
  138. package/src/build/runtime-discovery.ts +4 -1
  139. package/src/cache/cache-error.ts +104 -0
  140. package/src/cache/cache-key-utils.ts +58 -13
  141. package/src/cache/cache-policy.ts +108 -34
  142. package/src/cache/cache-runtime.ts +454 -101
  143. package/src/cache/cache-scope.ts +159 -54
  144. package/src/cache/cache-tag.ts +149 -0
  145. package/src/cache/cf/cf-base64.ts +33 -0
  146. package/src/cache/cf/cf-cache-constants.ts +127 -0
  147. package/src/cache/cf/cf-cache-store.ts +2170 -377
  148. package/src/cache/cf/cf-cache-types.ts +349 -0
  149. package/src/cache/cf/cf-kv-utils.ts +46 -0
  150. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  151. package/src/cache/cf/index.ts +6 -16
  152. package/src/cache/document-cache.ts +126 -41
  153. package/src/cache/handle-snapshot.ts +70 -0
  154. package/src/cache/index.ts +23 -20
  155. package/src/cache/memory-segment-store.ts +243 -37
  156. package/src/cache/profile-registry.ts +46 -31
  157. package/src/cache/read-through-swr.ts +56 -12
  158. package/src/cache/segment-codec.ts +13 -21
  159. package/src/cache/shell-snapshot.ts +417 -0
  160. package/src/cache/tag-invalidation.ts +230 -0
  161. package/src/cache/types.ts +194 -99
  162. package/src/cache/vercel/index.ts +11 -0
  163. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  164. package/src/client.rsc.tsx +39 -22
  165. package/src/client.tsx +28 -58
  166. package/src/cloudflare/index.ts +11 -0
  167. package/src/cloudflare/tracing.ts +108 -0
  168. package/src/component-utils.ts +19 -0
  169. package/src/components/DefaultDocument.tsx +8 -2
  170. package/src/context-var.ts +13 -1
  171. package/src/decode-loader-results.ts +18 -2
  172. package/src/defer.ts +185 -0
  173. package/src/deps/ssr.ts +0 -1
  174. package/src/encode-kv.ts +49 -0
  175. package/src/errors.ts +0 -3
  176. package/src/escape-script.ts +52 -0
  177. package/src/handle.ts +57 -40
  178. package/src/handles/MetaTags.tsx +24 -53
  179. package/src/handles/Scripts.tsx +183 -0
  180. package/src/handles/breadcrumbs.ts +35 -8
  181. package/src/handles/deferred-resolution.ts +127 -0
  182. package/src/handles/is-thenable.ts +18 -0
  183. package/src/handles/meta.ts +14 -40
  184. package/src/handles/script.ts +244 -0
  185. package/src/host/cookie-handler.ts +9 -60
  186. package/src/host/errors.ts +13 -22
  187. package/src/host/index.ts +7 -0
  188. package/src/host/pattern-matcher.ts +23 -52
  189. package/src/host/router.ts +1 -65
  190. package/src/host/testing.ts +40 -27
  191. package/src/host/types.ts +6 -2
  192. package/src/href-client.ts +7 -12
  193. package/src/index.rsc.ts +88 -8
  194. package/src/index.ts +90 -16
  195. package/src/internal-debug.ts +11 -10
  196. package/src/loader.rsc.ts +19 -9
  197. package/src/loader.ts +12 -4
  198. package/src/outlet-provider.tsx +1 -5
  199. package/src/prerender/param-hash.ts +16 -16
  200. package/src/prerender/store.ts +32 -37
  201. package/src/prerender.ts +75 -7
  202. package/src/redirect-origin.ts +114 -0
  203. package/src/regex-escape.ts +8 -0
  204. package/src/render-error-thrower.tsx +20 -0
  205. package/src/response-utils.ts +25 -0
  206. package/src/root-error-boundary.tsx +1 -19
  207. package/src/route-content-wrapper.tsx +13 -49
  208. package/src/route-definition/dsl-helpers.ts +60 -53
  209. package/src/route-definition/helper-factories.ts +0 -2
  210. package/src/route-definition/helpers-types.ts +46 -46
  211. package/src/route-definition/index.ts +1 -2
  212. package/src/route-definition/redirect.ts +44 -11
  213. package/src/route-definition/resolve-handler-use.ts +6 -1
  214. package/src/route-definition/use-item-types.ts +3 -6
  215. package/src/route-map-builder.ts +41 -20
  216. package/src/route-types.ts +0 -5
  217. package/src/router/content-negotiation.ts +58 -23
  218. package/src/router/error-handling.ts +44 -17
  219. package/src/router/find-match.ts +129 -30
  220. package/src/router/handler-context.ts +6 -1
  221. package/src/router/instrument.ts +355 -0
  222. package/src/router/intercept-resolution.ts +35 -2
  223. package/src/router/lazy-includes.ts +79 -56
  224. package/src/router/loader-resolution.ts +151 -73
  225. package/src/router/logging.ts +0 -6
  226. package/src/router/manifest.ts +74 -40
  227. package/src/router/match-api.ts +76 -52
  228. package/src/router/match-context.ts +0 -22
  229. package/src/router/match-handlers.ts +181 -178
  230. package/src/router/match-middleware/background-revalidation.ts +40 -24
  231. package/src/router/match-middleware/cache-lookup.ts +115 -194
  232. package/src/router/match-middleware/cache-store.ts +61 -50
  233. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  234. package/src/router/match-middleware/segment-resolution.ts +0 -22
  235. package/src/router/match-pipelines.ts +1 -42
  236. package/src/router/match-result.ts +36 -67
  237. package/src/router/metrics.ts +0 -34
  238. package/src/router/middleware-types.ts +0 -116
  239. package/src/router/middleware.ts +231 -120
  240. package/src/router/navigation-snapshot.ts +7 -56
  241. package/src/router/params-util.ts +23 -0
  242. package/src/router/parse-pattern.ts +115 -0
  243. package/src/router/pattern-matching.ts +99 -152
  244. package/src/router/prefetch-cache-ttl.ts +51 -0
  245. package/src/router/prefetch-limits.ts +37 -0
  246. package/src/router/prerender-match.ts +111 -66
  247. package/src/router/preview-match.ts +3 -1
  248. package/src/router/request-classification.ts +47 -42
  249. package/src/router/revalidation.ts +75 -81
  250. package/src/router/route-snapshot.ts +14 -3
  251. package/src/router/router-context.ts +6 -29
  252. package/src/router/router-interfaces.ts +70 -8
  253. package/src/router/router-options.ts +126 -4
  254. package/src/router/segment-resolution/fresh.ts +104 -80
  255. package/src/router/segment-resolution/helpers.ts +86 -6
  256. package/src/router/segment-resolution/loader-cache.ts +155 -39
  257. package/src/router/segment-resolution/loader-mask.ts +60 -0
  258. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  259. package/src/router/segment-resolution/mask-nested.ts +83 -0
  260. package/src/router/segment-resolution/revalidation.ts +215 -304
  261. package/src/router/segment-resolution/static-store.ts +19 -5
  262. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  263. package/src/router/segment-resolution/view-transition-default.ts +35 -15
  264. package/src/router/segment-resolution.ts +5 -1
  265. package/src/router/segment-wrappers.ts +6 -5
  266. package/src/router/state-cookie-name.ts +33 -0
  267. package/src/router/substitute-pattern-params.ts +54 -35
  268. package/src/router/telemetry-otel.ts +160 -200
  269. package/src/router/telemetry.ts +9 -23
  270. package/src/router/timeout.ts +0 -20
  271. package/src/router/tracing.ts +215 -0
  272. package/src/router/trie-matching.ts +171 -64
  273. package/src/router/types.ts +1 -63
  274. package/src/router/url-params.ts +13 -5
  275. package/src/router.ts +119 -48
  276. package/src/rsc/full-payload.ts +70 -0
  277. package/src/rsc/handler-context.ts +1 -0
  278. package/src/rsc/handler.ts +267 -152
  279. package/src/rsc/helpers.ts +78 -4
  280. package/src/rsc/index.ts +1 -4
  281. package/src/rsc/json-route-result.ts +38 -0
  282. package/src/rsc/loader-fetch.ts +114 -38
  283. package/src/rsc/manifest-init.ts +29 -42
  284. package/src/rsc/nonce.ts +10 -1
  285. package/src/rsc/origin-guard.ts +11 -15
  286. package/src/rsc/progressive-enhancement.ts +120 -13
  287. package/src/rsc/redirect-guard.ts +100 -0
  288. package/src/rsc/response-cache-serve.ts +238 -0
  289. package/src/rsc/response-error.ts +79 -12
  290. package/src/rsc/response-route-handler.ts +58 -141
  291. package/src/rsc/rsc-rendering.ts +492 -49
  292. package/src/rsc/runtime-warnings.ts +14 -0
  293. package/src/rsc/server-action.ts +268 -82
  294. package/src/rsc/shell-capture.ts +1190 -0
  295. package/src/rsc/shell-serve.ts +181 -0
  296. package/src/rsc/transition-gate.ts +89 -0
  297. package/src/rsc/types.ts +45 -3
  298. package/src/runtime-env.ts +18 -0
  299. package/src/search-params.ts +31 -26
  300. package/src/segment-loader-promise.ts +49 -4
  301. package/src/segment-system.tsx +260 -95
  302. package/src/server/context.ts +99 -9
  303. package/src/server/cookie-parse.ts +32 -0
  304. package/src/server/cookie-store.ts +125 -2
  305. package/src/server/handle-store.ts +21 -38
  306. package/src/server/loader-registry.ts +33 -42
  307. package/src/server/request-context.ts +379 -138
  308. package/src/ssr/index.tsx +491 -182
  309. package/src/ssr/inject-rsc-eager.ts +167 -0
  310. package/src/ssr/ssr-root.tsx +228 -0
  311. package/src/static-handler.ts +10 -13
  312. package/src/testing/cache-status.ts +44 -48
  313. package/src/testing/collect-handle.ts +14 -31
  314. package/src/testing/dispatch.ts +533 -160
  315. package/src/testing/e2e/fixture.ts +45 -11
  316. package/src/testing/e2e/index.ts +1 -22
  317. package/src/testing/e2e/matchers.ts +0 -16
  318. package/src/testing/e2e/parity.ts +85 -4
  319. package/src/testing/e2e/server.ts +12 -0
  320. package/src/testing/flight-matchers.ts +7 -14
  321. package/src/testing/flight-normalize.ts +11 -0
  322. package/src/testing/flight-runtime.d.ts +36 -0
  323. package/src/testing/flight-tree.ts +682 -0
  324. package/src/testing/flight.entry.ts +30 -0
  325. package/src/testing/flight.ts +145 -70
  326. package/src/testing/generated-routes.ts +26 -50
  327. package/src/testing/index.ts +18 -19
  328. package/src/testing/internal/context.ts +184 -68
  329. package/src/testing/internal/flight-client-globals.ts +30 -0
  330. package/src/testing/internal/seed-vars.ts +54 -0
  331. package/src/testing/render-handler.ts +357 -0
  332. package/src/testing/render-route.tsx +134 -115
  333. package/src/testing/run-loader.ts +140 -51
  334. package/src/testing/run-middleware.ts +59 -33
  335. package/src/testing/run-transition-when.ts +164 -0
  336. package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
  337. package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
  338. package/src/testing/vitest.ts +138 -16
  339. package/src/theme/ThemeProvider.tsx +56 -84
  340. package/src/theme/ThemeScript.tsx +7 -9
  341. package/src/theme/constants.ts +52 -13
  342. package/src/theme/index.ts +0 -7
  343. package/src/theme/theme-context.ts +1 -5
  344. package/src/theme/theme-script.ts +22 -21
  345. package/src/theme/use-theme.ts +0 -3
  346. package/src/types/boundaries.ts +0 -35
  347. package/src/types/cache-types.ts +13 -4
  348. package/src/types/error-types.ts +30 -90
  349. package/src/types/global-namespace.ts +15 -15
  350. package/src/types/handler-context.ts +45 -15
  351. package/src/types/index.ts +2 -10
  352. package/src/types/loader-types.ts +6 -3
  353. package/src/types/request-scope.ts +8 -22
  354. package/src/types/route-config.ts +20 -52
  355. package/src/types/route-entry.ts +0 -6
  356. package/src/types/segments.ts +100 -13
  357. package/src/urls/include-helper.ts +10 -12
  358. package/src/urls/include-provider.ts +71 -0
  359. package/src/urls/index.ts +2 -8
  360. package/src/urls/path-helper-types.ts +52 -14
  361. package/src/urls/path-helper.ts +5 -54
  362. package/src/urls/pattern-types.ts +36 -0
  363. package/src/urls/type-extraction.ts +76 -42
  364. package/src/urls/urls-function.ts +0 -14
  365. package/src/use-loader.tsx +0 -186
  366. package/src/vercel/index.ts +11 -0
  367. package/src/vercel/tracing.ts +88 -0
  368. package/src/vite/discovery/bundle-postprocess.ts +2 -1
  369. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  370. package/src/vite/discovery/discover-routers.ts +34 -43
  371. package/src/vite/discovery/discovery-errors.ts +61 -0
  372. package/src/vite/discovery/prerender-collection.ts +33 -46
  373. package/src/vite/discovery/state.ts +12 -1
  374. package/src/vite/discovery/virtual-module-codegen.ts +1 -11
  375. package/src/vite/index.ts +9 -0
  376. package/src/vite/inject-client-debug.ts +88 -0
  377. package/src/vite/plugin-types.ts +143 -10
  378. package/src/vite/plugins/cjs-to-esm.ts +8 -12
  379. package/src/vite/plugins/client-ref-dedup.ts +0 -11
  380. package/src/vite/plugins/client-ref-hashing.ts +0 -10
  381. package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
  382. package/src/vite/plugins/expose-action-id.ts +2 -73
  383. package/src/vite/plugins/expose-id-utils.ts +85 -56
  384. package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
  385. package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
  386. package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
  387. package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
  388. package/src/vite/plugins/expose-internal-ids.ts +10 -1
  389. package/src/vite/plugins/performance-tracks.ts +0 -3
  390. package/src/vite/plugins/refresh-cmd.ts +1 -1
  391. package/src/vite/plugins/use-cache-transform.ts +21 -46
  392. package/src/vite/plugins/vercel-output.ts +384 -0
  393. package/src/vite/plugins/version-injector.ts +22 -27
  394. package/src/vite/plugins/version-plugin.ts +6 -66
  395. package/src/vite/plugins/virtual-entries.ts +137 -26
  396. package/src/vite/rango.ts +146 -135
  397. package/src/vite/router-discovery.ts +189 -48
  398. package/src/vite/utils/ast-handler-extract.ts +11 -20
  399. package/src/vite/utils/bundle-analysis.ts +6 -13
  400. package/src/vite/utils/client-chunks.ts +0 -6
  401. package/src/vite/utils/directive-prologue.ts +40 -0
  402. package/src/vite/utils/forward-user-plugins.ts +0 -22
  403. package/src/vite/utils/manifest-utils.ts +4 -75
  404. package/src/vite/utils/package-resolution.ts +1 -73
  405. package/src/vite/utils/prerender-utils.ts +71 -44
  406. package/src/vite/utils/shared-utils.ts +55 -37
  407. package/src/browser/react/use-client-cache.ts +0 -58
  408. package/src/browser/shallow.ts +0 -40
  409. package/src/handles/index.ts +0 -7
  410. package/src/network-error-thrower.tsx +0 -23
  411. package/src/router/middleware-cookies.ts +0 -55
@@ -14,6 +14,7 @@ import {
14
14
  import type { RevalidationTraceEntry } from "./logging.js";
15
15
  import { _getRequestContext } from "../server/request-context.js";
16
16
  import { isAutoGeneratedRouteName } from "../route-name.js";
17
+ import { paramsEqual } from "./params-util.js";
17
18
 
18
19
  /**
19
20
  * Resolve a server-action reference's stable id, mirroring how the action
@@ -32,16 +33,21 @@ function resolveActionRefId(ref: unknown): string | undefined {
32
33
  }
33
34
 
34
35
  /**
35
- * Build the `isAction()` helper bound to the current action's id. Matches a
36
- * single imported action reference, several (variadic), or any export of a
37
- * namespace import (`import * as Mod`). Returns `false` when there is no action
38
- * (plain navigation) or nothing matches.
36
+ * Build the `isAction()` helper bound to the current action's id. Called with no
37
+ * arguments it answers "is this request an action at all?" (any action) `true`
38
+ * during action handling, `false` on plain navigation. Called with one or more
39
+ * action references it narrows to those: a single imported action, several
40
+ * (variadic), or any export of a namespace import (`import * as Mod`). Returns
41
+ * `false` when there is no action (plain navigation) or nothing matches.
39
42
  */
40
43
  function makeIsAction(
41
44
  currentActionId: string | undefined,
42
45
  ): (...actions: ActionRef[]) => boolean {
43
46
  return (...actions: ActionRef[]): boolean => {
44
47
  if (!currentActionId) return false;
48
+ // Bare isAction(): an action is in flight (currentActionId is set) and the
49
+ // caller did not narrow to a specific action, so this is "any action".
50
+ if (actions.length === 0) return true;
45
51
  for (const action of actions) {
46
52
  if (typeof action === "function") {
47
53
  if (resolveActionRefId(action) === currentActionId) return true;
@@ -56,22 +62,6 @@ function makeIsAction(
56
62
  };
57
63
  }
58
64
 
59
- function paramsEqual(
60
- a: Record<string, string>,
61
- b: Record<string, string>,
62
- ): boolean {
63
- if (a === b) return true;
64
-
65
- const keysA = Object.keys(a);
66
- if (keysA.length !== Object.keys(b).length) return false;
67
-
68
- for (const key of keysA) {
69
- if (a[key] !== b[key]) return false;
70
- }
71
-
72
- return true;
73
- }
74
-
75
65
  /**
76
66
  * Options for revalidation evaluation
77
67
  */
@@ -136,8 +126,6 @@ export async function evaluateRevalidation<TEnv>(
136
126
  const paramsChanged = !paramsEqual(nextParams, prevParams);
137
127
  const searchChanged = prevUrl.search !== nextUrl.search;
138
128
 
139
- // Trace helper: push a structured entry to the request-scoped trace buffer.
140
- // Guarded by isTraceActive() so object construction is skipped in production.
141
129
  function pushTrace(
142
130
  defaultVal: boolean,
143
131
  finalVal: boolean,
@@ -156,43 +144,28 @@ export async function evaluateRevalidation<TEnv>(
156
144
  });
157
145
  }
158
146
 
159
- // Calculate default revalidation based on segment type and request method
160
147
  let defaultShouldRevalidate: boolean;
161
148
  let defaultReason: string;
162
149
 
163
150
  if (defaultOverride) {
164
- // Caller injected the seed (e.g. parallel slot not in clientSegmentIds).
165
- // Skip the type-derived heuristic — caller knows better in this context.
166
151
  defaultShouldRevalidate = defaultOverride.value;
167
152
  defaultReason = defaultOverride.reason;
168
153
  } else if (request.method === "POST") {
169
- // Actions: revalidate segments that belong to the route, skip parent chain
170
154
  if (segment.type === "route") {
171
- // Route segment always revalidates on actions
172
155
  defaultShouldRevalidate = true;
173
156
  defaultReason = "action:route-segment";
174
157
  } else if (segment.type === "loader") {
175
- // Loaders always revalidate on actions - they often contain action-sensitive data
176
- // (e.g., cart count after add-to-cart action)
177
158
  defaultShouldRevalidate = true;
178
159
  defaultReason = "action:loader-segment";
179
160
  } else if (segment.belongsToRoute) {
180
- // Segment belongs to route (orphan layouts/parallels) - revalidate
181
161
  defaultShouldRevalidate = true;
182
162
  defaultReason = "action:belongs-to-route";
183
163
  } else {
184
- // Parent chain segment (shared layouts/parallels) - don't revalidate
185
164
  defaultShouldRevalidate = false;
186
165
  defaultReason = "action:parent-chain-skip";
187
166
  }
188
167
  } else {
189
- // Navigation (GET): Conservative defaults to minimize unnecessary revalidations
190
- // Only the route segment revalidates by default - all others require explicit opt-in
191
-
192
168
  if (segment.type === "route") {
193
- // Route segments revalidate when path params OR search params change.
194
- // Search params (e.g., ?page=2&sort=price) are server-parsed via ctx.search,
195
- // so the handler must re-execute to produce updated content.
196
169
  const routeChanged = paramsChanged || searchChanged;
197
170
  defaultShouldRevalidate = routeChanged;
198
171
  defaultReason = paramsChanged
@@ -208,8 +181,6 @@ export async function evaluateRevalidation<TEnv>(
208
181
  });
209
182
  }
210
183
  } else if (segment.belongsToRoute && (paramsChanged || searchChanged)) {
211
- // Children of the route path (loaders, orphan layouts/parallels)
212
- // revalidate when path params or search params change
213
184
  defaultShouldRevalidate = true;
214
185
  defaultReason = paramsChanged
215
186
  ? "nav:route-child-params-changed"
@@ -221,9 +192,6 @@ export async function evaluateRevalidation<TEnv>(
221
192
  searchChanged,
222
193
  });
223
194
  } else {
224
- // Parent layouts and parallels default to no revalidation
225
- // Cannot assume these segments depend on params without explicit declaration
226
- // Use custom revalidation functions to opt-in when needed
227
195
  defaultShouldRevalidate = false;
228
196
  defaultReason = "nav:non-route-skip";
229
197
  debugLog("revalidation", "non-route segment skipped by default", {
@@ -233,7 +201,6 @@ export async function evaluateRevalidation<TEnv>(
233
201
  }
234
202
  }
235
203
 
236
- // No custom revalidations defined - return default behavior without prev segment
237
204
  if (revalidations.length === 0) {
238
205
  if (defaultShouldRevalidate) {
239
206
  debugLog("revalidation", "default revalidate=true", {
@@ -250,14 +217,10 @@ export async function evaluateRevalidation<TEnv>(
250
217
  return defaultShouldRevalidate;
251
218
  }
252
219
 
253
- // Custom revalidations exist - may need full prev segment
254
- // Lazy load prev segment only if getPrevSegment provided
255
220
  const prevSegment = getPrevSegment ? await getPrevSegment() : null;
256
221
 
257
- // Execute revalidation functions with soft/hard decision pattern
258
222
  let currentSuggestion = defaultShouldRevalidate;
259
223
 
260
- // Compute public route names (filtered: undefined for auto-generated routes)
261
224
  const toRouteName =
262
225
  routeKey && !isAutoGeneratedRouteName(routeKey) ? routeKey : undefined;
263
226
  const reqCtx = _getRequestContext();
@@ -268,37 +231,73 @@ export async function evaluateRevalidation<TEnv>(
268
231
  : undefined;
269
232
 
270
233
  for (const { name, fn } of revalidations) {
271
- const result = fn({
272
- currentParams: prevSegment?.params || prevParams, // Use segment params if available, else route params
273
- currentUrl: prevUrl,
274
- nextParams,
275
- nextUrl,
276
- defaultShouldRevalidate: currentSuggestion,
277
- context,
278
- // Segment metadata (which segment is being evaluated)
279
- segmentType: segment.type,
280
- layoutName: segment.layoutName,
281
- slotName: segment.slot,
282
- // Action context (only populated when triggered by server action)
283
- actionId: actionContext?.actionId,
284
- isAction: makeIsAction(actionContext?.actionId),
285
- actionUrl: actionContext?.actionUrl,
286
- actionResult: actionContext?.actionResult,
287
- formData: actionContext?.formData,
288
- method: request.method, // GET for navigation, POST for actions
289
- routeName: toRouteName, // Navigation target route name (filtered)
290
- fromRouteName, // Navigation source route name (filtered)
291
- toRouteName, // Navigation target route name (filtered)
292
- // Stale cache context (only true for background revalidation after stale cache render)
293
- stale,
294
- });
234
+ let result: any;
235
+ try {
236
+ result = fn({
237
+ currentParams: prevSegment?.params || prevParams, // Use segment params if available, else route params
238
+ currentUrl: prevUrl,
239
+ nextParams,
240
+ nextUrl,
241
+ defaultShouldRevalidate: currentSuggestion,
242
+ context,
243
+ // Segment metadata (which segment is being evaluated)
244
+ segmentType: segment.type,
245
+ layoutName: segment.layoutName,
246
+ slotName: segment.slot,
247
+ // Action context (only populated when triggered by server action)
248
+ actionId: actionContext?.actionId,
249
+ isAction: makeIsAction(actionContext?.actionId),
250
+ actionUrl: actionContext?.actionUrl,
251
+ actionResult: actionContext?.actionResult,
252
+ formData: actionContext?.formData,
253
+ method: request.method,
254
+ routeName: toRouteName,
255
+ fromRouteName,
256
+ toRouteName,
257
+ stale,
258
+ });
259
+ } catch (error) {
260
+ // A thrown Response is control flow (e.g. `throw redirect(...)`), not a
261
+ // failure: re-throw it so the handler chokepoint (match-handlers.ts)
262
+ // turns it into the intended redirect/response. This mirrors how that
263
+ // catch special-cases `error instanceof Response`.
264
+ if (error instanceof Response) throw error;
265
+ // Fail open for genuine errors: a buggy user revalidate fn must not
266
+ // collapse the whole entry's loader batch into a failed partial render.
267
+ // Mirror the dynamic-tags fail-open in cache/cache-policy.ts: log and
268
+ // defer to the current default decision, leaving currentSuggestion
269
+ // unchanged. TODO: route through callOnError(phase "revalidation") once
270
+ // evaluateRevalidation is given the onError seam (today the error only
271
+ // reaches onError via the entry-collapse path in match-handlers.ts).
272
+ console.error(
273
+ `[revalidate] "${name}" threw for segment "${segment.id}"; using default decision:`,
274
+ error,
275
+ );
276
+ continue;
277
+ }
278
+
279
+ // The revalidate fn contract (handler-context.ts) is SYNCHRONOUS: it must
280
+ // return a boolean, a { defaultShouldRevalidate } object, or null/undefined.
281
+ // A Promise-returning (async) fn matches none of the decision branches below
282
+ // and silently falls through keeping the current default — a hard-to-find
283
+ // misuse. We do NOT await it (that would change the sync contract); instead
284
+ // we surface it as a dev-mode warning so the silent drop is diagnosable.
285
+ // Mirrors defer.ts: gated to dev, stripped from production builds.
286
+ if (
287
+ process.env.NODE_ENV !== "production" &&
288
+ result != null &&
289
+ typeof (result as { then?: unknown }).then === "function"
290
+ ) {
291
+ console.warn(
292
+ `[rango] revalidate fn "${name}" returned a Promise; revalidate ` +
293
+ `functions must be synchronous (return a boolean, ` +
294
+ `{ defaultShouldRevalidate }, or null/undefined). The async result ` +
295
+ `was IGNORED and the default (${currentSuggestion}) was kept. ` +
296
+ `Move async work into a loader instead.`,
297
+ );
298
+ }
295
299
 
296
- // Check return type:
297
- // - boolean: hard decision, short-circuit immediately
298
- // - { defaultShouldRevalidate: boolean }: soft decision, update suggestion and continue
299
- // - null/undefined: use default behavior (equivalent to returning { defaultShouldRevalidate })
300
300
  if (typeof result === "boolean") {
301
- // Hard decision - short-circuit
302
301
  debugLog("revalidation", "hard decision", {
303
302
  segmentId: segment.id,
304
303
  revalidator: name,
@@ -311,7 +310,6 @@ export async function evaluateRevalidation<TEnv>(
311
310
  typeof result === "object" &&
312
311
  "defaultShouldRevalidate" in result
313
312
  ) {
314
- // Soft decision - update suggestion and continue
315
313
  currentSuggestion = result.defaultShouldRevalidate;
316
314
  debugLog("revalidation", "soft decision", {
317
315
  segmentId: segment.id,
@@ -319,18 +317,14 @@ export async function evaluateRevalidation<TEnv>(
319
317
  revalidate: currentSuggestion,
320
318
  });
321
319
  } else if (result === null || result === undefined) {
322
- // Defer to default - equivalent to { defaultShouldRevalidate: currentSuggestion }
323
- // This means "I don't care, use whatever the default is"
324
320
  debugLog("revalidation", "deferred to current default", {
325
321
  segmentId: segment.id,
326
322
  revalidator: name,
327
323
  revalidate: currentSuggestion,
328
324
  });
329
- // currentSuggestion stays the same, continue to next function
330
325
  }
331
326
  }
332
327
 
333
- // All revalidators completed - use final suggestion
334
328
  debugLog("revalidation", "final decision", {
335
329
  segmentId: segment.id,
336
330
  revalidate: currentSuggestion,
@@ -48,6 +48,15 @@ export interface RouteSnapshot<TEnv = any> {
48
48
  isPassthrough: boolean;
49
49
  /** Response type for non-RSC routes (e.g. "application/json") */
50
50
  responseType?: string;
51
+ /**
52
+ * The isSSR flag the manifest was resolved with. Recorded so consumers can
53
+ * decide whether the snapshot is reusable: a snapshot resolved with isSSR
54
+ * differs (loading({ ssr: false }) entries) and is cached under a different
55
+ * manifest partition, so the full (document) path only reuses an isSSR:true
56
+ * snapshot and the partial path only reuses a non-isSSR one. Undefined on
57
+ * inline snapshots (redirect / not-found) that are never reused.
58
+ */
59
+ isSSR?: boolean;
51
60
  }
52
61
 
53
62
  export type ResolveRouteResult<TEnv = any> =
@@ -56,7 +65,9 @@ export type ResolveRouteResult<TEnv = any> =
56
65
  | null;
57
66
 
58
67
  export interface ResolveRouteDeps<TEnv = any> {
59
- findMatch: (pathname: string) => RouteMatchResult<TEnv> | null;
68
+ findMatch: (
69
+ pathname: string,
70
+ ) => RouteMatchResult<TEnv> | null | Promise<RouteMatchResult<TEnv> | null>;
60
71
  metricsStore?: MetricsStore;
61
72
  isSSR?: boolean;
62
73
  /**
@@ -105,7 +116,7 @@ export async function resolveRoute<TEnv = any>(
105
116
 
106
117
  const routeMatchStart =
107
118
  metricsStore && !skipRouteMatchMetric ? performance.now() : 0;
108
- const matched = deps.findMatch(pathname);
119
+ const matched = await deps.findMatch(pathname);
109
120
  if (metricsStore && !skipRouteMatchMetric) {
110
121
  metricsStore.metrics.push({
111
122
  label: "route-matching",
@@ -175,6 +186,7 @@ export async function resolveRoute<TEnv = any>(
175
186
  cacheScope,
176
187
  isPassthrough,
177
188
  responseType,
189
+ isSSR,
178
190
  },
179
191
  };
180
192
  }
@@ -230,7 +242,6 @@ export function createRouteSnapshot<TEnv = any>(
230
242
  entry: {} as any,
231
243
  routeKey: "test",
232
244
  params: {},
233
- optionalParams: new Set(),
234
245
  } as RouteMatchResult<TEnv>,
235
246
  manifestEntry: { type: "route", shortCode: "R0", parent: null } as any,
236
247
  entries: [],
@@ -19,6 +19,7 @@ import type {
19
19
  } from "../types.js";
20
20
  import type { RouteMatchResult } from "./pattern-matching.js";
21
21
  import type { TelemetrySink } from "./telemetry.js";
22
+ import type { ResolveSegmentOptions } from "./segment-resolution.js";
22
23
 
23
24
  /**
24
25
  * Revalidation context passed to segment resolution
@@ -54,10 +55,10 @@ export interface InterceptResult {
54
55
  * Instead of passing 20+ parameters, middleware calls getRouterContext() to access them.
55
56
  */
56
57
  export interface RouterContext<TEnv = any> {
57
- // Route matching
58
- findMatch: (pathname: string) => RouteMatchResult | null;
58
+ findMatch: (
59
+ pathname: string,
60
+ ) => RouteMatchResult | null | Promise<RouteMatchResult | null>;
59
61
 
60
- // Manifest loading
61
62
  loadManifest: (
62
63
  entry: any,
63
64
  routeKey: string,
@@ -66,10 +67,8 @@ export interface RouterContext<TEnv = any> {
66
67
  isSSR?: boolean,
67
68
  ) => Promise<EntryData>;
68
69
 
69
- // Entry traversal
70
70
  traverseBack: (entry: EntryData) => Generator<EntryData>;
71
71
 
72
- // Handler context creation
73
72
  createHandlerContext: (
74
73
  params: Record<string, string>,
75
74
  request: Request,
@@ -83,7 +82,6 @@ export interface RouterContext<TEnv = any> {
83
82
  isPassthroughRoute?: boolean,
84
83
  ) => HandlerContext<any, TEnv>;
85
84
 
86
- // Loader setup
87
85
  setupLoaderAccess: (
88
86
  ctx: HandlerContext<any, TEnv>,
89
87
  loaderPromises: Map<string, Promise<any>>,
@@ -94,7 +92,6 @@ export interface RouterContext<TEnv = any> {
94
92
  loaderPromises: Map<string, Promise<any>>,
95
93
  ) => void;
96
94
 
97
- // Context access
98
95
  getContext: () => {
99
96
  getOrCreateStore: (key: string) => any;
100
97
  runWithStore: <T>(
@@ -105,16 +102,13 @@ export interface RouterContext<TEnv = any> {
105
102
  ) => T;
106
103
  };
107
104
 
108
- // Metrics
109
105
  getMetricsStore: () => MetricsStore | undefined;
110
106
 
111
- // Cache
112
107
  createCacheScope: (
113
108
  cacheConfig: any,
114
109
  parent: CacheScope | null,
115
110
  ) => CacheScope | null;
116
111
 
117
- // Intercept detection
118
112
  findInterceptForRoute: (
119
113
  routeKey: string,
120
114
  parentEntry: EntryData | null,
@@ -122,7 +116,6 @@ export interface RouterContext<TEnv = any> {
122
116
  isAction: boolean,
123
117
  ) => InterceptResult | null;
124
118
 
125
- // Segment resolution (with revalidation)
126
119
  resolveAllSegmentsWithRevalidation: (
127
120
  entries: EntryData[],
128
121
  routeKey: string,
@@ -133,7 +126,6 @@ export interface RouterContext<TEnv = any> {
133
126
  request: Request,
134
127
  prevUrl: URL,
135
128
  nextUrl: URL,
136
- loaderPromises: Map<string, Promise<any>>,
137
129
  actionContext: any | undefined,
138
130
  interceptResult: InterceptResult | null,
139
131
  localRouteName: string,
@@ -164,14 +156,13 @@ export interface RouterContext<TEnv = any> {
164
156
  handlerContext: HandlerContext<any, TEnv>,
165
157
  belongsToRoute: boolean,
166
158
  revalidationContext?: RevalidationContext,
159
+ options?: { skipMiddleware?: boolean },
167
160
  ) => Promise<ResolvedSegment[]>;
168
161
 
169
- // Collect with markers
170
162
  collectWithMarkers?: <T>(
171
163
  gen: AsyncGenerator<T | { __type: "id"; id: string }>,
172
164
  ) => Promise<{ items: T[]; matchedIds: string[] }>;
173
165
 
174
- // Revalidation evaluation
175
166
  evaluateRevalidation: (params: {
176
167
  segment: ResolvedSegment;
177
168
  prevParams: Record<string, string>;
@@ -195,7 +186,6 @@ export interface RouterContext<TEnv = any> {
195
186
  | "intercept-loader";
196
187
  }) => Promise<boolean>;
197
188
 
198
- // Request context
199
189
  getRequestContext: () =>
200
190
  | {
201
191
  waitUntil: (fn: () => Promise<void>) => void;
@@ -203,17 +193,15 @@ export interface RouterContext<TEnv = any> {
203
193
  }
204
194
  | undefined;
205
195
 
206
- // Simple segment resolution (without revalidation - for full match)
207
196
  resolveAllSegments: (
208
197
  entries: EntryData[],
209
198
  routeKey: string,
210
199
  params: Record<string, string>,
211
200
  handlerContext: HandlerContext<any, TEnv>,
212
201
  loaderPromises: Map<string, Promise<any>>,
213
- options?: { skipLoaders?: boolean },
202
+ options?: ResolveSegmentOptions,
214
203
  ) => Promise<ResolvedSegment[]>;
215
204
 
216
- // Generator-based simple resolution
217
205
  resolveAllSegmentsGenerator?: (
218
206
  entries: EntryData[],
219
207
  routeKey: string,
@@ -222,21 +210,17 @@ export interface RouterContext<TEnv = any> {
222
210
  loaderPromises: Map<string, Promise<any>>,
223
211
  ) => AsyncGenerator<ResolvedSegment | { __type: "id"; id: string }>;
224
212
 
225
- // Collect segments from generator
226
213
  collectSegmentsFromGenerator?: <T>(
227
214
  gen: AsyncGenerator<T | { __type: "id"; id: string }>,
228
215
  ) => Promise<T[]>;
229
216
 
230
- // Handle store
231
217
  createHandleStore: () => any;
232
218
 
233
- // Loaders-only resolution (for full match cache hit - no revalidation)
234
219
  resolveLoadersOnly?: (
235
220
  entries: EntryData[],
236
221
  handlerContext: HandlerContext<any, TEnv>,
237
222
  ) => Promise<ResolvedSegment[]>;
238
223
 
239
- // Loaders-only resolution (for cache hit scenarios)
240
224
  resolveLoadersOnlyWithRevalidation?: (
241
225
  entries: EntryData[],
242
226
  handlerContext: HandlerContext<any, TEnv>,
@@ -258,10 +242,8 @@ export interface RouterContext<TEnv = any> {
258
242
  // Telemetry sink (optional, no-op when undefined)
259
243
  telemetry?: TelemetrySink;
260
244
 
261
- // Request ID for telemetry span correlation (set per-request in match handlers)
262
245
  requestId?: string;
263
246
 
264
- // Intercept loaders only (for cache hit + intercept scenarios)
265
247
  resolveInterceptLoadersOnly?: (
266
248
  intercept: InterceptEntry,
267
249
  entry: EntryData,
@@ -284,7 +266,6 @@ export interface RouterContext<TEnv = any> {
284
266
  } | null>;
285
267
  }
286
268
 
287
- // AsyncLocalStorage instance for router context
288
269
  const routerContext = new AsyncLocalStorage<RouterContext<any>>();
289
270
 
290
271
  /**
@@ -308,10 +289,6 @@ export function getRouterContext<TEnv = any>(): RouterContext<TEnv> {
308
289
  *
309
290
  * All async code within fn() can call getRouterContext() to access router closures.
310
291
  * This works across async boundaries thanks to AsyncLocalStorage.
311
- *
312
- * @param deps Router dependencies to make available
313
- * @param fn Function to run with dependencies available
314
- * @returns Result of fn()
315
292
  */
316
293
  export function runWithRouterContext<T, TEnv = any>(
317
294
  deps: RouterContext<TEnv>,
@@ -7,15 +7,16 @@ import type { EntryData } from "../server/context";
7
7
  import type { ErrorInfo, MatchResult } from "../types";
8
8
  import type { NonceProvider } from "../rsc/types.js";
9
9
  import type { ExecutionContext } from "../server/request-context.js";
10
- import type {
11
- SerializedSegmentData,
12
- SegmentHandleData,
13
- } from "../cache/types.js";
10
+ import type { SerializedSegmentData } from "../cache/types.js";
14
11
  import type { MiddlewareEntry, MiddlewareFn } from "./middleware.js";
12
+ import type { RouteMatchResult } from "./pattern-matching.js";
13
+ import type { ExtractParams } from "../types/route-config.js";
15
14
  import { RSC_ROUTER_BRAND } from "./router-registry.js";
16
15
  import type { RangoOptions, RootLayoutProps } from "./router-options.js";
17
16
  import type { DefaultVars } from "../types/global-namespace.js";
18
17
  import type { ResolvedTimeouts, OnTimeoutCallback } from "./timeout.js";
18
+ import type { ResolvedTracing } from "./tracing.js";
19
+ import type { TelemetrySink } from "./telemetry.js";
19
20
 
20
21
  /**
21
22
  * Options passed to router.fetch(), router.match(), and other request entrypoints.
@@ -108,9 +109,14 @@ export interface Rango<
108
109
  * createRouter({ document: RootLayout })
109
110
  * .use(loggerMiddleware) // All routes
110
111
  * .use("/api/*", rateLimiter) // Pattern match
112
+ * .use("/users/:id", (ctx) => {}) // ctx.params.id is typed
111
113
  * .routes(urlpatterns)
112
114
  * ```
113
115
  */
116
+ use<Pattern extends string>(
117
+ pattern: Pattern,
118
+ middleware: MiddlewareFn<TEnv, ExtractParams<Pattern>>,
119
+ ): Rango<TEnv, TRoutes>;
114
120
  use(
115
121
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
116
122
  middleware?: MiddlewareFn<TEnv>,
@@ -228,6 +234,10 @@ export interface RangoInternal<
228
234
  /**
229
235
  * Add global middleware that runs on all routes
230
236
  */
237
+ use<Pattern extends string>(
238
+ pattern: Pattern,
239
+ middleware: MiddlewareFn<TEnv, ExtractParams<Pattern>>,
240
+ ): Rango<TEnv, TRoutes>;
231
241
  use(
232
242
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
233
243
  middleware?: MiddlewareFn<TEnv>,
@@ -293,6 +303,27 @@ export interface RangoInternal<
293
303
  */
294
304
  readonly prefetchCacheTTL: number;
295
305
 
306
+ /**
307
+ * Maximum number of decoded prefetch payloads the client keeps in its
308
+ * in-memory prefetch cache (FIFO eviction at capacity). Shipped to the
309
+ * client in payload metadata. Derived from prefetchCacheSize.
310
+ */
311
+ readonly prefetchCacheSize: number;
312
+
313
+ /**
314
+ * Maximum number of speculative prefetch requests the client runs
315
+ * concurrently. Shipped to the client in payload metadata. Derived from
316
+ * prefetchConcurrency.
317
+ */
318
+ readonly prefetchConcurrency: number;
319
+
320
+ /**
321
+ * Resolved rango state cookie name (`{prefix}_{routerId}`), composed once at
322
+ * router init and shipped to the client in payload metadata. The server-side
323
+ * cookie writer reads it from here; the client reads it from metadata.
324
+ */
325
+ readonly resolvedStateCookieName: string;
326
+
296
327
  /**
297
328
  * Whether connection warmup is enabled.
298
329
  * When true, the client sends HEAD /?_rsc_warmup after idle periods
@@ -300,12 +331,34 @@ export interface RangoInternal<
300
331
  */
301
332
  readonly warmupEnabled: boolean;
302
333
 
334
+ /**
335
+ * Whether the client hydrates inside React.StrictMode. Resolved from
336
+ * createRouter({ strictMode }) (default true) and shipped to the client in
337
+ * the initial payload metadata.
338
+ */
339
+ readonly strictMode: boolean;
340
+
303
341
  /**
304
342
  * Whether router-wide performance debugging is enabled.
305
343
  * Used by the request handler to create metrics before middleware runs.
306
344
  */
307
345
  readonly debugPerformance?: boolean;
308
346
 
347
+ /**
348
+ * Resolved platform phase-span tracing (Cloudflare custom spans or OTel), or
349
+ * undefined when off. Threaded onto the request context and read at each
350
+ * traced phase.
351
+ */
352
+ readonly tracing?: ResolvedTracing;
353
+
354
+ /**
355
+ * Raw telemetry sink from RangoOptions, exposed so handler-level emitters
356
+ * (rsc/handler.ts timeout/origin/late-handle) can emit WITHOUT the
357
+ * RouterContext ALS, which only match()/matchPartial() enter. See
358
+ * observeEvent's emitter list in router/instrument.ts.
359
+ */
360
+ readonly telemetry?: TelemetrySink;
361
+
309
362
  /**
310
363
  * Whether ?__debug_manifest is allowed in production.
311
364
  * Always enabled in development.
@@ -395,11 +448,13 @@ export interface RangoInternal<
395
448
  devMode?: boolean,
396
449
  ): Promise<{
397
450
  segments: SerializedSegmentData[];
398
- handles: Record<string, SegmentHandleData>;
451
+ /** RSC-encoded handle map ("" when none) — see handle-snapshot.ts. */
452
+ handles: string;
399
453
  routeName: string;
400
454
  params: Record<string, string>;
401
455
  interceptSegments?: SerializedSegmentData[];
402
- interceptHandles?: Record<string, SegmentHandleData>;
456
+ /** RSC-encoded MERGED (main + intercept) handle map for the intercept artifact. */
457
+ interceptHandles?: string;
403
458
  passthrough?: true;
404
459
  } | null>;
405
460
 
@@ -413,7 +468,7 @@ export interface RangoInternal<
413
468
  routeName?: string,
414
469
  buildEnv?: any,
415
470
  devMode?: boolean,
416
- ): Promise<{ encoded: string; handles: Record<string, unknown[]> } | null>;
471
+ ): Promise<{ encoded: string; handles: string } | null>;
417
472
 
418
473
  /**
419
474
  * Preview match - returns route middleware without segment resolution.
@@ -471,7 +526,14 @@ export interface RangoInternal<
471
526
  * Used by classifyRequest() for request classification without
472
527
  * entering the full match pipeline.
473
528
  */
474
- findMatch(pathname: string, metricsStore?: any): any;
529
+ // Async since a lazy async include (`() => import()`) must resolve before its
530
+ // routes can match. Typed (not `any`) so a consumer doing
531
+ // `const m = router.findMatch(p); if (!m) ...; m.entry` gets a compile error
532
+ // (m is a Promise) instead of the silent always-truthy bug.
533
+ findMatch(
534
+ pathname: string,
535
+ metricsStore?: any,
536
+ ): Promise<RouteMatchResult<TEnv> | null>;
475
537
 
476
538
  /**
477
539
  * Debug utility to serialize the manifest for inspection