@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100

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 (329) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +1037 -4
  3. package/dist/bin/rango.js +1619 -157
  4. package/dist/vite/index.js +5762 -2301
  5. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  6. package/package.json +71 -63
  7. package/skills/breadcrumbs/SKILL.md +252 -0
  8. package/skills/cache-guide/SKILL.md +294 -0
  9. package/skills/caching/SKILL.md +93 -23
  10. package/skills/composability/SKILL.md +172 -0
  11. package/skills/debug-manifest/SKILL.md +12 -8
  12. package/skills/document-cache/SKILL.md +18 -16
  13. package/skills/fonts/SKILL.md +6 -4
  14. package/skills/handler-use/SKILL.md +364 -0
  15. package/skills/hooks/SKILL.md +367 -71
  16. package/skills/host-router/SKILL.md +218 -0
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +176 -8
  19. package/skills/layout/SKILL.md +124 -3
  20. package/skills/links/SKILL.md +304 -25
  21. package/skills/loader/SKILL.md +474 -47
  22. package/skills/middleware/SKILL.md +207 -37
  23. package/skills/migrate-nextjs/SKILL.md +562 -0
  24. package/skills/migrate-react-router/SKILL.md +769 -0
  25. package/skills/mime-routes/SKILL.md +15 -11
  26. package/skills/parallel/SKILL.md +272 -1
  27. package/skills/prerender/SKILL.md +467 -65
  28. package/skills/rango/SKILL.md +89 -21
  29. package/skills/response-routes/SKILL.md +152 -91
  30. package/skills/route/SKILL.md +305 -14
  31. package/skills/router-setup/SKILL.md +210 -32
  32. package/skills/server-actions/SKILL.md +739 -0
  33. package/skills/streams-and-websockets/SKILL.md +283 -0
  34. package/skills/theme/SKILL.md +9 -8
  35. package/skills/typesafety/SKILL.md +333 -86
  36. package/skills/use-cache/SKILL.md +324 -0
  37. package/skills/view-transitions/SKILL.md +212 -0
  38. package/src/__internal.ts +102 -4
  39. package/src/bin/rango.ts +312 -15
  40. package/src/browser/action-coordinator.ts +97 -0
  41. package/src/browser/action-response-classifier.ts +99 -0
  42. package/src/browser/app-shell.ts +52 -0
  43. package/src/browser/app-version.ts +14 -0
  44. package/src/browser/event-controller.ts +136 -68
  45. package/src/browser/history-state.ts +80 -0
  46. package/src/browser/intercept-utils.ts +52 -0
  47. package/src/browser/link-interceptor.ts +24 -4
  48. package/src/browser/logging.ts +55 -0
  49. package/src/browser/merge-segment-loaders.ts +20 -12
  50. package/src/browser/navigation-bridge.ts +374 -561
  51. package/src/browser/navigation-client.ts +228 -70
  52. package/src/browser/navigation-store.ts +97 -55
  53. package/src/browser/navigation-transaction.ts +297 -0
  54. package/src/browser/network-error-handler.ts +61 -0
  55. package/src/browser/partial-update.ts +376 -315
  56. package/src/browser/prefetch/cache.ts +314 -0
  57. package/src/browser/prefetch/fetch.ts +282 -0
  58. package/src/browser/prefetch/observer.ts +65 -0
  59. package/src/browser/prefetch/policy.ts +48 -0
  60. package/src/browser/prefetch/queue.ts +191 -0
  61. package/src/browser/prefetch/resource-ready.ts +77 -0
  62. package/src/browser/rango-state.ts +152 -0
  63. package/src/browser/react/Link.tsx +255 -71
  64. package/src/browser/react/NavigationProvider.tsx +152 -24
  65. package/src/browser/react/context.ts +11 -0
  66. package/src/browser/react/filter-segment-order.ts +55 -0
  67. package/src/browser/react/index.ts +15 -12
  68. package/src/browser/react/location-state-shared.ts +95 -53
  69. package/src/browser/react/location-state.ts +60 -15
  70. package/src/browser/react/mount-context.ts +6 -1
  71. package/src/browser/react/nonce-context.ts +23 -0
  72. package/src/browser/react/shallow-equal.ts +27 -0
  73. package/src/browser/react/use-action.ts +29 -51
  74. package/src/browser/react/use-client-cache.ts +5 -3
  75. package/src/browser/react/use-handle.ts +30 -120
  76. package/src/browser/react/use-link-status.ts +6 -5
  77. package/src/browser/react/use-navigation.ts +44 -65
  78. package/src/browser/react/use-params.ts +78 -0
  79. package/src/browser/react/use-pathname.ts +47 -0
  80. package/src/browser/react/use-reverse.ts +99 -0
  81. package/src/browser/react/use-router.ts +83 -0
  82. package/src/browser/react/use-search-params.ts +56 -0
  83. package/src/browser/react/use-segments.ts +85 -99
  84. package/src/browser/response-adapter.ts +73 -0
  85. package/src/browser/rsc-router.tsx +246 -64
  86. package/src/browser/scroll-restoration.ts +127 -52
  87. package/src/browser/segment-reconciler.ts +243 -0
  88. package/src/browser/segment-structure-assert.ts +16 -0
  89. package/src/browser/server-action-bridge.ts +510 -603
  90. package/src/browser/shallow.ts +6 -1
  91. package/src/browser/types.ts +158 -48
  92. package/src/browser/validate-redirect-origin.ts +29 -0
  93. package/src/build/generate-manifest.ts +84 -23
  94. package/src/build/generate-route-types.ts +39 -828
  95. package/src/build/index.ts +4 -5
  96. package/src/build/route-trie.ts +85 -32
  97. package/src/build/route-types/ast-helpers.ts +25 -0
  98. package/src/build/route-types/ast-route-extraction.ts +98 -0
  99. package/src/build/route-types/codegen.ts +102 -0
  100. package/src/build/route-types/include-resolution.ts +418 -0
  101. package/src/build/route-types/param-extraction.ts +48 -0
  102. package/src/build/route-types/per-module-writer.ts +128 -0
  103. package/src/build/route-types/router-processing.ts +618 -0
  104. package/src/build/route-types/scan-filter.ts +85 -0
  105. package/src/build/runtime-discovery.ts +231 -0
  106. package/src/cache/background-task.ts +34 -0
  107. package/src/cache/cache-key-utils.ts +44 -0
  108. package/src/cache/cache-policy.ts +125 -0
  109. package/src/cache/cache-runtime.ts +342 -0
  110. package/src/cache/cache-scope.ts +167 -307
  111. package/src/cache/cf/cf-cache-store.ts +573 -21
  112. package/src/cache/cf/index.ts +13 -3
  113. package/src/cache/document-cache.ts +116 -77
  114. package/src/cache/handle-capture.ts +81 -0
  115. package/src/cache/handle-snapshot.ts +41 -0
  116. package/src/cache/index.ts +1 -15
  117. package/src/cache/memory-segment-store.ts +191 -13
  118. package/src/cache/profile-registry.ts +73 -0
  119. package/src/cache/read-through-swr.ts +134 -0
  120. package/src/cache/segment-codec.ts +256 -0
  121. package/src/cache/taint.ts +153 -0
  122. package/src/cache/types.ts +72 -122
  123. package/src/client.rsc.tsx +6 -1
  124. package/src/client.tsx +118 -302
  125. package/src/component-utils.ts +4 -4
  126. package/src/components/DefaultDocument.tsx +5 -1
  127. package/src/context-var.ts +156 -0
  128. package/src/debug.ts +19 -9
  129. package/src/errors.ts +77 -7
  130. package/src/handle.ts +55 -10
  131. package/src/handles/MetaTags.tsx +73 -20
  132. package/src/handles/breadcrumbs.ts +66 -0
  133. package/src/handles/index.ts +1 -0
  134. package/src/handles/meta.ts +30 -13
  135. package/src/host/cookie-handler.ts +21 -15
  136. package/src/host/errors.ts +8 -8
  137. package/src/host/index.ts +4 -7
  138. package/src/host/pattern-matcher.ts +27 -27
  139. package/src/host/router.ts +61 -39
  140. package/src/host/testing.ts +8 -8
  141. package/src/host/types.ts +15 -7
  142. package/src/host/utils.ts +1 -1
  143. package/src/href-client.ts +65 -45
  144. package/src/index.rsc.ts +138 -21
  145. package/src/index.ts +206 -51
  146. package/src/internal-debug.ts +11 -0
  147. package/src/loader.rsc.ts +25 -143
  148. package/src/loader.ts +27 -10
  149. package/src/network-error-thrower.tsx +3 -1
  150. package/src/outlet-context.ts +1 -1
  151. package/src/outlet-provider.tsx +45 -0
  152. package/src/prerender/param-hash.ts +4 -2
  153. package/src/prerender/store.ts +159 -13
  154. package/src/prerender.ts +397 -29
  155. package/src/response-utils.ts +28 -0
  156. package/src/reverse.ts +231 -121
  157. package/src/root-error-boundary.tsx +41 -29
  158. package/src/route-content-wrapper.tsx +7 -4
  159. package/src/route-definition/dsl-helpers.ts +1134 -0
  160. package/src/route-definition/helper-factories.ts +200 -0
  161. package/src/route-definition/helpers-types.ts +483 -0
  162. package/src/route-definition/index.ts +55 -0
  163. package/src/route-definition/redirect.ts +101 -0
  164. package/src/route-definition/resolve-handler-use.ts +155 -0
  165. package/src/route-definition.ts +1 -1431
  166. package/src/route-map-builder.ts +162 -123
  167. package/src/route-name.ts +53 -0
  168. package/src/route-types.ts +66 -9
  169. package/src/router/content-negotiation.ts +215 -0
  170. package/src/router/debug-manifest.ts +72 -0
  171. package/src/router/error-handling.ts +9 -9
  172. package/src/router/find-match.ts +160 -0
  173. package/src/router/handler-context.ts +418 -86
  174. package/src/router/intercept-resolution.ts +35 -20
  175. package/src/router/lazy-includes.ts +237 -0
  176. package/src/router/loader-resolution.ts +359 -128
  177. package/src/router/logging.ts +251 -0
  178. package/src/router/manifest.ts +98 -32
  179. package/src/router/match-api.ts +196 -261
  180. package/src/router/match-context.ts +4 -2
  181. package/src/router/match-handlers.ts +441 -0
  182. package/src/router/match-middleware/background-revalidation.ts +108 -93
  183. package/src/router/match-middleware/cache-lookup.ts +415 -86
  184. package/src/router/match-middleware/cache-store.ts +91 -29
  185. package/src/router/match-middleware/intercept-resolution.ts +48 -21
  186. package/src/router/match-middleware/segment-resolution.ts +73 -9
  187. package/src/router/match-pipelines.ts +10 -45
  188. package/src/router/match-result.ts +154 -35
  189. package/src/router/metrics.ts +240 -15
  190. package/src/router/middleware-cookies.ts +55 -0
  191. package/src/router/middleware-types.ts +209 -0
  192. package/src/router/middleware.ts +373 -371
  193. package/src/router/navigation-snapshot.ts +182 -0
  194. package/src/router/pattern-matching.ts +292 -52
  195. package/src/router/prerender-match.ts +502 -0
  196. package/src/router/preview-match.ts +98 -0
  197. package/src/router/request-classification.ts +310 -0
  198. package/src/router/revalidation.ts +152 -39
  199. package/src/router/route-snapshot.ts +245 -0
  200. package/src/router/router-context.ts +41 -21
  201. package/src/router/router-interfaces.ts +484 -0
  202. package/src/router/router-options.ts +618 -0
  203. package/src/router/router-registry.ts +24 -0
  204. package/src/router/segment-resolution/fresh.ts +756 -0
  205. package/src/router/segment-resolution/helpers.ts +268 -0
  206. package/src/router/segment-resolution/loader-cache.ts +199 -0
  207. package/src/router/segment-resolution/revalidation.ts +1407 -0
  208. package/src/router/segment-resolution/static-store.ts +67 -0
  209. package/src/router/segment-resolution.ts +21 -1315
  210. package/src/router/segment-wrappers.ts +291 -0
  211. package/src/router/substitute-pattern-params.ts +56 -0
  212. package/src/router/telemetry-otel.ts +299 -0
  213. package/src/router/telemetry.ts +300 -0
  214. package/src/router/timeout.ts +148 -0
  215. package/src/router/trie-matching.ts +111 -39
  216. package/src/router/types.ts +17 -9
  217. package/src/router/url-params.ts +49 -0
  218. package/src/router.ts +642 -2011
  219. package/src/rsc/handler-context.ts +45 -0
  220. package/src/rsc/handler.ts +864 -1114
  221. package/src/rsc/helpers.ts +181 -19
  222. package/src/rsc/index.ts +0 -20
  223. package/src/rsc/loader-fetch.ts +229 -0
  224. package/src/rsc/manifest-init.ts +90 -0
  225. package/src/rsc/nonce.ts +14 -0
  226. package/src/rsc/origin-guard.ts +141 -0
  227. package/src/rsc/progressive-enhancement.ts +395 -0
  228. package/src/rsc/response-error.ts +37 -0
  229. package/src/rsc/response-route-handler.ts +360 -0
  230. package/src/rsc/rsc-rendering.ts +256 -0
  231. package/src/rsc/runtime-warnings.ts +42 -0
  232. package/src/rsc/server-action.ts +360 -0
  233. package/src/rsc/ssr-setup.ts +128 -0
  234. package/src/rsc/types.ts +52 -11
  235. package/src/search-params.ts +230 -0
  236. package/src/segment-content-promise.ts +67 -0
  237. package/src/segment-loader-promise.ts +122 -0
  238. package/src/segment-system.tsx +187 -38
  239. package/src/server/context.ts +333 -59
  240. package/src/server/cookie-store.ts +190 -0
  241. package/src/server/fetchable-loader-store.ts +37 -0
  242. package/src/server/handle-store.ts +113 -15
  243. package/src/server/loader-registry.ts +24 -64
  244. package/src/server/request-context.ts +603 -109
  245. package/src/server.ts +35 -155
  246. package/src/ssr/index.tsx +107 -30
  247. package/src/static-handler.ts +126 -0
  248. package/src/theme/ThemeProvider.tsx +21 -15
  249. package/src/theme/ThemeScript.tsx +5 -5
  250. package/src/theme/constants.ts +5 -2
  251. package/src/theme/index.ts +4 -14
  252. package/src/theme/theme-context.ts +4 -30
  253. package/src/theme/theme-script.ts +21 -18
  254. package/src/types/boundaries.ts +158 -0
  255. package/src/types/cache-types.ts +198 -0
  256. package/src/types/error-types.ts +192 -0
  257. package/src/types/global-namespace.ts +100 -0
  258. package/src/types/handler-context.ts +764 -0
  259. package/src/types/index.ts +88 -0
  260. package/src/types/loader-types.ts +209 -0
  261. package/src/types/request-scope.ts +126 -0
  262. package/src/types/route-config.ts +170 -0
  263. package/src/types/route-entry.ts +120 -0
  264. package/src/types/segments.ts +167 -0
  265. package/src/types.ts +1 -1757
  266. package/src/urls/include-helper.ts +207 -0
  267. package/src/urls/index.ts +53 -0
  268. package/src/urls/path-helper-types.ts +372 -0
  269. package/src/urls/path-helper.ts +364 -0
  270. package/src/urls/pattern-types.ts +107 -0
  271. package/src/urls/response-types.ts +108 -0
  272. package/src/urls/type-extraction.ts +372 -0
  273. package/src/urls/urls-function.ts +98 -0
  274. package/src/urls.ts +1 -1282
  275. package/src/use-loader.tsx +161 -81
  276. package/src/vite/debug.ts +184 -0
  277. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  278. package/src/vite/discovery/discover-routers.ts +376 -0
  279. package/src/vite/discovery/gate-state.ts +171 -0
  280. package/src/vite/discovery/prerender-collection.ts +486 -0
  281. package/src/vite/discovery/route-types-writer.ts +258 -0
  282. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  283. package/src/vite/discovery/state.ts +117 -0
  284. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  285. package/src/vite/index.ts +15 -2063
  286. package/src/vite/plugin-types.ts +103 -0
  287. package/src/vite/plugins/cjs-to-esm.ts +98 -0
  288. package/src/vite/plugins/client-ref-dedup.ts +131 -0
  289. package/src/vite/plugins/client-ref-hashing.ts +117 -0
  290. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  291. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  292. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  293. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
  294. package/src/vite/plugins/expose-id-utils.ts +299 -0
  295. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  296. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  297. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  298. package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
  299. package/src/vite/plugins/expose-ids/types.ts +45 -0
  300. package/src/vite/plugins/expose-internal-ids.ts +816 -0
  301. package/src/vite/plugins/performance-tracks.ts +96 -0
  302. package/src/vite/plugins/refresh-cmd.ts +127 -0
  303. package/src/vite/plugins/use-cache-transform.ts +336 -0
  304. package/src/vite/plugins/version-injector.ts +109 -0
  305. package/src/vite/plugins/version-plugin.ts +266 -0
  306. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  307. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  308. package/src/vite/rango.ts +497 -0
  309. package/src/vite/router-discovery.ts +1423 -0
  310. package/src/vite/utils/ast-handler-extract.ts +517 -0
  311. package/src/vite/utils/banner.ts +36 -0
  312. package/src/vite/utils/bundle-analysis.ts +137 -0
  313. package/src/vite/utils/manifest-utils.ts +70 -0
  314. package/src/vite/utils/package-resolution.ts +161 -0
  315. package/src/vite/utils/prerender-utils.ts +222 -0
  316. package/src/vite/utils/shared-utils.ts +170 -0
  317. package/CLAUDE.md +0 -43
  318. package/src/browser/lru-cache.ts +0 -69
  319. package/src/browser/request-controller.ts +0 -164
  320. package/src/cache/memory-store.ts +0 -253
  321. package/src/href-context.ts +0 -33
  322. package/src/router.gen.ts +0 -6
  323. package/src/urls.gen.ts +0 -8
  324. package/src/vite/expose-handle-id.ts +0 -209
  325. package/src/vite/expose-loader-id.ts +0 -426
  326. package/src/vite/expose-location-state-id.ts +0 -177
  327. package/src/vite/expose-prerender-handler-id.ts +0 -429
  328. package/src/vite/package-resolution.ts +0 -125
  329. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -8,6 +8,9 @@ argument-hint: [@slot-name] [route-to-intercept]
