@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.140

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 (460) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +432 -7
  3. package/dist/bin/rango.js +2073 -213
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +7258 -2714
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +140 -67
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +329 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +487 -0
  12. package/skills/caching/SKILL.md +357 -25
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +246 -0
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +16 -10
  19. package/skills/document-cache/SKILL.md +87 -62
  20. package/skills/fonts/SKILL.md +6 -4
  21. package/skills/handler-use/SKILL.md +364 -0
  22. package/skills/hooks/SKILL.md +557 -79
  23. package/skills/host-router/SKILL.md +320 -0
  24. package/skills/i18n/SKILL.md +276 -0
  25. package/skills/intercept/SKILL.md +207 -15
  26. package/skills/layout/SKILL.md +146 -6
  27. package/skills/links/SKILL.md +304 -25
  28. package/skills/loader/SKILL.md +616 -54
  29. package/skills/middleware/SKILL.md +217 -37
  30. package/skills/migrate-nextjs/SKILL.md +611 -0
  31. package/skills/migrate-react-router/SKILL.md +927 -0
  32. package/skills/mime-routes/SKILL.md +42 -11
  33. package/skills/observability/SKILL.md +194 -0
  34. package/skills/parallel/SKILL.md +284 -3
  35. package/skills/ppr/SKILL.md +426 -0
  36. package/skills/prerender/SKILL.md +437 -52
  37. package/skills/rango/SKILL.md +369 -22
  38. package/skills/react-compiler/SKILL.md +168 -0
  39. package/skills/response-routes/SKILL.md +263 -121
  40. package/skills/route/SKILL.md +350 -21
  41. package/skills/router-setup/SKILL.md +246 -33
  42. package/skills/scripts/SKILL.md +179 -0
  43. package/skills/server-actions/SKILL.md +775 -0
  44. package/skills/shell-manifest/SKILL.md +185 -0
  45. package/skills/streams-and-websockets/SKILL.md +283 -0
  46. package/skills/tailwind/SKILL.md +27 -3
  47. package/skills/testing/SKILL.md +126 -222
  48. package/skills/testing/bindings.md +103 -0
  49. package/skills/testing/cache-prerender.md +127 -0
  50. package/skills/testing/client-components.md +124 -0
  51. package/skills/testing/e2e-parity.md +125 -0
  52. package/skills/testing/flight.md +91 -0
  53. package/skills/testing/handles.md +131 -0
  54. package/skills/testing/loader.md +128 -0
  55. package/skills/testing/middleware.md +99 -0
  56. package/skills/testing/render-handler.md +122 -0
  57. package/skills/testing/response-routes.md +95 -0
  58. package/skills/testing/reverse-and-types.md +85 -0
  59. package/skills/testing/server-actions.md +107 -0
  60. package/skills/testing/server-tree.md +128 -0
  61. package/skills/testing/setup.md +123 -0
  62. package/skills/theme/SKILL.md +9 -8
  63. package/skills/typesafety/SKILL.md +532 -103
  64. package/skills/use-cache/SKILL.md +367 -0
  65. package/skills/vercel/SKILL.md +128 -0
  66. package/skills/view-transitions/SKILL.md +337 -0
  67. package/src/__augment-tests__/augment.ts +81 -0
  68. package/src/__augment-tests__/augmented.check.ts +116 -0
  69. package/src/__internal.ts +77 -44
  70. package/src/bin/rango.ts +312 -15
  71. package/src/browser/action-coordinator.ts +114 -0
  72. package/src/browser/action-fence.ts +47 -0
  73. package/src/browser/app-shell.ts +39 -0
  74. package/src/browser/app-version.ts +14 -0
  75. package/src/browser/connection-warmup.ts +134 -0
  76. package/src/browser/cookie-name.ts +140 -0
  77. package/src/browser/event-controller.ts +293 -202
  78. package/src/browser/history-state.ts +101 -0
  79. package/src/browser/index.ts +3 -3
  80. package/src/browser/intercept-utils.ts +52 -0
  81. package/src/browser/invalidate-client-cache.ts +52 -0
  82. package/src/browser/link-interceptor.ts +24 -4
  83. package/src/browser/logging.ts +11 -0
  84. package/src/browser/merge-segment-loaders.ts +20 -12
  85. package/src/browser/navigation-bridge.ts +385 -576
  86. package/src/browser/navigation-client.ts +245 -75
  87. package/src/browser/navigation-store-handle.ts +38 -0
  88. package/src/browser/navigation-store.ts +184 -118
  89. package/src/browser/navigation-transaction.ts +247 -0
  90. package/src/browser/network-error-handler.ts +88 -0
  91. package/src/browser/partial-update.ts +412 -364
  92. package/src/browser/prefetch/cache.ts +359 -0
  93. package/src/browser/prefetch/fetch.ts +452 -0
  94. package/src/browser/prefetch/observer.ts +65 -0
  95. package/src/browser/prefetch/policy.ts +48 -0
  96. package/src/browser/prefetch/queue.ts +209 -0
  97. package/src/browser/prefetch/resource-ready.ts +77 -0
  98. package/src/browser/rango-state.ts +194 -0
  99. package/src/browser/react/Link.tsx +275 -68
  100. package/src/browser/react/NavigationProvider.tsx +265 -109
  101. package/src/browser/react/ScrollRestoration.tsx +10 -6
  102. package/src/browser/react/context.ts +11 -0
  103. package/src/browser/react/filter-segment-order.ts +70 -0
  104. package/src/browser/react/index.ts +0 -48
  105. package/src/browser/react/location-state-shared.ts +272 -60
  106. package/src/browser/react/location-state.ts +90 -20
  107. package/src/browser/react/mount-context.ts +6 -1
  108. package/src/browser/react/nonce-context.ts +23 -0
  109. package/src/browser/react/shallow-equal.ts +27 -0
  110. package/src/browser/react/use-action.ts +35 -66
  111. package/src/browser/react/use-handle.ts +39 -126
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +39 -13
  114. package/src/browser/react/use-navigation.ts +53 -69
  115. package/src/browser/react/use-params.ts +75 -0
  116. package/src/browser/react/use-pathname.ts +47 -0
  117. package/src/browser/react/use-reverse.ts +106 -0
  118. package/src/browser/react/use-router.ts +98 -0
  119. package/src/browser/react/use-search-params.ts +51 -0
  120. package/src/browser/react/use-segments.ts +72 -99
  121. package/src/browser/response-adapter.ts +164 -0
  122. package/src/browser/rsc-router.tsx +300 -72
  123. package/src/browser/scroll-restoration.ts +138 -50
  124. package/src/browser/segment-reconciler.ts +243 -0
  125. package/src/browser/segment-structure-assert.ts +17 -1
  126. package/src/browser/server-action-bridge.ts +668 -613
  127. package/src/browser/types.ts +223 -51
  128. package/src/browser/validate-redirect-origin.ts +56 -0
  129. package/src/build/collect-fallback-refs.ts +107 -0
  130. package/src/build/generate-manifest.ts +252 -161
  131. package/src/build/generate-route-types.ts +41 -1038
  132. package/src/build/index.ts +12 -7
  133. package/src/build/prefix-tree-utils.ts +123 -0
  134. package/src/build/route-trie.ts +225 -42
  135. package/src/build/route-types/ast-helpers.ts +25 -0
  136. package/src/build/route-types/ast-route-extraction.ts +105 -0
  137. package/src/build/route-types/codegen.ts +113 -0
  138. package/src/build/route-types/include-resolution.ts +812 -0
  139. package/src/build/route-types/param-extraction.ts +51 -0
  140. package/src/build/route-types/per-module-writer.ts +144 -0
  141. package/src/build/route-types/router-processing.ts +695 -0
  142. package/src/build/route-types/scan-filter.ts +85 -0
  143. package/src/build/route-types/source-scan.ts +216 -0
  144. package/src/build/runtime-discovery.ts +223 -0
  145. package/src/cache/background-task.ts +34 -0
  146. package/src/cache/cache-error.ts +104 -0
  147. package/src/cache/cache-key-utils.ts +60 -0
  148. package/src/cache/cache-policy.ts +199 -0
  149. package/src/cache/cache-runtime.ts +525 -0
  150. package/src/cache/cache-scope.ts +298 -332
  151. package/src/cache/cache-tag.ts +103 -0
  152. package/src/cache/cf/cf-base64.ts +33 -0
  153. package/src/cache/cf/cf-cache-constants.ts +127 -0
  154. package/src/cache/cf/cf-cache-store.ts +2500 -158
  155. package/src/cache/cf/cf-cache-types.ts +349 -0
  156. package/src/cache/cf/cf-kv-utils.ts +46 -0
  157. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  158. package/src/cache/cf/index.ts +17 -17
  159. package/src/cache/document-cache.ts +199 -92
  160. package/src/cache/handle-capture.ts +81 -0
  161. package/src/cache/handle-snapshot.ts +111 -0
  162. package/src/cache/index.ts +29 -35
  163. package/src/cache/memory-segment-store.ts +363 -30
  164. package/src/cache/profile-registry.ts +88 -0
  165. package/src/cache/read-through-swr.ts +178 -0
  166. package/src/cache/segment-codec.ts +248 -0
  167. package/src/cache/shell-cache.ts +386 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +156 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1102 -0
  173. package/src/client.rsc.tsx +43 -21
  174. package/src/client.tsx +131 -347
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +109 -0
  177. package/src/component-utils.ts +23 -4
  178. package/src/components/DefaultDocument.tsx +13 -3
  179. package/src/context-var.ts +168 -0
  180. package/src/debug.ts +19 -9
  181. package/src/decode-loader-results.ts +52 -0
  182. package/src/defer.ts +185 -0
  183. package/src/deps/ssr.ts +0 -1
  184. package/src/encode-kv.ts +49 -0
  185. package/src/errors.ts +106 -10
  186. package/src/escape-script.ts +52 -0
  187. package/src/handle.ts +110 -35
  188. package/src/handles/MetaTags.tsx +83 -59
  189. package/src/handles/Scripts.tsx +183 -0
  190. package/src/handles/breadcrumbs.ts +93 -0
  191. package/src/handles/deferred-resolution.ts +127 -0
  192. package/src/handles/is-thenable.ts +18 -0
  193. package/src/handles/meta.ts +44 -53
  194. package/src/handles/script.ts +244 -0
  195. package/src/host/cookie-handler.ts +20 -65
  196. package/src/host/errors.ts +21 -30
  197. package/src/host/index.ts +13 -9
  198. package/src/host/pattern-matcher.ts +50 -79
  199. package/src/host/router.ts +151 -121
  200. package/src/host/testing.ts +45 -32
  201. package/src/host/types.ts +52 -11
  202. package/src/host/utils.ts +2 -2
  203. package/src/href-client.ts +192 -57
  204. package/src/index.rsc.ts +177 -35
  205. package/src/index.ts +255 -71
  206. package/src/internal-debug.ts +9 -2
  207. package/src/loader-store.ts +500 -0
  208. package/src/loader.rsc.ts +31 -99
  209. package/src/loader.ts +30 -12
  210. package/src/missing-id-error.ts +68 -0
  211. package/src/outlet-context.ts +1 -1
  212. package/src/outlet-provider.tsx +41 -0
  213. package/src/prerender/param-hash.ts +16 -14
  214. package/src/prerender/store.ts +121 -21
  215. package/src/prerender.ts +460 -26
  216. package/src/redirect-origin.ts +100 -0
  217. package/src/regex-escape.ts +8 -0
  218. package/src/render-error-thrower.tsx +20 -0
  219. package/src/response-utils.ts +62 -0
  220. package/src/reverse.ts +198 -128
  221. package/src/root-error-boundary.tsx +42 -48
  222. package/src/route-content-wrapper.tsx +22 -77
  223. package/src/route-definition/dsl-helpers.ts +1116 -0
  224. package/src/route-definition/helper-factories.ts +88 -0
  225. package/src/route-definition/helpers-types.ts +505 -0
  226. package/src/route-definition/index.ts +54 -0
  227. package/src/route-definition/redirect.ts +134 -0
  228. package/src/route-definition/resolve-handler-use.ts +160 -0
  229. package/src/route-definition/use-item-types.ts +29 -0
  230. package/src/route-definition.ts +1 -1481
  231. package/src/route-map-builder.ts +82 -144
  232. package/src/route-name.ts +53 -0
  233. package/src/route-types.ts +71 -45
  234. package/src/router/basename.ts +14 -0
  235. package/src/router/content-negotiation.ts +263 -0
  236. package/src/router/debug-manifest.ts +72 -0
  237. package/src/router/error-handling.ts +54 -27
  238. package/src/router/find-match.ts +245 -0
  239. package/src/router/handler-context.ts +377 -125
  240. package/src/router/instrument.ts +350 -0
  241. package/src/router/intercept-resolution.ts +59 -28
  242. package/src/router/lazy-includes.ts +254 -0
  243. package/src/router/loader-resolution.ts +421 -157
  244. package/src/router/logging.ts +106 -6
  245. package/src/router/manifest.ts +131 -57
  246. package/src/router/match-api.ts +167 -246
  247. package/src/router/match-context.ts +4 -24
  248. package/src/router/match-handlers.ts +440 -0
  249. package/src/router/match-middleware/background-revalidation.ts +117 -93
  250. package/src/router/match-middleware/cache-lookup.ts +297 -150
  251. package/src/router/match-middleware/cache-store.ts +123 -51
  252. package/src/router/match-middleware/intercept-resolution.ts +44 -43
  253. package/src/router/match-middleware/segment-resolution.ts +64 -22
  254. package/src/router/match-pipelines.ts +11 -87
  255. package/src/router/match-result.ts +121 -50
  256. package/src/router/metrics.ts +219 -28
  257. package/src/router/middleware-types.ts +93 -0
  258. package/src/router/middleware.ts +505 -441
  259. package/src/router/navigation-snapshot.ts +133 -0
  260. package/src/router/params-util.ts +23 -0
  261. package/src/router/parse-pattern.ts +115 -0
  262. package/src/router/pattern-matching.ts +311 -142
  263. package/src/router/prefetch-cache-ttl.ts +51 -0
  264. package/src/router/prefetch-limits.ts +37 -0
  265. package/src/router/prerender-match.ts +547 -0
  266. package/src/router/preview-match.ts +102 -0
  267. package/src/router/request-classification.ts +278 -0
  268. package/src/router/revalidation.ts +203 -62
  269. package/src/router/route-snapshot.ts +246 -0
  270. package/src/router/router-context.ts +45 -48
  271. package/src/router/router-interfaces.ts +554 -0
  272. package/src/router/router-options.ts +779 -0
  273. package/src/router/router-registry.ts +21 -0
  274. package/src/router/segment-resolution/fresh.ts +772 -0
  275. package/src/router/segment-resolution/helpers.ts +348 -0
  276. package/src/router/segment-resolution/loader-cache.ts +250 -0
  277. package/src/router/segment-resolution/loader-mask.ts +44 -0
  278. package/src/router/segment-resolution/revalidation.ts +1331 -0
  279. package/src/router/segment-resolution/static-store.ts +81 -0
  280. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  281. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  282. package/src/router/segment-resolution.ts +25 -1354
  283. package/src/router/segment-wrappers.ts +292 -0
  284. package/src/router/state-cookie-name.ts +33 -0
  285. package/src/router/substitute-pattern-params.ts +75 -0
  286. package/src/router/telemetry-otel.ts +261 -0
  287. package/src/router/telemetry.ts +377 -0
  288. package/src/router/timeout.ts +128 -0
  289. package/src/router/tracing.ts +206 -0
  290. package/src/router/trie-matching.ts +240 -61
  291. package/src/router/types.ts +23 -70
  292. package/src/router/url-params.ts +57 -0
  293. package/src/router.ts +781 -2378
  294. package/src/rsc/full-payload.ts +70 -0
  295. package/src/rsc/handler-context.ts +46 -0
  296. package/src/rsc/handler.ts +905 -1142
  297. package/src/rsc/helpers.ts +275 -19
  298. package/src/rsc/index.ts +2 -25
  299. package/src/rsc/json-route-result.ts +38 -0
  300. package/src/rsc/loader-fetch.ts +305 -0
  301. package/src/rsc/manifest-init.ts +77 -0
  302. package/src/rsc/nonce.ts +14 -0
  303. package/src/rsc/origin-guard.ts +155 -0
  304. package/src/rsc/progressive-enhancement.ts +502 -0
  305. package/src/rsc/redirect-guard.ts +99 -0
  306. package/src/rsc/response-cache-serve.ts +238 -0
  307. package/src/rsc/response-error.ts +104 -0
  308. package/src/rsc/response-route-handler.ts +257 -0
  309. package/src/rsc/rsc-rendering.ts +337 -0
  310. package/src/rsc/runtime-warnings.ts +55 -0
  311. package/src/rsc/server-action.ts +522 -0
  312. package/src/rsc/shell-capture.ts +439 -0
  313. package/src/rsc/ssr-setup.ts +144 -0
  314. package/src/rsc/transition-gate.ts +89 -0
  315. package/src/rsc/types.ts +95 -12
  316. package/src/runtime-env.ts +18 -0
  317. package/src/search-params.ts +99 -82
  318. package/src/segment-content-promise.ts +67 -0
  319. package/src/segment-loader-promise.ts +149 -0
  320. package/src/segment-system.tsx +349 -134
  321. package/src/serialize.ts +243 -0
  322. package/src/server/context.ts +452 -85
  323. package/src/server/cookie-parse.ts +32 -0
  324. package/src/server/cookie-store.ts +310 -0
  325. package/src/server/fetchable-loader-store.ts +11 -6
  326. package/src/server/handle-store.ts +123 -42
  327. package/src/server/live.ts +130 -0
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +842 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +412 -136
  332. package/src/ssr/ssr-root.tsx +228 -0
  333. package/src/static-handler.ts +45 -18
  334. package/src/testing/cache-status.ts +162 -0
  335. package/src/testing/collect-handle.ts +46 -0
  336. package/src/testing/dispatch.ts +701 -0
  337. package/src/testing/dom.entry.ts +22 -0
  338. package/src/testing/e2e/fixture.ts +188 -0
  339. package/src/testing/e2e/index.ts +128 -0
  340. package/src/testing/e2e/matchers.ts +35 -0
  341. package/src/testing/e2e/page-helpers.ts +272 -0
  342. package/src/testing/e2e/parity.ts +387 -0
  343. package/src/testing/e2e/server.ts +195 -0
  344. package/src/testing/flight-matchers.ts +97 -0
  345. package/src/testing/flight-normalize.ts +11 -0
  346. package/src/testing/flight-runtime.d.ts +57 -0
  347. package/src/testing/flight-tree.ts +682 -0
  348. package/src/testing/flight.entry.ts +52 -0
  349. package/src/testing/flight.ts +257 -0
  350. package/src/testing/generated-routes.ts +199 -0
  351. package/src/testing/index.ts +105 -0
  352. package/src/testing/internal/context.ts +371 -0
  353. package/src/testing/internal/flight-client-globals.ts +30 -0
  354. package/src/testing/internal/seed-vars.ts +54 -0
  355. package/src/testing/render-handler.ts +357 -0
  356. package/src/testing/render-route.tsx +584 -0
  357. package/src/testing/run-loader.ts +385 -0
  358. package/src/testing/run-middleware.ts +205 -0
  359. package/src/testing/run-transition-when.ts +164 -0
  360. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  361. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  362. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  363. package/src/testing/vitest-stubs/version.ts +5 -0
  364. package/src/testing/vitest.ts +305 -0
  365. package/src/theme/ThemeProvider.tsx +40 -72
  366. package/src/theme/ThemeScript.tsx +12 -14
  367. package/src/theme/constants.ts +57 -15
  368. package/src/theme/index.ts +3 -20
  369. package/src/theme/theme-context.ts +5 -35
  370. package/src/theme/theme-script.ts +43 -39
  371. package/src/theme/use-theme.ts +0 -3
  372. package/src/types/boundaries.ts +123 -0
  373. package/src/types/cache-types.ts +207 -0
  374. package/src/types/error-types.ts +132 -0
  375. package/src/types/global-namespace.ts +113 -0
  376. package/src/types/handler-context.ts +839 -0
  377. package/src/types/index.ts +81 -0
  378. package/src/types/loader-types.ts +212 -0
  379. package/src/types/request-scope.ts +112 -0
  380. package/src/types/route-config.ts +138 -0
  381. package/src/types/route-entry.ts +114 -0
  382. package/src/types/segments.ts +271 -0
  383. package/src/types.ts +1 -1795
  384. package/src/urls/include-helper.ts +162 -0
  385. package/src/urls/include-provider.ts +71 -0
  386. package/src/urls/index.ts +43 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +275 -0
  389. package/src/urls/pattern-types.ts +124 -0
  390. package/src/urls/response-types.ts +109 -0
  391. package/src/urls/type-extraction.ts +316 -0
  392. package/src/urls/urls-function.ts +80 -0
  393. package/src/urls.ts +1 -1341
  394. package/src/use-loader.tsx +406 -141
  395. package/src/vercel/index.ts +11 -0
  396. package/src/vercel/tracing.ts +88 -0
  397. package/src/vite/debug.ts +185 -0
  398. package/src/vite/discovery/bundle-postprocess.ts +182 -0
  399. package/src/vite/discovery/discover-routers.ts +389 -0
  400. package/src/vite/discovery/discovery-errors.ts +255 -0
  401. package/src/vite/discovery/gate-state.ts +171 -0
  402. package/src/vite/discovery/prerender-collection.ts +467 -0
  403. package/src/vite/discovery/route-types-writer.ts +214 -0
  404. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  405. package/src/vite/discovery/state.ts +161 -0
  406. package/src/vite/discovery/virtual-module-codegen.ts +183 -0
  407. package/src/vite/index.ts +23 -2255
  408. package/src/vite/inject-client-debug.ts +36 -0
  409. package/src/vite/plugin-types.ts +303 -0
  410. package/src/vite/plugins/cjs-to-esm.ts +90 -0
  411. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  412. package/src/vite/plugins/client-ref-hashing.ts +118 -0
  413. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  414. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  415. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  416. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
  417. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
  418. package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
  419. package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
  420. package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
  421. package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
  422. package/src/vite/plugins/expose-ids/types.ts +45 -0
  423. package/src/vite/plugins/expose-internal-ids.ts +805 -0
  424. package/src/vite/plugins/performance-tracks.ts +89 -0
  425. package/src/vite/plugins/refresh-cmd.ts +127 -0
  426. package/src/vite/plugins/use-cache-transform.ts +313 -0
  427. package/src/vite/plugins/vercel-output.ts +384 -0
  428. package/src/vite/plugins/version-injector.ts +94 -0
  429. package/src/vite/plugins/version-plugin.ts +263 -0
  430. package/src/vite/plugins/virtual-entries.ts +234 -0
  431. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  432. package/src/vite/rango.ts +560 -0
  433. package/src/vite/router-discovery.ts +1638 -0
  434. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
  435. package/src/vite/utils/banner.ts +36 -0
  436. package/src/vite/utils/bundle-analysis.ts +132 -0
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +15 -0
  441. package/src/vite/utils/package-resolution.ts +89 -0
  442. package/src/vite/utils/prerender-utils.ts +249 -0
  443. package/src/vite/utils/shared-utils.ts +269 -0
  444. package/CLAUDE.md +0 -43
  445. package/dist/vite/index.named-routes.gen.ts +0 -103
  446. package/src/browser/lru-cache.ts +0 -69
  447. package/src/browser/react/use-client-cache.ts +0 -56
  448. package/src/browser/request-controller.ts +0 -164
  449. package/src/browser/shallow.ts +0 -35
  450. package/src/cache/memory-store.ts +0 -253
  451. package/src/handles/index.ts +0 -6
  452. package/src/href-context.ts +0 -33
  453. package/src/network-error-thrower.tsx +0 -21
  454. package/src/router.gen.ts +0 -6
  455. package/src/static-handler.gen.ts +0 -5
  456. package/src/urls.gen.ts +0 -8
  457. package/src/vite/expose-internal-ids.ts +0 -1167
  458. package/src/vite/package-resolution.ts +0 -125
  459. package/src/vite/virtual-entries.ts +0 -114
  460. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -23,9 +23,13 @@ import { urls } from "@rangojs/router";
