@rangojs/router 0.0.0-experimental.98 → 0.0.0-experimental.98914650

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 (355) hide show
  1. package/README.md +24 -9
  2. package/dist/bin/rango.js +157 -63
  3. package/dist/testing/vitest.js +82 -0
  4. package/dist/vite/index.js +1584 -639
  5. package/package.json +60 -11
  6. package/skills/api-client/SKILL.md +211 -0
  7. package/skills/breadcrumbs/SKILL.md +60 -0
  8. package/skills/bundle-analysis/SKILL.md +159 -0
  9. package/skills/cache-guide/SKILL.md +222 -30
  10. package/skills/caching/SKILL.md +263 -8
  11. package/skills/composability/SKILL.md +27 -2
  12. package/skills/css/SKILL.md +76 -0
  13. package/skills/document-cache/SKILL.md +78 -55
  14. package/skills/handler-use/SKILL.md +3 -1
  15. package/skills/hooks/SKILL.md +235 -28
  16. package/skills/host-router/SKILL.md +122 -22
  17. package/skills/intercept/SKILL.md +29 -5
  18. package/skills/layout/SKILL.md +13 -9
  19. package/skills/links/SKILL.md +173 -17
  20. package/skills/loader/SKILL.md +170 -23
  21. package/skills/middleware/SKILL.md +16 -10
  22. package/skills/migrate-nextjs/SKILL.md +38 -16
  23. package/skills/mime-routes/SKILL.md +27 -0
  24. package/skills/observability/SKILL.md +137 -0
  25. package/skills/parallel/SKILL.md +11 -7
  26. package/skills/prerender/SKILL.md +14 -33
  27. package/skills/rango/SKILL.md +250 -26
  28. package/skills/react-compiler/SKILL.md +168 -0
  29. package/skills/response-routes/SKILL.md +114 -47
  30. package/skills/route/SKILL.md +22 -5
  31. package/skills/router-setup/SKILL.md +3 -3
  32. package/skills/server-actions/SKILL.md +78 -42
  33. package/skills/tailwind/SKILL.md +27 -3
  34. package/skills/testing/SKILL.md +129 -0
  35. package/skills/testing/bindings.md +89 -0
  36. package/skills/testing/cache-prerender.md +124 -0
  37. package/skills/testing/client-components.md +122 -0
  38. package/skills/testing/e2e-parity.md +125 -0
  39. package/skills/testing/flight.md +92 -0
  40. package/skills/testing/handles.md +129 -0
  41. package/skills/testing/loader.md +128 -0
  42. package/skills/testing/middleware.md +99 -0
  43. package/skills/testing/render-handler.md +121 -0
  44. package/skills/testing/response-routes.md +95 -0
  45. package/skills/testing/reverse-and-types.md +84 -0
  46. package/skills/testing/server-actions.md +107 -0
  47. package/skills/testing/server-tree.md +128 -0
  48. package/skills/testing/setup.md +120 -0
  49. package/skills/typesafety/SKILL.md +310 -26
  50. package/skills/use-cache/SKILL.md +36 -5
  51. package/skills/vercel/SKILL.md +107 -0
  52. package/skills/view-transitions/SKILL.md +294 -0
  53. package/src/__augment-tests__/augment.ts +81 -0
  54. package/src/__augment-tests__/augmented.check.ts +116 -0
  55. package/src/__internal.ts +0 -65
  56. package/src/browser/action-coordinator.ts +53 -36
  57. package/src/browser/action-fence.ts +47 -0
  58. package/src/browser/app-shell.ts +14 -27
  59. package/src/browser/cookie-name.ts +140 -0
  60. package/src/browser/event-controller.ts +37 -143
  61. package/src/browser/history-state.ts +21 -0
  62. package/src/browser/index.ts +3 -3
  63. package/src/browser/invalidate-client-cache.ts +52 -0
  64. package/src/browser/navigation-bridge.ts +30 -59
  65. package/src/browser/navigation-client.ts +96 -84
  66. package/src/browser/navigation-store-handle.ts +38 -0
  67. package/src/browser/navigation-store.ts +32 -82
  68. package/src/browser/navigation-transaction.ts +9 -59
  69. package/src/browser/partial-update.ts +60 -127
  70. package/src/browser/prefetch/cache.ts +82 -72
  71. package/src/browser/prefetch/fetch.ts +108 -33
  72. package/src/browser/prefetch/queue.ts +6 -3
  73. package/src/browser/rango-state.ts +157 -115
  74. package/src/browser/react/Link.tsx +0 -2
  75. package/src/browser/react/NavigationProvider.tsx +41 -48
  76. package/src/browser/react/ScrollRestoration.tsx +10 -6
  77. package/src/browser/react/filter-segment-order.ts +0 -2
  78. package/src/browser/react/index.ts +0 -48
  79. package/src/browser/react/location-state-shared.ts +166 -8
  80. package/src/browser/react/location-state.ts +39 -14
  81. package/src/browser/react/use-action.ts +6 -15
  82. package/src/browser/react/use-handle.ts +17 -14
  83. package/src/browser/react/use-link-status.ts +0 -4
  84. package/src/browser/react/use-navigation.ts +0 -3
  85. package/src/browser/react/use-params.ts +3 -6
  86. package/src/browser/react/use-reverse.ts +106 -0
  87. package/src/browser/react/use-router.ts +20 -5
  88. package/src/browser/react/use-search-params.ts +0 -5
  89. package/src/browser/react/use-segments.ts +0 -13
  90. package/src/browser/response-adapter.ts +52 -1
  91. package/src/browser/rsc-router.tsx +70 -34
  92. package/src/browser/scroll-restoration.ts +22 -14
  93. package/src/browser/segment-structure-assert.ts +2 -2
  94. package/src/browser/server-action-bridge.ts +168 -44
  95. package/src/browser/types.ts +36 -21
  96. package/src/browser/validate-redirect-origin.ts +43 -16
  97. package/src/build/collect-fallback-refs.ts +107 -0
  98. package/src/build/generate-manifest.ts +60 -35
  99. package/src/build/generate-route-types.ts +3 -0
  100. package/src/build/index.ts +8 -2
  101. package/src/build/prefix-tree-utils.ts +123 -0
  102. package/src/build/route-trie.ts +89 -11
  103. package/src/build/route-types/codegen.ts +4 -4
  104. package/src/build/route-types/include-resolution.ts +1 -1
  105. package/src/build/route-types/param-extraction.ts +6 -3
  106. package/src/build/route-types/per-module-writer.ts +7 -4
  107. package/src/build/route-types/router-processing.ts +122 -22
  108. package/src/build/route-types/scan-filter.ts +1 -1
  109. package/src/build/route-types/source-scan.ts +118 -0
  110. package/src/build/runtime-discovery.ts +9 -20
  111. package/src/cache/cache-error.ts +104 -0
  112. package/src/cache/cache-policy.ts +68 -28
  113. package/src/cache/cache-runtime.ts +134 -32
  114. package/src/cache/cache-scope.ts +100 -74
  115. package/src/cache/cache-tag.ts +98 -0
  116. package/src/cache/cf/cf-cache-store.ts +2255 -238
  117. package/src/cache/cf/index.ts +6 -16
  118. package/src/cache/document-cache.ts +61 -20
  119. package/src/cache/handle-snapshot.ts +63 -0
  120. package/src/cache/index.ts +22 -20
  121. package/src/cache/memory-segment-store.ts +136 -37
  122. package/src/cache/profile-registry.ts +6 -30
  123. package/src/cache/read-through-swr.ts +41 -11
  124. package/src/cache/segment-codec.ts +0 -16
  125. package/src/cache/tag-invalidation.ts +230 -0
  126. package/src/cache/types.ts +33 -100
  127. package/src/cache/vercel/index.ts +11 -0
  128. package/src/cache/vercel/vercel-cache-store.ts +799 -0
  129. package/src/client.rsc.tsx +6 -21
  130. package/src/client.tsx +25 -61
  131. package/src/component-utils.ts +19 -0
  132. package/src/context-var.ts +17 -5
  133. package/src/decode-loader-results.ts +36 -0
  134. package/src/defer.ts +196 -0
  135. package/src/deps/ssr.ts +0 -1
  136. package/src/errors.ts +30 -4
  137. package/src/handle.ts +31 -23
  138. package/src/handles/MetaTags.tsx +0 -14
  139. package/src/handles/breadcrumbs.ts +16 -5
  140. package/src/handles/meta.ts +0 -39
  141. package/src/host/cookie-handler.ts +0 -36
  142. package/src/host/errors.ts +0 -24
  143. package/src/host/index.ts +8 -2
  144. package/src/host/pattern-matcher.ts +7 -50
  145. package/src/host/router.ts +107 -99
  146. package/src/host/testing.ts +40 -27
  147. package/src/host/types.ts +37 -4
  148. package/src/host/utils.ts +1 -1
  149. package/src/href-client.ts +137 -22
  150. package/src/index.rsc.ts +63 -9
  151. package/src/index.ts +64 -9
  152. package/src/internal-debug.ts +2 -4
  153. package/src/loader-store.ts +500 -0
  154. package/src/loader.rsc.ts +20 -13
  155. package/src/loader.ts +12 -11
  156. package/src/missing-id-error.ts +68 -0
  157. package/src/network-error-thrower.tsx +1 -6
  158. package/src/outlet-provider.tsx +1 -5
  159. package/src/prerender/param-hash.ts +10 -11
  160. package/src/prerender/store.ts +32 -37
  161. package/src/prerender.ts +61 -6
  162. package/src/redirect-origin.ts +100 -0
  163. package/src/response-utils.ts +9 -0
  164. package/src/reverse.ts +65 -41
  165. package/src/root-error-boundary.tsx +1 -19
  166. package/src/route-content-wrapper.tsx +7 -72
  167. package/src/route-definition/dsl-helpers.ts +244 -281
  168. package/src/route-definition/helper-factories.ts +29 -139
  169. package/src/route-definition/helpers-types.ts +40 -17
  170. package/src/route-definition/redirect.ts +43 -9
  171. package/src/route-definition/resolve-handler-use.ts +6 -0
  172. package/src/route-definition/use-item-types.ts +32 -0
  173. package/src/route-map-builder.ts +0 -16
  174. package/src/route-types.ts +19 -41
  175. package/src/router/basename.ts +14 -0
  176. package/src/router/content-negotiation.ts +15 -15
  177. package/src/router/error-handling.ts +13 -17
  178. package/src/router/find-match.ts +44 -23
  179. package/src/router/handler-context.ts +4 -42
  180. package/src/router/intercept-resolution.ts +14 -19
  181. package/src/router/lazy-includes.ts +9 -46
  182. package/src/router/loader-resolution.ts +91 -46
  183. package/src/router/logging.ts +0 -6
  184. package/src/router/manifest.ts +18 -29
  185. package/src/router/match-api.ts +0 -20
  186. package/src/router/match-context.ts +0 -22
  187. package/src/router/match-handlers.ts +57 -58
  188. package/src/router/match-middleware/background-revalidation.ts +0 -7
  189. package/src/router/match-middleware/cache-lookup.ts +150 -271
  190. package/src/router/match-middleware/cache-store.ts +3 -33
  191. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  192. package/src/router/match-middleware/segment-resolution.ts +0 -22
  193. package/src/router/match-pipelines.ts +1 -42
  194. package/src/router/match-result.ts +31 -80
  195. package/src/router/metrics.ts +0 -34
  196. package/src/router/middleware-types.ts +0 -116
  197. package/src/router/middleware.ts +118 -133
  198. package/src/router/navigation-snapshot.ts +0 -51
  199. package/src/router/params-util.ts +23 -0
  200. package/src/router/pattern-matching.ts +20 -58
  201. package/src/router/prerender-match.ts +99 -63
  202. package/src/router/preview-match.ts +3 -1
  203. package/src/router/request-classification.ts +28 -62
  204. package/src/router/revalidation.ts +50 -56
  205. package/src/router/route-snapshot.ts +0 -1
  206. package/src/router/router-context.ts +0 -27
  207. package/src/router/router-interfaces.ts +68 -35
  208. package/src/router/router-options.ts +55 -1
  209. package/src/router/router-registry.ts +2 -5
  210. package/src/router/segment-resolution/fresh.ts +44 -63
  211. package/src/router/segment-resolution/helpers.ts +34 -0
  212. package/src/router/segment-resolution/loader-cache.ts +40 -37
  213. package/src/router/segment-resolution/revalidation.ts +203 -285
  214. package/src/router/segment-resolution/static-store.ts +19 -5
  215. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  216. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  217. package/src/router/segment-resolution.ts +4 -1
  218. package/src/router/segment-wrappers.ts +0 -3
  219. package/src/router/state-cookie-name.ts +33 -0
  220. package/src/router/substitute-pattern-params.ts +56 -0
  221. package/src/router/telemetry-otel.ts +0 -20
  222. package/src/router/telemetry.ts +96 -19
  223. package/src/router/timeout.ts +0 -20
  224. package/src/router/trie-matching.ts +87 -47
  225. package/src/router/types.ts +9 -63
  226. package/src/router/url-params.ts +0 -5
  227. package/src/router.ts +80 -41
  228. package/src/rsc/handler-context.ts +3 -2
  229. package/src/rsc/handler.ts +83 -78
  230. package/src/rsc/helpers.ts +93 -5
  231. package/src/rsc/index.ts +1 -1
  232. package/src/rsc/json-route-result.ts +38 -0
  233. package/src/rsc/manifest-init.ts +28 -41
  234. package/src/rsc/origin-guard.ts +39 -25
  235. package/src/rsc/progressive-enhancement.ts +12 -1
  236. package/src/rsc/redirect-guard.ts +99 -0
  237. package/src/rsc/response-error.ts +79 -12
  238. package/src/rsc/response-route-handler.ts +76 -62
  239. package/src/rsc/rsc-rendering.ts +41 -60
  240. package/src/rsc/runtime-warnings.ts +23 -10
  241. package/src/rsc/server-action.ts +62 -67
  242. package/src/rsc/ssr-setup.ts +16 -0
  243. package/src/rsc/types.ts +10 -5
  244. package/src/runtime-env.ts +18 -0
  245. package/src/search-params.ts +4 -20
  246. package/src/segment-loader-promise.ts +14 -2
  247. package/src/segment-system.tsx +199 -142
  248. package/src/serialize.ts +243 -0
  249. package/src/server/context.ts +150 -51
  250. package/src/server/cookie-store.ts +80 -5
  251. package/src/server/handle-store.ts +7 -24
  252. package/src/server/loader-registry.ts +5 -24
  253. package/src/server/request-context.ts +165 -87
  254. package/src/ssr/index.tsx +14 -14
  255. package/src/static-handler.ts +10 -13
  256. package/src/testing/cache-status.ts +162 -0
  257. package/src/testing/collect-handle.ts +40 -0
  258. package/src/testing/dispatch.ts +618 -0
  259. package/src/testing/dom.entry.ts +22 -0
  260. package/src/testing/e2e/fixture.ts +188 -0
  261. package/src/testing/e2e/index.ts +128 -0
  262. package/src/testing/e2e/matchers.ts +35 -0
  263. package/src/testing/e2e/page-helpers.ts +272 -0
  264. package/src/testing/e2e/parity.ts +387 -0
  265. package/src/testing/e2e/server.ts +195 -0
  266. package/src/testing/flight-matchers.ts +97 -0
  267. package/src/testing/flight-normalize.ts +11 -0
  268. package/src/testing/flight-runtime.d.ts +57 -0
  269. package/src/testing/flight-tree.ts +682 -0
  270. package/src/testing/flight.entry.ts +52 -0
  271. package/src/testing/flight.ts +232 -0
  272. package/src/testing/generated-routes.ts +183 -0
  273. package/src/testing/index.ts +99 -0
  274. package/src/testing/internal/context.ts +348 -0
  275. package/src/testing/internal/flight-client-globals.ts +30 -0
  276. package/src/testing/internal/seed-vars.ts +54 -0
  277. package/src/testing/render-handler.ts +330 -0
  278. package/src/testing/render-route.tsx +566 -0
  279. package/src/testing/run-loader.ts +378 -0
  280. package/src/testing/run-middleware.ts +205 -0
  281. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  282. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  283. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  284. package/src/testing/vitest-stubs/version.ts +5 -0
  285. package/src/testing/vitest.ts +305 -0
  286. package/src/theme/ThemeProvider.tsx +0 -52
  287. package/src/theme/ThemeScript.tsx +0 -6
  288. package/src/theme/constants.ts +0 -12
  289. package/src/theme/index.ts +0 -7
  290. package/src/theme/theme-context.ts +1 -5
  291. package/src/theme/theme-script.ts +0 -14
  292. package/src/theme/use-theme.ts +0 -3
  293. package/src/types/boundaries.ts +0 -35
  294. package/src/types/cache-types.ts +13 -4
  295. package/src/types/error-types.ts +30 -90
  296. package/src/types/global-namespace.ts +54 -41
  297. package/src/types/handler-context.ts +97 -22
  298. package/src/types/index.ts +1 -10
  299. package/src/types/loader-types.ts +6 -3
  300. package/src/types/request-scope.ts +0 -19
  301. package/src/types/route-config.ts +6 -50
  302. package/src/types/route-entry.ts +0 -6
  303. package/src/types/segments.ts +18 -14
  304. package/src/urls/include-helper.ts +9 -56
  305. package/src/urls/index.ts +1 -11
  306. package/src/urls/path-helper-types.ts +19 -5
  307. package/src/urls/path-helper.ts +17 -106
  308. package/src/urls/pattern-types.ts +36 -19
  309. package/src/urls/response-types.ts +20 -19
  310. package/src/urls/type-extraction.ts +58 -139
  311. package/src/urls/urls-function.ts +1 -18
  312. package/src/use-loader.tsx +292 -107
  313. package/src/vite/debug.ts +1 -0
  314. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  315. package/src/vite/discovery/discover-routers.ts +95 -82
  316. package/src/vite/discovery/discovery-errors.ts +194 -0
  317. package/src/vite/discovery/prerender-collection.ts +26 -34
  318. package/src/vite/discovery/route-types-writer.ts +40 -84
  319. package/src/vite/discovery/state.ts +39 -1
  320. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  321. package/src/vite/index.ts +4 -0
  322. package/src/vite/plugin-types.ts +185 -10
  323. package/src/vite/plugins/cjs-to-esm.ts +3 -18
  324. package/src/vite/plugins/client-ref-dedup.ts +0 -11
  325. package/src/vite/plugins/client-ref-hashing.ts +12 -11
  326. package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
  327. package/src/vite/plugins/expose-action-id.ts +4 -75
  328. package/src/vite/plugins/expose-id-utils.ts +3 -54
  329. package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
  330. package/src/vite/plugins/expose-ids/handler-transform.ts +6 -74
  331. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
  332. package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
  333. package/src/vite/plugins/expose-internal-ids.ts +57 -67
  334. package/src/vite/plugins/performance-tracks.ts +9 -16
  335. package/src/vite/plugins/refresh-cmd.ts +1 -1
  336. package/src/vite/plugins/use-cache-transform.ts +26 -49
  337. package/src/vite/plugins/vercel-output.ts +258 -0
  338. package/src/vite/plugins/version-injector.ts +2 -32
  339. package/src/vite/plugins/version-plugin.ts +32 -23
  340. package/src/vite/plugins/virtual-entries.ts +35 -17
  341. package/src/vite/rango.ts +148 -115
  342. package/src/vite/router-discovery.ts +220 -68
  343. package/src/vite/utils/ast-handler-extract.ts +15 -31
  344. package/src/vite/utils/bundle-analysis.ts +10 -15
  345. package/src/vite/utils/client-chunks.ts +184 -0
  346. package/src/vite/utils/forward-user-plugins.ts +171 -0
  347. package/src/vite/utils/manifest-utils.ts +4 -59
  348. package/src/vite/utils/package-resolution.ts +1 -73
  349. package/src/vite/utils/prerender-utils.ts +0 -35
  350. package/src/vite/utils/shared-utils.ts +95 -43
  351. package/src/browser/action-response-classifier.ts +0 -99
  352. package/src/browser/react/use-client-cache.ts +0 -58
  353. package/src/browser/shallow.ts +0 -40
  354. package/src/handles/index.ts +0 -7
  355. package/src/router/middleware-cookies.ts +0 -55