8
8
 
9
9
  Intercept routes render a different component during soft navigation (client-side) while preserving the background route. Hard navigation (direct URL) shows the full page.
10
10
 
11
+ Canonical semantics reference:
12
+ [docs/execution-model.md](../../docs/internal/execution-model.md)
13
+
11
14
  ## Basic Intercept
12
15
 
13
16
  ```typescript
@@ -45,11 +48,11 @@ export const urlpatterns = urls(({ path, layout, intercept, loader }) => [
45
48
 
46
49
  ## Navigation Behavior
47
50
 
48
- | Navigation Type | What Renders |
49
- |-----------------|--------------|
51
+ | Navigation Type | What Renders |
52
+ | ------------------------------ | ---------------------------------------------------- |
50
53
  | Click link `/shop/product/abc` | `<ProductModal />` in `@modal`, background preserved |
51
- | Direct URL `/shop/product/abc` | Full `<ProductPage />` page |
52
- | Browser back | Close modal, restore previous state |
54
+ | Direct URL `/shop/product/abc` | Full `<ProductPage />` page |
55
+ | Browser back | Close modal, restore previous state |
53
56
 
54
57
  ## Intercept with Layout
55
58
 
@@ -68,6 +71,78 @@ intercept(
68
71
  )
69
72
  ```
