@rangojs/router 0.0.0-experimental.b9cb8739 → 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 (449) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +303 -741
  3. package/dist/bin/rango.js +730 -184
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +4344 -1335
  6. package/dist/vite/index.js.bak +5448 -0
  7. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  8. package/package.json +86 -15
  9. package/skills/api-client/SKILL.md +211 -0
  10. package/skills/breadcrumbs/SKILL.md +85 -6
  11. package/skills/bundle-analysis/SKILL.md +159 -0
  12. package/skills/cache-guide/SKILL.md +251 -24
  13. package/skills/caching/SKILL.md +375 -17
  14. package/skills/catalog.json +271 -0
  15. package/skills/comparison/SKILL.md +50 -0
  16. package/skills/comparison/agents/openai.yaml +4 -0
  17. package/skills/comparison/references/framework-comparison.md +837 -0
  18. package/skills/composability/SKILL.md +110 -4
  19. package/skills/css/SKILL.md +76 -0
  20. package/skills/debug-manifest/SKILL.md +5 -3
  21. package/skills/defer-hydration/SKILL.md +235 -0
  22. package/skills/document-cache/SKILL.md +87 -56
  23. package/skills/fonts/SKILL.md +1 -1
  24. package/skills/handler-use/SKILL.md +364 -0
  25. package/skills/hooks/SKILL.md +73 -691
  26. package/skills/hooks/data.md +273 -0
  27. package/skills/hooks/handle-and-actions.md +103 -0
  28. package/skills/hooks/navigation.md +110 -0
  29. package/skills/hooks/outlets.md +41 -0
  30. package/skills/hooks/state.md +228 -0
  31. package/skills/hooks/urls.md +135 -0
  32. package/skills/host-router/SKILL.md +129 -27
  33. package/skills/i18n/SKILL.md +276 -0
  34. package/skills/intercept/SKILL.md +94 -18
  35. package/skills/layout/SKILL.md +62 -19
  36. package/skills/links/SKILL.md +249 -17
  37. package/skills/loader/SKILL.md +302 -54
  38. package/skills/middleware/SKILL.md +59 -16
  39. package/skills/migrate-nextjs/SKILL.md +745 -0
  40. package/skills/migrate-react-router/SKILL.md +153 -0
  41. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  42. package/skills/migrate-react-router/component-migration.md +196 -0
  43. package/skills/migrate-react-router/data-and-actions.md +225 -0
  44. package/skills/migrate-react-router/route-mapping.md +271 -0
  45. package/skills/mime-routes/SKILL.md +29 -2
  46. package/skills/observability/SKILL.md +202 -0
  47. package/skills/parallel/SKILL.md +225 -10
  48. package/skills/ppr/SKILL.md +622 -0
  49. package/skills/prerender/SKILL.md +178 -124
  50. package/skills/rango/SKILL.md +318 -24
  51. package/skills/react-compiler/SKILL.md +168 -0
  52. package/skills/response-routes/SKILL.md +138 -49
  53. package/skills/route/SKILL.md +172 -9
  54. package/skills/router-setup/SKILL.md +131 -11
  55. package/skills/scripts/SKILL.md +179 -0
  56. package/skills/server-actions/SKILL.md +776 -0
  57. package/skills/shell-manifest/SKILL.md +185 -0
  58. package/skills/streams-and-websockets/SKILL.md +283 -0
  59. package/skills/tailwind/SKILL.md +28 -4
  60. package/skills/testing/SKILL.md +130 -0
  61. package/skills/testing/bindings.md +103 -0
  62. package/skills/testing/cache-prerender.md +127 -0
  63. package/skills/testing/client-components.md +124 -0
  64. package/skills/testing/e2e-parity.md +125 -0
  65. package/skills/testing/flight.md +91 -0
  66. package/skills/testing/handles.md +131 -0
  67. package/skills/testing/loader.md +128 -0
  68. package/skills/testing/middleware.md +99 -0
  69. package/skills/testing/render-handler.md +122 -0
  70. package/skills/testing/response-routes.md +95 -0
  71. package/skills/testing/reverse-and-types.md +85 -0
  72. package/skills/testing/server-actions.md +107 -0
  73. package/skills/testing/server-tree.md +128 -0
  74. package/skills/testing/setup.md +123 -0
  75. package/skills/theme/SKILL.md +1 -1
  76. package/skills/typesafety/SKILL.md +45 -616
  77. package/skills/typesafety/env-and-bindings.md +254 -0
  78. package/skills/typesafety/generated-files-and-cli.md +335 -0
  79. package/skills/typesafety/params-and-search.md +153 -0
  80. package/skills/typesafety/route-types.md +209 -0
  81. package/skills/use-cache/SKILL.md +74 -15
  82. package/skills/vercel/SKILL.md +128 -0
  83. package/skills/view-transitions/SKILL.md +337 -0
  84. package/src/__augment-tests__/augment.ts +81 -0
  85. package/src/__augment-tests__/augmented.check.ts +116 -0
  86. package/src/__internal.ts +1 -66
  87. package/src/browser/action-coordinator.ts +53 -36
  88. package/src/browser/action-fence.ts +47 -0
  89. package/src/browser/app-shell.ts +39 -0
  90. package/src/browser/app-version.ts +14 -0
  91. package/src/browser/connection-warmup.ts +134 -0
  92. package/src/browser/cookie-name.ts +140 -0
  93. package/src/browser/event-controller.ts +257 -158
  94. package/src/browser/history-state.ts +21 -0
  95. package/src/browser/index.ts +3 -3
  96. package/src/browser/invalidate-client-cache.ts +52 -0
  97. package/src/browser/logging.ts +28 -0
  98. package/src/browser/merge-segment-loaders.ts +6 -4
  99. package/src/browser/navigation-bridge.ts +132 -33
  100. package/src/browser/navigation-client.ts +218 -68
  101. package/src/browser/navigation-store-handle.ts +38 -0
  102. package/src/browser/navigation-store.ts +203 -80
  103. package/src/browser/navigation-transaction.ts +18 -66
  104. package/src/browser/network-error-handler.ts +34 -7
  105. package/src/browser/partial-update.ts +241 -127
  106. package/src/browser/prefetch/cache.ts +271 -44
  107. package/src/browser/prefetch/fetch.ts +367 -40
  108. package/src/browser/prefetch/queue.ts +144 -23
  109. package/src/browser/prefetch/resource-ready.ts +77 -0
  110. package/src/browser/rango-state.ts +158 -76
  111. package/src/browser/react/Link.tsx +121 -16
  112. package/src/browser/react/NavigationProvider.tsx +240 -122
  113. package/src/browser/react/ScrollRestoration.tsx +10 -6
  114. package/src/browser/react/context.ts +7 -2
  115. package/src/browser/react/filter-segment-order.ts +66 -7
  116. package/src/browser/react/index.ts +0 -48
  117. package/src/browser/react/location-state-shared.ts +178 -8
  118. package/src/browser/react/location-state.ts +39 -14
  119. package/src/browser/react/use-action.ts +6 -15
  120. package/src/browser/react/use-handle.ts +23 -69
  121. package/src/browser/react/use-href.tsx +8 -1
  122. package/src/browser/react/use-link-status.ts +33 -8
  123. package/src/browser/react/use-navigation.ts +32 -7
  124. package/src/browser/react/use-params.ts +20 -10
  125. package/src/browser/react/use-reverse.ts +106 -0
  126. package/src/browser/react/use-router.ts +46 -11
  127. package/src/browser/react/use-search-params.ts +0 -5
  128. package/src/browser/react/use-segments.ts +11 -21
  129. package/src/browser/response-adapter.ts +99 -8
  130. package/src/browser/rsc-router.tsx +272 -80
  131. package/src/browser/scroll-restoration.ts +56 -22
  132. package/src/browser/segment-reconciler.ts +44 -7
  133. package/src/browser/segment-structure-assert.ts +2 -2
  134. package/src/browser/server-action-bridge.ts +244 -71
  135. package/src/browser/types.ts +136 -12
  136. package/src/browser/validate-redirect-origin.ts +43 -16
  137. package/src/build/collect-fallback-refs.ts +107 -0
  138. package/src/build/generate-manifest.ts +207 -158
  139. package/src/build/generate-route-types.ts +6 -1
  140. package/src/build/index.ts +11 -3
  141. package/src/build/prefix-tree-utils.ts +123 -0
  142. package/src/build/route-trie.ts +198 -41
  143. package/src/build/route-types/ast-route-extraction.ts +15 -8
  144. package/src/build/route-types/codegen.ts +16 -5
  145. package/src/build/route-types/include-resolution.ts +464 -63
  146. package/src/build/route-types/param-extraction.ts +6 -3
  147. package/src/build/route-types/per-module-writer.ts +22 -6
  148. package/src/build/route-types/router-processing.ts +336 -110
  149. package/src/build/route-types/scan-filter.ts +9 -2
  150. package/src/build/route-types/source-scan.ts +216 -0
  151. package/src/build/runtime-discovery.ts +13 -21
  152. package/src/cache/cache-error.ts +104 -0
  153. package/src/cache/cache-key-utils.ts +58 -13
  154. package/src/cache/cache-policy.ts +108 -34
  155. package/src/cache/cache-runtime.ts +454 -97
  156. package/src/cache/cache-scope.ts +235 -103
  157. package/src/cache/cache-tag.ts +149 -0
  158. package/src/cache/cf/cf-base64.ts +33 -0
  159. package/src/cache/cf/cf-cache-constants.ts +127 -0
  160. package/src/cache/cf/cf-cache-store.ts +2446 -170
  161. package/src/cache/cf/cf-cache-types.ts +349 -0
  162. package/src/cache/cf/cf-kv-utils.ts +46 -0
  163. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  164. package/src/cache/cf/index.ts +11 -17
  165. package/src/cache/document-cache.ts +144 -49
  166. package/src/cache/handle-snapshot.ts +70 -0
  167. package/src/cache/index.ts +24 -20
  168. package/src/cache/memory-segment-store.ts +243 -37
  169. package/src/cache/profile-registry.ts +46 -31
  170. package/src/cache/read-through-swr.ts +56 -12
  171. package/src/cache/segment-codec.ts +13 -21
  172. package/src/cache/shell-snapshot.ts +417 -0
  173. package/src/cache/tag-invalidation.ts +230 -0
  174. package/src/cache/taint.ts +55 -0
  175. package/src/cache/types.ts +194 -99
  176. package/src/cache/vercel/index.ts +11 -0
  177. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  178. package/src/client.rsc.tsx +41 -21
  179. package/src/client.tsx +116 -290
  180. package/src/cloudflare/index.ts +11 -0
  181. package/src/cloudflare/tracing.ts +108 -0
  182. package/src/component-utils.ts +19 -0
  183. package/src/components/DefaultDocument.tsx +8 -2
  184. package/src/context-var.ts +84 -2
  185. package/src/debug.ts +2 -2
  186. package/src/decode-loader-results.ts +52 -0
  187. package/src/defer.ts +185 -0
  188. package/src/deps/ssr.ts +0 -1
  189. package/src/encode-kv.ts +49 -0
  190. package/src/errors.ts +30 -4
  191. package/src/escape-script.ts +52 -0
  192. package/src/handle.ts +104 -34
  193. package/src/handles/MetaTags.tsx +24 -53
  194. package/src/handles/Scripts.tsx +183 -0
  195. package/src/handles/breadcrumbs.ts +35 -8
  196. package/src/handles/deferred-resolution.ts +127 -0
  197. package/src/handles/is-thenable.ts +18 -0
  198. package/src/handles/meta.ts +14 -40
  199. package/src/handles/script.ts +244 -0
  200. package/src/host/cookie-handler.ts +9 -60
  201. package/src/host/errors.ts +13 -22
  202. package/src/host/index.ts +9 -2
  203. package/src/host/pattern-matcher.ts +23 -52
  204. package/src/host/router.ts +107 -99
  205. package/src/host/testing.ts +40 -27
  206. package/src/host/types.ts +37 -4
  207. package/src/host/utils.ts +1 -1
  208. package/src/href-client.ts +137 -22
  209. package/src/index.rsc.ts +100 -13
  210. package/src/index.ts +143 -19
  211. package/src/internal-debug.ts +11 -10
  212. package/src/loader-store.ts +500 -0
  213. package/src/loader.rsc.ts +20 -13
  214. package/src/loader.ts +12 -11
  215. package/src/missing-id-error.ts +68 -0
  216. package/src/outlet-context.ts +1 -1
  217. package/src/outlet-provider.tsx +1 -5
  218. package/src/prerender/param-hash.ts +16 -16
  219. package/src/prerender/store.ts +37 -41
  220. package/src/prerender.ts +215 -86
  221. package/src/redirect-origin.ts +114 -0
  222. package/src/regex-escape.ts +8 -0
  223. package/src/render-error-thrower.tsx +20 -0
  224. package/src/response-utils.ts +62 -0
  225. package/src/reverse.ts +65 -15
  226. package/src/root-error-boundary.tsx +1 -19
  227. package/src/route-content-wrapper.tsx +19 -77
  228. package/src/route-definition/dsl-helpers.ts +485 -303
  229. package/src/route-definition/helper-factories.ts +28 -140
  230. package/src/route-definition/helpers-types.ts +153 -77
  231. package/src/route-definition/index.ts +4 -2
  232. package/src/route-definition/redirect.ts +53 -12
  233. package/src/route-definition/resolve-handler-use.ts +160 -0
  234. package/src/route-definition/use-item-types.ts +29 -0
  235. package/src/route-map-builder.ts +48 -21
  236. package/src/route-types.ts +37 -46
  237. package/src/router/basename.ts +14 -0
  238. package/src/router/content-negotiation.ts +164 -17
  239. package/src/router/error-handling.ts +45 -18
  240. package/src/router/find-match.ts +130 -29
  241. package/src/router/handler-context.ts +83 -39
  242. package/src/router/instrument.ts +355 -0
  243. package/src/router/intercept-resolution.ts +50 -24
  244. package/src/router/lazy-includes.ts +89 -63
  245. package/src/router/loader-resolution.ts +286 -56
  246. package/src/router/logging.ts +5 -8
  247. package/src/router/manifest.ts +105 -56
  248. package/src/router/match-api.ts +178 -218
  249. package/src/router/match-context.ts +0 -22
  250. package/src/router/match-handlers.ts +211 -165
  251. package/src/router/match-middleware/background-revalidation.ts +66 -22
  252. package/src/router/match-middleware/cache-lookup.ts +214 -263
  253. package/src/router/match-middleware/cache-store.ts +105 -50
  254. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  255. package/src/router/match-middleware/segment-resolution.ts +52 -18
  256. package/src/router/match-pipelines.ts +1 -42
  257. package/src/router/match-result.ts +128 -44
  258. package/src/router/metrics.ts +5 -34
  259. package/src/router/middleware-types.ts +13 -142
  260. package/src/router/middleware.ts +301 -177
  261. package/src/router/navigation-snapshot.ts +133 -0
  262. package/src/router/params-util.ts +23 -0
  263. package/src/router/parse-pattern.ts +115 -0
  264. package/src/router/pattern-matching.ts +181 -150
  265. package/src/router/prefetch-cache-ttl.ts +51 -0
  266. package/src/router/prefetch-limits.ts +37 -0
  267. package/src/router/prerender-match.ts +203 -58
  268. package/src/router/preview-match.ts +35 -103
  269. package/src/router/request-classification.ts +291 -0
  270. package/src/router/revalidation.ts +123 -73
  271. package/src/router/route-snapshot.ts +256 -0
  272. package/src/router/router-context.ts +11 -29
  273. package/src/router/router-interfaces.ts +146 -35
  274. package/src/router/router-options.ts +202 -15
  275. package/src/router/router-registry.ts +2 -5
  276. package/src/router/segment-resolution/fresh.ts +301 -78
  277. package/src/router/segment-resolution/helpers.ts +115 -30
  278. package/src/router/segment-resolution/loader-cache.ts +156 -39
  279. package/src/router/segment-resolution/loader-mask.ts +60 -0
  280. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  281. package/src/router/segment-resolution/mask-nested.ts +83 -0
  282. package/src/router/segment-resolution/revalidation.ts +477 -385
  283. package/src/router/segment-resolution/static-store.ts +19 -5
  284. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  285. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  286. package/src/router/segment-resolution.ts +5 -1
  287. package/src/router/segment-wrappers.ts +8 -5
  288. package/src/router/state-cookie-name.ts +33 -0
  289. package/src/router/substitute-pattern-params.ts +75 -0
  290. package/src/router/telemetry-otel.ts +160 -200
  291. package/src/router/telemetry.ts +105 -20
  292. package/src/router/timeout.ts +0 -20
  293. package/src/router/tracing.ts +215 -0
  294. package/src/router/trie-matching.ts +171 -59
  295. package/src/router/types.ts +10 -63
  296. package/src/router/url-params.ts +57 -0
  297. package/src/router.ts +210 -71
  298. package/src/rsc/full-payload.ts +70 -0
  299. package/src/rsc/handler-context.ts +3 -2
  300. package/src/rsc/handler.ts +682 -508
  301. package/src/rsc/helpers.ts +168 -46
  302. package/src/rsc/index.ts +2 -5
  303. package/src/rsc/json-route-result.ts +38 -0
  304. package/src/rsc/loader-fetch.ts +127 -31
  305. package/src/rsc/manifest-init.ts +33 -42
  306. package/src/rsc/nonce.ts +10 -1
  307. package/src/rsc/origin-guard.ts +39 -25
  308. package/src/rsc/progressive-enhancement.ts +138 -15
  309. package/src/rsc/redirect-guard.ts +100 -0
  310. package/src/rsc/response-cache-serve.ts +238 -0
  311. package/src/rsc/response-error.ts +79 -12
  312. package/src/rsc/response-route-handler.ts +99 -189
  313. package/src/rsc/rsc-rendering.ts +509 -73
  314. package/src/rsc/runtime-warnings.ts +23 -10
  315. package/src/rsc/server-action.ts +287 -113
  316. package/src/rsc/shell-capture.ts +1190 -0
  317. package/src/rsc/shell-serve.ts +181 -0
  318. package/src/rsc/ssr-setup.ts +18 -2
  319. package/src/rsc/transition-gate.ts +89 -0
  320. package/src/rsc/types.ts +62 -6
  321. package/src/runtime-env.ts +18 -0
  322. package/src/search-params.ts +35 -30
  323. package/src/segment-content-promise.ts +67 -0
  324. package/src/segment-loader-promise.ts +167 -0
  325. package/src/segment-system.tsx +449 -132
  326. package/src/serialize.ts +243 -0
  327. package/src/server/context.ts +367 -61
  328. package/src/server/cookie-parse.ts +32 -0
  329. package/src/server/cookie-store.ts +152 -5
  330. package/src/server/handle-store.ts +40 -38
  331. package/src/server/loader-registry.ts +38 -46
  332. package/src/server/request-context.ts +558 -173
  333. package/src/ssr/index.tsx +491 -174
  334. package/src/ssr/inject-rsc-eager.ts +167 -0
  335. package/src/ssr/ssr-root.tsx +228 -0
  336. package/src/static-handler.ts +27 -18
  337. package/src/testing/cache-status.ts +162 -0
  338. package/src/testing/collect-handle.ts +46 -0
  339. package/src/testing/dispatch.ts +813 -0
  340. package/src/testing/dom.entry.ts +22 -0
  341. package/src/testing/e2e/fixture.ts +188 -0
  342. package/src/testing/e2e/index.ts +128 -0
  343. package/src/testing/e2e/matchers.ts +35 -0
  344. package/src/testing/e2e/page-helpers.ts +272 -0
  345. package/src/testing/e2e/parity.ts +387 -0
  346. package/src/testing/e2e/server.ts +195 -0
  347. package/src/testing/flight-matchers.ts +97 -0
  348. package/src/testing/flight-normalize.ts +11 -0
  349. package/src/testing/flight-runtime.d.ts +57 -0
  350. package/src/testing/flight-tree.ts +682 -0
  351. package/src/testing/flight.entry.ts +52 -0
  352. package/src/testing/flight.ts +257 -0
  353. package/src/testing/generated-routes.ts +199 -0
  354. package/src/testing/index.ts +105 -0
  355. package/src/testing/internal/context.ts +371 -0
  356. package/src/testing/internal/flight-client-globals.ts +30 -0
  357. package/src/testing/internal/seed-vars.ts +54 -0
  358. package/src/testing/render-handler.ts +357 -0
  359. package/src/testing/render-route.tsx +584 -0
  360. package/src/testing/run-loader.ts +385 -0
  361. package/src/testing/run-middleware.ts +205 -0
  362. package/src/testing/run-transition-when.ts +164 -0
  363. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  364. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  365. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  366. package/src/testing/vitest-stubs/version.ts +5 -0
  367. package/src/testing/vitest.ts +305 -0
  368. package/src/theme/ThemeProvider.tsx +56 -84
  369. package/src/theme/ThemeScript.tsx +7 -9
  370. package/src/theme/constants.ts +52 -13
  371. package/src/theme/index.ts +0 -7
  372. package/src/theme/theme-context.ts +1 -5
  373. package/src/theme/theme-script.ts +22 -21
  374. package/src/theme/use-theme.ts +0 -3
  375. package/src/types/boundaries.ts +0 -35
  376. package/src/types/cache-types.ts +17 -8
  377. package/src/types/error-types.ts +30 -90
  378. package/src/types/global-namespace.ts +54 -41
  379. package/src/types/handler-context.ts +234 -82
  380. package/src/types/index.ts +3 -10
  381. package/src/types/loader-types.ts +44 -15
  382. package/src/types/request-scope.ts +112 -0
  383. package/src/types/route-config.ts +20 -52
  384. package/src/types/route-entry.ts +19 -7
  385. package/src/types/segments.ts +137 -14
  386. package/src/urls/include-helper.ts +40 -75
  387. package/src/urls/include-provider.ts +71 -0
  388. package/src/urls/index.ts +2 -11
  389. package/src/urls/path-helper-types.ts +102 -23
  390. package/src/urls/path-helper.ts +62 -111
  391. package/src/urls/pattern-types.ts +84 -19
  392. package/src/urls/response-types.ts +25 -22
  393. package/src/urls/type-extraction.ts +98 -154
  394. package/src/urls/urls-function.ts +1 -19
  395. package/src/use-loader.tsx +346 -89
  396. package/src/vercel/index.ts +11 -0
  397. package/src/vercel/tracing.ts +88 -0
  398. package/src/vite/debug.ts +185 -0
  399. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  400. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  401. package/src/vite/discovery/discover-routers.ts +130 -85
  402. package/src/vite/discovery/discovery-errors.ts +255 -0
  403. package/src/vite/discovery/gate-state.ts +171 -0
  404. package/src/vite/discovery/prerender-collection.ts +214 -132
  405. package/src/vite/discovery/route-types-writer.ts +40 -84
  406. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  407. package/src/vite/discovery/state.ts +57 -6
  408. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  409. package/src/vite/index.ts +15 -0
  410. package/src/vite/inject-client-debug.ts +88 -0
  411. package/src/vite/plugin-types.ts +234 -62
  412. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  413. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  414. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  415. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  416. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  417. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  418. package/src/vite/plugins/expose-action-id.ts +49 -98
  419. package/src/vite/plugins/expose-id-utils.ts +96 -51
  420. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  421. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  422. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  423. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  424. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  425. package/src/vite/plugins/performance-tracks.ts +89 -0
  426. package/src/vite/plugins/refresh-cmd.ts +89 -27
  427. package/src/vite/plugins/use-cache-transform.ts +73 -83
  428. package/src/vite/plugins/vercel-output.ts +384 -0
  429. package/src/vite/plugins/version-injector.ts +40 -29
  430. package/src/vite/plugins/version-plugin.ts +46 -37
  431. package/src/vite/plugins/virtual-entries.ts +138 -27
  432. package/src/vite/rango.ts +353 -303
  433. package/src/vite/router-discovery.ts +1090 -166
  434. package/src/vite/utils/ast-handler-extract.ts +26 -35
  435. package/src/vite/utils/banner.ts +4 -4
  436. package/src/vite/utils/bundle-analysis.ts +10 -15
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +4 -59
  441. package/src/vite/utils/package-resolution.ts +20 -52
  442. package/src/vite/utils/prerender-utils.ts +98 -38
  443. package/src/vite/utils/shared-utils.ts +144 -44
  444. package/src/browser/action-response-classifier.ts +0 -99
  445. package/src/browser/react/use-client-cache.ts +0 -58
  446. package/src/browser/shallow.ts +0 -40
  447. package/src/handles/index.ts +0 -7
  448. package/src/network-error-thrower.tsx +0 -23
  449. package/src/router/middleware-cookies.ts +0 -55
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,36 @@ 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";
114
+ import {
115
+ resolvePrefetchCacheSize,
116
+ resolvePrefetchConcurrency,
117
+ } from "./router/prefetch-limits.js";
113
118
 