@@ -2,18 +2,16 @@ import type { ComponentType, ReactNode } from "react";
2
2
  import type { SerializedManifest } from "../debug.js";
3
3
  import type { ReverseFunction } from "../reverse.js";
4
4
  import type { UrlPatterns } from "../urls.js";
5
- import type { UrlBuilder } from "../urls/pattern-types.js";
5
+ import type { UrlBuilder, EnvCompatible } from "../urls/pattern-types.js";
6
6
  import type { EntryData } from "../server/context";
7
7
  import type { ErrorInfo, MatchResult } from "../types";
8
8
  import type { NonceProvider } from "../rsc/types.js";
9
9
  import type { ExecutionContext } from "../server/request-context.js";
10
- import type {
11
- SerializedSegmentData,
12
- SegmentHandleData,
13
- } from "../cache/types.js";
10
+ import type { SerializedSegmentData } from "../cache/types.js";
14
11
  import type { MiddlewareEntry, MiddlewareFn } from "./middleware.js";
12
+ import type { ExtractParams } from "../types/route-config.js";
15
13
  import { RSC_ROUTER_BRAND } from "./router-registry.js";
16
- import type { RSCRouterOptions, RootLayoutProps } from "./router-options.js";
14
+ import type { RangoOptions, RootLayoutProps } from "./router-options.js";
17
15
  import type { DefaultVars } from "../types/global-namespace.js";