70
73
 
74
+ ## Intercept Middleware
75
+
76
+ Intercepts support their own middleware chain via the use callback. The full chain for an intercept request is:
77
+
78
+ ```
79
+ global mw (router.use) -> route mw (urls middleware()) -> intercept mw -> intercept handler -> intercept loaders
80
+ ```
81
+
82
+ ```typescript
83
+ intercept(
84
+ "@modal",
85
+ "product",
86
+ <ProductModal />,
87
+ () => [
88
+ middleware(async (ctx, next) => {
89
+ // Runs only for this intercept, after global and route middleware
90
+ ctx.set("interceptSource", "modal");
91
+ await next();
92
+ }),
93
+ loader(ProductLoader),
94
+ ]
95
+ )
96
+ ```
97
+
98
+ The intercept handler can read context variables set by all upstream middleware layers (global, route, and intercept-specific).
99
+
100
+ Handler/layout `ctx.set()` data follows the same rule as elsewhere:
101
+ intercepts see data produced in the current render pass, but partial
102
+ action revalidation only recomputes segments that actually revalidate.
103
+ If an intercept depends on data established by an outer layout/handler,
104
+ revalidate that outer segment too or reload/guard the data inside the
105
+ intercept.
106
+
107
+ ### Revalidation Contracts for Intercept Dependencies
108
+
109
+ Use named revalidation contracts on both the outer producer and the intercept
110
+ consumer when they share `ctx.set()` data:
111
+
112
+ ```typescript
113
+ export const revalidateProductShell = ({ actionId }) =>
114
+ actionId?.includes("src/actions/product.ts#") ?? false;
115
+
116
+ layout(ProductLayout, () => [
117
+ revalidate(revalidateProductShell), // producer reruns
118
+ intercept("@modal", "product", <ProductModal />, () => [
119
+ revalidate(revalidateProductShell), // consumer reruns
120
+ loader(ProductLoader),
121
+ ]),
122
+ ]);
123
+ ```
124
+
125
+ Compose multiple contracts if the intercept depends on multiple upstream
126
+ domains.
127
+
128
+ Helper handoff style keeps intercept trees terse:
129
+
130
+ ```typescript
131
+ import { revalidate } from "@rangojs/router";
132
+
133
+ export const revalidateProduct = () => [
134
+ revalidate(revalidateProductShell),
135
+ ];
136
+
137
+ layout(ProductLayout, () => [
138
+ revalidateProduct(),
139
+ intercept("@modal", "product", <ProductModal />, () => [
140
+ revalidateProduct(),
141
+ loader(ProductLoader),
142
+ ]),
143
+ ]);
144
+ ```
145
+
71
146
  ## Conditional Intercept with when()
