@rangojs/router 0.0.0-experimental.002d056c

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 +9 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1606 -0
  4. package/dist/vite/index.js +5153 -0
  5. package/package.json +177 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +253 -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 +638 -0
  43. package/src/browser/navigation-client.ts +261 -0
  44. package/src/browser/navigation-store.ts +806 -0
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +582 -0
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +145 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +128 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +368 -0
  55. package/src/browser/react/NavigationProvider.tsx +413 -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 +464 -0
  79. package/src/browser/scroll-restoration.ts +397 -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 +547 -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 +479 -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 +982 -0
  105. package/src/cache/cf/index.ts +29 -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 +44 -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 +281 -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 +160 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +397 -0
  172. package/src/router/lazy-includes.ts +236 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +251 -0
  175. package/src/router/manifest.ts +269 -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 +193 -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 +749 -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 +320 -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 +1242 -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 +291 -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 +1006 -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 +237 -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 +920 -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 +109 -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 +108 -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 +48 -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 +363 -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 +266 -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 +445 -0
  298. package/src/vite/router-discovery.ts +777 -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,385 @@
1
+ ---
2
+ name: route
3
+ description: Define routes with path() in @rangojs/router
4
+ argument-hint: [pattern]
5
+ ---
6
+
7
+ # Defining Routes with path()
8
+
9
+ ## Basic Route
10
+
11
+ ```typescript
12
+ import { urls } from "@rangojs/router";
13
+
14
+ export const urlpatterns = urls(({ path }) => [
15
+ path("/", HomePage, { name: "home" }),
16
+ path("/about", AboutPage, { name: "about" }),
17
+ path("/contact", ContactPage, { name: "contact" }),
18
+ ]);
19
+ ```
20
+
21
+ ## Route with Parameters
22
+
23
+ ```typescript
24
+ urls(({ path }) => [
25
+ // Single parameter
26
+ path("/product/:slug", ProductPage, { name: "product" }),
27
+
28
+ // Multiple parameters
29
+ path("/blog/:year/:month/:slug", BlogPostPage, { name: "blogPost" }),
30
+
31
+ // Optional parameter (add ? suffix)
32
+ path("/search/:query?", SearchPage, { name: "search" }),
33
+ ]);
34
+ ```
35
+
36
+ ## Route Handler Patterns
37
+
38
+ ### Component Function
39
+
40
+ ```typescript
41
+ path("/about", AboutPage, { name: "about" })
42
+
43
+ // AboutPage receives context
44
+ function AboutPage(ctx: HandlerContext) {
45
+ return <div>About Us</div>;
46
+ }
47
+ ```
48
+
49
+ ### Inline JSX
50
+
51
+ ```typescript
52
+ path("/about", () => <AboutPage />, { name: "about" })
53
+ ```
54
+
55
+ ### Handler with Context Access
56
+
57
+ ```typescript
58
+ path("/product/:slug", (ctx) => {
59
+ const { slug } = ctx.params;
60
+ return <ProductPage slug={slug} />;
61
+ }, { name: "product" })
62
+ ```
63
+
64
+ ### Async Handler (Streaming)
65
+
66
+ ```typescript
67
+ path("/product/:slug", async (ctx) => {
68
+ const product = await fetchProduct(ctx.params.slug);
69
+ return <ProductPage product={product} />;
70
+ }, { name: "product" })
71
+ ```
72
+
73
+ ## Route Options
74
+
75
+ ```typescript
76
+ path("/product/:slug", ProductPage, {
77
+ name: "product", // Route name for href() and navigation
78
+ });
79
+ ```
80
+
81
+ ### Typed Search Params
82
+
83
+ Add a `search` schema to get typed `ctx.search`:
84
+
85
+ ```typescript
86
+ path("/search", SearchPage, {
87
+ name: "search",
88
+ search: { q: "string", page: "number?", sort: "string?" },
89
+ });
90
+ ```
91
+
92
+ Use `Handler<"name">` for typed search params (resolves from the generated route map automatically):
93
+
94
+ ```typescript
95
+ import type { Handler } from "@rangojs/router";
96
+
97
+ export const SearchPage: Handler<"search"> = (ctx) => {
98
+ // ctx.search is typed: { q: string; page?: number; sort?: string }
99
+ const { q, page, sort } = ctx.search;
100
+ // ctx.searchParams is always URLSearchParams
101
+ return <SearchResults q={q} page={page} sort={sort} />;
102
+ };
103
+ ```
104
+
105
+ Supported types: `"string"`, `"number"`, `"boolean"`, with `?` suffix for optional.
106
+ Missing params are `undefined` regardless of required/optional. The required/optional
107
+ distinction is a consumer-facing contract (for `href()` and `reverse()` autocomplete).
108
+
109
+ Use `RouteSearchParams<"name">` and `RouteParams<"name">` to extract types for props:
110
+
111
+ ```typescript
112
+ import type { RouteSearchParams, RouteParams } from "@rangojs/router";
113
+
114
+ type SP = RouteSearchParams<"search">; // { q: string; page?: number; sort?: string }
115
+ type P = RouteParams<"blogPost">; // { slug: string }
116
+ ```
117
+
118
+ ## Route Children
119
+
120
+ Add loaders, loading states, and other features as children:
121
+
122
+ ```typescript
123
+ path("/product/:slug", ProductPage, { name: "product" }, () => [
124
+ loader(ProductLoader),
125
+ loading(<ProductSkeleton />),
126
+ revalidate(productRevalidation),
127
+ ])
128
+ ```
129
+
130
+ ## Handler Data Ownership
131
+
132
+ When a route has children (orphan layouts, parallels), the handler executes
133
+ first. Use `ctx.set(key, value)` to share data with children, who read it
134
+ via `ctx.get(key)`. Caching wraps all segments together, so either all run
135
+ or none do.
136
+
137
+ ### Typed context variables with createVar
138
+
139
+ Use `createVar<T>()` to create a typed token for `ctx.set()`/`ctx.get()`.
140
+ The token is imported by both the handler (producer) and layout (consumer),
141
+ making the data contract explicit and compile-time verified:
142
+
143
+ ```typescript
144
+ import { createVar } from "@rangojs/router";
145
+ import { Outlet, ParallelOutlet } from "@rangojs/router/client";
146
+
147
+ // Typed token -- shared between handler and layout
148
+ interface DashboardData {
149
+ title: string;
150
+ stats: { views: number };
151
+ }
152
+ const Dashboard = createVar<DashboardData>();
153
+
154
+ path("/dashboard/:id", async (ctx) => {
155
+ const data = await fetchDashboard(ctx.params.id);
156
+ ctx.set(Dashboard, data); // type-checked
157
+ return <DashboardPage data={data} />;
158
+ }, { name: "dashboard" }, () => [
159
+ layout((ctx) => {
160
+ const data = ctx.get(Dashboard); // typed as DashboardData | undefined
161
+ return (
162
+ <div>
163
+ <h1>{data?.title}</h1>
164
+ <Outlet />
165
+ <ParallelOutlet name="@sidebar" />
166
+ </div>
167
+ );
168
+ }),
169
+ parallel({
170
+ "@sidebar": (ctx) => {
171
+ const data = ctx.get(Dashboard);
172
+ return <Sidebar stats={data?.stats} />;
173
+ },
174
+ }),
175
+ ])
176
+ ```
177
+
178
+ String keys still work (`ctx.set("key", value)` / `ctx.get("key")`), but
179
+ `createVar<T>()` is preferred for type safety.
180
+
181
+ Only route handlers and middleware can call `ctx.set()`. Layouts, parallels,
182
+ and intercepts can only read via `ctx.get()`.
183
+
184
+ ### Revalidation Contracts for Handler Data
185
+
186
+ Handler-first guarantees apply within a single full render pass. For partial
187
+ action revalidation, define named revalidation contracts and reuse them on both
188
+ the producer route and the consumer child segments.
189
+
190
+ ```typescript
191
+ // revalidation-contracts.ts
192
+ export const revalidateCheckoutData = ({ actionId }) =>
193
+ actionId?.includes("src/actions/checkout.ts#") ?? false;
194
+
195
+ path("/checkout", CheckoutPage, { name: "checkout" }, () => [
196
+ revalidate(revalidateCheckoutData), // producer (route handler) reruns
197
+ layout(CheckoutLayout, () => [
198
+ revalidate(revalidateCheckoutData), // consumer reruns
199
+ parallel({ "@summary": CheckoutSummary }, () => [
200
+ revalidate(revalidateCheckoutData),
201
+ ]),
202
+ ]),
203
+ ]);
204
+ ```
205
+
206
+ If children depend on multiple upstream domains, compose multiple contracts on
207
+ the same segment (`revalidateAuthData`, `revalidateCheckoutData`, and so on).
208
+
209
+ For cleaner route trees, expose contract helpers and spread them:
210
+
211
+ ```typescript
212
+ import { revalidate } from "@rangojs/router";
213
+
214
+ export const revalidateCheckout = () => [revalidate(revalidateCheckoutData)];
215
+
216
+ path("/checkout", CheckoutPage, { name: "checkout" }, () => [
217
+ revalidateCheckout(),
218
+ layout(CheckoutLayout, () => [revalidateCheckout()]),
219
+ ]);
220
+ ```
221
+
222
+ For scope/revalidation guarantees and non-guarantees, see:
223
+ [docs/execution-model.md](../../docs/internal/execution-model.md)
224
+
225
+ ## Redirects
226
+
227
+ ### Basic redirect
228
+
229
+ ```typescript
230
+ import { redirect } from "@rangojs/router";
231
+
232
+ path("/old-page", () => redirect("/new-page"), { name: "oldPage" });
233
+ ```
234
+
235
+ ### Redirect with custom status
236
+
237
+ ```typescript
238
+ path("/moved", () => redirect("/new-location", 301), { name: "moved" });
239
+ ```
240
+
241
+ ### Redirect with location state
242
+
243
+ Carry typed state through redirects (e.g. flash messages):
244
+
245
+ ```typescript
246
+ import { redirect, createLocationState } from "@rangojs/router";
247
+
248
+ export const FlashMessage = createLocationState<{ text: string }>({
249
+ flash: true,
250
+ });
251
+
252
+ path(
253
+ "/save",
254
+ (ctx) => {
255
+ // ... save logic
256
+ return redirect("/dashboard", {
257
+ state: [FlashMessage({ text: "Item saved!" })],
258
+ });
259
+ },
260
+ { name: "save" },
261
+ );
262
+
263
+ // With custom status + state
264
+ path(
265
+ "/action",
266
+ (ctx) => {
267
+ return redirect("/target", {
268
+ status: 303,
269
+ state: [FlashMessage({ text: "Action complete" })],
270
+ });
271
+ },
272
+ { name: "action" },
273
+ );
274
+ ```
275
+
276
+ Read the state on the target page with `useLocationState(FlashMessage)`. The
277
+ `{ flash: true }` option makes it auto-clear. Without `{ flash: true }`,
278
+ state persists on back/forward. See `/hooks` for details.
279
+
280
+ ### ctx.setLocationState()
281
+
282
+ Attach location state to any server response (not just redirects):
283
+
284
+ ```typescript
285
+ path("/dashboard", (ctx) => {
286
+ ctx.setLocationState(ServerInfo({ data: "welcome" }));
287
+ return <Dashboard />;
288
+ }, { name: "dashboard" })
289
+ ```
290
+
291
+ State flows to the browser via the RSC payload and is merged into
292
+ `history.pushState()`. Only works for SPA (partial) navigations.
293
+
294
+ ## Handler Context
295
+
296
+ Every handler receives a context object:
297
+
298
+ ```typescript
299
+ interface HandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch = {}> {
300
+ params: TParams; // URL parameters
301
+ request: Request; // Original request
302
+ searchParams: URLSearchParams; // Query params (always URLSearchParams)
303
+ search: {} | ResolveSearchSchema<TSearch>; // Typed search params (from search schema)
304
+ url: URL; // Parsed URL
305
+ env: TEnv; // Environment (bindings + variables)
306
+ set(key: string, value: any): void; // Set context variable (untyped string key)
307
+ set<T>(contextVar: ContextVar<T>, value: T): void; // Set typed context variable
308
+ get(key: string): any; // Read context variable (untyped string key)
309
+ get<T>(contextVar: ContextVar<T>): T | undefined; // Read typed context variable
310
+ use<T>(handle: Handle<T>): T; // Access handles
311
+ reverse(
312
+ name: string,
313
+ params?: Record<string, string>,
314
+ search?: Record<string, unknown>,
315
+ ): string; // URL generation
316
+ setLocationState(entries: LocationStateEntry[]): void; // Attach state to response
317
+ }
318
+ ```
319
+
320
+ ### Using Context
321
+
322
+ ```typescript
323
+ path("/product/:slug", (ctx) => {
324
+ // Access URL params
325
+ const { slug } = ctx.params;
326
+
327
+ // Access query params (untyped - use search schema for typed access)
328
+ const tab = ctx.searchParams.get("tab");
329
+
330
+ // Access platform bindings
331
+ const db = ctx.env.DB;
332
+
333
+ // Access handles
334
+ const breadcrumbs = ctx.use(Breadcrumbs);
335
+ breadcrumbs.push({ label: "Product", href: `/product/${slug}` });
336
+
337
+ return <ProductPage slug={slug} tab={tab} />;
338
+ }, { name: "product" })
339
+ ```
340
+
341
+ ## Nested Routes
342
+
343
+ Use layouts to nest routes:
344
+
345
+ ```typescript
346
+ urls(({ path, layout }) => [
347
+ layout(<ShopLayout />, () => [
348
+ path("/shop", ShopIndex, { name: "shop.index" }),
349
+ path("/shop/cart", CartPage, { name: "shop.cart" }),
350
+ path("/shop/product/:slug", ProductPage, { name: "shop.product" }),
351
+ ]),
352
+ ])
353
+ ```
354
+
355
+ ## Complete Example
356
+
357
+ ```typescript
358
+ import { urls, Breadcrumbs } from "@rangojs/router";
359
+
360
+ export const urlpatterns = urls(({ path, layout, loader, loading }) => [
361
+ // Simple route
362
+ path("/", HomePage, { name: "home" }),
363
+
364
+ // Route with loader
365
+ path("/about", AboutPage, { name: "about" }, () => [
366
+ loader(TeamLoader),
367
+ ]),
368
+
369
+ // Dynamic route with handler
370
+ path("/product/:slug", (ctx) => {
371
+ const push = ctx.use(Breadcrumbs);
372
+ push({ label: ctx.params.slug, href: `/product/${ctx.params.slug}` });
373
+ return <ProductPage slug={ctx.params.slug} />;
374
+ }, { name: "product" }, () => [
375
+ loader(ProductLoader),
376
+ loading(<ProductSkeleton />, { ssr: true }),
377
+ ]),
378
+
379
+ // Nested routes in layout
380
+ layout(<BlogLayout />, () => [
381
+ path("/blog", BlogIndex, { name: "blog.index" }),
382
+ path("/blog/:slug", BlogPost, { name: "blog.post" }),
383
+ ]),
384
+ ]);
385
+ ```