18
16
  import type { ResolvedTimeouts, OnTimeoutCallback } from "./timeout.js";
19
17
 
@@ -49,16 +47,16 @@ type MergeRoutesWithResponses<
49
47
  };
50
48
 
51
49
  /**
52
- * Public RSC Router interface — the user-facing API surface.
50
+ * Public Rango router interface — the user-facing API surface.
53
51
  *
54
52
  * Users interact with this type when building and using routers.
55
- * Internal framework code uses RSCRouterInternal (via toInternal()) to access
53
+ * Internal framework code uses RangoInternal (via toInternal()) to access
56
54
  * matching, build-time, and configuration members that are not part of the
57
55
  * public contract.
58
56
  *
59
57
  * TRoutes accumulates all registered route types through the builder chain.
60
58
  */
61
- export interface RSCRouter<
59
+ export interface Rango<
62
60
  TEnv = any,
63
61
  TRoutes extends Record<string, unknown> = Record<string, string>,
64
62
  > {
@@ -89,16 +87,16 @@ export interface RSCRouter<
89
87
  * ])
90
88
  * ```
91
89
  */
92
- routes<T extends UrlPatterns<TEnv, any>>(
93
- patterns: T,
94
- ): RSCRouter<
90
+ routes<T extends UrlPatterns<any, any, any>>(
91
+ patterns: T & EnvCompatible<T, TEnv>,
92
+ ): Rango<
95
93
  TEnv,
96
94
  TRoutes &
97
95
  (NonNullable<T["_routes"]> extends Record<string, unknown>
98
96
  ? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
99
97
  : Record<string, string>)
100
98
  >;
101
- routes(builder: UrlBuilder<TEnv>): RSCRouter<TEnv, TRoutes>;
99
+ routes(builder: UrlBuilder<TEnv>): Rango<TEnv, TRoutes>;
102
100
 
103
101
  /**
104
102
  * Add global middleware that runs on all routes
@@ -108,13 +106,18 @@ export interface RSCRouter<
108
106
  * createRouter({ document: RootLayout })
109
107
  * .use(loggerMiddleware) // All routes
110
108
  * .use("/api/*", rateLimiter) // Pattern match
109
+ * .use("/users/:id", (ctx) => {}) // ctx.params.id is typed
111
110
  * .routes(urlpatterns)
112
111
  * ```