72
147
 
73
148
  Only intercept based on navigation context:
@@ -106,15 +181,15 @@ Use navigation to close:
106
181
 
107
182
  ```typescript
108
183
  "use client";
109
- import { useNavigation } from "@rangojs/router/client";
184
+ import { useRouter } from "@rangojs/router/client";
110
185
 
111
186
  function ModalWrapper({ children }) {
112
- const { goBack } = useNavigation();
187
+ const router = useRouter();
113
188
 
114
189
  return (
115
- <div className="modal-overlay" onClick={goBack}>
190
+ <div className="modal-overlay" onClick={() => router.back()}>
116
191
  <div className="modal" onClick={(e) => e.stopPropagation()}>
117
- <button onClick={goBack}>Close</button>
192
+ <button onClick={() => router.back()}>Close</button>
118
193
  {children}
119
194
  </div>
120
195
  </div>
@@ -122,6 +197,79 @@ function ModalWrapper({ children }) {
122
197
  }
123
198
  ```
124
199
 
200
+ ## Interaction with View Transitions
201
+
202
+ A layout that owns the `@modal` slot can also configure `transition()` for page
203
+ fades — opening a modal does **not** fire the layout's view transition. Rango
204
+ narrows the layout's `<ViewTransition>` wrap to the layout's default outlet
205
+ content, so `<ParallelOutlet />` (the slot where the modal mounts) is a sibling
206
+ of the wrap, not inside its subtree. Form actions submitted from inside an open
207
+ modal also commit without firing the underlying layout's transition, and the
208
+ modal subtree identity is preserved across revalidation (no remount,
209
+ `useActionState` survives). Closing the modal restores the page without a
210
+ stray transition.
211
+
212
+ For a modal-only morph (e.g. when intercepted URLs change while the modal
213
+ stays open), use an element-level React `<ViewTransition>` inside the modal
214
+ component — `transition()` accepted on `intercept()` via the DSL is not
215
+ applied to slot rendering today.
216
+
217
+ Caveat: route-level `transition()` wraps the route component itself, so a
218
+ `<ParallelOutlet />` rendered directly inside that route component would still
219
+ be inside the route's VT subtree. Mount the slot in a layout instead when you
220
+ combine intercept modals with route-level transitions.
221
+
222
+ See [skills/view-transitions](../view-transitions/SKILL.md) for the full
223
+ contract and direction-aware examples.
224
+
225
+ ## Interaction with Prerender
226
+
227
+ When the target route of an intercept uses `Prerender`, the intercept handler is
228
+ also resolved at build time and stored alongside the main pre-rendered segments.
229
+ This means intercept navigations to pre-rendered routes are served from the
230
+ prerender store without executing handler code at runtime.
231
+
232
+ ```typescript
233
+ // The detail route is pre-rendered
234
+ export const ProductDetail = Prerender(
235
+ async () => [{ slug: "shoes" }, { slug: "jacket" }],
236
+ async (ctx) => <ProductPage slug={ctx.params.slug} />,
237
+ );
238
+
239
+ // urls.tsx
240
+ layout(ShopLayout, () => [
241
+ path("/:slug", ProductDetail, { name: "detail" }, () => [
242
+ loader(ProductLoader),
243
+ ]),
244
+
245
+ // This intercept is also pre-rendered at build time
246
+ intercept("@modal", ".detail", <ProductModal />, () => [
247
+ when(({ from }) => from.pathname.startsWith("/shop")),
248
+ loader(ProductLoader),
249
+ ]),
250
+ ])
251
+ ```
252
+
253
+ Build-time behavior:
254
+
255
+ - The intercept handler (`<ProductModal />`) is resolved with BuildContext
256
+ - Result is stored under the key `"detail/paramHash/i"` (intercept variant)
257
+ - `when()` conditions are skipped at build time (all intercepts pre-rendered unconditionally)
258
+ - `when()` is still evaluated at runtime by the intercept-resolution middleware
259
+
260
+ Runtime behavior:
261
+
262
+ - Intercept navigation: prerender store serves the `/i` variant (frozen handler + fresh loaders)
263
+ - Direct navigation: prerender store serves the main variant (full page)
264
+ - If no intercept prerender entry exists, falls through to live intercept resolution
265
+
266
+ Loaders inside the intercept always run fresh at request time, same as regular
267
+ pre-rendered routes.
268
+
269
+ During action-driven partial revalidation, this same partial rule applies:
270
+ refreshing the intercept does not implicitly rebuild non-revalidated outer
271
+ segments.
272
+
125
273
  ## Complete Example
