@rangojs/router 0.0.0-experimental.0f44aca1

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 (305) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1601 -0
  4. package/dist/vite/index.js +5214 -0
  5. package/package.json +176 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +220 -0
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +112 -0
  11. package/skills/document-cache/SKILL.md +182 -0
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +704 -0
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +313 -0
  16. package/skills/layout/SKILL.md +310 -0
  17. package/skills/links/SKILL.md +239 -0
  18. package/skills/loader/SKILL.md +596 -0
  19. package/skills/middleware/SKILL.md +339 -0
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +305 -0
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +118 -0
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +385 -0
  26. package/skills/router-setup/SKILL.md +439 -0
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +79 -0
  29. package/skills/typesafety/SKILL.md +623 -0
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +273 -0
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/event-controller.ts +899 -0
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/index.ts +18 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +141 -0
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +134 -0
  42. package/src/browser/navigation-bridge.ts +645 -0
  43. package/src/browser/navigation-client.ts +215 -0
  44. package/src/browser/navigation-store.ts +806 -0
  45. package/src/browser/navigation-transaction.ts +295 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +550 -0
  48. package/src/browser/prefetch/cache.ts +146 -0
  49. package/src/browser/prefetch/fetch.ts +135 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +42 -0
  52. package/src/browser/prefetch/queue.ts +88 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +360 -0
  55. package/src/browser/react/NavigationProvider.tsx +386 -0
  56. package/src/browser/react/ScrollRestoration.tsx +94 -0
  57. package/src/browser/react/context.ts +59 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +52 -0
  60. package/src/browser/react/location-state-shared.ts +162 -0
  61. package/src/browser/react/location-state.ts +107 -0
  62. package/src/browser/react/mount-context.ts +37 -0
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +218 -0
  66. package/src/browser/react/use-client-cache.ts +58 -0
  67. package/src/browser/react/use-handle.ts +162 -0
  68. package/src/browser/react/use-href.tsx +40 -0
  69. package/src/browser/react/use-link-status.ts +135 -0
  70. package/src/browser/react/use-mount.ts +31 -0
  71. package/src/browser/react/use-navigation.ts +99 -0
  72. package/src/browser/react/use-params.ts +65 -0
  73. package/src/browser/react/use-pathname.ts +47 -0
  74. package/src/browser/react/use-router.ts +63 -0
  75. package/src/browser/react/use-search-params.ts +56 -0
  76. package/src/browser/react/use-segments.ts +171 -0
  77. package/src/browser/response-adapter.ts +73 -0
  78. package/src/browser/rsc-router.tsx +431 -0
  79. package/src/browser/scroll-restoration.ts +400 -0
  80. package/src/browser/segment-reconciler.ts +216 -0
  81. package/src/browser/segment-structure-assert.ts +83 -0
  82. package/src/browser/server-action-bridge.ts +667 -0
  83. package/src/browser/shallow.ts +40 -0
  84. package/src/browser/types.ts +538 -0
  85. package/src/browser/validate-redirect-origin.ts +29 -0
  86. package/src/build/generate-manifest.ts +438 -0
  87. package/src/build/generate-route-types.ts +36 -0
  88. package/src/build/index.ts +35 -0
  89. package/src/build/route-trie.ts +265 -0
  90. package/src/build/route-types/ast-helpers.ts +25 -0
  91. package/src/build/route-types/ast-route-extraction.ts +98 -0
  92. package/src/build/route-types/codegen.ts +102 -0
  93. package/src/build/route-types/include-resolution.ts +411 -0
  94. package/src/build/route-types/param-extraction.ts +48 -0
  95. package/src/build/route-types/per-module-writer.ts +128 -0
  96. package/src/build/route-types/router-processing.ts +469 -0
  97. package/src/build/route-types/scan-filter.ts +78 -0
  98. package/src/build/runtime-discovery.ts +231 -0
  99. package/src/cache/background-task.ts +34 -0
  100. package/src/cache/cache-key-utils.ts +44 -0
  101. package/src/cache/cache-policy.ts +125 -0
  102. package/src/cache/cache-runtime.ts +338 -0
  103. package/src/cache/cache-scope.ts +382 -0
  104. package/src/cache/cf/cf-cache-store.ts +540 -0
  105. package/src/cache/cf/index.ts +25 -0
  106. package/src/cache/document-cache.ts +369 -0
  107. package/src/cache/handle-capture.ts +81 -0
  108. package/src/cache/handle-snapshot.ts +41 -0
  109. package/src/cache/index.ts +43 -0
  110. package/src/cache/memory-segment-store.ts +328 -0
  111. package/src/cache/profile-registry.ts +73 -0
  112. package/src/cache/read-through-swr.ts +134 -0
  113. package/src/cache/segment-codec.ts +256 -0
  114. package/src/cache/taint.ts +98 -0
  115. package/src/cache/types.ts +342 -0
  116. package/src/client.rsc.tsx +85 -0
  117. package/src/client.tsx +601 -0
  118. package/src/component-utils.ts +76 -0
  119. package/src/components/DefaultDocument.tsx +27 -0
  120. package/src/context-var.ts +86 -0
  121. package/src/debug.ts +243 -0
  122. package/src/default-error-boundary.tsx +88 -0
  123. package/src/deps/browser.ts +8 -0
  124. package/src/deps/html-stream-client.ts +2 -0
  125. package/src/deps/html-stream-server.ts +2 -0
  126. package/src/deps/rsc.ts +10 -0
  127. package/src/deps/ssr.ts +2 -0
  128. package/src/errors.ts +365 -0
  129. package/src/handle.ts +135 -0
  130. package/src/handles/MetaTags.tsx +246 -0
  131. package/src/handles/breadcrumbs.ts +66 -0
  132. package/src/handles/index.ts +7 -0
  133. package/src/handles/meta.ts +264 -0
  134. package/src/host/cookie-handler.ts +165 -0
  135. package/src/host/errors.ts +97 -0
  136. package/src/host/index.ts +53 -0
  137. package/src/host/pattern-matcher.ts +214 -0
  138. package/src/host/router.ts +352 -0
  139. package/src/host/testing.ts +79 -0
  140. package/src/host/types.ts +146 -0
  141. package/src/host/utils.ts +25 -0
  142. package/src/href-client.ts +222 -0
  143. package/src/index.rsc.ts +233 -0
  144. package/src/index.ts +277 -0
  145. package/src/internal-debug.ts +11 -0
  146. package/src/loader.rsc.ts +89 -0
  147. package/src/loader.ts +64 -0
  148. package/src/network-error-thrower.tsx +23 -0
  149. package/src/outlet-context.ts +15 -0
  150. package/src/outlet-provider.tsx +45 -0
  151. package/src/prerender/param-hash.ts +37 -0
  152. package/src/prerender/store.ts +185 -0
  153. package/src/prerender.ts +463 -0
  154. package/src/reverse.ts +330 -0
  155. package/src/root-error-boundary.tsx +289 -0
  156. package/src/route-content-wrapper.tsx +196 -0
  157. package/src/route-definition/dsl-helpers.ts +934 -0
  158. package/src/route-definition/helper-factories.ts +200 -0
  159. package/src/route-definition/helpers-types.ts +430 -0
  160. package/src/route-definition/index.ts +52 -0
  161. package/src/route-definition/redirect.ts +93 -0
  162. package/src/route-definition.ts +1 -0
  163. package/src/route-map-builder.ts +275 -0
  164. package/src/route-name.ts +53 -0
  165. package/src/route-types.ts +259 -0
  166. package/src/router/content-negotiation.ts +116 -0
  167. package/src/router/debug-manifest.ts +72 -0
  168. package/src/router/error-handling.ts +287 -0
  169. package/src/router/find-match.ts +158 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +395 -0
  172. package/src/router/lazy-includes.ts +234 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +248 -0
  175. package/src/router/manifest.ts +267 -0
  176. package/src/router/match-api.ts +620 -0
  177. package/src/router/match-context.ts +266 -0
  178. package/src/router/match-handlers.ts +440 -0
  179. package/src/router/match-middleware/background-revalidation.ts +223 -0
  180. package/src/router/match-middleware/cache-lookup.ts +634 -0
  181. package/src/router/match-middleware/cache-store.ts +295 -0
  182. package/src/router/match-middleware/index.ts +81 -0
  183. package/src/router/match-middleware/intercept-resolution.ts +306 -0
  184. package/src/router/match-middleware/segment-resolution.ts +192 -0
  185. package/src/router/match-pipelines.ts +179 -0
  186. package/src/router/match-result.ts +219 -0
  187. package/src/router/metrics.ts +282 -0
  188. package/src/router/middleware-cookies.ts +55 -0
  189. package/src/router/middleware-types.ts +222 -0
  190. package/src/router/middleware.ts +748 -0
  191. package/src/router/pattern-matching.ts +563 -0
  192. package/src/router/prerender-match.ts +402 -0
  193. package/src/router/preview-match.ts +170 -0
  194. package/src/router/revalidation.ts +289 -0
  195. package/src/router/router-context.ts +316 -0
  196. package/src/router/router-interfaces.ts +452 -0
  197. package/src/router/router-options.ts +592 -0
  198. package/src/router/router-registry.ts +24 -0
  199. package/src/router/segment-resolution/fresh.ts +570 -0
  200. package/src/router/segment-resolution/helpers.ts +263 -0
  201. package/src/router/segment-resolution/loader-cache.ts +198 -0
  202. package/src/router/segment-resolution/revalidation.ts +1239 -0
  203. package/src/router/segment-resolution/static-store.ts +67 -0
  204. package/src/router/segment-resolution.ts +21 -0
  205. package/src/router/segment-wrappers.ts +289 -0
  206. package/src/router/telemetry-otel.ts +299 -0
  207. package/src/router/telemetry.ts +300 -0
  208. package/src/router/timeout.ts +148 -0
  209. package/src/router/trie-matching.ts +239 -0
  210. package/src/router/types.ts +170 -0
  211. package/src/router.ts +1002 -0
  212. package/src/rsc/handler-context.ts +45 -0
  213. package/src/rsc/handler.ts +1089 -0
  214. package/src/rsc/helpers.ts +198 -0
  215. package/src/rsc/index.ts +36 -0
  216. package/src/rsc/loader-fetch.ts +209 -0
  217. package/src/rsc/manifest-init.ts +86 -0
  218. package/src/rsc/nonce.ts +32 -0
  219. package/src/rsc/origin-guard.ts +141 -0
  220. package/src/rsc/progressive-enhancement.ts +379 -0
  221. package/src/rsc/response-error.ts +37 -0
  222. package/src/rsc/response-route-handler.ts +347 -0
  223. package/src/rsc/rsc-rendering.ts +235 -0
  224. package/src/rsc/runtime-warnings.ts +42 -0
  225. package/src/rsc/server-action.ts +348 -0
  226. package/src/rsc/ssr-setup.ts +128 -0
  227. package/src/rsc/types.ts +263 -0
  228. package/src/search-params.ts +230 -0
  229. package/src/segment-system.tsx +454 -0
  230. package/src/server/context.ts +591 -0
  231. package/src/server/cookie-store.ts +190 -0
  232. package/src/server/fetchable-loader-store.ts +37 -0
  233. package/src/server/handle-store.ts +308 -0
  234. package/src/server/loader-registry.ts +133 -0
  235. package/src/server/request-context.ts +914 -0
  236. package/src/server/root-layout.tsx +10 -0
  237. package/src/server/tsconfig.json +14 -0
  238. package/src/server.ts +51 -0
  239. package/src/ssr/index.tsx +365 -0
  240. package/src/static-handler.ts +114 -0
  241. package/src/theme/ThemeProvider.tsx +297 -0
  242. package/src/theme/ThemeScript.tsx +61 -0
  243. package/src/theme/constants.ts +62 -0
  244. package/src/theme/index.ts +48 -0
  245. package/src/theme/theme-context.ts +44 -0
  246. package/src/theme/theme-script.ts +155 -0
  247. package/src/theme/types.ts +182 -0
  248. package/src/theme/use-theme.ts +44 -0
  249. package/src/types/boundaries.ts +158 -0
  250. package/src/types/cache-types.ts +198 -0
  251. package/src/types/error-types.ts +192 -0
  252. package/src/types/global-namespace.ts +100 -0
  253. package/src/types/handler-context.ts +687 -0
  254. package/src/types/index.ts +88 -0
  255. package/src/types/loader-types.ts +183 -0
  256. package/src/types/route-config.ts +170 -0
  257. package/src/types/route-entry.ts +102 -0
  258. package/src/types/segments.ts +148 -0
  259. package/src/types.ts +1 -0
  260. package/src/urls/include-helper.ts +197 -0
  261. package/src/urls/index.ts +53 -0
  262. package/src/urls/path-helper-types.ts +339 -0
  263. package/src/urls/path-helper.ts +329 -0
  264. package/src/urls/pattern-types.ts +95 -0
  265. package/src/urls/response-types.ts +106 -0
  266. package/src/urls/type-extraction.ts +372 -0
  267. package/src/urls/urls-function.ts +98 -0
  268. package/src/urls.ts +1 -0
  269. package/src/use-loader.tsx +354 -0
  270. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  271. package/src/vite/discovery/discover-routers.ts +344 -0
  272. package/src/vite/discovery/prerender-collection.ts +385 -0
  273. package/src/vite/discovery/route-types-writer.ts +258 -0
  274. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  275. package/src/vite/discovery/state.ts +110 -0
  276. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  277. package/src/vite/index.ts +16 -0
  278. package/src/vite/plugin-types.ts +131 -0
  279. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  280. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  281. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  282. package/src/vite/plugins/expose-action-id.ts +365 -0
  283. package/src/vite/plugins/expose-id-utils.ts +287 -0
  284. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  285. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  286. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  287. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  288. package/src/vite/plugins/expose-ids/types.ts +45 -0
  289. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  290. package/src/vite/plugins/refresh-cmd.ts +65 -0
  291. package/src/vite/plugins/use-cache-transform.ts +323 -0
  292. package/src/vite/plugins/version-injector.ts +83 -0
  293. package/src/vite/plugins/version-plugin.ts +254 -0
  294. package/src/vite/plugins/version.d.ts +12 -0
  295. package/src/vite/plugins/virtual-entries.ts +123 -0
  296. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  297. package/src/vite/rango.ts +510 -0
  298. package/src/vite/router-discovery.ts +785 -0
  299. package/src/vite/utils/ast-handler-extract.ts +517 -0
  300. package/src/vite/utils/banner.ts +36 -0
  301. package/src/vite/utils/bundle-analysis.ts +137 -0
  302. package/src/vite/utils/manifest-utils.ts +70 -0
  303. package/src/vite/utils/package-resolution.ts +121 -0
  304. package/src/vite/utils/prerender-utils.ts +189 -0
  305. package/src/vite/utils/shared-utils.ts +169 -0