113
112
  */
113
+ use<Pattern extends string>(
114
+ pattern: Pattern,
115
+ middleware: MiddlewareFn<TEnv, ExtractParams<Pattern>>,
116
+ ): Rango<TEnv, TRoutes>;
114
117
  use(
115
118
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
116
119
  middleware?: MiddlewareFn<TEnv>,
117
- ): RSCRouter<TEnv, TRoutes>;
120
+ ): Rango<TEnv, TRoutes>;
118
121
 
119
122
  /**
120
123
  * Type-safe URL builder for registered routes
@@ -141,7 +144,7 @@ export interface RSCRouter<
141
144
  * type AppRoutes = typeof _router.routeMap;
142
145
  *
143
146
  * declare global {
144
- * namespace RSCRouter {
147
+ * namespace Rango {
145
148
  * interface RegisteredRoutes extends AppRoutes {}
146
149
  * }
147
150
  * }
@@ -177,16 +180,16 @@ export interface RSCRouter<
177
180
  }
178
181
 
179
182
  /**
180
- * Internal RSC Router interface — the full framework-facing API.
183
+ * Internal Rango router interface — the full framework-facing API.
181
184
  *
182
185
  * This type includes all members used by the Vite plugin, RSC handler,
183
186
  * pre-rendering pipeline, and other framework internals. It is NOT exported
184
187
  * from the public package API.
185
188
  *
186
- * Use toInternal(router) to assert a public RSCRouter into this type
189
+ * Use toInternal(router) to assert a public Rango into this type
187
190
  * at the boundary where framework code receives a user-provided router.
188
191
  */