126
274
 
127
275
  ```typescript
@@ -188,3 +336,23 @@ export const shopPatterns = urls(({
188
336
  ]),
189
337
  ]);
190
338
  ```
339
+
340
+ ## Handler-attached `.use`
341
+
342
+ Intercept handlers can carry their own middleware, loaders, loading state, error/notFound boundaries, and even nested `layout`/`route`/`when` defaults via `.use` — useful for self-contained modal components that travel with their own data and chrome.
343
+
344
+ ```typescript
345
+ const QuickViewModal: Handler = async (ctx) => {
346
+ const product = await ctx.use(ProductLoader);
347
+ return <QuickView product={product} />;
348
+ };
349
+ QuickViewModal.use = () => [
350
+ loader(ProductLoader),
351
+ loading(<QuickViewSkeleton />),
352
+ layout(<ModalChrome />),
353
+ ];
354
+
355
+ intercept("@modal", "product", QuickViewModal);
356
+ ```
357
+
358
+ Explicit `use()` at the mount site merges with `handler.use` (handler defaults first, explicit second). See [skills/handler-use](../handler-use/SKILL.md) for merge order and the per-mount-site allowed-types table.
@@ -8,6 +8,9 @@ argument-hint: [component]
8
8
 
9
9
  Layouts wrap child routes and persist during navigation within their scope.
