@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
@@ -1,8 +1,9 @@
1
1
  import type { LocationStateEntry } from "../browser/react/location-state-shared.js";
2
2
  import {
3
- requireRequestContext,
4
3
  getRequestContext,
4
+ _getRequestContext,
5
5
  } from "../server/request-context.js";
6
+ import { markExternalRedirect } from "../redirect-origin.js";
6
7
 
7
8
  /**
8
9
  * Create a soft redirect Response for middleware short-circuit
@@ -38,6 +39,11 @@ import {
38
39
  * status: 303,
39
40
  * state: [Flash({ text: "Session expired" })],
40
41
  * });
42
+ *
43
+ * // Off-host redirect (opt out of the same-origin guard). Without
44
+ * // `external: true`, a cross-origin target is blocked and replaced with the
45
+ * // app root, matching the client's open-redirect protection.
46
+ * return redirect('https://accounts.example.com/oauth', { external: true });
41
47
  * ```
42
48
  */
43
49
  export function redirect(url: string, status?: number): Response;
@@ -46,13 +52,18 @@ export function redirect(
46
52
  options: {
47
53
  status?: number;
48
54
  state?: LocationStateEntry | LocationStateEntry[];
55
+ external?: boolean;
49
56
  },
50
57
  ): Response;
51
58
  export function redirect(
52
59
  url: string,
53
60
  statusOrOptions?:
54
61
  | number
55
- | { status?: number; state?: LocationStateEntry | LocationStateEntry[] },
62
+ | {
63
+ status?: number;
64
+ state?: LocationStateEntry | LocationStateEntry[];
65
+ external?: boolean;
66
+ },
56
67
  ): Response {
57
68
  const status =
58
69
  typeof statusOrOptions === "number"
@@ -60,9 +71,11 @@ export function redirect(
60
71
  : (statusOrOptions?.status ?? 302);
61
72
  const state =
62
73
  typeof statusOrOptions === "object" ? statusOrOptions?.state : undefined;
74
+ const external =
75
+ typeof statusOrOptions === "object" ? statusOrOptions?.external : undefined;
63
76
 
64
77
  if (state) {
65
- const ctx = requireRequestContext();
78
+ const ctx = getRequestContext();
66
79
  ctx.setLocationState(state);
67
80
 
68
81
  if (process.env.NODE_ENV !== "production") {
@@ -71,9 +84,9 @@ export function redirect(
71
84
  // actions both deliver state through Flight payloads, so suppress for those.
72
85
  if (
73
86
  reqCtx &&
74
- !reqCtx.url.searchParams.has("_rsc_partial") &&
87
+ !reqCtx.originalUrl.searchParams.has("_rsc_partial") &&
75
88
  !reqCtx.request.headers.has("rsc-action") &&
76
- !reqCtx.url.searchParams.has("_rsc_action")
89
+ !reqCtx.originalUrl.searchParams.has("_rsc_action")
77
90
  ) {
78
91
  console.warn(
79
92
  `[Router] redirect() with state during a full-page (SSR) request to "${url}". ` +
@@ -83,11 +96,39 @@ export function redirect(
83
96
  }
84
97
  }
85
98
 
86
- return new Response(null, {
87
- status,
88
- headers: {
89
- Location: url,
90
- "X-RSC-Redirect": "soft",
91
- },
92
- });
99
+ // Auto-prefix root-relative URLs with basename for app-local redirects.
100
+ // Treat the URL as already-prefixed when the basename is followed by a path
101
+ // separator, a query, a fragment, or end-of-string, so "/admin?tab=x" and
102
+ // "/admin#frag" are not double-prefixed into "/admin/admin?tab=x".
103
+ const bn = _getRequestContext()?._basename;
104
+ let resolvedUrl = url;
105
+ if (
106
+ bn &&
107
+ url.startsWith("/") &&
108
+ url !== bn &&
109
+ !url.startsWith(bn + "/") &&
110
+ !url.startsWith(bn + "?") &&
111
+ !url.startsWith(bn + "#")
112
+ ) {
113
+ resolvedUrl = url === "/" ? bn : bn + url;
114
+ }
115
+
116
+ const headers: Record<string, string> = {
117
+ Location: resolvedUrl,
118
+ "X-RSC-Redirect": "soft",
119
+ };
120
+
121
+ const response = new Response(null, { status, headers });
122
+
123
+ // Mark an explicit off-host redirect with an out-of-band brand so the
124
+ // same-origin guard (rsc/redirect-guard.ts) lets it through. The brand is a
125
+ // WeakSet membership on this Response object -- NOT a wire header -- so the
126
+ // opt-in cannot be forged by an attacker-controlled upstream response a
127
+ // proxy-style response route might copy. The internal redirect-rebuild paths
128
+ // transfer the brand; the guard reads and clears it (see markExternalRedirect).
129
+ if (external) {
130
+ markExternalRedirect(response);
131
+ }
132
+
133
+ return response;
93
134
  }
@@ -0,0 +1,160 @@
1
+ import type { AllUseItems } from "../route-types.js";
2
+ import { isPrerenderHandler, isPassthroughHandler } from "../prerender.js";
3
+ import { isStaticHandler } from "../static-handler.js";
4
+
5
+ /**
6
+ * Extract the .use callback from any handler shape.
7
+ *
8
+ * Checks definition brands first (objects with __brand), then plain functions.
9
+ * ReactNode handlers return undefined (no .use possible).
10
+ */
11
+ export function resolveHandlerUse(handler: unknown): (() => any[]) | undefined {
12
+ if (handler == null) return undefined;
13
+
14
+ // Check branded definitions first — they're objects but also have typeof "object"
15
+ if (isPassthroughHandler(handler)) {
16
+ return (handler as any).use;
17
+ }
18
+ if (isPrerenderHandler(handler)) {
19
+ return (handler as any).use;
20
+ }
21
+ if (isStaticHandler(handler)) {
22
+ return (handler as any).use;
23
+ }
24
+ // Loader definitions from createLoader() — branded objects with optional .use
25
+ if (typeof handler === "object" && (handler as any).__brand === "loader") {
26
+ return (handler as any).use;
27
+ }
28
+ // Plain handler function
29
+ if (typeof handler === "function") {
30
+ return (handler as any).use;
31
+ }
32
+ // ReactNode or other — no .use
33
+ return undefined;
34
+ }
35
+
36
+ /**
37
+ * Allowed item types per mount site.
38
+ * Mirrors the RouteUseItem / ParallelUseItem / InterceptUseItem / LayoutUseItem unions
39
+ * from route-types.ts for runtime validation.
40
+ */
41
+ const MOUNT_SITE_ALLOWED_TYPES: Record<string, Set<string>> = {
42
+ path: new Set([
43
+ "layout",
44
+ "parallel",
45
+ "intercept",
46
+ "middleware",
47
+ "revalidate",
48
+ "loader",
49
+ "loading",
50
+ "errorBoundary",
51
+ "notFoundBoundary",
52
+ "cache",
53
+ "transition",
54
+ ]),
55
+ // Response routes (path.json, path.text, etc.) — mirrors ResponseRouteUseItem
56
+ response: new Set(["middleware", "cache"]),
57
+ route: new Set([
58
+ "layout",
59
+ "parallel",
60
+ "intercept",
61
+ "middleware",
62
+ "revalidate",
63
+ "loader",
64
+ "loading",
65
+ "errorBoundary",
66
+ "notFoundBoundary",
67
+ "cache",
68
+ "transition",
69
+ ]),
70
+ // layout allows AllUseItems — no validation needed, but included for completeness
71
+ layout: new Set([
72
+ "layout",
73
+ "route",
74
+ "middleware",
75
+ "revalidate",
76
+ "parallel",
77
+ "intercept",
78
+ "loader",
79
+ "loading",
80
+ "errorBoundary",
81
+ "notFoundBoundary",
82
+ "cache",
83
+ "transition",
84
+ "include",
85
+ ]),
86
+ parallel: new Set([
87
+ "revalidate",
88
+ "loader",
89
+ "loading",
90
+ "errorBoundary",
91
+ "notFoundBoundary",
92
+ "transition",
93
+ ]),
94
+ intercept: new Set([
95
+ "middleware",
96
+ "revalidate",
97
+ "loader",
98
+ "loading",
99
+ "errorBoundary",
100
+ "notFoundBoundary",
101
+ "layout",
102
+ "route",
103
+ "transition",
104
+ ]),
105
+ // LoaderUseItem — only revalidate + cache can attach to a loader entry
106
+ loader: new Set(["revalidate", "cache"]),
107
+ };
108
+
109
+ /**
110
+ * Validate that items from handler.use() are valid for the given mount site.
111
+ * Throws a descriptive error if any item is not allowed.
112
+ */
113
+ export function validateHandlerUseItems(
114
+ items: AllUseItems[],
115
+ mountSite: string,
116
+ ): void {
117
+ const allowed = MOUNT_SITE_ALLOWED_TYPES[mountSite];
118
+ if (!allowed) return;
119
+ for (const item of items) {
120
+ if (item == null) continue;
121
+ if (!allowed.has((item as any).type)) {
122
+ throw new Error(
123
+ `handler.use() returned ${(item as any).type}() which is not valid inside ${mountSite}(). ` +
124
+ `Allowed types: ${[...allowed].join(", ")}.`,
125
+ );
126
+ }
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Create a merged use callback from handler.use and explicit use.
132
+ * handler.use items come first (defaults), explicit items second (overrides).
133
+ * Returns undefined if both are absent.
134
+ */
135
+ export function mergeHandlerUse(
136
+ handlerUse: (() => any[]) | undefined,
137
+ explicitUse: (() => any[]) | undefined,
138
+ mountSite: string,
139
+ ): (() => any[]) | undefined {
140
+ if (!handlerUse && !explicitUse) return undefined;
141
+ // Validation asymmetry (intentional, pre-1.0): only handler.use() items are
142
+ // checked against the mount-site allow-list (validateHandlerUseItems below).
143
+ // Explicit use() items pass through unvalidated on both the explicit-only
144
+ // branch here and the merged branch, so a structurally-valid-but-prohibited
145
+ // item (e.g. middleware() inside a parallel slot) is not rejected at this seam.
146
+ // Documented rather than enforced for now; revisit before 1.0 (#569).
147
+ if (!handlerUse) return explicitUse;
148
+ if (!explicitUse) {
149
+ return () => {
150
+ const items = handlerUse().flat(3);
151
+ validateHandlerUseItems(items, mountSite);
152
+ return items;
153
+ };
154
+ }
155
+ return () => {
156
+ const hItems = handlerUse().flat(3);
157
+ validateHandlerUseItems(hItems, mountSite);
158
+ return [...hItems, ...explicitUse()];
159
+ };
160
+ }
@@ -0,0 +1,29 @@
1
+ import type { AllUseItems } from "../route-types.js";
2
+
3
+ /**
4
+ * The set of valid use-item `type` discriminants — the single runtime source of
5
+ * truth for "is this a well-formed use item?" shape validation.
6
+ *
7
+ * Declared via a `Record<...>` so that adding a member to the union without
8
+ * updating this map is a compile error. This is shape validation only; per-mount-
9
+ * site rules remain the narrower hand-written tables in resolve-handler-use.ts.
10
+ */
11
+ const USE_ITEM_TYPES: Record<AllUseItems["type"], true> = {
12
+ layout: true,
13
+ route: true,
14
+ middleware: true,
15
+ revalidate: true,
16
+ parallel: true,
17
+ intercept: true,
18
+ loader: true,
19
+ loading: true,
20
+ errorBoundary: true,
21
+ notFoundBoundary: true,
22
+ cache: true,
23
+ transition: true,
24
+ include: true,
25
+ };
26
+
27
+ export const ALL_USE_ITEM_TYPES: ReadonlySet<string> = new Set(
28
+ Object.keys(USE_ITEM_TYPES),
29
+ );
@@ -8,15 +8,10 @@
8
8
  * See docs/manifests.md for the full data flow.
9
9
  */
10
10
 
11
- // Singleton route map instance - populated incrementally as routes are encountered
12
11
  let globalRouteMap: Record<string, string> = {};
13
12
 
14
- // Cached complete manifest - includes all routes (including lazy includes)
15
- // Set from runtime cache or build-time import
16
13
  let cachedManifest: Record<string, string> | null = null;
17
14
 
18
- // Pre-computed route entries from build-time prefix tree leaf nodes.
19
- // Used by evaluateLazyEntry() to skip running the handler for route matching.
20
15
  let cachedPrecomputedEntries: Array<{
21
16
  staticPrefix: string;
22
17
  routes: Record<string, string>;
@@ -25,11 +20,25 @@ let cachedPrecomputedEntries: Array<{
25
20
  /**
26
21
  * Register routes into the global route map.
27
22
  * Routes are merged with any existing registered routes.
28
- * Called by createRouter() during module evaluation.
23
+ * Called by createRouter() during module evaluation, and by lazy-include
24
+ * expansion (src/router/lazy-includes.ts) with each expansion's route delta.
25
+ *
26
+ * Merges IN PLACE — O(|map|), not O(total routes). The previous
27
+ * `globalRouteMap = { ...globalRouteMap, ...map }` copy made every
28
+ * lazy-include first hit O(total routes) on the request path: with a 26k-route
29
+ * manifest the spread measured 8.9ms/call (M4, node), paid once per level of a
30
+ * nested async-include chain (3 calls on a 3-level chain — the 464ms edge
31
+ * cold-hit in issue #666).
32
+ *
33
+ * In-place mutation is safe because every getGlobalRouteMap() consumer reads
34
+ * it fresh per call (server/request-context.ts, rsc/loader-fetch.ts,
35
+ * router/intercept-resolution.ts, testing/generated-routes.ts,
36
+ * rsc/manifest-init.ts) — none memoizes the returned reference. If you add a
37
+ * consumer that caches the map object, it will now observe later
38
+ * registrations; snapshot it yourself if you need frozen contents.
29
39
  */
30
40
  export function registerRouteMap(map: Record<string, string>): void {
31
- // Always merge with existing map (don't replace)
32
- globalRouteMap = { ...globalRouteMap, ...map };
41
+ Object.assign(globalRouteMap, map);
33
42
  }
34
43
 
35
44
  /**
@@ -43,7 +52,6 @@ export function registerRouteMap(map: Record<string, string>): void {
43
52
  * @internal
44
53
  */
45
54
  export function getGlobalRouteMap(): Record<string, string> {
46
- // Cached manifest is complete (includes lazy routes), so prefer it
47
55
  if (cachedManifest) {
48
56
  return cachedManifest;
49
57
  }
@@ -145,6 +153,7 @@ export function clearAllRouterData(): void {
145
153
  perRouterManifestMap.clear();
146
154
  perRouterTrieMap.clear();
147
155
  perRouterPrecomputedEntriesMap.clear();
156
+ authoritativeTrieRouters.clear();
148
157
  }
149
158
 
150
159
  export function setRouterManifest(
@@ -168,6 +177,23 @@ export function setRouterTrie(
168
177
  perRouterTrieMap.set(routerId, trie);
169
178
  }
170
179
 
180
+ // Routers whose trie came from the COMPLETE build manifest (deserialized via
181
+ // ensureRouterManifest). For these, a trie miss is a real 404 and findMatch
182
+ // skips the regex fallback scan — the only remaining route-count-proportional
183
+ // match path (#664). Dev rebuilds (manifest-init.ts, router-discovery HMR
184
+ // pushes) deliberately never mark authoritative: the dev-only trie-gap warning
185
+ // in find-match.ts depends on the fallback running on misses, and dev route
186
+ // churn (HMR, dev-time routes) makes a stale-trie 404 unacceptable there.
187
+ const authoritativeTrieRouters: Set<string> = new Set();
188
+
189
+ export function markRouterTrieAuthoritative(routerId: string): void {
190
+ authoritativeTrieRouters.add(routerId);
191
+ }
192
+
193
+ export function isRouterTrieAuthoritative(routerId: string): boolean {
194
+ return authoritativeTrieRouters.has(routerId);
195
+ }
196
+
171
197
  export function getRouterTrie(
172
198
  routerId: string,
173
199
  ): import("./build/route-trie.js").TrieNode | undefined {
@@ -199,12 +225,23 @@ export function registerRouterManifestLoader(
199
225
  }
200
226
 
201
227
  export async function ensureRouterManifest(routerId: string): Promise<void> {
202
- if (perRouterManifestMap.has(routerId)) return;
228
+ // Check both manifest AND trie. The virtual module's setRouterManifest()
229
+ // pre-sets the manifest at startup, but the per-router trie is only
230
+ // available from the lazy loader. Without this, the lazy loader never
231
+ // runs and findMatch falls back to the global merged trie — which
232
+ // contains routes from ALL routers and breaks multi-router setups.
233
+ if (perRouterManifestMap.has(routerId) && perRouterTrieMap.has(routerId))
234
+ return;
203
235
  const loader = routerManifestLoaders.get(routerId);
204
236
  if (loader) {
205
237
  const mod = await loader();
206
238
  if (mod.manifest) perRouterManifestMap.set(routerId, mod.manifest);
207
- if (mod.trie) perRouterTrieMap.set(routerId, mod.trie);
239
+ if (mod.trie) {
240
+ perRouterTrieMap.set(routerId, mod.trie);
241
+ // A trie serialized into the build manifest comes from complete
242
+ // discovery — misses are authoritative 404s (see find-match.ts).
243
+ markRouterTrieAuthoritative(routerId);
244
+ }
208
245
  if (mod.precomputedEntries)
209
246
  perRouterPrecomputedEntriesMap.set(routerId, mod.precomputedEntries);
210
247
  routerManifestLoaders.delete(routerId);
@@ -225,10 +262,6 @@ export function waitForManifestReady(): Promise<void> | null {
225
262
  return manifestReadyPromise;
226
263
  }
227
264
 
228
- // ============================================================================
229
- // Route Scope Registry
230
- // ============================================================================
231
-
232
265
  // Tracks whether each route is at root scope (no named include boundary above).
233
266
  // Used by dot-local reverse resolution to decide whether bare-name fallback
234
267
  // is allowed after scoped lookups are exhausted.
@@ -253,14 +286,8 @@ export function isRouteRootScoped(routeName: string): boolean | undefined {
253
286
  return rootScopeRoutes.get(routeName);
254
287
  }
255
288
 
256
- // ============================================================================
257
- // Search Schema Registry
258
- // ============================================================================
259
-
260
289
  import type { SearchSchema } from "./search-params.js";
261
290
 
262
- // Global search schema map: route name -> search schema descriptor.
263
- // Populated by path() when a search option is provided.
264
291
  const globalSearchSchemas: Map<string, SearchSchema> = new Map();
265
292
 
266
293
  export function registerSearchSchema(
@@ -5,47 +5,43 @@
5
5
  */
6
6
 
7
7
  /**
8
- * Branded return types for route helpers
8
+ * Brand for UrlPatterns nominal typing (see pattern-types.ts). The route-item
9
+ * types below are discriminated by their `type` literal, so they carry no brand.
9
10
  */
10
- export declare const LayoutBrand: unique symbol;
11
- export declare const RouteBrand: unique symbol;
12
- export declare const ParallelBrand: unique symbol;
13
- export declare const InterceptBrand: unique symbol;
14
- export declare const MiddlewareBrand: unique symbol;
15
- export declare const RevalidateBrand: unique symbol;
16
- export declare const LoaderBrand: unique symbol;
17
- export declare const LoadingBrand: unique symbol;
18
- export declare const ErrorBoundaryBrand: unique symbol;
19
- export declare const NotFoundBoundaryBrand: unique symbol;
20
- export declare const WhenBrand: unique symbol;
21
- export declare const CacheBrand: unique symbol;
22
- export declare const TransitionBrand: unique symbol;
23
- export declare const IncludeBrand: unique symbol;
24
11
  export declare const UrlPatternsBrand: unique symbol;
25
12
 
26
13
  export type LayoutItem = {
27
14
  name: string;
28
15
  type: "layout";
29
16
  uses?: AllUseItems[];
30
- [LayoutBrand]: void;
31
17
  };
32
18
 
33
19
  /**
34
- * Typed layout item that carries child routes as phantom type
35
- * Used for type inference in urls() API
20
+ * Phantom inference fields attached to wrapper items (layout/cache/transition)
21
+ * so the urls() type extractor can read their child routes/responses. The fields
22
+ * never exist at runtime.
36
23
  */
37
- export type TypedLayoutItem<
24
+ type WithChildren<
25
+ TBase,
38
26
  TChildRoutes extends Record<string, any> = Record<string, string>,
39
27
  TChildResponses extends Record<string, unknown> = Record<string, unknown>,
40
- > = LayoutItem & {
28
+ > = TBase & {
41
29
  readonly __childRoutes?: TChildRoutes;
42
30
  readonly __childResponses?: TChildResponses;
43
31
  };
32
+
33
+ /**
34
+ * Typed layout item that carries child routes as phantom type
35
+ * Used for type inference in urls() API
36
+ */
37
+ export type TypedLayoutItem<
38
+ TChildRoutes extends Record<string, any> = Record<string, string>,
39
+ TChildResponses extends Record<string, unknown> = Record<string, unknown>,
40
+ > = WithChildren<LayoutItem, TChildRoutes, TChildResponses>;
44
41
  export type RouteItem = {
45
42
  name: string;
46
43
  type: "route";
47
44
  uses?: AllUseItems[];
48
- [RouteBrand]: void;
49
45
  };
50
46
 
51
47
  /**
@@ -67,64 +63,49 @@ export type ParallelItem = {
67
63
  name: string;
68
64
  type: "parallel";
69
65
  uses?: ParallelUseItem[];
70
- [ParallelBrand]: void;
71
66
  };
72
67
  export type InterceptItem = {
73
68
  name: string;
74
69
  type: "intercept";
75
70
  uses?: InterceptUseItem[];
76
- [InterceptBrand]: void;
77
71
  };
78
72
  export type LoaderItem = {
79
73
  name: string;
80
74
  type: "loader";
81
75
  uses?: LoaderUseItem[];
82
- [LoaderBrand]: void;
83
76
  };
84
77
  export type MiddlewareItem = {
85
78
  name: string;
86
79
  type: "middleware";
87
80
  uses?: AllUseItems[];
88
- [MiddlewareBrand]: void;
89
81
  };
90
82
  export type RevalidateItem = {
91
83
  name: string;
92
84
  type: "revalidate";
93
85
  uses?: AllUseItems[];
94
- [RevalidateBrand]: void;
95
86
  };
96
87
  export type LoadingItem = {
97
88
  name: string;
98
89
  type: "loading";
99
- [LoadingBrand]: void;
100
90
  };
101
91
  export type ErrorBoundaryItem = {
102
92
  name: string;
103
93
  type: "errorBoundary";
104
94
  uses?: AllUseItems[];
105
- [ErrorBoundaryBrand]: void;
106
95
  };
107
96
  export type NotFoundBoundaryItem = {
108
97
  name: string;
109
98
  type: "notFoundBoundary";
110
99
  uses?: AllUseItems[];
111
- [NotFoundBoundaryBrand]: void;
112
- };
113
- export type WhenItem = {
114
- name: string;
115
- type: "when";
116
- [WhenBrand]: void;
117
100
  };
118
101
  export type CacheItem = {
119
102
  name: string;
120
103
  type: "cache";
121
104
  uses?: AllUseItems[];
122
- [CacheBrand]: void;
123
105
  };
124
106
  export type TransitionItem = {
125
107
  name: string;
126
108
  type: "transition";
127
- [TransitionBrand]: void;
128
109
  };
129
110
 
130
111
  /**
@@ -134,10 +115,7 @@ export type TransitionItem = {
134
115
  export type TypedTransitionItem<
135
116
  TChildRoutes extends Record<string, any> = Record<string, string>,
136
117
  TChildResponses extends Record<string, unknown> = Record<string, unknown>,
137
- > = TransitionItem & {
138
- readonly __childRoutes?: TChildRoutes;
139
- readonly __childResponses?: TChildResponses;
140
- };
118
+ > = WithChildren<TransitionItem, TChildRoutes, TChildResponses>;
141
119
 
142
120
  /**
143
121
  * Typed cache item that carries child routes as phantom type
@@ -146,10 +124,7 @@ export type TypedTransitionItem<
146
124
  export type TypedCacheItem<
147
125
  TChildRoutes extends Record<string, any> = Record<string, string>,
148
126
  TChildResponses extends Record<string, unknown> = Record<string, unknown>,
149
- > = CacheItem & {
150
- readonly __childRoutes?: TChildRoutes;
151
- readonly __childResponses?: TChildResponses;
152
- };
127
+ > = WithChildren<CacheItem, TChildRoutes, TChildResponses>;
153
128
 
154
129
  /**
155
130
  * Include item for URL pattern composition (used by urls() API)
@@ -176,8 +151,14 @@ export type IncludeItem = {
176
151
  >;
177
152
  /** Root scope flag for dot-local reverse resolution */
178
153
  rootScoped?: boolean;
154
+ /**
155
+ * Positional include scope token composed from the parent scope plus this
156
+ * include's sibling index (`${parentScope}I${idx}`). Applied to direct-
157
+ * descendant shortCodes during lazy evaluation so routes inside the
158
+ * include cannot collide with siblings declared outside it.
159
+ */
160
+ includeScope?: string;
179
161
  };
180
- [IncludeBrand]: void;
181
162
  };
182
163
 
183
164
  /**
@@ -246,7 +227,6 @@ export type InterceptUseItem =
246
227
  | NotFoundBoundaryItem
247
228
  | LayoutItem
248
229
  | RouteItem
249
- | WhenItem
250
230
  | TransitionItem;
251
231
  export type LoaderUseItem = RevalidateItem | CacheItem;
252
232
 
@@ -257,3 +237,14 @@ export type LoaderUseItem = RevalidateItem | CacheItem;
257
237
  * runtime via .flat(3).
258
238
  */
259
239
  export type UseItems<T> = (T | readonly T[])[];
240
+
241
+ /**
242
+ * Union of all items that handler.use() may return.
243
+ * A handler doesn't know its mount site at definition time, so the type
244
+ * is intentionally broad — validation happens per-mount-site at runtime.
245
+ */
246
+ export type HandlerUseItem =
247
+ | RouteUseItem
248
+ | LayoutUseItem
249
+ | ParallelUseItem
250
+ | InterceptUseItem;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Normalize a router basename to its canonical form: a single leading slash,
3
+ * no trailing slash, and `undefined` for an empty or bare-"/" value.
4
+ *
5
+ * This is the single source of truth used by both createRouter() (so the RSC
6
+ * handler stores a canonical basename on the request context) and the testing
7
+ * primitives (so a consumer can pass the same un-normalized string their
8
+ * createRouter() accepts and observe the same redirect() prefixing).
9
+ */
10
+ export function normalizeBasename(basename?: string): string | undefined {
11
+ if (!basename) return undefined;
12
+ const trimmed = basename.replace(/^\/+|\/+$/g, "");
13
+ return trimmed ? "/" + trimmed : undefined;
14
+ }