114
119
  // Re-export public types and values from extracted modules
115
120
  export { RSC_ROUTER_BRAND, RouterRegistry } from "./router/router-registry.js";
116
121
  export type {
117
- RSCRouterOptions,
122
+ RangoOptions,
118
123
  RootLayoutProps,
119
124
  SSRStreamMode,
120
125
  SSROptions,
121
126
  ResolveStreamingContext,
122
127
  } from "./router/router-options.js";
123
128
  export type {
124
- RSCRouter,
125
- RSCRouterInternal,
129
+ Rango,
130
+ RangoInternal,
126
131
  RouterRequestInput,
127
132
  } from "./router/router-interfaces.js";
128
133
  export { toInternal } from "./router/router-interfaces.js";
129
134
 
130
135
  export function createRouter<TEnv = any>(
131
- options: RSCRouterOptions<TEnv> = {},
132
- ): RSCRouter<TEnv, {}> {
136
+ options: RangoOptions<TEnv> = {},
137
+ ): Rango<TEnv, {}> {
133
138
  const {
134
139
  id: userProvidedId,
135
140
  $$id: injectedId,
141
+ basename: basenameOption,
136
142
  debugPerformance = false,
137
143
  document: documentOption,
138
144
  defaultErrorBoundary,
@@ -148,24 +154,48 @@ export function createRouter<TEnv = any>(
148
154
  nonce,
149
155
  version,
150
156
  prefetchCacheTTL: prefetchCacheTTLOption,
157
+ prefetchCacheSize: prefetchCacheSizeOption,
158
+ prefetchConcurrency: prefetchConcurrencyOption,
159
+ stateCookiePrefix: stateCookiePrefixOption,
151
160
  warmup: warmupOption,
152
161
  allowDebugManifest: allowDebugManifestOption = false,
153
162
  telemetry: telemetrySink,
163
+ tracing: tracingOption,
154
164
  ssr: ssrOption,
155
165
  timeout: timeoutShorthand,
156
166
  timeouts: timeoutsOption,
157
167
  onTimeout,
158
168
  originCheck: originCheckOption,
169
+ viewTransition: viewTransitionOption = "auto",
170
+ debugCacheSignal: debugCacheSignalOption = false,
171
+ strictMode: strictModeOption = true,
159
172
  } = options;
160
173
 
174
+ // Debug cache signal gate (DEVELOPMENT/TEST ONLY). Enabled by the
175
+ // debugCacheSignal option OR the RANGO_TEST_SIGNALS=1 env flag. When off,
176
+ // no X-Rango-Cache header is emitted and output is byte-identical.
177
+ const cacheSignalEnabled =
178
+ debugCacheSignalOption ||
179
+ (typeof process !== "undefined" &&
180
+ (process as { env?: Record<string, string | undefined> }).env
181
+ ?.RANGO_TEST_SIGNALS === "1");
182
+
183
+ // Normalize basename: ensure leading slash, strip trailing slash.
184
+ // A bare "/" is equivalent to no basename. Shared with the testing
185
+ // primitives via normalizeBasename so they can never drift.
186
+ const basename = normalizeBasename(basenameOption);
187
+
161
188
  // Resolve telemetry sink (no-op when not configured)
162
189
  const telemetry = resolveSink(telemetrySink);
163
190
 
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")).
191
+ // Resolve span tracing (undefined when not configured; every traceSpan() call
192
+ // is then a direct pass-through with zero behavior change).
193
+ const resolvedTracing = resolveTracing(tracingOption);
194
+
195
+ // Resolve cache profiles: merge user config with the guaranteed default
196
+ // profile. This resolved map is threaded onto each request context; the
197
+ // "use cache: <profile>" runtime path reads it request-scoped.
167
198
  const resolvedCacheProfiles = resolveCacheProfiles(cacheProfilesOption);
168
- setCacheProfiles(resolvedCacheProfiles);
169
199
 
170
200
  // Source file: prefer Vite-injected path (zero cost), fall back to
171
201
  // stack trace parsing for non-Vite environments (e.g. tests).
@@ -200,21 +230,40 @@ export function createRouter<TEnv = any>(
200
230
  const routerId =
201
231
  userProvidedId ?? injectedId ?? `router_${nextRouterAutoId()}`;
202
232
 
203
- // Resolve prefetch cache TTL (default: 300 seconds / 5 minutes)
204
- // Clamp to a non-negative integer for valid Cache-Control max-age.
205
- const rawTTL =
206
- prefetchCacheTTLOption !== undefined ? prefetchCacheTTLOption : 300;
207
- const prefetchCacheTTLSeconds =
208
- rawTTL === false ? 0 : Math.max(0, Math.floor(rawTTL));
209
- const prefetchCacheTTL = prefetchCacheTTLSeconds * 1000;
233
+ // Resolve the rango state cookie name once, here, so the two cookie writers
234
+ // (the client document.cookie writer and the server Set-Cookie writer)
235
+ // consume one pre-composed name and cannot drift.
236
+ const resolvedStateCookieName = resolveStateCookieName(
237
+ stateCookiePrefixOption,
238
+ routerId,
239
+ );
240
+
241
+ // Resolve prefetch cache TTL (default: 300 seconds / 5 minutes). Clamps to a
242
+ // non-negative integer and guards non-finite (NaN/Infinity) inputs so a
243
+ // malformed `Cache-Control: max-age=NaN` can never reach the wire.
244
+ const resolvedPrefetchCacheTTL = resolvePrefetchCacheTTL(
245
+ prefetchCacheTTLOption,
246
+ );
247
+ const prefetchCacheTTL = resolvedPrefetchCacheTTL.ms;
210
248
  const prefetchCacheControl: string | false =
211
- prefetchCacheTTLSeconds === 0
212
- ? false
213
- : `private, max-age=${prefetchCacheTTLSeconds}`;
249
+ resolvedPrefetchCacheTTL.cacheControl;
250
+
251
+ // Resolve client-side prefetch limits (in-memory cache size and queue
252
+ // concurrency). Both are positive-integer counts; sub-1/non-finite inputs
253
+ // fall back to the defaults. Shipped to the client in payload metadata.
254
+ const prefetchCacheSize = resolvePrefetchCacheSize(prefetchCacheSizeOption);
255
+ const prefetchConcurrency = resolvePrefetchConcurrency(
256
+ prefetchConcurrencyOption,
257
+ );
214
258
 
215
259
  // Resolve warmup enabled flag (default: true)
216
260
  const warmupEnabled = warmupOption !== false;
217
261
 
262
+ // Resolve StrictMode flag (default: true). Shipped to the client in payload
263
+ // metadata; the browser entry reads it once to decide whether to wrap the
264
+ // hydrated tree in React.StrictMode.
265
+ const strictMode = strictModeOption !== false;
266
+
218
267
  // Resolve theme config (null if theme not enabled)
219
268
  const resolvedThemeConfig = themeOption
220
269
  ? resolveThemeConfig(themeOption)
@@ -246,9 +295,14 @@ export function createRouter<TEnv = any>(
246
295
  invokeOnError(onError, error, phase, context, "Router");
247
296
  }
248
297
 
249
- // Validate document is a client component
298
+ // Validate document is a client component. Under a test runner the "use
299
+ // client" transform has not run, so a real exported document has no marker;
300
+ // allowServerInTest lets the router construct in a bare unit test (for
301
+ // dispatch / assertGeneratedRoutesMatch) while a real build still throws.
250
302
  if (documentOption !== undefined) {
251
- assertClientComponent(documentOption, "document");
303
+ assertClientComponent(documentOption, "document", {
304
+ allowServerInTest: true,
305
+ });
252
306
  }
253
307
 
254
308
  // Use default document if none provided (keeps internal name as rootLayout)
@@ -312,7 +366,7 @@ export function createRouter<TEnv = any>(
312
366
  let regex: RegExp | null = null;
313
367
  let paramNames: string[] = [];
314
368
  if (fullPattern) {
315
- const parsed = parsePattern(fullPattern);
369
+ const parsed = compileMiddlewarePattern(fullPattern);
316
370
  regex = parsed.regex;
317
371
  paramNames = parsed.paramNames;
318
372
  }
@@ -322,7 +376,6 @@ export function createRouter<TEnv = any>(
322
376
  regex,
323
377
  paramNames,
324
378
  handler,
325
- mountPrefix,
326
379
  });
327
380
  }
328
381
 
@@ -356,23 +409,28 @@ export function createRouter<TEnv = any>(
356
409
  getRouterPrecomputedEntries(routerId) ?? getPrecomputedEntries();
357
410
  if (current !== precomputedSource) {
358
411
  precomputedSource = current;
359
- precomputedByPrefix = current
360
- ? new Map(current.map((e) => [e.staticPrefix, e.routes]))
361
- : null;
412
+ // buildPrecomputedByPrefix drops any staticPrefix owned by more than one
413
+ // leaf include instead of collapsing it last-wins (which would mis-assign
414
+ // one include's routes to another's entry and 500 a valid sibling route).
415
+ // Such shared-prefix includes resolve via the handler path instead.
416
+ precomputedByPrefix = current ? buildPrecomputedByPrefix(current) : null;
362
417
  }
363
418
  return precomputedByPrefix;
364
419
  }
365
420
 
366
421
  // Wrapper to pass debugPerformance to external createMetricsStore.
367
422
  // Also checks per-request flag set by ctx.debugPerformance() in middleware.
423
+ // With no active request context there is nowhere to hang the store, so return
424
+ // undefined: an orphan store would collect metrics no reader can reach (nothing
425
+ // holds it, and appendMetric(undefined, ...) is already a no-op).
368
426
  const getMetricsStore = () => {
369
427
  const reqCtx = _getRequestContext();
370
428
  const enabled = debugPerformance || !!reqCtx?._debugPerformance;
371
- if (!enabled) return undefined;
372
- if (!reqCtx) {
373
- return createMetricsStore(true);
374
- }
375
- reqCtx._metricsStore ??= createMetricsStore(true);
429
+ if (!enabled || !reqCtx) return undefined;
430
+ // Anchor a mid-request store to the true request entry (reqCtx._handlerStart),
431
+ // not this call's performance.now(); undefined falls back to now() inside
432
+ // createMetricsStore (metrics.ts).
433
+ reqCtx._metricsStore ??= createMetricsStore(true, reqCtx._handlerStart);
376
434
  return reqCtx._metricsStore;
377
435
  };
378
436
 
@@ -383,11 +441,6 @@ export function createRouter<TEnv = any>(
383
441
  const findNearestNotFoundBoundary = (entry: EntryData | null) =>
384
442
  findNotFoundBoundary(entry, defaultNotFoundBoundary);
385
443
 
386
- // Helper to get handleStore from request context
387
- const getHandleStore = (): HandleStore | undefined => {
388
- return _getRequestContext()?._handleStore;
389
- };
390
-
391
444
  // Track a pending handler promise (non-blocking).
392
445
  // Attaches a side-effect .catch() to report streaming handler errors to onError
393
446
  // without altering the rejection chain (React's streaming error boundary still handles it).
@@ -398,13 +451,14 @@ export function createRouter<TEnv = any>(
398
451
  segmentType?: string;
399
452
  },
400
453
  ): Promise<T> => {
401
- const store = getHandleStore();
454
+ // One ALS read serves both the store lookup and the onError closure.
455
+ const reqCtx = _getRequestContext();
456
+ const store = reqCtx?._handleStore;
402
457
  const tracked = store ? store.track(promise) : promise;
403
458
 
404
459
  // Report streaming handler errors to onError as a side-effect.
405
460
  // The rejection still propagates to the RSC stream for client error boundaries.
406
461
  // Captures request context eagerly (closure) so the catch handler has full context.
407
- const reqCtx = _getRequestContext();
408
462
  if (reqCtx && onError) {
409
463
  tracked.catch((error) => {
410
464
  callOnError(error, "handler", {
@@ -446,8 +500,12 @@ export function createRouter<TEnv = any>(
446
500
  ? getRequestId(errorContext.request)
447
501
  : undefined
448
502
  : undefined;
503
+ // Derived once here for both the loader.start and loader.end emits (the
504
+ // loader.error emit uses ctx.loaderName from wrapLoaderWithErrorHandling).
505
+ const loaderName = telemetrySink
506
+ ? segmentId.split(".").pop() || "unknown"
507
+ : "";
449
508
  if (telemetrySink) {
450
- const loaderName = segmentId.split(".").pop() || "unknown";
451
509
  safeEmit(telemetry, {
452
510
  type: "loader.start",
453
511
  timestamp: loaderStart,
@@ -501,7 +559,6 @@ export function createRouter<TEnv = any>(
501
559
 
502
560
  // Emit loader.end after the promise settles (fire-and-forget)
503
561
  if (telemetrySink) {
504
- const loaderName = segmentId.split(".").pop() || "unknown";
505
562
  result.then((r) => {
506
563
  safeEmit(telemetry, {
507
564
  type: "loader.end",
@@ -526,7 +583,9 @@ export function createRouter<TEnv = any>(
526
583
  trackHandler,
527
584
  findNearestErrorBoundary,
528
585
  findNearestNotFoundBoundary,
586
+ notFoundComponent: notFound,
529
587
  callOnError,
588
+ viewTransitionDefault: viewTransitionOption,
530
589
  };
531
590
 
532
591
  // Match API dependencies
@@ -560,10 +619,18 @@ export function createRouter<TEnv = any>(
560
619
  mergedRouteMap,
561
620
  nextMountIndex: () => mountIndex++,
562
621
  getPrecomputedByPrefix,
622
+ routerId,
563
623
  };
564
624
 
565
- function evaluateLazyEntry(entry: RouteEntry<TEnv>): void {
566
- _evaluateLazyEntry(entry, lazyEvalDeps);
625
+ // Must return the Promise from _evaluateLazyEntry: an async include provider
626
+ // (`() => import("./routes")`) resolves off the startup path, and createFindMatch
627
+ // awaits this to know when the import + expansion have completed. Dropping it
628
+ // (typing this `void`) makes the import fire-and-forget, so findMatch spins the
629
+ // lazy-eval retry loop to its cap and returns null on the first request to any
630
+ // async include whose prefix isn't already covered by a unique precomputed entry
631
+ // (nested includes, shared prefixes, regex fallback).
632
+ function evaluateLazyEntry(entry: RouteEntry<TEnv>): void | Promise<void> {
633
+ return _evaluateLazyEntry(entry, lazyEvalDeps);
567
634
  }
568
635
 
569
636
  // Create findMatch with single-entry cache, bound to router state
@@ -613,6 +680,8 @@ export function createRouter<TEnv = any>(
613
680
  params: Record<string, string>,
614
681
  buildVars?: Record<string, any>,
615
682
  isPassthroughRoute?: boolean,
683
+ buildEnv?: TEnv,
684
+ devMode?: boolean,
616
685
  ) {
617
686
  return _matchForPrerender(
618
687
  pathname,
@@ -620,6 +689,8 @@ export function createRouter<TEnv = any>(
620
689
  prerenderDeps,
621
690
  buildVars,
622
691
  isPassthroughRoute,
692
+ buildEnv,
693
+ devMode,
623
694
  );
624
695
  }
625
696
 
@@ -627,12 +698,16 @@ export function createRouter<TEnv = any>(
627
698
  handler: Function,
628
699
  handlerId: string,
629
700
  routeName?: string,
701
+ buildEnv?: TEnv,
702
+ devMode?: boolean,
630
703
  ) {
631
704
  return _renderStaticSegment<TEnv>(
632
705
  handler,
633
706
  handlerId,
634
707
  mergedRouteMap,
635
708
  routeName,
709
+ buildEnv,
710
+ devMode,
636
711
  );
637
712
  }
638
713
 
@@ -645,6 +720,7 @@ export function createRouter<TEnv = any>(
645
720
  findMatch,
646
721
  findInterceptForRoute,
647
722
  telemetry: telemetrySink,
723
+ cacheSignalEnabled,
648
724
  });
649
725
 
650
726
  const { match, matchPartial, matchError, previewMatch } = matchHandlers;
@@ -654,11 +730,18 @@ export function createRouter<TEnv = any>(
654
730
  * The type system tracks accumulated routes through the builder chain
655
731
  * Initial TRoutes is {} (empty) to avoid poisoning accumulated types with Record<string, string>
656
732
  */
657
- const router: RSCRouterInternal<TEnv, {}> = {
733
+ const router: RangoInternal<TEnv, {}> = {
658
734
  __brand: RSC_ROUTER_BRAND,
659
735
  id: routerId,
736
+ basename,
737
+
738
+ routes(patternsOrBuilder: UrlPatterns<TEnv> | UrlBuilder<TEnv>): any {
739
+ // Wrap builder functions in urls() automatically
740
+ const urlPatterns: UrlPatterns<TEnv> =
741
+ typeof patternsOrBuilder === "function"
742
+ ? (urls(patternsOrBuilder) as UrlPatterns<TEnv>)
743
+ : patternsOrBuilder;
660
744
 
661
- routes(urlPatterns: UrlPatterns<TEnv>): any {
662
745
  // Store reference for runtime manifest generation
663
746
  storedUrlPatterns = urlPatterns;
664
747
  const currentMountIndex = mountIndex++;
@@ -689,13 +772,13 @@ export function createRouter<TEnv = any>(
689
772
  errorBoundary: [],
690
773
  notFoundBoundary: [],
691
774
  layout: [],
692
- parallel: [],
775
+ parallel: {},
693
776
  intercept: [],
694
777
  loader: [],
695
778
  };
696
779
 
697
780
  let handlerResult: AllUseItems[] = [];
698
- RSCRouterContext.run(
781
+ RangoContext.run(
699
782
  {
700
783
  manifest,
701
784
  patterns: routePatterns,
@@ -706,6 +789,10 @@ export function createRouter<TEnv = any>(
706
789
  counters: {},
707
790
  mountIndex: currentMountIndex,
708
791
  cacheProfiles: resolvedCacheProfiles,
792
+ // basename sets the initial URL prefix so all path() patterns
793
+ // are registered with the prefix (e.g. "/admin" + "/users" = "/admin/users").
794
+ // No namePrefix — route names stay unprefixed.
795
+ ...(basename ? { urlPrefix: basename } : {}),
709
796
  },
710
797
  () => {
711
798
  handlerResult = urlPatterns.handler() as AllUseItems[];
@@ -725,7 +812,7 @@ export function createRouter<TEnv = any>(
725
812
  if (entry.type === "route" && entry.isPrerender) {
726
813
  if (!prerenderRouteKeys) prerenderRouteKeys = new Set();
727
814
  prerenderRouteKeys.add(name);
728
- if (entry.prerenderDef?.options?.passthrough === true) {
815
+ if (entry.isPassthrough === true) {
729
816
  if (!passthroughRouteKeys) passthroughRouteKeys = new Set();
730
817
  passthroughRouteKeys.add(name);
731
818
  }
@@ -751,6 +838,7 @@ export function createRouter<TEnv = any>(
751
838
  trailingSlash: trailingSlashConfig,
752
839
  handler: urlPatterns.handler,
753
840
  mountIndex: currentMountIndex,
841
+ routerId,
754
842
  cacheProfiles: resolvedCacheProfiles,
755
843
  ...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
756
844
  ...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
@@ -770,6 +858,7 @@ export function createRouter<TEnv = any>(
770
858
  trailingSlash: trailingSlashConfig,
771
859
  handler: urlPatterns.handler,
772
860
  mountIndex: currentMountIndex,
861
+ routerId,
773
862
  cacheProfiles: resolvedCacheProfiles,
774
863
  ...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
775
864
  ...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
@@ -801,10 +890,13 @@ export function createRouter<TEnv = any>(
801
890
 
802
891
  // Create placeholder RouteEntry for each lazy include
803
892
  for (const lazyInclude of lazyIncludes) {
804
- // Compute the full URL prefix (combining parent prefix if any)
805
- const fullPrefix = lazyInclude.context.urlPrefix
806
- ? lazyInclude.context.urlPrefix + lazyInclude.prefix
807
- : lazyInclude.prefix;
893
+ // Compute the full URL prefix (combining parent prefix if any). Use the
894
+ // slash-collapsing join so a trailing-slash parent prefix does not
895
+ // produce a double-slash staticPrefix the trie's sp can never match.
896
+ const fullPrefix = joinPrefix(
897
+ lazyInclude.context.urlPrefix,
898
+ lazyInclude.prefix,
899
+ );
808
900
 
809
901
  const lazyEntry: RouteEntry<TEnv> & { _lazyPrefix?: string } = {
810
902
  prefix: "",
@@ -813,6 +905,7 @@ export function createRouter<TEnv = any>(
813
905
  trailingSlash: trailingSlashConfig,
814
906
  handler: urlPatterns.handler,
815
907
  mountIndex: mountIndex++,
908
+ routerId,
816
909
  // Lazy evaluation fields
817
910
  lazy: true,
818
911
  lazyPatterns: lazyInclude.patterns,
@@ -851,8 +944,18 @@ export function createRouter<TEnv = any>(
851
944
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
852
945
  middleware?: MiddlewareFn<TEnv>,
853
946
  ): any {
854
- // Global middleware - no mount prefix
855
- addMiddleware(patternOrMiddleware, middleware, null);
947
+ // Auto-prefix pattern with basename so router-level middleware
948
+ // patterns are router-relative (e.g. "/users/*" matches "/app/users/*").
949
+ if (basename && typeof patternOrMiddleware === "string") {
950
+ const pattern = patternOrMiddleware;
951
+ const prefixed =
952
+ pattern === "/*" || pattern === "*"
953
+ ? `${basename}/*`
954
+ : `${basename}${pattern}`;
955
+ addMiddleware(prefixed, middleware, null);
956
+ } else {
957
+ addMiddleware(patternOrMiddleware, middleware, null);
958
+ }
856
959
  return router;
857
960
  },
858
961
 
@@ -888,13 +991,31 @@ export function createRouter<TEnv = any>(
888
991
  // Expose prefetch cache settings
889
992
  prefetchCacheControl,
890
993
  prefetchCacheTTL,
994
+ prefetchCacheSize,
995
+ prefetchConcurrency,
996
+
997
+ // Expose the resolved rango state cookie name for the server-side writer
998
+ // (invalidateClientCache) and for shipping to the client in metadata.
999
+ resolvedStateCookieName,
891
1000
 
892
1001
  // Expose warmup enabled flag for handler and client
893
1002
  warmupEnabled,
894
1003
 
1004
+ // Expose StrictMode flag for the initial-render payload metadata
1005
+ strictMode,
1006
+
895
1007
  // Expose router-wide performance debugging for request-level metrics setup
896
1008
  debugPerformance,
897
1009
 
1010
+ // Expose resolved span tracing for the handler (Cloudflare custom spans)
1011
+ tracing: resolvedTracing,
1012
+
1013
+ // Expose the raw telemetry sink so handler-level emitters (timeout, origin
1014
+ // rejection, late-handle handler.error) can emit outside the match ALS.
1015
+ // Raw (not the resolveSink no-op wrapper) so router.telemetry stays
1016
+ // undefined when unconfigured and call sites gate on truthiness.
1017
+ telemetry: telemetrySink,
1018
+
898
1019
  // Expose debug manifest flag for handler
899
1020
  allowDebugManifest: allowDebugManifestOption,
900
1021
 
@@ -953,6 +1074,16 @@ export function createRouter<TEnv = any>(
953
1074
  // Expose source file for per-router type generation
954
1075
  __sourceFile,
955
1076
 
1077
+ // Expose basename for runtime manifest generation
1078
+ __basename: basename,
1079
+
1080
+ // Expose router-level boundary defaults for build-time clientChunks
1081
+ // discovery (so a "use client" default boundary lands in app-fallback).
1082
+ // These are createRouter options, never pushed onto EntryData.
1083
+ __defaultErrorBoundary: defaultErrorBoundary,
1084
+ __defaultNotFoundBoundary: defaultNotFoundBoundary,
1085
+ __notFound: notFound,
1086
+
956
1087
  // RSC request handler (lazily created on first call)
957
1088
  fetch: (() => {
958
1089
  // Handler is created on first call and reused
@@ -970,8 +1101,10 @@ export function createRouter<TEnv = any>(
970
1101
  if (!handler) {
971
1102
  // Lazy import deferred to first request to avoid dev mode issues
972
1103
  const { createRSCHandler } = await import("./rsc/handler.js");
973
- // Cast: handler.ts still accepts (request, env) will be updated
974
- // separately to accept RouterRequestInput.
1104
+ // Cast: createRSCHandler receives `router as any`, which erases TEnv
1105
+ // and infers its handler as RouterRequestInput<unknown>. Re-narrow the
1106
+ // returned handler to RouterRequestInput<TEnv> so the call below stays
1107
+ // typed. (The handler already accepts (request, RouterRequestInput).)
975
1108
  handler = createRSCHandler({
976
1109
  router: router as any,
977
1110
  cache,
@@ -986,6 +1119,10 @@ export function createRouter<TEnv = any>(
986
1119
  };
987
1120
  })(),
988
1121
 
1122
+ // Low-level route matching for request classification
1123
+ findMatch: (pathname: string, metricsStore?: any) =>
1124
+ findMatch(pathname, metricsStore),
1125
+
989
1126
  // Debug utility for manifest inspection
990
1127
  debugManifest: () => buildDebugManifest<TEnv>(routesEntries),
991
1128
  };
@@ -994,8 +1131,10 @@ export function createRouter<TEnv = any>(
994
1131
  RouterRegistry.set(routerId, router);
995
1132
 
996
1133
  // If urls option was provided, auto-register them
997
- if (urlsOption) {
998
- return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
1134
+ if (typeof urlsOption === "function") {
1135
+ return router.routes(urlsOption) as Rango<TEnv, {}>;
1136
+ } else if (urlsOption) {
1137
+ return router.routes(urlsOption) as Rango<TEnv, {}>;
999
1138
  }
1000
1139
 
1001
1140
  return router;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Full (initial-load / document) RSC payload builder.
3
+ *
4
+ * Extracted from rsc-rendering.ts so both the foreground render AND the PPR
5
+ * background shell capture (shell-capture.ts) build the SAME payload shape over
6
+ * their own handle store. `resume` requires the captured tree to match the served
7
+ * tree; building an identical payload over the same replayed segments is what
8
+ * makes that hold. Keep this byte-identical to the normal full-render path.
9
+ */
10
+
11
+ import type { MatchResult } from "../types.js";
12
+ import type { RscPayload } from "./types.js";
13
+ import type { HandlerContext } from "./handler-context.js";
14
+ import type { RequestContext } from "../server/request-context.js";
15
+ import type { HandleStore } from "../server/handle-store.js";
16
+ import { gateTransitions } from "./transition-gate.js";
17
+ import { resolvedHandleStream } from "../handles/deferred-resolution.js";
18
+
19
+ /**
20
+ * Build the metadata payload for a full (non-partial) document render.
21
+ *
22
+ * @param handleStore - the store whose resolved handle stream feeds the payload;
23
+ * the foreground passes reqCtx's store, the background capture passes its fresh
24
+ * derived-context store.
25
+ */
26
+ export function buildFullPayload(
27
+ m: MatchResult,
28
+ // env is opaque here (the payload never reads it); `any` avoids the invariance
29
+ // friction of HandlerContext<TEnv> vs the ambient RequestContext env at the two
30
+ // call sites (foreground render + background capture).
31
+ ctx: HandlerContext<any>,
32
+ url: URL,
33
+ reqCtx: RequestContext<any>,
34
+ handleStore: HandleStore,
35
+ ): RscPayload {
36
+ return {
37
+ metadata: {
38
+ pathname: url.pathname,
39
+ routerId: ctx.router.id,
40
+ basename: ctx.router.basename,
41
+ segments: gateTransitions(m.segments, reqCtx, ctx.router.onError),
42
+ matched: m.matched,
43
+ diff: m.diff,
44
+ resolvedIds: m.resolvedIds,
45
+ params: m.params,
46
+ isPartial: false,
47
+ rootLayout: ctx.router.rootLayout,
48
+ // Full render: resolve deferred handle values server-side so SSR markup and
49
+ // the first sync useHandle read see resolved values. Partial payloads
50
+ // (rsc-rendering.ts) keep streaming (handleStore.stream()).
51
+ handles: resolvedHandleStream(handleStore),
52
+ version: ctx.version,
53
+ prefetchCacheTTL: ctx.router.prefetchCacheTTL,
54
+ prefetchCacheSize: ctx.router.prefetchCacheSize,
55
+ prefetchConcurrency: ctx.router.prefetchConcurrency,
56
+ stateCookieName: ctx.router.resolvedStateCookieName,
57
+ themeConfig: ctx.router.themeConfig,
58
+ // Carry warmupEnabled on the initial full-render payload so the client
59
+ // respects warmup:false from first load. The 404 and PE payloads already
60
+ // include it; without it here warmup could never be disabled on the
61
+ // normal full-load path (partial payloads omit it by design).
62
+ warmupEnabled: ctx.router.warmupEnabled,
63
+ // Carry strictMode on the initial full-render payload so the browser
64
+ // entry knows whether to wrap hydration in React.StrictMode. Partial
65
+ // (navigation) payloads omit it by design; StrictMode is decided once.
66
+ strictMode: ctx.router.strictMode,
67
+ initialTheme: reqCtx.theme,
68
+ },
69
+ };
70
+ }