10
10
 
11
+ Canonical semantics reference:
12
+ [docs/execution-model.md](../../docs/internal/execution-model.md)
13
+
11
14
  ## Basic Layout
12
15
 
13
16
  ```typescript
@@ -45,9 +48,7 @@ layout(<ShopLayout />, () => [
45
48
  ### Component Function
46
49
 
47
50
  ```typescript
48
- layout(ShopLayout, () => [
49
- path("/shop", ShopIndex, { name: "shop" }),
50
- ])
51
+ layout(ShopLayout, () => [path("/shop", ShopIndex, { name: "shop" })]);
51
52
  ```
52
53
 
53
54
  ### Handler with Context
@@ -117,6 +118,8 @@ function ShopLayout() {
117
118
  }
118
119
  ```
119
120
 
121
+ A layout's `transition()` config wraps the content that flows through `<Outlet />` — not the layout chrome itself, and not sibling `<ParallelOutlet />` slots. Stacking transitions across nested layouts collapses around the deepest default outlet content. See [skills/view-transitions](../view-transitions/SKILL.md) for the full wrap rules and intercept-modal interaction.
122
+
120
123
  ## Named Outlets
121
124
 
122
125
  For parallel routes, use named outlets:
@@ -141,6 +144,54 @@ function DashboardLayout() {
141
144
  }