189
- export interface RSCRouterInternal<
192
+ export interface RangoInternal<
190
193
  TEnv = any,
191
194
  TRoutes extends Record<string, unknown> = Record<string, string>,
192
195
  > {
@@ -206,26 +209,36 @@ export interface RSCRouterInternal<
206
209
  readonly basename: string | undefined;
207
210
 
208
211
  /**
209
- * Register routes using URL patterns from urls() or a builder function
210
- */
211
- routes<T extends UrlPatterns<TEnv, any>>(
212
- patterns: T,
213
- ): RSCRouter<
212
+ * Register routes using URL patterns from urls() or a builder function.
213
+ *
214
+ * Env compatibility is checked by EnvCompatible: an env-agnostic urls() block
215
+ * (its env is `unknown` — e.g. a shared module, or an app that does not augment
216
+ * `Rango.Env`) attaches to any router, while a urls<TEnv>() block carrying a
217
+ * concrete env is accepted only when this router's `TEnv` satisfies it. So a
218
+ * `urls<{ DB }>()` cannot be mounted on a `createRouter<{}>()`.
219
+ */
220
+ routes<T extends UrlPatterns<any, any, any>>(
221
+ patterns: T & EnvCompatible<T, TEnv>,
222
+ ): Rango<
214
223
  TEnv,
215
224
  TRoutes &
216
225
  (NonNullable<T["_routes"]> extends Record<string, unknown>
217
226
  ? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
218
227
  : Record<string, string>)
219
228
  >;
220
- routes(builder: UrlBuilder<TEnv>): RSCRouter<TEnv, TRoutes>;
229
+ routes(builder: UrlBuilder<TEnv>): Rango<TEnv, TRoutes>;
221
230
 
222
231
  /**
223
232
  * Add global middleware that runs on all routes
224
233
  */
234
+ use<Pattern extends string>(
235
+ pattern: Pattern,
236
+ middleware: MiddlewareFn<TEnv, ExtractParams<Pattern>>,
237
+ ): Rango<TEnv, TRoutes>;
225
238
  use(
226
239
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
227
240
  middleware?: MiddlewareFn<TEnv>,
228
- ): RSCRouter<TEnv, TRoutes>;
241
+ ): Rango<TEnv, TRoutes>;
229
242
 
230
243
  /**
231
244
  * Type-safe URL builder for registered routes
@@ -247,17 +260,17 @@ export interface RSCRouterInternal<
247
260
  * Error callback for monitoring/alerting
248
261
  * Called when errors occur in loaders, actions, or routes
249
262
  */
250
- readonly onError?: RSCRouterOptions<TEnv>["onError"];
263
+ readonly onError?: RangoOptions<TEnv>["onError"];
251
264
 
252
265
  /**
253
266
  * Cache configuration
254
267
  */
255
- readonly cache?: RSCRouterOptions<TEnv>["cache"];
268
+ readonly cache?: RangoOptions<TEnv>["cache"];
256
269
 
257
270
  /**
258
271
  * Not found component to render when no route matches
259
272
  */
260
- readonly notFound?: RSCRouterOptions<TEnv>["notFound"];
273
+ readonly notFound?: RangoOptions<TEnv>["notFound"];
261
274
 
262
275
  /**
263
276
  * Resolved theme configuration (null if theme not enabled)
@@ -287,6 +300,13 @@ export interface RSCRouterInternal<
287
300
  */
288
301
  readonly prefetchCacheTTL: number;
289
302
 
303
+ /**
304
+ * Resolved rango state cookie name (`{prefix}_{routerId}`), composed once at
305
+ * router init and shipped to the client in payload metadata. The server-side
306
+ * cookie writer reads it from here; the client reads it from metadata.
307
+ */
308
+ readonly resolvedStateCookieName: string;
309
+
290
310
  /**
291
311
  * Whether connection warmup is enabled.
292
312
  * When true, the client sends HEAD /?_rsc_warmup after idle periods
@@ -359,6 +379,17 @@ export interface RSCRouterInternal<
359
379
  /** @internal basename for runtime manifest generation */