@@ -0,0 +1,623 @@
1
+ ---
2
+ name: typesafety
3
+ description: Set up type-safe routes, params, and environment types in @rangojs/router
4
+ argument-hint: [setup]
5
+ ---
6
+
7
+ # Type Safety Setup
8
+
9
+ @rangojs/router provides end-to-end type safety for routes, parameters, and environment.
10
+
11
+ ## Router Setup
12
+
13
+ ```typescript
14
+ // router.tsx
15
+ import { createRouter } from "@rangojs/router";
16
+ import { urlpatterns } from "./urls";
17
+
18
+ const router = createRouter<AppBindings>({
19
+ document: Document,
20
+ }).routes(urlpatterns);
21
+
22
+ // Server-side named-route reverse (type-safe via routeMap)
23
+ export const reverse = router.reverse;
24
+
25
+ export default router;
26
+ ```
27
+
28
+ ### Which global type should I use?
29
+
30
+ Use the generated route map by default. Manual `RegisteredRoutes` augmentation
31
+ is only needed when you want the richer `typeof router.routeMap` shape
32
+ available globally.
33
+
34
+ - `GeneratedRouteMap` — auto-registered by `router.named-routes.gen.ts`
35
+ Use for `Handler<"name">`, `Prerender<"name">`, server `ctx.reverse()`,
36
+ and named-route param/search inference.
37
+ - `typeof router.routeMap` — the real merged route map from your router
38
+ instance, including response-route metadata such as `{ path, response }`.
39
+ - `RegisteredRoutes` — manual global hook for exposing `typeof router.routeMap`
40
+ to utilities like `href()`, `ValidPaths`, and `PathResponse`.
41
+
42
+ Recommended setup:
43
+
44
+ ```typescript
45
+ // router.tsx
46
+ import { createRouter } from "@rangojs/router";
47
+ import { urlpatterns } from "./urls";
48
+ import type { AppBindings, AppVars } from "./env";
49
+
50
+ export const router = createRouter<AppBindings>({}).routes(urlpatterns);
51
+
52
+ declare global {
53
+ namespace RSCRouter {
54
+ interface Env extends AppBindings {}
55
+ interface Vars extends AppVars {}
56
+ interface RegisteredRoutes extends typeof router.routeMap {}
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Route Definition with Type-Safe Names
62
+
63
+ ```typescript
64
+ // urls.tsx
65
+ import { urls } from "@rangojs/router";
66
+
67
+ export const urlpatterns = urls(({ path, layout }) => [
68
+ path("/", HomePage, { name: "home" }),
69
+ path("/products", ProductsPage, { name: "products" }),
70
+ path("/product/:slug", ProductPage, { name: "product" }),
71
+ path("/cart", CartPage, { name: "cart" }),
72
+ path("/checkout/:step?", CheckoutPage, { name: "checkout" }),
73
+ ]);
74
+
75
+ // Route names are inferred from the { name } option
76
+ ```
77
+
78
+ ## Type-Safe href()
79
+
80
+ ### Server: ctx.reverse with route names
81
+
82
+ In route handlers, `ctx.reverse()` uses two namespaces:
83
+
84
+ - **`.name`** — local route, resolved within the current `include()` scope
85
+ - **`name`** — global route, from the named-routes definition
86
+
87
+ ```typescript
88
+ import type { Handler } from "@rangojs/router";
89
+
90
+ export const ProductHandler: Handler<"shop.product"> = (ctx) => {
91
+ ctx.reverse(".cart"); // Local: /shop/cart
92
+ ctx.reverse(".product", { slug: "widget" }); // Local: /shop/product/widget
93
+ ctx.reverse("blog.post", { slug: "1" }); // Global: /blog/1
94
+ };
95
+ ```
96
+
97
+ For type-safe local names, generate a route types file with `npx rango generate urls/shop.tsx`
98
+ and pass it as the second generic to `Handler` or `Prerender`:
99
+
100
+ ```typescript
101
+ import type { Handler } from "@rangojs/router";
102
+ import type { routes } from "./shop.gen.js";
103
+
104
+ export const ProductHandler: Handler<"shop.product", routes> = (ctx) => {
105
+ ctx.reverse(".cart"); // Type-safe local name
106
+ ctx.reverse(".product", { slug: "widget" }); // Type-safe local with params
107
+ ctx.reverse("blog.post", { slug: "hi" }); // Type-safe global name
108
+ };
109
+ ```
110
+
111
+ ### Client: href + useHref
112
+
113
+ On the client, `href()` validates paths against registered route patterns at compile time:
114
+
115
+ ```typescript
116
+ "use client";
117
+ import { href, useHref, Link } from "@rangojs/router/client";
118
+
119
+ // href() validates absolute paths via PatternToPath types
120
+ href("/about"); // Valid path
121
+ href("/blog/hello"); // Matches /blog/:slug
122
+
123
+ // useHref() auto-prefixes with include() mount
124
+ function ShopNav() {
125
+ const href = useHref();
126
+ return <Link to={href("/cart")}>Cart</Link>; // "/shop/cart"
127
+ }
128
+ ```
129
+
130
+ `href()` and path-based response utilities read from `RegisteredRoutes`, so if
131
+ you want them typed globally you should augment:
132
+
133
+ ```typescript
134
+ declare global {
135
+ namespace RSCRouter {
136
+ interface RegisteredRoutes extends typeof router.routeMap {}
137
+ }
138
+ }
139
+ ```
140
+
141
+ See `/links` for full URL generation guide.
142
+
143
+ ## Environment Type Setup
144
+
145
+ Define your app's environment for type-safe bindings and variables:
146
+
147
+ ```typescript
148
+ // env.ts
149
+
150
+ // Cloudflare bindings — passed as TEnv to createRouter<TEnv>()
151
+ export interface AppBindings {
152
+ DB: D1Database;
153
+ KV: KVNamespace;
154
+ CACHE: KVNamespace;
155
+ AI: Ai;
156
+ }
157
+
158
+ // Variables set by middleware — declared via module augmentation
159
+ export interface AppVariables {
160
+ user?: { id: string; email: string; role: string };
161
+ requestId?: string;
162
+ permissions?: string[];
163
+ }
164
+ ```
165
+
166
+ ### Using Environment Types
167
+
168
+ ```typescript
169
+ // router.tsx
170
+ import type { AppBindings, AppVariables } from "./env";
171
+
172
+ const router = createRouter<AppBindings>({
173
+ document: Document,
174
+ }).routes(urlpatterns);
175
+
176
+ // Register bindings and variables globally for implicit typing
177
+ declare global {
178
+ namespace RSCRouter {
179
+ interface Env extends AppBindings {}
180
+ interface Vars extends AppVariables {}
181
+ }
182
+ }
183
+
184
+ // middleware - typed via ctx.set / ctx.get
185
+ import type { Middleware } from "@rangojs/router";
186
+
187
+ export const authMiddleware: Middleware = async (ctx, next) => {
188
+ ctx.set("user", {
189
+ id: "123",
190
+ email: "user@example.com",
191
+ role: "admin",
192
+ });
193
+ await next();
194
+ };
195
+
196
+ // loaders - typed context
197
+ export const UserLoader = createLoader(async (ctx) => {
198
+ const db = ctx.env.DB; // D1Database (plain bindings)
199
+ const userId = ctx.get("user")?.id; // from RSCRouter.Vars
200
+ return db.prepare("SELECT * FROM users WHERE id = ?").bind(userId).first();
201
+ });
202
+ ```
203
+
204
+ ## Global Environment Registration
205
+
206
+ Register environment types globally for implicit typing:
207
+
208
+ ```typescript
209
+ // router.tsx
210
+ declare global {
211
+ namespace RSCRouter {
212
+ interface Env extends AppBindings {}
213
+ interface Vars extends AppVariables {}
214
+ }
215
+ }
216
+ ```
217
+
218
+ Now handlers have typed context without explicit imports:
219
+
220
+ ```typescript
221
+ // In loaders
222
+ export const DashboardLoader = createLoader(async (ctx) => {
223
+ // ctx.env.DB is typed from global RSCRouter.Env
224
+ // ctx.get("user") is typed from global RSCRouter.Vars
225
+ const user = ctx.get("user");
226
+ return { user };
227
+ });
228
+ ```
229
+
230
+ ## Typed Search Params
231
+
232
+ Add a `search` schema to `path()` options for type-safe query parameters:
233
+
234
+ ```typescript
235
+ // Route definition with search schema
236
+ path("/search", SearchPage, {
237
+ name: "search",
238
+ search: { q: "string", page: "number?", sort: "string?" },
239
+ });
240
+ ```
241
+
242
+ ### Handler with typed search params
243
+
244
+ `Handler<"name">` automatically resolves route params and search params from the
245
+ global `GeneratedRouteMap` (the gen file). No explicit route map import needed:
246
+
247
+ ```typescript
248
+ // pages/search.tsx
249
+ import type { Handler } from "@rangojs/router";
250
+
251
+ export const SearchPage: Handler<"search"> = (ctx) => {
252
+ // ctx.search is typed: { q: string; page?: number; sort?: string }
253
+ const { q, page, sort } = ctx.search;
254
+ return <SearchResults q={q} page={page} sort={sort} />;
255
+ };
256
+ ```
257
+
258
+ This avoids circular references because `Handler` defaults to `GeneratedRouteMap`
259
+ (from `router.named-routes.gen.ts`) instead of `RegisteredRoutes` (which depends on `router.tsx`).
260
+
261
+ You can also pass an explicit route map for per-module isolation (opt-in,
262
+ after running `npx rango generate`):
263
+
264
+ ```typescript
265
+ import type { Handler } from "@rangojs/router";
266
+ import type { routes } from "./urls.gen.js";
267
+
268
+ export const SearchPage: Handler<"search", routes> = (ctx) => { ... };
269
+ ```
270
+
271
+ Supported types: `"string"`, `"number"`, `"boolean"`, with `?` suffix for optional.
272
+ Values are automatically coerced from query string (e.g., `"2"` becomes `2` for numbers).
273
+ Routes without a `search` schema keep the standard `URLSearchParams` behavior.
274
+
275
+ ### RouteSearchParams and RouteParams utility types
276
+
277
+ Extract typed params by route name for use in component props, return types, or anywhere:
278
+
279
+ ```typescript
280
+ import type { RouteSearchParams, RouteParams } from "@rangojs/router";
281
+
282
+ // RouteSearchParams<"name"> resolves the search schema to a typed object
283
+ type SP = RouteSearchParams<"search">;
284
+ // { q: string | undefined; page?: number; sort?: string }
285
+
286
+ // RouteParams<"name"> resolves URL params from the route pattern
287
+ type P = RouteParams<"blogPost">;
288
+ // { slug: string }
289
+
290
+ // Use in component props
291
+ interface SearchResultsProps {
292
+ params: RouteSearchParams<"search">;
293
+ }
294
+ ```
295
+
296
+ Both default to the global route map (`RegisteredRoutes` or `GeneratedRouteMap`).
297
+ Pass an explicit route map as the second type argument when needed:
298
+
299
+ ```typescript
300
+ import type { routes } from "./urls.gen.js";
301
+
302
+ type SP = RouteSearchParams<"search", routes>;
303
+ type P = RouteParams<"blogPost", routes>;
304
+ ```
305
+
306
+ ### Generated route types
307
+
308
+ In the generated `router.named-routes.gen.ts`, routes with search schemas
309
+ use `{ path, search }` objects:
310
+
311
+ ```typescript
312
+ // router.named-routes.gen.ts (auto-generated)
313
+ export const NamedRoutes = {
314
+ "search.index": {
315
+ path: "/search",
316
+ search: { q: "string", page: "number?", sort: "string?" },
317
+ },
318
+ "home.index": "/", // No search schema -> plain string
319
+ } as const;
320
+ ```
321
+
322
+ ## Loader Type Safety
323
+
324
+ Loaders have typed return values:
325
+
326
+ ```typescript
327
+ // loaders/product.ts
328
+ export const ProductLoader = createLoader(async (ctx) => {
329
+ return {
330
+ id: ctx.params.slug,
331
+ name: "Widget",
332
+ price: 99,
333
+ };
334
+ });
335
+
336
+ // In server component - type is inferred
337
+ import { useLoader } from "@rangojs/router/client";
338
+
339
+ async function ProductPage() {
340
+ const product = await useLoader(ProductLoader);
341
+ // product: { id: string; name: string; price: number }
342
+ return <h1>{product.name}</h1>;
343
+ }
344
+
345
+ // In client component - same type
346
+ "use client";
347
+ import { useLoader } from "@rangojs/router/client";
348
+
349
+ function ProductPrice() {
350
+ const { data } = useLoader(ProductLoader);
351
+ // data: { id: string; name: string; price: number }
352
+ const product = data;
353
+ return <span>${product.price}</span>;
354
+ }
355
+ ```
356
+
357
+ ## Typed Context Variables
358
+
359
+ `createVar<T>()` creates a typed token for `ctx.set()`/`ctx.get()`, making
360
+ handler-to-layout data contracts explicit and compile-time verified:
361
+
362
+ ```typescript
363
+ import { createVar } from "@rangojs/router";
364
+
365
+ // Define a typed token (shared between producer and consumer)
366
+ interface PaginationData {
367
+ current: number;
368
+ total: number;
369
+ perPage: number;
370
+ }
371
+ export const Pagination = createVar<PaginationData>();
372
+ ```
373
+
374
+ ### Producer (handler or middleware)
375
+
376
+ ```typescript
377
+ import { Pagination } from "../vars/pagination.js";
378
+
379
+ const ArticleList: Handler<"articles.list"> = async (ctx) => {
380
+ ctx.set(Pagination, { // type-checked
381
+ current: 1,
382
+ total: 10,
383
+ perPage: 5,
384
+ });
385
+ return <Articles />;
386
+ };
387
+ ```
388
+
389
+ ### Consumer (layout, parallel, or any context with get)
390
+
391
+ ```typescript
392
+ import { Pagination } from "../vars/pagination.js";
393
+
394
+ export function PaginationLayout(ctx: any) {
395
+ const pagination = ctx.get(Pagination); // typed as PaginationData | undefined
396
+ if (!pagination) return <Outlet />;
397
+ return <nav>Page {pagination.current} of {pagination.total}</nav>;
398
+ }
399
+ ```
400
+
401
+ ### Why not just use RSCRouter.Vars?
402
+
403
+ `RSCRouter.Vars` (via module augmentation) provides app-global typing for
404
+ `ctx.get("key")` / `ctx.set("key", value)`. It works for middleware state
405
+ shared app-wide. `createVar<T>()` is for route-local or feature-scoped
406
+ context -- the producer and consumer import the same token, creating a
407
+ scoped contract without polluting global types.
408
+
409
+ Both approaches coexist: `ctx.get("user")` (global via Vars) and
410
+ `ctx.get(Pagination)` (scoped via createVar) work side by side.
411
+
412
+ ## Handle Type Safety
413
+
414
+ Handles have typed data:
415
+
416
+ ```typescript
417
+ // Built-in Breadcrumbs handle — import from "@rangojs/router"
418
+ import { Breadcrumbs } from "@rangojs/router";
419
+ // Type: Handle<BreadcrumbItem, BreadcrumbItem[]>
420
+ // BreadcrumbItem: { label: string; href: string; content?: ReactNode | Promise<ReactNode> }
421
+
422
+ // In route handler — push is fully typed
423
+ path("/shop/product/:slug", (ctx) => {
424
+ const breadcrumb = ctx.use(Breadcrumbs);
425
+ breadcrumb({ label: "Products", href: "/shop/products" });
426
+ return <ProductPage />;
427
+ }, { name: "product" });
428
+
429
+ // In client — typed array
430
+ import { useHandle, Breadcrumbs } from "@rangojs/router/client";
431
+ function BreadcrumbNav() {
432
+ const crumbs = useHandle(Breadcrumbs);
433
+ // crumbs: BreadcrumbItem[]
434
+ }
435
+
436
+ // Custom handles also work the same way
437
+ import { createHandle } from "@rangojs/router";
438
+ export const PageTitle = createHandle<string, string>(
439
+ (segments) => segments.flat().at(-1) ?? "Default Title"
440
+ );
441
+ ```
442
+
443
+ ## Ref Prop Type Safety (Loaders & Handles)
444
+
445
+ Loaders and handles can be passed as props from server to client components.
446
+ Use `typeof` to get the full typed definition without manually specifying generics:
447
+
448
+ ```typescript
449
+ // loaders.ts
450
+ export const ProductLoader = createLoader(async (ctx) => {
451
+ return { product: await fetchProduct(ctx.params.slug) };
452
+ });
453
+
454
+ // Built-in Breadcrumbs — or any custom handle created with createHandle()
455
+
456
+ // Client component — typeof infers all generics
457
+ ("use client");
458
+ import { useLoader, useHandle, type Breadcrumbs } from "@rangojs/router/client";
459
+ import type { ProductLoader } from "../loaders";
460
+
461
+ function MyComponent({
462
+ loader,
463
+ handle,
464
+ }: {
465
+ loader: typeof ProductLoader; // LoaderDefinition<{ product: Product }>
466
+ handle: typeof Breadcrumbs; // Handle<{ label: string; href: string }>
467
+ }) {
468
+ const { data } = useLoader(loader); // data is typed
469
+ const crumbs = useHandle(handle); // crumbs is typed array
470
+ // ...
471
+ }
472
+ ```
473
+
474
+ RSC Flight serialization calls `toJSON()` on both loaders and handles,
475
+ sending only `{ __brand, $$id }` to the client. The hooks recover the
476
+ full functionality from module-level registries.
477
+
478
+ ## Location State Type Safety
479
+
480
+ ```typescript
481
+ // location-states.ts
482
+ import { createLocationState } from "@rangojs/router";
483
+
484
+ // All export patterns work: export const, const + export { X }, export { X as Y }
485
+ export const ProductPreview = createLocationState<{
486
+ name: string;
487
+ price: number;
488
+ image: string;
489
+ }>();
490
+
491
+ // Passing state through Link
492
+ <Link
493
+ to={href("product", { slug: "widget" })}
494
+ state={[ProductPreview({ name: "Widget", price: 99, image: "/img.jpg" })]}
495
+ >
496
+ View Product
497
+ </Link>
498
+
499
+ // Reading state in component
500
+ function ProductHeader() {
501
+ const preview = useLocationState(ProductPreview);
502
+ // preview: { name: string; price: number; image: string } | undefined
503
+
504
+ if (preview) {
505
+ return <h1>{preview.name} - ${preview.price}</h1>;
506
+ }
507
+ return <h1>Loading...</h1>;
508
+ }
509
+ ```
510
+
511
+ ## Multi-Project tsconfig Setup
512
+
513
+ For monorepos or multi-app setups, use a shared base tsconfig. Each app only needs
514
+ to extend the base and add its `router.tsx` to `files` so TypeScript picks up the
515
+ global type declarations (like `RSCRouter.Env`).
516
+
517
+ ```jsonc
518
+ // tsconfig.base.json (root)
519
+ {
520
+ "compilerOptions": {
521
+ "target": "ES2022",
522
+ "module": "ESNext",
523
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
524
+ "jsx": "react-jsx",
525
+ "moduleResolution": "bundler",
526
+ "strict": true,
527
+ "noEmit": true,
528
+ "skipLibCheck": true,
529
+ "isolatedModules": true,
530
+ "esModuleInterop": true,
531
+ "resolveJsonModule": true,
532
+ },
533
+ }
534
+ ```
535
+
536
+ ```jsonc
537
+ // apps/shop/tsconfig.json
538
+ {
539
+ "extends": "../../tsconfig.base.json",
540
+ "include": ["src"],
541
+ "files": ["src/router.tsx"],
542
+ }
543
+ ```
544
+
545
+ ```jsonc
546
+ // apps/blog/tsconfig.json
547
+ {
548
+ "extends": "../../tsconfig.base.json",
549
+ "include": ["src"],
550
+ "files": ["src/router.tsx"],
551
+ }
552
+ ```
553
+
554
+ The `files` array ensures `router.tsx` (which contains `declare global { namespace RSCRouter { interface Env; interface Vars } }`)
555
+ is always included in the compilation even if nothing directly imports it. Route types come from the
556
+ auto-generated `*.named-routes.gen.ts` file (via `rango generate`), not from manual declaration.
557
+ Each app gets its own typed environment without interfering with other apps.
558
+
559
+ ## Complete Type-Safe Setup
560
+
561
+ ```typescript
562
+ // 1. env.ts - Environment types
563
+ export interface AppBindings {
564
+ DB: D1Database;
565
+ KV: KVNamespace;
566
+ }
567
+
568
+ export interface AppVariables {
569
+ user?: { id: string; email: string; role: string };
570
+ }
571
+
572
+ // 2. urls.tsx - Route definitions with names
573
+ import { urls } from "@rangojs/router";
574
+
575
+ export const urlpatterns = urls(({ path, layout, loader }) => [
576
+ path("/", HomePage, { name: "home" }),
577
+
578
+ layout(<ShopLayout />, () => [
579
+ path("/shop", ShopIndex, { name: "shop" }),
580
+ path("/shop/product/:slug", ProductPage, { name: "product" }, () => [
581
+ loader(ProductLoader),
582
+ ]),
583
+ ]),
584
+ ]);
585
+
586
+ // 3. router.tsx - Create router and export reverse
587
+ const router = createRouter<AppBindings>({
588
+ document: Document,
589
+ }).routes(urlpatterns);
590
+
591
+ // Register bindings and variables globally for implicit typing
592
+ declare global {
593
+ namespace RSCRouter {
594
+ interface Env extends AppBindings {}
595
+ interface Vars extends AppVariables {}
596
+ }
597
+ }
598
+
599
+ export const reverse = router.reverse;
600
+ export default router;
601
+
602
+ // 4. Run `npx rango generate src/router.tsx` to generate
603
+ // router.named-routes.gen.ts (auto-registers GeneratedRouteMap globally).
604
+ // No manual RegisteredRoutes declaration needed.
605
+
606
+ // 5. loaders/*.ts - Type-safe loaders
607
+ export const ProductLoader = createLoader(async (ctx) => {
608
+ // ctx.params: { slug: string }
609
+ // ctx.get("user"): User | undefined (from RSCRouter.Vars)
610
+ // ctx.env.DB: D1Database (plain bindings from RSCRouter.Env)
611
+ return { product: await fetchProduct(ctx.params.slug) };
612
+ });
613
+
614
+ // 6. Server: ctx.reverse for named routes
615
+ path("/product/:slug", (ctx) => {
616
+ return <Link to={ctx.reverse("shop")}>Back to Shop</Link>;
617
+ }, { name: "product" })
618
+
619
+ // 7. Client: useHref for mounted paths, href for absolute
620
+ "use client";
621
+ import { useHref, href, Link } from "@rangojs/router/client";
622
+ <Link to={href("/shop/product/widget")}>Widget</Link>
623
+ ```