142
145
  ```
143
146
 
147
+ ## Orphan Layout (inside route)
148
+
149
+ A layout as a child of `path()` wraps the route content and can read
150
+ data set by the route handler via `ctx.get()`. The handler always
151
+ executes before its children.
152
+
153
+ This handler-first guarantee applies to a single full render pass
154
+ (initial render, prerender, or full HTML re-render). During partial
155
+ action revalidation, only the segments that revalidate are recomputed.
156
+ If an orphan layout depends on data established by an outer handler or
157
+ layout, that outer segment must also revalidate, or the orphan must
158
+ guard/reload the data independently.
159
+
160
+ ```typescript
161
+ import { Outlet, ParallelOutlet } from "@rangojs/router/client";
162
+
163
+ urls(({ path, layout, parallel }) => [
164
+ path("/product/:slug", (ctx) => {
165
+ const product = await fetchProduct(ctx.params.slug);
166
+ ctx.set("product", product);
167
+ return <ProductPage product={product} />;
168
+ }, { name: "product" }, () => [
169
+ layout((ctx) => {
170
+ const product = ctx.get("product");
171
+ return (
172
+ <div>
173
+ <Breadcrumb name={product?.name} />
174
+ <Outlet />
175
+ <ParallelOutlet name="@related" />
176
+ </div>
177
+ );
178
+ }, () => [
179
+ parallel({
180
+ "@related": (ctx) => {
181
+ const product = ctx.get("product");
182
+ return <RelatedProducts category={product?.category} />;
183
+ },
184
+ }),
185
+ ]),
186
+ ]),
187
+ ])
188
+ ```
189
+
190
+ Orphan layouts can call `ctx.get()` to read data set by their parent
191
+ handler. They can also call `ctx.set()`, though the primary pattern is
192
+ for route handlers and middleware to write context variables and for
193
+ orphan layouts to read them.
194
+
144
195
  ## Layout Revalidation
145
196
 
146
197
  Layouts don't revalidate by default. Control with `revalidate()`:
@@ -161,6 +212,54 @@ layout(<CartLayout />, () => [
161
212
  ])
162
213
  ```