360
380
  readonly __basename?: string;
361
381
 
382
+ /**
383
+ * @internal Router-level error/notFound fallbacks (`createRouter` options),
384
+ * exposed for the build-time clientChunks discovery so a `"use client"`
385
+ * default boundary is routed into the dedicated `app-fallback` chunk. Unlike
386
+ * the route-tree `errorBoundary()`/`notFoundBoundary()` helpers these never
387
+ * land in `EntryData`, so they are read directly off the router instance.
388
+ */
389
+ readonly __defaultErrorBoundary?: RangoOptions<TEnv>["defaultErrorBoundary"];
390
+ readonly __defaultNotFoundBoundary?: RangoOptions<TEnv>["defaultNotFoundBoundary"];
391
+ readonly __notFound?: RangoOptions<TEnv>["notFound"];
392
+
362
393
  match(
363
394
  request: Request,
364
395
  input?: RouterRequestInput<TEnv>,
@@ -378,11 +409,13 @@ export interface RSCRouterInternal<
378
409
  devMode?: boolean,
379
410
  ): Promise<{
380
411
  segments: SerializedSegmentData[];
381
- handles: Record<string, SegmentHandleData>;
412
+ /** RSC-encoded handle map ("" when none) — see handle-snapshot.ts. */
413
+ handles: string;
382
414
  routeName: string;
383
415
  params: Record<string, string>;
384
416
  interceptSegments?: SerializedSegmentData[];
385
- interceptHandles?: Record<string, SegmentHandleData>;
417
+ /** RSC-encoded MERGED (main + intercept) handle map for the intercept artifact. */
418
+ interceptHandles?: string;
386
419
  passthrough?: true;
387
420
  } | null>;
388
421
 
@@ -396,7 +429,7 @@ export interface RSCRouterInternal<
396
429
  routeName?: string,
397
430
  buildEnv?: any,
398
431
  devMode?: boolean,
399
- ): Promise<{ encoded: string; handles: Record<string, unknown[]> } | null>;
432
+ ): Promise<{ encoded: string; handles: string } | null>;
400
433
 
401
434
  /**
402
435
  * Preview match - returns route middleware without segment resolution.
@@ -469,16 +502,16 @@ export interface RSCRouterInternal<
469
502
  }
470
503
 
471
504
  /**
472
- * Assert a public RSCRouter into the internal type.
505
+ * Assert a public Rango into the internal type.
473
506
  *
474
507
  * Use this at the boundary where framework code receives a user-provided
475
508
  * router and needs access to internal members (match, config, build-time).
476
509
  * The cast is safe because createRouter() always produces an object that
477
- * satisfies RSCRouterInternal; the public type is just a narrower view.
510
+ * satisfies RangoInternal; the public type is just a narrower view.
478
511
  */
479
512
  export function toInternal<
480
513
  TEnv = any,
481
514
  TRoutes extends Record<string, unknown> = Record<string, string>,