23
23
  export const urlpatterns = urls(({ path, layout, include }) => [
24
24
  // RSC page + JSON API on the same URL
25
25
  path("/products/:id", ProductPage, { name: "product" }),
26
- path.json("/products/:id", (ctx) => {
27
- return db.getProduct(ctx.params.id);
28
- }, { name: "productJson" }),
26
+ path.json(
27
+ "/products/:id",
28
+ (ctx) => {
29
+ return db.getProduct(ctx.params.id);
30
+ },
31
+ { name: "productJson" },
32
+ ),
29
33
  ]);
30
34
  ```
31
35
 
@@ -45,14 +49,14 @@ There is no special short-circuit — RSC follows the same negotiation rules as
45
49
 
46
50
  The MIME mapping used for matching:
47
51
 
48
- | Tag | MIME type |
49
- |-----|-----------|
52
+ | Tag | MIME type |
53
+ | -------------------- | ------------------------------------------------------------ |
50
54
  | RSC (plain `path()`) | `text/html` (negotiation) / `text/x-component` (wire format) |
51
- | `json` | `application/json` |
52
- | `text` | `text/plain` |
53
- | `xml` | `application/xml` |
54
- | `html` | `text/html` |
55
- | `md` | `text/markdown` |
55
+ | `json` | `application/json` |
56
+ | `text` | `text/plain` |
57
+ | `xml` | `application/xml` |
58
+ | `html` | `text/html` |
59
+ | `md` | `text/markdown` |
56
60
 
57
61
  RSC routes negotiate as `text/html` but respond with `text/x-component` (the RSC wire format).
58
62
  The browser's RSC runtime decodes this transparently — clients requesting `text/html` get
@@ -77,7 +81,7 @@ export const urlpatterns = urls(({ path }) => [
77
81
  - `Accept: application/json` — JSON handler
78
82
  - `Accept: text/plain` — text handler
79
83
  - `Accept: application/xml` — XML handler
80
- - `Accept: */*` — first variant (JSON, since it was registered first)
84
+ - `Accept: */*` — RSC page (the primary, since it was registered first)
81
85
 
82
86
  ## Wildcard Routes
83
87
 
@@ -104,6 +108,33 @@ path.text("/api/data", () => "plain text version", { name: "dataText" }),
104
108
  Without an RSC primary, there is no `text/html` candidate — the Accept header
105
109
  picks among the response-type candidates directly.
106
110
 
111
+ ## Type Safety For Negotiated Paths
112
+
113
+ `router.named-routes.gen.ts` validates route names, params, search, `href()`, and
114
+ the `Rango.Path` type, but it does not carry response payload metadata. For MIME or
115
+ response payload types, use one of these surfaces:
116
+
117
+ - `RouteResponse<typeof patterns, "routeName">` for a specific response variant
118
+ by route name. This is the clearest option when several MIME variants share
119
+ one URL pattern.
120
+ - `Rango.PathResponse<"/products/:id">` (ambient, no import) for global lookup by URL pattern or concrete path after the app
121
+ registers `typeof router.routeMap`:
122
+
123
+ ```typescript
124
+ // router.tsx
125
+ export const router = createRouter({ document: Document }).routes(urlpatterns);
126
+
127
+ declare global {
128
+ namespace Rango {
129
+ interface RegisteredRoutes extends typeof router.routeMap {}
130
+ }
131
+ }
132
+ ```
133
+
134
+ `RegisteredRoutes` is what exposes the richer routeMap entries containing
135
+ response payload metadata. Without it, URL-pattern response lookup has paths but
136
+ no payloads, so response types resolve to `never`.
137
+
107
138
  ## How It Works
108
139
 
109
140
  1. **Build time**: `buildRouteTrie()` calls `mergeLeaves()` when multiple routes share a pattern.
@@ -0,0 +1,194 @@
1
+ ---
2
+ name: observability
3
+ description: Debug Rango request performance with debugPerformance, Server-Timing, structured telemetry, and tracing
4
+ argument-hint:
5
+ ---
6
+
7
+ # Observability
8
+
9
+ Use this when you need to understand request latency, cache decisions,
10
+ revalidation behavior, loader overlap, or production traces.
11
+
12
+ Rango exposes two complementary observability surfaces:
13
+
14
+ 1. **Performance timeline** (`debugPerformance`) — per-request waterfall for
15
+ local or targeted debugging. It prints to the console and emits
16
+ `Server-Timing`.
17
+ 2. **Structured telemetry** (`telemetry`) — lifecycle events sent to a pluggable
18
+ sink for production monitoring, OpenTelemetry, or custom metrics.
19
+
20
+ The essentials are below. The exported `TelemetryEvent` union type
21
+ (`import type { TelemetryEvent } from "@rangojs/router"`) is the full event
22
+ contract — every event kind and its fields are typed there.
23
+
24
+ ## Performance timeline
25
+
26
+ Enable globally while debugging:
27
+
28
+ ```typescript
29
+ import { createRouter } from "@rangojs/router";
30
+
31
+ const router = createRouter({
32
+ document: Document,
33
+ urls: urlpatterns,
34
+ debugPerformance: true,
35
+ });
36
+ ```
37
+
38
+ Or enable for selected requests from middleware:
39
+
40
+ ```typescript
41
+ middleware(async (ctx, next) => {
42
+ if (ctx.url.searchParams.has("debug")) {
43
+ ctx.debugPerformance();
44
+ }
45
+ await next();
46
+ });
47
+ ```
48
+
49
+ Call `ctx.debugPerformance()` before `await next()`. The request then prints a
50
+ shared-axis waterfall and adds a `Server-Timing` header.
51
+
52
+ Read the timeline as intervals:
53
+
54
+ - `handler:total` is the whole router request.
55
+ - `render:total` / `ssr-render-html` show the render pass.
56
+ - `loader:*` rows should overlap render work. If a loader starts only after the
57
+ render bar, it is serialized latency.
58
+ - Cache, route matching, middleware pre/post, RSC serialization, and SSR phases
59
+ appear as separate spans, so the slow phase is visible without guessing.
60
+
61
+ ## Structured telemetry
62
+
63
+ Use telemetry when you want durable production events rather than a one-request
64
+ debug waterfall.
65
+
66
+ ```typescript
67
+ import { createRouter, createConsoleSink } from "@rangojs/router";
68
+
69
+ const router = createRouter({
70
+ document: Document,
71
+ urls: urlpatterns,
72
+ telemetry: createConsoleSink(),
73
+ });
74
+ ```
75
+
76
+ For OpenTelemetry — phase spans come from the `tracing` slot
77
+ (`createOTelTracing`), discrete-fact spans from the `telemetry` sink
78
+ (`createOTelSink`):
79
+
80
+ ```typescript
81
+ import {
82
+ createRouter,
83
+ createOTelTracing,
84
+ createOTelSink,
85
+ } from "@rangojs/router";
86
+ import { trace } from "@opentelemetry/api";
87
+
88
+ const tracer = trace.getTracer("my-app");
89
+
90
+ const router = createRouter({
91
+ document: Document,
92
+ urls: urlpatterns,
93
+ tracing: createOTelTracing(tracer), // request/loader/render/… phase spans
94
+ telemetry: createOTelSink(tracer), // handler errors, cache decisions, …
95
+ });
96
+ ```
97
+
98
+ On **Cloudflare Workers**, use `createCloudflareTracing` for the `tracing` slot
99
+ instead — it emits the same phases as native Cloudflare custom spans (in the
100
+ Workers trace waterfall, next to the automatic KV/D1/fetch spans), with no
101
+ `@opentelemetry/api` dependency:
102
+
103
+ ```typescript
104
+ import { createRouter } from "@rangojs/router";
105
+ import { createCloudflareTracing } from "@rangojs/router/cloudflare";
106
+
107
+ const router = createRouter({
108
+ document: Document,
109
+ urls: urlpatterns,
110
+ tracing: createCloudflareTracing(), // all phases on by default
111
+ // tracing: createCloudflareTracing({ spans: { ssr: false } }), // toggle phases
112
+ });
113
+ ```
114
+
115
+ On **Vercel Functions** (Node runtime), use `createVercelTracing` — a thin
116
+ wrapper over `createOTelTracing` that reads the global OTel tracer
117
+ `@vercel/otel`'s `registerOTel()` installs, so you do not call `trace.getTracer`
118
+ yourself. Custom spans are Node-only (unsupported on the Edge runtime):
119
+
120
+ ```typescript
121
+ // instrumentation.ts — install the provider, then export the tracing config.
122
+ // Importing this module is what runs registerOTel() — a Rango/Vite app does not
123
+ // auto-load instrumentation.ts like Next.js, so a standalone registerOTel() that
124
+ // nothing imports is a silent no-op.
125
+ import { registerOTel } from "@vercel/otel";
126
+ import { createVercelTracing } from "@rangojs/router/vercel";
127
+ registerOTel({ serviceName: "my-app" });
128
+ export const tracing = createVercelTracing(); // { enabled, spans, tracerName, tracer }
129
+
130
+ // router.tsx — importing `tracing` runs instrumentation.ts
131
+ import { createRouter } from "@rangojs/router";
132
+ import { tracing } from "./instrumentation.js";
133
+
134
+ const router = createRouter({ document: Document, urls: urlpatterns, tracing });
135
+ ```
136
+
137
+ These factories return a `RouterTracingConfig` for the same `tracing` slot;
138
+ `telemetry` stays independent (events only, no phase spans). Phase spans:
139
+ `rango.request`, `rango.middleware`, `rango.action`, `rango.loader`,
140
+ `rango.render`, `rango.ssr` — the same phases the `debugPerformance` timeline
141
+ shows, co-emitted from one site. Off-platform (no Cloudflare tracing destination
142
+ / no OTel SDK) every span call is a transparent pass-through, so the request
143
+ behaves as if tracing were off.
144
+
145
+ Custom sinks implement `emit(event)`:
146
+
147
+ ```typescript
148
+ import { createRouter } from "@rangojs/router";
149
+
150
+ const router = createRouter({
151
+ document: Document,
152
+ urls: urlpatterns,
153
+ telemetry: {
154
+ emit(event) {
155
+ myMetrics.record(event);
156
+ },
157
+ },
158
+ });
159
+ ```
160
+
161
+ Events include `request.start/end/error`, `loader.start/end/error`,
162
+ `handler.error`, `cache.decision`, `revalidation.decision`, `request.timeout`,
163
+ and `request.origin-rejected`.
164
+
165
+ ## Debugging revalidation and stale data
166
+
167
+ When stale UI or unexpected partial renders are the question, use all three
168
+ layers together:
169
+
170
+ ```typescript
171
+ import { createConsoleSink, createRouter } from "@rangojs/router";
172
+
173
+ const router = createRouter({
174
+ document: Document,
175
+ urls: urlpatterns,
176
+ debugPerformance: true,
177
+ telemetry: createConsoleSink(),
178
+ });
179
+ ```
180
+
181
+ Then inspect:
182
+
183
+ - `revalidation.decision` telemetry to see which segment re-ran or skipped.
184
+ - cache spans / `cache.decision` events to see hit, miss, stale, and background
185
+ revalidation behavior.
186
+ - loader spans to confirm live loaders overlap the render rather than blocking
187
+ first paint.
188
+ - the `Server-Timing` header to compare local logs with browser-network timing.
189
+
190
+ ## Zero-overhead defaults
191
+
192
+ `debugPerformance` is off by default, and `telemetry` emits nothing unless a sink
193
+ is configured. Per-request `ctx.debugPerformance()` lets you turn on the
194
+ waterfall only for the route, user, or query param you are investigating.
@@ -54,6 +54,108 @@ parallel({
54
54
  })
55
55
  ```