163
214
 
215
+ If child segments read data that was established by this layout or by a
216
+ route handler above them, revalidate the outer segment too. Partial
217
+ revalidation does not re-run non-revalidated ancestors just to rebuild
218
+ their `ctx.set()` state.
219
+
220
+ ### Revalidation Contracts
221
+
222
+ For shared upstream data, define named revalidation functions and reuse
223
+ them on both producer and consumer segments:
224
+
225
+ ```typescript
226
+ // revalidation-contracts.ts
227
+ export const revalidateCartData = ({ actionId }) =>
228
+ actionId?.includes("src/actions/cart.ts#addToCart") ?? false;
229
+ ```
230
+
231
+ ```typescript
232
+ layout(<CartLayout />, () => [
233
+ revalidate(revalidateCartData), // producer
234
+ path("/cart", CartPage, { name: "cart" }, () => [
235
+ revalidate(revalidateCartData), // consumer
236
+ ]),
237
+ ]);
238
+ ```
239
+
240
+ If a segment depends on multiple upstream domains, compose multiple
241
+ contracts (`revalidateAuthData`, `revalidateCartData`, and so on).
242
+
243
+ You can also package them as importable handoff helpers:
244
+
245
+ ```typescript
246
+ // revalidation-contracts.ts
247
+ import { revalidate } from "@rangojs/router";
248
+
249
+ export const revalidateAuthData = ({ actionId }) =>
250
+ actionId?.includes("src/actions/auth.ts#") ?? false;
251
+ export const revalidateAuth = () => [revalidate(revalidateAuthData)];
252
+ ```
253
+
254
+ ```typescript
255
+ layout(<ShellLayout />, () => [
256
+ revalidateAuth(),
257
+ path("/account", AccountPage, { name: "account" }, () => [
258
+ revalidateAuth(),
259
+ ]),
260
+ ]);
261
+ ```
262
+
164
263
  ## Complete Example
165
264
 
166
265
  ```typescript
@@ -211,3 +310,25 @@ export const shopPatterns = urls(({ path, layout, parallel, loader, revalidate }
211
310
  ]),
212
311
  ]);
213
312
  ```
313
+
314
+ ## Handler-attached `.use`
315
+
316
+ Layout handlers can carry their own middleware, default parallels, and includes via `.use` so a layout becomes a self-contained unit reusable across mount sites.
317
+
318
+ ```typescript
319
+ const AdminLayout: Handler = (ctx) => {
320
+ const user = ctx.get(CurrentUser);
321
+ return <Admin user={user} />;
322
+ };
323
+ AdminLayout.use = () => [
324
+ middleware(requireAdmin),
325
+ parallel({ "@adminNotifs": AdminNotifsSlot }),
326
+ ];
327
+
328
+ // Mount site declares structure only; defaults travel with the layout.
329
+ layout(AdminLayout, () => [
330
+ path("/admin", AdminIndex, { name: "admin.index" }),
331
+ ]);
332
+ ```
333
+
334
+ Allowed item types in a layout's `.use` mirror the layout `use()` callback (the broadest set). Explicit `use()` at the mount site merges with `handler.use` (handler defaults first, explicit second). See [skills/handler-use](../handler-use/SKILL.md) for merge order and per-mount-site allowed types.