482
- >(router: RSCRouter<TEnv, TRoutes>): RSCRouterInternal<TEnv, TRoutes> {
483
- return router as RSCRouterInternal<TEnv, TRoutes>;
515
+ >(router: Rango<TEnv, TRoutes>): RangoInternal<TEnv, TRoutes> {
516
+ return router as RangoInternal<TEnv, TRoutes>;
484
517
  }
@@ -73,7 +73,7 @@ export interface RootLayoutProps {
73
73
  /**
74
74
  * Router configuration options
75
75
  */
76
- export interface RSCRouterOptions<TEnv = any> {
76
+ export interface RangoOptions<TEnv = any> {
77
77
  /**
78
78
  * Unique identifier for this router instance.
79
79
  * Used to namespace static output files and route maps.
@@ -132,6 +132,21 @@ export interface RSCRouterOptions<TEnv = any> {
132
132
  */
133
133
  allowDebugManifest?: boolean;
134
134
 
135
+ /**
136
+ * DEVELOPMENT/TEST ONLY. Emit an `X-Rango-Cache` response header describing
137
+ * the cache status of the matched route, for use by testing primitives such
138
+ * as `assertCacheStatus`.
139
+ *
140
+ * Defaults to `false`. When neither this option nor the
141
+ * `RANGO_TEST_SIGNALS=1` environment flag is set, NO header is emitted and
142
+ * router output is byte-identical to the default.
143
+ *
144
+ * The header encodes per-segment (v1: coarse route-level) status keyed by the
145
+ * route NAME, e.g. `X-Rango-Cache: product.detail=hit`. Do NOT enable in
146
+ * production — it exposes internal cache decisions.
147
+ */
148
+ debugCacheSignal?: boolean;
149
+
135
150
  /**
136
151
  * Document component that wraps the entire application.
137
152
  *
@@ -357,6 +372,30 @@ export interface RSCRouterOptions<TEnv = any> {
357
372
  */
358
373
  theme?: import("../theme/types.js").ThemeConfig | true;
359
374
 
375
+ /**
376
+ * Default for whether the router wraps `transition()` segments in its own
377
+ * React `<ViewTransition>` boundary (experimental React only).
378
+ *
379
+ * - "auto" (default): every route/layout that opts in via `transition()`
380
+ * gets a router-owned cross-fade.
381
+ * - false: the router never places its own boundary. Routes that use
382
+ * `transition()` still drive navigation through startTransition (so loaders
383
+ * hold instead of flashing a skeleton) and still let consumer-placed
384
+ * `<ViewTransition>` elements animate — the router just contributes no
385
+ * cross-fade of its own. This is the "router triggers, you place the
386
+ * transitions" model.
387
+ *
388
+ * A per-segment `transition({ viewTransition })` overrides this default.
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * // App-wide: drive + hold, but never auto-wrap. Place <ViewTransition>
393
+ * // yourself in components where you want a morph.
394
+ * const router = createRouter<AppEnv>({ viewTransition: false });
395
+ * ```
396
+ */
397
+ viewTransition?: "auto" | false;
398
+
360
399
  /**
361
400
  * URL patterns to register with the router.
362
401
  *
@@ -457,6 +496,21 @@ export interface RSCRouterOptions<TEnv = any> {
457
496
  */
458
497
  prefetchCacheTTL?: number | false;
459
498
 
499
+ /**
500
+ * Prefix for the rango state cookie name. The resolved name is
501
+ * `{prefix}_{routerId}`; the prefix is sanitized to cookie-name-safe
502
+ * characters (`[A-Za-z0-9-]`) and an empty result falls back to the default.
503
+ *
504
+ * The rango state cookie keys the client's prefetch / HTTP caches. Overriding
505
+ * the prefix lets you align it with cookie-naming policies or consent-manager
506
+ * classification lists, or avoid colliding with an existing `rango-state`
507
+ * cookie. It is not a full-name override: the `_{routerId}` suffix is what
508
+ * keeps sibling apps on one origin from clobbering each other's state.
509
+ *
510
+ * @default "rango-state"
511
+ */
512
+ stateCookiePrefix?: string;
513
+
460
514
  /**
461
515
  * Enable connection warmup to keep TCP+TLS alive after idle periods.
462
516
  *
@@ -1,4 +1,4 @@
1
- import type { RSCRouterInternal } from "./router-interfaces.js";
1
+ import type { RangoInternal } from "./router-interfaces.js";
2
2
 
3
3
  /**
4
4
  * Brand marker for identifying router instances at build time.
@@ -12,10 +12,7 @@ export const RSC_ROUTER_BRAND = "__rsc_router__" as const;
12
12
  * Used by the Vite plugin at build time to discover routers and extract
13
13
  * manifests, prefix trees, and pre-render candidates.
14
14
  */
15
- export const RouterRegistry: Map<
16
- string,
17
- RSCRouterInternal<any, any>
18
- > = new Map();
15
+ export const RouterRegistry: Map<string, RangoInternal<any, any>> = new Map();
19
16
 
20
17
  export let routerAutoId = 0;
21
18
 
@@ -27,58 +27,17 @@ import {
27
27
  tryStaticSlot,
28
28
  resolveLayoutComponent,
29
29
  resolveWithErrorBoundary,
30
+ warnOnStreamedResponse,
30
31
  } from "./helpers.js";
32
+ import { applyViewTransitionDefault } from "./view-transition-default.js";
31
33
  import { getRouterContext } from "../router-context.js";
32
- import { resolveSink, safeEmit } from "../telemetry.js";
34
+ import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
33
35
  import {
34
36
  track,
35
- RSCRouterContext,
37
+ RangoContext,
36
38
  runInsideLoaderScope,
37
39
  } from "../../server/context.js";
38
40
 
39
- // ---------------------------------------------------------------------------
40
- // Streamed handler telemetry
41
- // ---------------------------------------------------------------------------
42
-
43
- /**
44
- * Attach a fire-and-forget rejection observer to a streamed handler promise.
45
- * React catches the actual error via its error boundary; this only emits
46
- * the handler.error telemetry event.
47
- */
48
- function observeStreamedHandler(
49
- promise: Promise<ReactNode>,
50
- segmentId: string,
51
- segmentType: string,
52
- pathname?: string,
53
- routeKey?: string,
54
- params?: Record<string, string>,
55
- ): void {
56
- let routerCtx;
57
- try {
58
- routerCtx = getRouterContext();
59
- } catch {
60
- return;
61
- }
62
- if (!routerCtx?.telemetry) return;
63
- const sink = resolveSink(routerCtx.telemetry);
64
- const reqId = routerCtx.requestId;
65
- promise.catch((err: unknown) => {
66
- const errorObj = err instanceof Error ? err : new Error(String(err));
67
- safeEmit(sink, {
68
- type: "handler.error",
69
- timestamp: performance.now(),
70
- requestId: reqId,
71
- segmentId,
72
- segmentType,
73
- error: errorObj,
74
- handledByBoundary: true,
75
- pathname,
76
- routeKey,
77
- params,
78
- });
79
- });
80
- }
81
-
82
41
  // ---------------------------------------------------------------------------
83
42
  // Fresh path (full match, no revalidation)
84
43
  // ---------------------------------------------------------------------------
@@ -132,18 +91,32 @@ export async function resolveLoaders<TEnv>(
132
91
 
133
92
  // Loading disabled: still start all loaders in parallel, but only emit
134
93
  // settled promises so handlers don't stream loading placeholders.
135
- const pendingLoaderData = loaderEntries.map((loaderEntry) => {
94
+ //
95
+ // Wrap each loader promise with wrapLoaderPromise BEFORE awaiting. The wrapped
96
+ // promise resolves to a LoaderDataResult and never rejects, routing a failed
97
+ // loader to its own per-loader error boundary. Awaiting the RAW promises here
98
+ // instead would (1) propagate a rejection to the segment-level boundary,
99
+ // collapsing the whole entry and discarding successful sibling data, and
100
+ // (2) leave the other in-flight raw promises without a .catch, producing
101
+ // unhandled rejections. Mirrors the loading path and intercept-resolution.
102
+ const pendingLoaderData = loaderEntries.map((loaderEntry, i) => {
103
+ const { loader } = loaderEntry;
104
+ const segmentId = `${shortCode}D${i}.${loader.$$id}`;
136
105
  const start = performance.now();
137
- const promise = runInsideLoaderScope(() =>
138
- resolveLoaderData(loaderEntry, ctx, ctx.pathname),
106
+ const wrapped = deps.wrapLoaderPromise(
107
+ runInsideLoaderScope(() =>
108
+ resolveLoaderData(loaderEntry, ctx, ctx.pathname),
109
+ ),
110
+ entry,
111
+ segmentId,
112
+ ctx.pathname,
139
113
  );
140
- return { promise, start, loaderId: loaderEntry.loader.$$id };
114
+ return { wrapped, start, segmentId, loaderId: loader.$$id };
141
115
  });
142
- await Promise.all(pendingLoaderData.map((p) => p.promise));
116
+ await Promise.all(pendingLoaderData.map((p) => p.wrapped));
143
117
 
144
118
  return loaderEntries.map((loaderEntry, i) => {
145
119
  const { loader } = loaderEntry;
146
- const segmentId = `${shortCode}D${i}.${loader.$$id}`;
147
120
  const pending = pendingLoaderData[i]!;
148
121
  if (ms && !ms.metrics.some((m) => m.label === `loader:${loader.$$id}`)) {
149
122
  // All loaders ran in parallel via Promise.all — each span covers
@@ -159,19 +132,14 @@ export async function resolveLoaders<TEnv>(
159
132
  );
160
133
  }
161
134
  return {
162
- id: segmentId,
135
+ id: pending.segmentId,
163
136
  namespace: entry.id,
164
137
  type: "loader" as const,
165
138
  index: i,
166
139
  component: null,
167
140
  params: ctx.params,
168
141
  loaderId: loader.$$id,
169
- loaderData: deps.wrapLoaderPromise(
170
- pending.promise,
171
- entry,
172
- segmentId,
173
- ctx.pathname,
174
- ),
142
+ loaderData: pending.wrapped,
175
143
  belongsToRoute,
176
144
  };
177
145
  });
@@ -224,7 +192,10 @@ export async function resolveSegment<TEnv>(
224
192
  index: 0,
225
193
  component,
226
194
  loading: entry.loading === false ? null : entry.loading,
227
- transition: entry.transition,
195
+ transition: applyViewTransitionDefault(
196
+ entry.transition,
197
+ deps.viewTransitionDefault,
198
+ ),
228
199
  params,
229
200
  belongsToRoute: false,
230
201
  layoutName: entry.id,
@@ -293,6 +264,7 @@ export async function resolveSegment<TEnv>(
293
264
  if (entry.loading) {
294
265
  const result = handleHandlerResult(handler(context));
295
266
  if (result instanceof Promise) {
267
+ warnOnStreamedResponse(result, entry.id);
296
268
  result.finally(doneRouteHandler).catch(() => {});
297
269
  const tracked = deps.trackHandler(result, {
298
270
  segmentId: entry.shortCode,
@@ -359,7 +331,10 @@ export async function resolveSegment<TEnv>(
359
331
  index: 0,
360
332
  component: component ?? null,
361
333
  loading: entry.loading === false ? null : entry.loading,
362
- transition: entry.transition,
334
+ transition: applyViewTransitionDefault(
335
+ entry.transition,
336
+ deps.viewTransitionDefault,
337
+ ),
363
338
  params,
364
339
  belongsToRoute: true,
365
340
  ...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
@@ -443,7 +418,10 @@ export async function resolveOrphanLayout<TEnv>(
443
418
  belongsToRoute,
444
419
  layoutName: orphan.id,
445
420
  loading: orphan.loading === false ? null : orphan.loading,
446
- transition: orphan.transition,
421
+ transition: applyViewTransitionDefault(
422
+ orphan.transition,
423
+ deps.viewTransitionDefault,
424
+ ),
447
425
  ...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
448
426
  });
449
427
 
@@ -565,7 +543,10 @@ export async function resolveParallelEntry<TEnv>(
565
543
  index: 0,
566
544
  component,
567
545
  loading: parallelEntry.loading === false ? null : parallelEntry.loading,
568
- transition: parallelEntry.transition,
546
+ transition: applyViewTransitionDefault(
547
+ parallelEntry.transition,
548
+ deps.viewTransitionDefault,
549
+ ),
569
550
  params,
570
551
  slot,
571
552
  belongsToRoute,
@@ -632,7 +613,7 @@ export async function resolveAllSegments<TEnv>(
632
613
  // can guard non-cacheable variable reads. Also guards response-level
633
614
  // side effects (headers.set). Persists for all descendant entries.
634
615
  if (entry.type === "cache") {
635
- const store = RSCRouterContext.getStore();
616
+ const store = RangoContext.getStore();
636
617
  if (store) store.insideCacheScope = true;
637
618
  }
638
619
  const doneEntry = track(`segment:${entry.id}`, 1);
@@ -52,6 +52,40 @@ export function handleHandlerResult(
52
52
  return result;
53
53
  }
54
54
 
55
+ /**
56
+ * Dev-only: warn when a handler on a route that declares loading() resolves or
57
+ * rejects with a Response (e.g. redirect()).
58
+ *
59
+ * On a non-loading route a returned/thrown Response short-circuits to an HTTP
60
+ * redirect. But when the route declares loading(), the handler result is
61
+ * streamed (not awaited at the resolution boundary), so the Response surfaces
62
+ * only during RSC serialization and is rendered into the stream instead of
63
+ * becoming a 302/308 — a silent failure mode. Issue redirects from middleware,
64
+ * a loader, or a synchronous handler return instead. Compiled out in production.
65
+ */
66
+ export function warnOnStreamedResponse(
67
+ result: Promise<unknown>,
68
+ entryId: string,
69
+ ): void {
70
+ if (process.env.NODE_ENV === "production") return;
71
+ // A Response can surface either as a rejection (handleHandlerResult rethrows a
72
+ // resolved Response) or as a resolved value (the raw parallel-slot handler is
73
+ // not run through handleHandlerResult). Check both so every streamed path is
74
+ // covered. Each handler is an independent observer; it does not consume the
75
+ // rejection for the trackHandler/observeStreamedHandler chains.
76
+ const check = (value: unknown) => {
77
+ if (value instanceof Response) {
78
+ console.warn(
79
+ `[rango] Handler for "${entryId}" returned a Response (e.g. ` +
80
+ `redirect()), but it declares loading(): the Response is rendered ` +
81
+ `into the RSC stream, NOT sent as an HTTP redirect. Issue redirects ` +
82
+ `from middleware, a loader, or a synchronous handler return.`,
83
+ );
84
+ }
85
+ };
86
+ result.then(check, check);
87
+ }
88
+
55
89
  // ---------------------------------------------------------------------------
56
90
  // Static handler interception
57
91
  // ---------------------------------------------------------------------------