56
56
 
57
+ ## Reading Handler Data
58
+
59
+ Parallels can read `ctx.set()` values from their parent handler or layout
60
+ via `ctx.get()`. The handler always executes before its parallels
61
+ (handler-first).
62
+
63
+ Visibility follows tree structure:
64
+
65
+ - Layout-level parallels see layout data, but not path handler data
66
+ (the path is a separate entry).
67
+ - Parallels inside a path (or its orphan layouts) see both layout and
68
+ path handler data.
69
+
70
+ This applies to full render passes. During partial action revalidation,
71
+ only revalidated segments are recomputed. If a parallel depends on data
72
+ set by an outer handler or layout, revalidate that outer segment too, or
73
+ have the parallel reload/guard the data itself.
74
+
75
+ ```typescript
76
+ path("/dashboard/:id", (ctx) => {
77
+ const user = await getUser(ctx.params.id);
78
+ ctx.set("user", user);
79
+ return <DashboardPage user={user} />;
80
+ }, { name: "dashboard" }, () => [
81
+ layout(DashboardLayout, () => [
82
+ parallel({
83
+ "@sidebar": (ctx) => {
84
+ const user = ctx.get("user");
85
+ return <Sidebar role={user?.role} />;
86
+ },
87
+ }),
88
+ ]),
89
+ ])
90
+ ```
91
+
92
+ ## Setting Handles (Meta, Breadcrumbs)
93
+
94
+ Parallel slot handlers can call `ctx.use(Meta)` or `ctx.use(Breadcrumbs)` to
95
+ push handle data. The data is associated with the **parent** layout or route
96
+ segment, not the parallel segment itself. This is because parallels execute
97
+ after their parent handler and inherit its segment scope.
98
+
99
+ This works well for document-level metadata — the handle data follows the
100
+ parent's lifecycle (appears when the parent is mounted, removed when it
101
+ unmounts).
102
+
103
+ ```typescript
104
+ parallel({
105
+ "@meta": (ctx) => {
106
+ const meta = ctx.use(Meta);
107
+ meta({ title: "Product Detail" });
108
+ meta({ name: "description", content: "..." });
109
+ return null; // UI-less slot, only sets metadata
110
+ },
111
+ "@sidebar": (ctx) => <Sidebar />,
112
+ })
113
+ ```
114
+
115
+ Multiple parallels on the same parent can each push handle data — they all
116
+ accumulate under the parent segment ID.
117
+
118
+ ### Pattern: `@meta` slot for per-route metadata overrides
119
+
120
+ A dedicated `@meta` parallel slot lets routes define metadata separately from
121
+ their handler logic. The layout sets defaults via a title template, and each
122
+ route overrides via its own `@meta` slot. Since child segments push after
123
+ parents and `collectMeta` uses last-wins deduplication, overrides work
124
+ naturally.
125
+
126
+ ```typescript
127
+ // Layout sets defaults
128
+ layout((ctx) => {
129
+ ctx.use(Meta)({ title: { template: "%s | Store", default: "Store" } });
130
+ return <StoreLayout />;
131
+ }, () => [
132
+ // Route with @meta override — decoupled from handler rendering
133
+ path("/:slug", ProductPage, { name: "product" }, () => [
134
+ parallel({
135
+ "@meta": async (ctx) => {
136
+ const product = await ctx.use(ProductLoader);
137
+ const meta = ctx.use(Meta);
138
+ meta({ title: product.name });
139
+ meta({ name: "description", content: product.description });
140
+ meta({
141
+ "script:ld+json": {
142
+ "@context": "https://schema.org",
143
+ "@type": "Product",
144
+ name: product.name,
145
+ description: product.description,
146
+ },
147
+ });
148
+ return null; // UI-less slot
149
+ },
150
+ }),
151
+ ]),
152
+ ])
153
+ ```
154
+
155
+ This keeps the route handler focused on rendering UI while metadata
156
+ (title, description, Open Graph, JSON-LD) lives in a composable slot that
157
+ can be added, removed, or swapped per route without touching the handler.
158
+
57
159
  ## Parallel Routes with Loaders
58
160
 
59
161
  Add loaders and loading states to parallel routes:
@@ -71,6 +173,126 @@ parallel(
71
173
  )
72
174
  ```
73
175
 
176
+ ### Streaming Behavior
177
+
178
+ Parallels with `loading()` are **independent streaming units**. They don't
179
+ block the parent layout or sibling routes during SSR:
180
+
181
+ - **With `loading()`**: The skeleton renders immediately. The loader runs
182
+ in the background and streams data to the client when ready. The rest
183
+ of the page (layout, route content, other parallels) renders without
184
+ waiting.
185
+ - **Without `loading()`**: The parallel's loaders block the parent layout's
186
+ rendering. Use this when the data must be available before the page
187
+ paints (e.g., critical above-the-fold content).
188
+ - **SPA navigation**: Parallel loaders resolve in the background. The
189
+ existing parallel UI stays visible — no skeleton flash on route changes
190
+ within the same layout.
191
+
192
+ ```typescript
193
+ // Sidebar streams independently — page renders immediately
194
+ parallel(
195
+ { "@sidebar": () => <Sidebar /> },
196
+ () => [loader(SlowSidebarLoader), loading(<SidebarSkeleton />)]
197
+ )
198
+
199
+ // Cart data blocks layout — must be ready before paint
200
+ parallel(
201
+ { "@cartBadge": () => <CartBadge /> },
202
+ () => [loader(CartCountLoader)] // No loading() = awaited
203
+ )
204
+ ```
205
+
206
+ ## Composable Slots via `handler.use`
207
+
208
+ Slot handlers can carry their own loader, loading, error/notFound boundaries, revalidation, and transition defaults via `.use`. The mount site then declares **just the slot names** — no per-call data wiring.
209
+
210
+ ```typescript
211
+ const CartSummary: Handler = async (ctx) => {
212
+ const cart = await ctx.use(CartLoader);
213
+ return <CartSummaryView cart={cart} />;
214
+ };
215
+ CartSummary.use = () => [
216
+ loader(CartLoader),
217
+ loading(<CartSkeleton />),
218
+ revalidate(revalidateCartData),
219
+ ];
220
+
221
+ // Same slot, no copy-pasted plumbing across layouts.
222
+ layout(<DashboardLayout />, () => [
223
+ parallel({ "@cart": CartSummary }),
224
+ path("/dashboard", DashboardIndex, { name: "dashboard.index" }),
225
+ ]);
226
+
227
+ layout(<AccountLayout />, () => [
228
+ parallel({ "@cart": CartSummary }),
229
+ path("/account", AccountIndex, { name: "account.index" }),
230
+ ]);
231
+ ```
232
+
233
+ A slot's `loading()` (whether from `handler.use` or explicit) makes that slot an independent streaming unit, exactly as in the **Streaming Behavior** section above.
234
+
235
+ The `parallel` mount site has the narrowest allow-list for `handler.use` items — slots cannot bring their own middleware or layout, only `revalidate`, `loader`, `loading`, `errorBoundary`, `notFoundBoundary`, and `transition`. See [skills/handler-use](../handler-use/SKILL.md) for the full table and merge rules.
236
+
237
+ `transition` is allowed in the slot allow-list, but slot-level rendering does **not** currently apply a `<ViewTransition>` wrapper — only the layout/route wraps take effect at render time. For a modal-only morph today, use an element-level React `<ViewTransition>` inside the slot's component. The reverse direction is the useful guarantee: a layout-level `transition()` fires when the layout's default outlet content changes but **not** when a `<ParallelOutlet />` mounts new content (modal opens are not subtree updates of the layout VT). See [skills/view-transitions](../view-transitions/SKILL.md) for the wrap rules and the intercept caveat.
238
+
239
+ ### Two scopes for explicit `use`: shared (broadcast) and slot-local
240
+
241
+ `parallel({...slots}, () => [...use])` runs the shared `use()` callback **once per slot** ([dsl-helpers.ts](../../src/route-definition/dsl-helpers.ts)) — items in that callback land on every slot's entry. That's the right behavior for the items the parallel allow-list permits and that accumulate (`loader`, `revalidate`, `errorBoundary`, `notFoundBoundary`, `transition`). (Slots cannot bring `middleware` or `layout` — see the allowed-types note above.)
242
+
243
+ For single-assignment items like `loading()`, broadcasting overwrites every slot's `handler.use` default. Pass a **slot descriptor** `{ handler, use }` instead — items in the descriptor's `use` apply only to that slot:
244
+
245
+ ```typescript
246
+ // @cart gets a custom skeleton; @notifs keeps its handler.use default.
247
+ parallel({
248
+ "@cart": {
249
+ handler: Cart,
250
+ use: () => [loading(<CustomCartSkeleton />)],
251
+ },
252
+ "@notifs": Notifs,
253
+ });
254
+
255
+ // Opt one slot out of streaming while siblings still stream the broadcast.
256
+ parallel(
257
+ {
258
+ "@cart": { handler: Cart, use: () => [loading(false)] },
259
+ "@notifs": Notifs,
260
+ },
261
+ () => [loading(<BroadcastSkeleton />)],
262
+ );
263
+ ```
264
+
265
+ Per-slot merge order is **handler.use → shared use → slot-local use**. Slot-local is the narrowest scope, so it wins for last-write-wins items. See [skills/handler-use § `loading()` is a single-assignment item — scope it correctly](../handler-use/SKILL.md#loading-is-a-single-assignment-item--scope-it-correctly) for the full reasoning.
266
+
267
+ ## Slot Override Semantics
268
+
269
+ When multiple `parallel()` calls define the same slot name, **the last
270
+ definition wins**. Earlier definitions of that slot are removed. Other
271
+ slots from the earlier call are preserved.
272
+
273
+ This enables composition patterns where included routes override
274
+ parent-defined slots:
275
+
276
+ ```typescript
277
+ layout(DashboardLayout, () => [
278
+ // Base slots
279
+ parallel({
280
+ "@sidebar": () => <DefaultSidebar />,
281
+ "@footer": () => <Footer />,
282
+ }),
283
+
284
+ // Override just @sidebar — @footer is preserved
285
+ parallel({ "@sidebar": () => <CustomSidebar /> }),
286
+
287
+ path("/", DashboardIndex, { name: "index" }),
288
+ ])
289
+ ```
290
+
291
+ After resolution, the layout has two parallel entries:
292
+
293
+ - `{ "@footer": () => <Footer /> }` (first call, `@sidebar` removed)
294
+ - `{ "@sidebar": () => <CustomSidebar /> }` (second call, wins)
295
+
74
296
  ## Multiple Parallel Slots
75
297
 
76
298
  ```typescript
@@ -97,7 +319,7 @@ Render different content based on context:
97
319
  ```typescript
98
320
  parallel({
99
321
  "@sidebar": (ctx) => {
100
- const user = ctx.env.Variables.user;
322
+ const user = ctx.get("user");
101
323
  return user ? <UserSidebar user={user} /> : <GuestSidebar />;
102
324
  },
103
325
  })
@@ -108,6 +330,8 @@ parallel({
108
330
  Control when parallel routes revalidate:
109
331
 
110
332
  ```typescript
333
+ import * as CartActions from "./actions/cart";
334
+
111
335
  parallel(
112
336
  {
113
337
  "@cart": () => <CartSummary />,
@@ -115,11 +339,67 @@ parallel(
115
339
  () => [
116
340
  loader(CartLoader),
117
341
  // Revalidate when cart actions occur
118
- revalidate(({ actionId }) => actionId?.includes("Cart") ?? false),
342
+ revalidate((ctx) => ctx.isAction(CartActions) || undefined),
119
343
  ]
120
344
  )
121
345
  ```
122
346
 
347
+ Where the slot sits decides its action default. A parallel under a
348
+ `path()` (or one of its orphan layouts) belongs to the route entry and
349
+ revalidates together with it on every action — handler-set data stays
350
+ consistent with no configuration. A parallel under a standalone
351
+ `layout()` entry follows the parent-chain default instead: skipped on
352
+ actions unless a `revalidate()` opts it in.
353
+
354
+ In either position, revalidating only the parallel does not re-run outer
355
+ handlers/layouts. If the slot reads `ctx.get()` data established above
356
+ it, opt the outer segment into revalidation as well (see `/rango` →
357
+ "Passing data down the tree").
358
+
359
+ A `revalidate()` callback may return a hard `boolean`, a soft
360
+ `{ defaultShouldRevalidate }` object, or nothing (`void` / `null` /
361
+ `undefined`) to defer to the next revalidator. See
362
+ [loader/SKILL.md#revalidate-return-shapes](../loader/SKILL.md#revalidate-return-shapes)
363
+ for the full contract — it's the same across `loader()`, `path()`,
364
+ `layout()`, `parallel()`, and `intercept()`.
365
+
366
+ ### Revalidation Contracts for Parallel Dependencies
367
+
368
+ Prefer named revalidation contracts shared by both the upstream producer and
369
+ the parallel consumer:
370
+
371
+ ```typescript
372
+ // revalidation-contracts.ts
373
+ import * as CartActions from "./actions/cart";
374
+
375
+ export const revalidateCartData = (ctx) =>
376
+ ctx.isAction(CartActions) || undefined;
377
+
378
+ layout(CartLayout, () => [
379
+ revalidate(revalidateCartData), // producer reruns
380
+ parallel(
381
+ { "@cart": CartSummary },
382
+ () => [revalidate(revalidateCartData)], // consumer reruns
383
+ ),
384
+ ]);
385
+ ```
386
+
387
+ If the slot consumes multiple upstream domains, compose the contracts on both
388
+ segments.
389
+
390
+ Handoff helper style also works:
391
+
392
+ ```typescript
393
+ import { revalidate } from "@rangojs/router";
394
+
395
+ export const revalidateCart = () => [revalidate(revalidateCartData)];
396
+
397
+ layout(CartLayout, () => [
398
+ revalidateCart(),
399
+ parallel({ "@cart": CartSummary }, () => [revalidateCart()]),
400
+ ]);
401
+ ```
402
+
123
403
  ## Named Outlets
124
404
 
125
405
  Use `ParallelOutlet` to render slots in layouts:
@@ -161,6 +441,7 @@ function MyLayout() {
161
441
  ```typescript
162
442
  import { urls } from "@rangojs/router";
163
443
  import { Outlet, ParallelOutlet } from "@rangojs/router/client";
444
+ import * as CartActions from "./actions/cart";
164
445
 
165
446
  function ShopLayout() {
166
447
  return (
@@ -211,7 +492,7 @@ export const shopPatterns = urls(({
211
492
  () => [
212
493
  loader(CartLoader),
213
494
  loading(<CartSkeleton />),
214
- revalidate(({ actionId }) => actionId?.includes("Cart") ?? false),
495
+ revalidate((ctx) => ctx.isAction(CartActions) || undefined),
215
496
  ]
216
497
  ),
217
498