@rangojs/router 0.0.0-experimental.79 → 0.0.0-experimental.7c7e4327

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 (440) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +301 -797
  3. package/dist/bin/rango.js +603 -145
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3750 -1160
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +96 -24
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +85 -6
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +228 -33
  12. package/skills/caching/SKILL.md +336 -19
  13. package/skills/catalog.json +271 -0
  14. package/skills/comparison/SKILL.md +50 -0
  15. package/skills/comparison/agents/openai.yaml +4 -0
  16. package/skills/comparison/references/framework-comparison.md +837 -0
  17. package/skills/composability/SKILL.md +110 -4
  18. package/skills/css/SKILL.md +76 -0
  19. package/skills/debug-manifest/SKILL.md +5 -3
  20. package/skills/defer-hydration/SKILL.md +235 -0
  21. package/skills/document-cache/SKILL.md +87 -56
  22. package/skills/fonts/SKILL.md +1 -1
  23. package/skills/handler-use/SKILL.md +12 -10
  24. package/skills/hooks/SKILL.md +73 -691
  25. package/skills/hooks/data.md +273 -0
  26. package/skills/hooks/handle-and-actions.md +103 -0
  27. package/skills/hooks/navigation.md +110 -0
  28. package/skills/hooks/outlets.md +41 -0
  29. package/skills/hooks/state.md +228 -0
  30. package/skills/hooks/urls.md +135 -0
  31. package/skills/host-router/SKILL.md +129 -27
  32. package/skills/i18n/SKILL.md +276 -0
  33. package/skills/intercept/SKILL.md +75 -19
  34. package/skills/layout/SKILL.md +40 -19
  35. package/skills/links/SKILL.md +247 -17
  36. package/skills/loader/SKILL.md +248 -10
  37. package/skills/middleware/SKILL.md +25 -13
  38. package/skills/migrate-nextjs/SKILL.md +205 -20
  39. package/skills/migrate-react-router/SKILL.md +59 -670
  40. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  41. package/skills/migrate-react-router/component-migration.md +196 -0
  42. package/skills/migrate-react-router/data-and-actions.md +225 -0
  43. package/skills/migrate-react-router/route-mapping.md +271 -0
  44. package/skills/mime-routes/SKILL.md +29 -2
  45. package/skills/observability/SKILL.md +202 -0
  46. package/skills/parallel/SKILL.md +40 -10
  47. package/skills/ppr/SKILL.md +616 -0
  48. package/skills/prerender/SKILL.md +72 -60
  49. package/skills/rango/SKILL.md +318 -26
  50. package/skills/react-compiler/SKILL.md +168 -0
  51. package/skills/response-routes/SKILL.md +138 -49
  52. package/skills/route/SKILL.md +117 -9
  53. package/skills/router-setup/SKILL.md +44 -9
  54. package/skills/scripts/SKILL.md +179 -0
  55. package/skills/server-actions/SKILL.md +776 -0
  56. package/skills/shell-manifest/SKILL.md +185 -0
  57. package/skills/streams-and-websockets/SKILL.md +283 -0
  58. package/skills/tailwind/SKILL.md +28 -4
  59. package/skills/testing/SKILL.md +130 -0
  60. package/skills/testing/bindings.md +103 -0
  61. package/skills/testing/cache-prerender.md +127 -0
  62. package/skills/testing/client-components.md +124 -0
  63. package/skills/testing/e2e-parity.md +125 -0
  64. package/skills/testing/flight.md +91 -0
  65. package/skills/testing/handles.md +131 -0
  66. package/skills/testing/loader.md +128 -0
  67. package/skills/testing/middleware.md +99 -0
  68. package/skills/testing/render-handler.md +122 -0
  69. package/skills/testing/response-routes.md +95 -0
  70. package/skills/testing/reverse-and-types.md +85 -0
  71. package/skills/testing/server-actions.md +107 -0
  72. package/skills/testing/server-tree.md +128 -0
  73. package/skills/testing/setup.md +123 -0
  74. package/skills/theme/SKILL.md +1 -1
  75. package/skills/typesafety/SKILL.md +45 -626
  76. package/skills/typesafety/env-and-bindings.md +254 -0
  77. package/skills/typesafety/generated-files-and-cli.md +335 -0
  78. package/skills/typesafety/params-and-search.md +153 -0
  79. package/skills/typesafety/route-types.md +209 -0
  80. package/skills/use-cache/SKILL.md +74 -15
  81. package/skills/vercel/SKILL.md +128 -0
  82. package/skills/view-transitions/SKILL.md +337 -0
  83. package/src/__augment-tests__/augment.ts +81 -0
  84. package/src/__augment-tests__/augmented.check.ts +116 -0
  85. package/src/__internal.ts +0 -65
  86. package/src/browser/action-coordinator.ts +53 -36
  87. package/src/browser/action-fence.ts +47 -0
  88. package/src/browser/app-shell.ts +39 -0
  89. package/src/browser/connection-warmup.ts +134 -0
  90. package/src/browser/cookie-name.ts +140 -0
  91. package/src/browser/event-controller.ts +252 -158
  92. package/src/browser/history-state.ts +21 -0
  93. package/src/browser/index.ts +3 -3
  94. package/src/browser/invalidate-client-cache.ts +52 -0
  95. package/src/browser/logging.ts +28 -0
  96. package/src/browser/merge-segment-loaders.ts +6 -4
  97. package/src/browser/navigation-bridge.ts +94 -25
  98. package/src/browser/navigation-client.ts +144 -79
  99. package/src/browser/navigation-store-handle.ts +38 -0
  100. package/src/browser/navigation-store.ts +161 -73
  101. package/src/browser/navigation-transaction.ts +9 -59
  102. package/src/browser/network-error-handler.ts +34 -7
  103. package/src/browser/partial-update.ts +183 -144
  104. package/src/browser/prefetch/cache.ts +242 -77
  105. package/src/browser/prefetch/fetch.ts +325 -69
  106. package/src/browser/prefetch/queue.ts +61 -12
  107. package/src/browser/rango-state.ts +158 -76
  108. package/src/browser/react/Link.tsx +58 -20
  109. package/src/browser/react/NavigationProvider.tsx +202 -120
  110. package/src/browser/react/ScrollRestoration.tsx +10 -6
  111. package/src/browser/react/filter-segment-order.ts +66 -7
  112. package/src/browser/react/index.ts +0 -48
  113. package/src/browser/react/location-state-shared.ts +178 -8
  114. package/src/browser/react/location-state.ts +39 -14
  115. package/src/browser/react/use-action.ts +6 -15
  116. package/src/browser/react/use-handle.ts +17 -14
  117. package/src/browser/react/use-href.tsx +8 -1
  118. package/src/browser/react/use-link-status.ts +33 -8
  119. package/src/browser/react/use-navigation.ts +32 -7
  120. package/src/browser/react/use-params.ts +20 -10
  121. package/src/browser/react/use-reverse.ts +106 -0
  122. package/src/browser/react/use-router.ts +25 -3
  123. package/src/browser/react/use-search-params.ts +0 -5
  124. package/src/browser/react/use-segments.ts +11 -21
  125. package/src/browser/response-adapter.ts +99 -8
  126. package/src/browser/rsc-router.tsx +145 -28
  127. package/src/browser/scroll-restoration.ts +37 -22
  128. package/src/browser/segment-reconciler.ts +31 -21
  129. package/src/browser/segment-structure-assert.ts +2 -2
  130. package/src/browser/server-action-bridge.ts +236 -65
  131. package/src/browser/types.ts +102 -9
  132. package/src/browser/validate-redirect-origin.ts +43 -16
  133. package/src/build/collect-fallback-refs.ts +107 -0
  134. package/src/build/generate-manifest.ts +203 -154
  135. package/src/build/generate-route-types.ts +3 -1
  136. package/src/build/index.ts +11 -3
  137. package/src/build/prefix-tree-utils.ts +123 -0
  138. package/src/build/route-trie.ts +152 -21
  139. package/src/build/route-types/ast-route-extraction.ts +15 -8
  140. package/src/build/route-types/codegen.ts +16 -5
  141. package/src/build/route-types/include-resolution.ts +456 -62
  142. package/src/build/route-types/param-extraction.ts +6 -3
  143. package/src/build/route-types/per-module-writer.ts +22 -6
  144. package/src/build/route-types/router-processing.ts +128 -51
  145. package/src/build/route-types/scan-filter.ts +1 -1
  146. package/src/build/route-types/source-scan.ts +216 -0
  147. package/src/build/runtime-discovery.ts +13 -21
  148. package/src/cache/cache-error.ts +104 -0
  149. package/src/cache/cache-key-utils.ts +58 -13
  150. package/src/cache/cache-policy.ts +108 -34
  151. package/src/cache/cache-runtime.ts +421 -58
  152. package/src/cache/cache-scope.ts +187 -96
  153. package/src/cache/cache-tag.ts +149 -0
  154. package/src/cache/cf/cf-base64.ts +33 -0
  155. package/src/cache/cf/cf-cache-constants.ts +127 -0
  156. package/src/cache/cf/cf-cache-store.ts +2202 -372
  157. package/src/cache/cf/cf-cache-types.ts +349 -0
  158. package/src/cache/cf/cf-kv-utils.ts +46 -0
  159. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  160. package/src/cache/cf/index.ts +6 -16
  161. package/src/cache/document-cache.ts +126 -41
  162. package/src/cache/handle-snapshot.ts +70 -0
  163. package/src/cache/index.ts +23 -20
  164. package/src/cache/memory-segment-store.ts +243 -37
  165. package/src/cache/profile-registry.ts +46 -31
  166. package/src/cache/read-through-swr.ts +56 -12
  167. package/src/cache/segment-codec.ts +13 -21
  168. package/src/cache/shell-snapshot.ts +417 -0
  169. package/src/cache/tag-invalidation.ts +230 -0
  170. package/src/cache/types.ts +180 -99
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1127 -0
  173. package/src/client.rsc.tsx +41 -21
  174. package/src/client.tsx +33 -61
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +108 -0
  177. package/src/component-utils.ts +19 -0
  178. package/src/components/DefaultDocument.tsx +8 -2
  179. package/src/context-var.ts +18 -6
  180. package/src/decode-loader-results.ts +52 -0
  181. package/src/defer.ts +185 -0
  182. package/src/deps/ssr.ts +0 -1
  183. package/src/encode-kv.ts +49 -0
  184. package/src/errors.ts +30 -4
  185. package/src/escape-script.ts +52 -0
  186. package/src/handle.ts +67 -37
  187. package/src/handles/MetaTags.tsx +24 -53
  188. package/src/handles/Scripts.tsx +183 -0
  189. package/src/handles/breadcrumbs.ts +35 -8
  190. package/src/handles/deferred-resolution.ts +127 -0
  191. package/src/handles/is-thenable.ts +18 -0
  192. package/src/handles/meta.ts +14 -40
  193. package/src/handles/script.ts +244 -0
  194. package/src/host/cookie-handler.ts +9 -60
  195. package/src/host/errors.ts +13 -22
  196. package/src/host/index.ts +9 -2
  197. package/src/host/pattern-matcher.ts +23 -52
  198. package/src/host/router.ts +107 -99
  199. package/src/host/testing.ts +40 -27
  200. package/src/host/types.ts +37 -4
  201. package/src/host/utils.ts +1 -1
  202. package/src/href-client.ts +137 -22
  203. package/src/index.rsc.ts +97 -12
  204. package/src/index.ts +98 -14
  205. package/src/internal-debug.ts +11 -10
  206. package/src/loader-store.ts +500 -0
  207. package/src/loader.rsc.ts +20 -13
  208. package/src/loader.ts +12 -11
  209. package/src/missing-id-error.ts +68 -0
  210. package/src/outlet-context.ts +1 -1
  211. package/src/outlet-provider.tsx +1 -5
  212. package/src/prerender/param-hash.ts +16 -16
  213. package/src/prerender/store.ts +32 -37
  214. package/src/prerender.ts +78 -10
  215. package/src/redirect-origin.ts +114 -0
  216. package/src/regex-escape.ts +8 -0
  217. package/src/render-error-thrower.tsx +20 -0
  218. package/src/response-utils.ts +62 -0
  219. package/src/reverse.ts +65 -39
  220. package/src/root-error-boundary.tsx +1 -19
  221. package/src/route-content-wrapper.tsx +19 -77
  222. package/src/route-definition/dsl-helpers.ts +304 -309
  223. package/src/route-definition/helper-factories.ts +28 -140
  224. package/src/route-definition/helpers-types.ts +87 -59
  225. package/src/route-definition/index.ts +1 -2
  226. package/src/route-definition/redirect.ts +44 -11
  227. package/src/route-definition/resolve-handler-use.ts +12 -1
  228. package/src/route-definition/use-item-types.ts +29 -0
  229. package/src/route-map-builder.ts +41 -20
  230. package/src/route-types.ts +19 -46
  231. package/src/router/basename.ts +14 -0
  232. package/src/router/content-negotiation.ts +73 -25
  233. package/src/router/error-handling.ts +45 -18
  234. package/src/router/find-match.ts +129 -30
  235. package/src/router/handler-context.ts +27 -42
  236. package/src/router/instrument.ts +355 -0
  237. package/src/router/intercept-resolution.ts +39 -20
  238. package/src/router/lazy-includes.ts +82 -59
  239. package/src/router/loader-resolution.ts +167 -72
  240. package/src/router/logging.ts +0 -6
  241. package/src/router/manifest.ts +74 -40
  242. package/src/router/match-api.ts +80 -55
  243. package/src/router/match-context.ts +0 -22
  244. package/src/router/match-handlers.ts +211 -165
  245. package/src/router/match-middleware/background-revalidation.ts +40 -24
  246. package/src/router/match-middleware/cache-lookup.ts +159 -285
  247. package/src/router/match-middleware/cache-store.ts +64 -52
  248. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  249. package/src/router/match-middleware/segment-resolution.ts +0 -22
  250. package/src/router/match-pipelines.ts +1 -42
  251. package/src/router/match-result.ts +69 -79
  252. package/src/router/metrics.ts +0 -34
  253. package/src/router/middleware-types.ts +7 -134
  254. package/src/router/middleware.ts +298 -172
  255. package/src/router/navigation-snapshot.ts +7 -56
  256. package/src/router/params-util.ts +23 -0
  257. package/src/router/parse-pattern.ts +115 -0
  258. package/src/router/pattern-matching.ts +181 -150
  259. package/src/router/prefetch-cache-ttl.ts +51 -0
  260. package/src/router/prefetch-limits.ts +37 -0
  261. package/src/router/prerender-match.ts +112 -67
  262. package/src/router/preview-match.ts +6 -2
  263. package/src/router/request-classification.ts +50 -69
  264. package/src/router/revalidation.ts +123 -73
  265. package/src/router/route-snapshot.ts +14 -3
  266. package/src/router/router-context.ts +6 -29
  267. package/src/router/router-interfaces.ts +115 -36
  268. package/src/router/router-options.ts +166 -5
  269. package/src/router/router-registry.ts +2 -5
  270. package/src/router/segment-resolution/fresh.ts +131 -86
  271. package/src/router/segment-resolution/helpers.ts +86 -6
  272. package/src/router/segment-resolution/loader-cache.ts +139 -39
  273. package/src/router/segment-resolution/loader-mask.ts +67 -0
  274. package/src/router/segment-resolution/loader-snapshot.ts +251 -0
  275. package/src/router/segment-resolution/revalidation.ts +272 -320
  276. package/src/router/segment-resolution/static-store.ts +19 -5
  277. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  278. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  279. package/src/router/segment-resolution.ts +5 -1
  280. package/src/router/segment-wrappers.ts +6 -5
  281. package/src/router/state-cookie-name.ts +33 -0
  282. package/src/router/substitute-pattern-params.ts +75 -0
  283. package/src/router/telemetry-otel.ts +160 -200
  284. package/src/router/telemetry.ts +105 -20
  285. package/src/router/timeout.ts +0 -20
  286. package/src/router/tracing.ts +215 -0
  287. package/src/router/trie-matching.ts +171 -59
  288. package/src/router/types.ts +9 -63
  289. package/src/router/url-params.ts +57 -0
  290. package/src/router.ts +157 -71
  291. package/src/rsc/full-payload.ts +70 -0
  292. package/src/rsc/handler-context.ts +3 -2
  293. package/src/rsc/handler.ts +291 -217
  294. package/src/rsc/helpers.ts +168 -46
  295. package/src/rsc/index.ts +2 -5
  296. package/src/rsc/json-route-result.ts +38 -0
  297. package/src/rsc/loader-fetch.ts +114 -38
  298. package/src/rsc/manifest-init.ts +29 -42
  299. package/src/rsc/nonce.ts +10 -1
  300. package/src/rsc/origin-guard.ts +39 -25
  301. package/src/rsc/progressive-enhancement.ts +124 -13
  302. package/src/rsc/redirect-guard.ts +100 -0
  303. package/src/rsc/response-cache-serve.ts +238 -0
  304. package/src/rsc/response-error.ts +79 -12
  305. package/src/rsc/response-route-handler.ts +99 -189
  306. package/src/rsc/rsc-rendering.ts +421 -76
  307. package/src/rsc/runtime-warnings.ts +23 -10
  308. package/src/rsc/server-action.ts +282 -116
  309. package/src/rsc/shell-capture.ts +1158 -0
  310. package/src/rsc/shell-serve.ts +150 -0
  311. package/src/rsc/ssr-setup.ts +16 -0
  312. package/src/rsc/transition-gate.ts +89 -0
  313. package/src/rsc/types.ts +53 -5
  314. package/src/runtime-env.ts +18 -0
  315. package/src/search-params.ts +35 -30
  316. package/src/segment-loader-promise.ts +49 -4
  317. package/src/segment-system.tsx +350 -149
  318. package/src/serialize.ts +243 -0
  319. package/src/server/context.ts +208 -51
  320. package/src/server/cookie-parse.ts +32 -0
  321. package/src/server/cookie-store.ts +152 -5
  322. package/src/server/handle-store.ts +21 -38
  323. package/src/server/loader-registry.ts +33 -42
  324. package/src/server/request-context.ts +395 -176
  325. package/src/ssr/index.tsx +458 -178
  326. package/src/ssr/ssr-root.tsx +228 -0
  327. package/src/static-handler.ts +10 -13
  328. package/src/testing/cache-status.ts +162 -0
  329. package/src/testing/collect-handle.ts +46 -0
  330. package/src/testing/dispatch.ts +813 -0
  331. package/src/testing/dom.entry.ts +22 -0
  332. package/src/testing/e2e/fixture.ts +188 -0
  333. package/src/testing/e2e/index.ts +128 -0
  334. package/src/testing/e2e/matchers.ts +35 -0
  335. package/src/testing/e2e/page-helpers.ts +272 -0
  336. package/src/testing/e2e/parity.ts +387 -0
  337. package/src/testing/e2e/server.ts +195 -0
  338. package/src/testing/flight-matchers.ts +97 -0
  339. package/src/testing/flight-normalize.ts +11 -0
  340. package/src/testing/flight-runtime.d.ts +57 -0
  341. package/src/testing/flight-tree.ts +682 -0
  342. package/src/testing/flight.entry.ts +52 -0
  343. package/src/testing/flight.ts +257 -0
  344. package/src/testing/generated-routes.ts +199 -0
  345. package/src/testing/index.ts +105 -0
  346. package/src/testing/internal/context.ts +371 -0
  347. package/src/testing/internal/flight-client-globals.ts +30 -0
  348. package/src/testing/internal/seed-vars.ts +54 -0
  349. package/src/testing/render-handler.ts +357 -0
  350. package/src/testing/render-route.tsx +584 -0
  351. package/src/testing/run-loader.ts +385 -0
  352. package/src/testing/run-middleware.ts +205 -0
  353. package/src/testing/run-transition-when.ts +164 -0
  354. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  355. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  356. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  357. package/src/testing/vitest-stubs/version.ts +5 -0
  358. package/src/testing/vitest.ts +305 -0
  359. package/src/theme/ThemeProvider.tsx +56 -84
  360. package/src/theme/ThemeScript.tsx +7 -9
  361. package/src/theme/constants.ts +52 -13
  362. package/src/theme/index.ts +0 -7
  363. package/src/theme/theme-context.ts +1 -5
  364. package/src/theme/theme-script.ts +22 -21
  365. package/src/theme/use-theme.ts +0 -3
  366. package/src/types/boundaries.ts +0 -35
  367. package/src/types/cache-types.ts +13 -4
  368. package/src/types/error-types.ts +30 -90
  369. package/src/types/global-namespace.ts +54 -41
  370. package/src/types/handler-context.ts +110 -62
  371. package/src/types/index.ts +3 -10
  372. package/src/types/loader-types.ts +11 -9
  373. package/src/types/request-scope.ts +112 -0
  374. package/src/types/route-config.ts +20 -52
  375. package/src/types/route-entry.ts +0 -6
  376. package/src/types/segments.ts +135 -14
  377. package/src/urls/include-helper.ts +19 -64
  378. package/src/urls/include-provider.ts +71 -0
  379. package/src/urls/index.ts +2 -11
  380. package/src/urls/path-helper-types.ts +63 -17
  381. package/src/urls/path-helper.ts +22 -106
  382. package/src/urls/pattern-types.ts +72 -19
  383. package/src/urls/response-types.ts +22 -29
  384. package/src/urls/type-extraction.ts +98 -154
  385. package/src/urls/urls-function.ts +1 -19
  386. package/src/use-loader.tsx +292 -107
  387. package/src/vercel/index.ts +11 -0
  388. package/src/vercel/tracing.ts +88 -0
  389. package/src/vite/debug.ts +185 -0
  390. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  391. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  392. package/src/vite/discovery/discover-routers.ts +127 -86
  393. package/src/vite/discovery/discovery-errors.ts +255 -0
  394. package/src/vite/discovery/gate-state.ts +171 -0
  395. package/src/vite/discovery/prerender-collection.ts +96 -68
  396. package/src/vite/discovery/route-types-writer.ts +40 -84
  397. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  398. package/src/vite/discovery/state.ts +45 -1
  399. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  400. package/src/vite/index.ts +4 -0
  401. package/src/vite/inject-client-debug.ts +88 -0
  402. package/src/vite/plugin-types.ts +210 -10
  403. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  404. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  405. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  406. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  407. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  408. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  409. package/src/vite/plugins/expose-action-id.ts +48 -95
  410. package/src/vite/plugins/expose-id-utils.ts +88 -55
  411. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  412. package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
  413. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  414. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  415. package/src/vite/plugins/expose-internal-ids.ts +505 -486
  416. package/src/vite/plugins/performance-tracks.ts +26 -25
  417. package/src/vite/plugins/refresh-cmd.ts +1 -1
  418. package/src/vite/plugins/use-cache-transform.ts +73 -83
  419. package/src/vite/plugins/vercel-output.ts +384 -0
  420. package/src/vite/plugins/version-injector.ts +40 -29
  421. package/src/vite/plugins/version-plugin.ts +37 -40
  422. package/src/vite/plugins/virtual-entries.ts +138 -27
  423. package/src/vite/rango.ts +236 -138
  424. package/src/vite/router-discovery.ts +927 -136
  425. package/src/vite/utils/ast-handler-extract.ts +26 -35
  426. package/src/vite/utils/banner.ts +1 -1
  427. package/src/vite/utils/bundle-analysis.ts +10 -15
  428. package/src/vite/utils/client-chunks.ts +184 -0
  429. package/src/vite/utils/directive-prologue.ts +40 -0
  430. package/src/vite/utils/forward-user-plugins.ts +171 -0
  431. package/src/vite/utils/manifest-utils.ts +4 -59
  432. package/src/vite/utils/package-resolution.ts +20 -52
  433. package/src/vite/utils/prerender-utils.ts +71 -43
  434. package/src/vite/utils/shared-utils.ts +142 -43
  435. package/src/browser/action-response-classifier.ts +0 -99
  436. package/src/browser/react/use-client-cache.ts +0 -58
  437. package/src/browser/shallow.ts +0 -40
  438. package/src/handles/index.ts +0 -7
  439. package/src/network-error-thrower.tsx +0 -23
  440. package/src/router/middleware-cookies.ts +0 -55
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: prerender
3
- description: Pre-render route segments at build time with Prerender and Passthrough live fallback
3
+ description: Pre-render route segments at build time with Prerender and Passthrough live fallback. Use when a page's content is mostly static and shouldn't render on every request, speeding up cold responses, or deciding which routes to prerender vs render live.
4
4
  argument-hint: [passthrough]
5
5
  ---
6
6
 
@@ -11,8 +11,12 @@ deserialization path, same segment system. The worker handles every request --
11
11
  there are NO static .html or .rsc files served from assets. The worker reads
12
12
  pre-computed Flight payloads instead of executing handler code.
13
13
 
14
- Canonical semantics reference:
15
- [docs/execution-model.md](../../docs/internal/execution-model.md)
14
+ ## Not this skill if…
15
+
16
+ - You want a cached HTML shell captured at runtime, with holes and loaders
17
+ staying live per request — see `/ppr`.
18
+ - You want runtime segment caching with TTL/SWR — that is the `cache()` DSL:
19
+ see `/caching`. Prerender is the same cache filled at build time.
16
20
 
17
21
  ## API: Prerender
18
22
 
@@ -122,6 +126,8 @@ interface BuildContext<TParams> {
122
126
  use: <T>(handle: Handle<T>) => (data: T) => void; // Push handle data
123
127
  url: URL; // Synthetic URL from pattern + params
124
128
  pathname: string; // Pathname from synthetic URL
129
+ searchParams: URLSearchParams; // URLSearchParams from the synthetic URL (always empty for prerender)
130
+ search: {}; // Typed search params -- always {} for prerender (no real query string)
125
131
  set(key: string, value: any): void; // Set context variable (string key)
126
132
  set<T>(contextVar: ContextVar<T>, value: T): void; // Set typed context variable
127
133
  get(key: string): any; // Read context variable (string key)
@@ -244,16 +250,16 @@ path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
244
250
 
245
251
  ## Interaction with DSL Items
246
252
 
247
- | DSL item | Behavior with Prerender |
248
- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
249
- | `loader()` | Live at runtime, bundled normally. Use `cache()` for caching. |
250
- | `revalidate()` | Not allowed without Passthrough. Allowed with Passthrough. |
251
- | `cache()` | Orthogonal -- use on parent layouts and loaders. |
252
- | `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live. |
253
- | `parallel()` | Parallel slots inside path are pre-rendered. |
254
- | `middleware()` | Skipped during pre-render (no request). Runs at request time for loaders. |
255
- | `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough. |
256
- | `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when()` conditions are skipped at build time (all intercepts are pre-rendered unconditionally). |
253
+ | DSL item | Behavior with Prerender |
254
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
255
+ | `loader()` | Live at runtime, bundled normally. Use `cache()` for caching. |
256
+ | `revalidate()` | Not allowed without Passthrough. Allowed with Passthrough. |
257
+ | `cache()` | Orthogonal -- use on parent layouts and loaders. |
258
+ | `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live. |
259
+ | `parallel()` | Parallel slots inside path are pre-rendered. |
260
+ | `middleware()` | Skipped during pre-render (no request). Runs at request time for loaders. |
261
+ | `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough. |
262
+ | `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when` config conditions are skipped at build time (all intercepts are pre-rendered unconditionally). |
257
263
 
258
264
  When Passthrough revalidation is enabled, remember that revalidation is
259
265
  still partial: opting a child segment into revalidation does not
@@ -346,14 +352,31 @@ export const TocSidebar = Static(() => {
346
352
 
347
353
  ### Error behavior at build time
348
354
 
349
- | Handler outcome | Effect |
350
- | --------------------------- | ----------------------------------------------------- |
351
- | JSX / `null` | Normal prerender entry, log OK |
352
- | `return ctx.passthrough()` | Skip entry, log PASS, continue (Passthrough routes) |
353
- | `throw new Skip("reason")` | Skip entry, log SKIP, continue with remaining entries |
354
- | `throw new Error("reason")` | Log FAIL, stop ALL pre-rendering, fail the build |
355
+ When a render throws a non-`Skip` error, it is **surfaced to the build** — never
356
+ baked into a frozen error page served as a 200 (issue #587). What happens next is
357
+ controlled by `prerender.onError` in your `rango()` options:
358
+
359
+ ```ts
360
+ rango({ prerender: { onError: "warn" } }); // default is "fail"
361
+ ```
355
362
 
356
- Both error types propagate to the router's `onError` callback with phase
363
+ | Handler outcome | `onError: "fail"` (default) | `onError: "warn"` |
364
+ | --------------------------- | -------------------------------------------- | -------------------------------- |
365
+ | JSX / `null` | Normal prerender entry, log OK | Normal prerender entry, log OK |
366
+ | `return ctx.passthrough()` | Skip entry, log PASS (Passthrough routes) | Skip entry, log PASS |
367
+ | `throw new Skip("reason")` | Skip entry, log SKIP, continue | Skip entry, log SKIP, continue |
368
+ | `throw new Error("reason")` | Log FAIL, stop ALL pre-rendering, fail build | Log WARN, skip the URL, continue |
369
+
370
+ With `"warn"` the errored entry is logged and left un-baked (never served as a baked
371
+ 200 error page). `"warn"` is a build-unblock, not a runtime contract: the route falls
372
+ through to normal resolution — it may render live (its handler is still bundled) or
373
+ 404 (once other baked entries trigger prerender handler eviction), so the outcome
374
+ depends on the rest of the build, and a skipped `Static()` handler's evicted code can
375
+ surface as an error. For DEFINED runtime behavior reach for `Passthrough()` (a live
376
+ fallback) or `throw new Skip()` (an intentional skip — works in the render fn, not
377
+ only `getParams()`); otherwise prefer the default `"fail"`.
378
+
379
+ Both `Skip` and hard errors propagate to the router's `onError` callback with phase
357
380
  `"prerender"` or `"static"`.
358
381
 
359
382
  ### Build logs
@@ -361,21 +384,23 @@ Both error types propagate to the router's `onError` callback with phase
361
384
  The build produces per-URL timing logs:
362
385
 
363
386
  ```
364
- [rsc-router] Pre-rendering 12 URL(s) (concurrency: 4)...
365
- [rsc-router] OK /articles/hello (42ms)
366
- [rsc-router] PASS /articles/remote-only (5ms) - live fallback
367
- [rsc-router] SKIP /articles/draft-post (3ms) - Article is a draft
368
- [rsc-router] Pre-render complete: 11 done, 1 skipped (1204ms total)
369
-
370
- [rsc-router] Rendering 3 static handler(s)...
371
- [rsc-router] OK DocsLayout (28ms)
372
- [rsc-router] SKIP TocSidebar (1ms) - Not ready
373
- [rsc-router] Static render complete: 2 done, 1 skipped (120ms total)
387
+ [rango] Pre-rendering 12 URL(s) (concurrency: 4)...
388
+ [rango] OK /articles/hello (42ms)
389
+ [rango] PASS /articles/remote-only (5ms) - live fallback
390
+ [rango] SKIP /articles/draft-post (3ms) - Article is a draft
391
+ [rango] Pre-render complete: 11 done, 1 skipped (1204ms total)
392
+
393
+ [rango] Rendering 3 static handler(s)...
394
+ [rango] OK DocsLayout (28ms)
395
+ [rango] SKIP TocSidebar (1ms) - Not ready
396
+ [rango] Static render complete: 2 done, 1 skipped (120ms total)
374
397
  ```
375
398
 
376
- A `FAIL` line is logged per-URL when a handler throws a non-Skip error. The
377
- error is re-thrown immediately, so no summary line is printed — the build
378
- stops at the first failure.
399
+ A `FAIL` line is logged per-URL when a handler throws a non-Skip error (with the
400
+ default `prerender.onError: "fail"`). The error is re-thrown immediately, so no
401
+ summary line is printed — the build stops at the first failure. Under
402
+ `prerender.onError: "warn"` the same case logs a `WARN` line, skips that URL, and
403
+ the build continues.
379
404
 
380
405
  ### Dev mode behavior
381
406
 
@@ -466,9 +491,9 @@ export const Product = Passthrough(ProductDef, async (ctx) => {
466
491
  Passthrough entries are logged distinctly:
467
492
 
468
493
  ```
469
- [rsc-router] OK /blog/a (42ms)
470
- [rsc-router] PASS /blog/b (3ms) - live fallback
471
- [rsc-router] OK /blog/c (38ms)
494
+ [rango] OK /blog/a (42ms)
495
+ [rango] PASS /blog/b (3ms) - live fallback
496
+ [rango] OK /blog/c (38ms)
472
497
  ```
473
498
 
474
499
  ## Edge Cases and Constraints
@@ -591,12 +616,12 @@ At runtime, the cache-lookup middleware checks `ctx.isIntercept`:
591
616
  (filtered by `namespace?.startsWith("intercept:")`) and sets up slots.
592
617
  - **Direct navigation**: looks up `paramHash` (no suffix). Standard prerender path.
593
618
  - **Intercept miss (no `/i` entry)**: falls through to the normal pipeline so
594
- intercept-resolution middleware runs live. This handles `when()` conditions
619
+ intercept-resolution middleware runs live. This handles `when` config conditions
595
620
  that prevented pre-rendering.
596
621
 
597
- The `when()` callback receives an `InterceptSelectorContext` with `from.pathname`
622
+ The `when` config selector receives an `InterceptSelectorContext` with `from.pathname`
598
623
  which is unknown at build time. All intercepts are pre-rendered unconditionally;
599
- `when()` is evaluated at runtime by the intercept-resolution middleware.
624
+ `when` is evaluated at runtime by the intercept-resolution middleware.
600
625
 
601
626
  ### Example: Pre-rendered route with intercept
602
627
 
@@ -615,10 +640,13 @@ layout(ShopLayout, () => [
615
640
 
616
641
  // Intercept detail from shop index into a modal.
617
642
  // At build time, this is resolved and stored under the /i key.
618
- intercept("@modal", ".detail", <ProductModal />, () => [
619
- when(({ from }) => from.pathname === "/shop"),
620
- loader(ProductLoader),
621
- ]),
643
+ intercept(
644
+ "@modal",
645
+ ".detail",
646
+ <ProductModal />,
647
+ { when: ({ from }) => from.pathname === "/shop" },
648
+ () => [loader(ProductLoader)],
649
+ ),
622
650
  ])
623
651
  ```
624
652
 
@@ -640,16 +668,7 @@ At runtime, the cache-lookup middleware uses these flags:
640
668
 
641
669
  ## Contributor Checklist
642
670
 
643
- Before changing prerender behavior, read these docs and run these tests.
644
-
645
- ### Docs to re-read
646
-
647
- - [Prerender API design](../../docs/prerender-api-design.md) -- canonical
648
- architecture: build-time flow, runtime flow, storage, Passthrough, intercept
649
- - [Execution model](../../docs/internal/execution-model.md) -- handler-first
650
- ordering, middleware scope, context visibility rules
651
- - [Semantic change checklist](../../docs/internal/semantic-change-checklist.md)
652
- -- gate for any change to execution semantics
671
+ Before changing prerender behavior, run these tests.
653
672
 
654
673
  ### Tests to run
655
674
 
@@ -676,10 +695,3 @@ pnpm --filter @rangojs/router exec playwright test handler-first
676
695
  dev/build-only and do not need a production counterpart.
677
696
  - Behavioral assertions (rendered content, loader freshness, Passthrough
678
697
  fallback, intercept variant selection) must work in the production build.
679
-
680
- ## Maintenance References
681
-
682
- - [Stability next steps plan](../../docs/internal/stability-next-steps-plan.md)
683
- -- completed parity and cleanup pass (reference for decisions made)
684
- - [Test quality baseline](../../docs/internal/test-quality-baseline.md) --
685
- measured test inventory, sleep debt, production coverage gaps
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rango
3
- description: Overview of @rangojs/router and available skills
3
+ description: Overview of @rangojs/router and available skills. Use when unsure which skill to reach for, starting a new task in a Rango app, or asking "what can this router do".
4
4
  argument-hint:
5
5
  ---
6
6
 
@@ -8,32 +8,311 @@ argument-hint:
8
8
 
9
9
  Django-inspired RSC router with composable URL patterns, type-safe href, and server components.
10
10
 
11
+ This page is the mental model to read **before** the catalog. A flat list of
12
+ skills gives nothing to slot details into, so a reader free-associates from local
13
+ vocabulary — which is exactly how `revalidate()` gets misread as caching. Start
14
+ with the shape, then pick a primitive.
15
+
16
+ ## The shape of rango (read first)
17
+
18
+ - **Routes are expressed, not configured.** The `urls()` tree shows where every
19
+ route, layout, loader, and cache lives. No file-system convention, no hunting.
20
+ - **Two freshness axes, orthogonal:**
21
+ - _stored-value freshness_ — `"use cache"`, `cache()`, loader `cache()`
22
+ (SWR is first-class where the store supports it; `"use cache"` ships a
23
+ default SWR window; see `/cache-guide`)
24
+ - _client-update selection_ — `revalidate()`
25
+ - **Loaders are the live data layer** — fresh every request by default, even
26
+ inside a cached render. They run **in parallel** right after middleware and
27
+ **stream**, so data latency overlaps first paint instead of blocking it (a
28
+ cache hit streams UI instantly while loaders resolve fresh alongside). Opt into
29
+ caching explicitly. See `/loader` → "Parallel and streaming".
30
+ - **One identity, one store** — loaders, handles, cached fns, and actions are all
31
+ `path#export`; all caches share one store. Entries expire by TTL/SWR, and are
32
+ tagged via `cache({ tags })` or runtime `cacheTag(...tags)`; built-in stores
33
+ index by tag and invalidate via `updateTag(...tags)` (awaitable, read-your-own-writes)
34
+ or `revalidateTag(...tags)` (background, non-blocking).
35
+ - **Type-safe end to end** — route names, params, search schemas, loader return
36
+ types, context vars, and `href` / `reverse` are checked at compile time
37
+ (`/typesafety`).
38
+ - **See where time goes** — turn on `debugPerformance` early (router option, or
39
+ `ctx.debugPerformance()` in middleware for per-request opt-in). It prints a
40
+ per-request waterfall + `Server-Timing` header; loaders should overlap the
41
+ render bar, not serialize after it. For production, wire `telemetry` to a
42
+ console, OpenTelemetry, or custom sink. See `/observability`.
43
+
44
+ Most features are **just-in-time**: the core is `urls()`, `path()`, `layout()`,
45
+ `include()`, and `reverse()`. Caching, parallel routes, intercepts, prerender,
46
+ i18n, themes, and the rest are opt-in — reach for them when a requirement
47
+ appears, not up front.
48
+
49
+ ## Composability: structure vs config
50
+
51
+ - `path()` / `include()` are **structure** — they define URLs and must stay
52
+ visible in `urls()`. They cannot be hidden in a factory. `include()` composes
53
+ whole modules (separation of real concerns); `path()` places a leaf.
54
+ - Everything else — `cache`, `loader`, `loading`, `middleware`, `revalidate`,
55
+ `parallel`, `intercept`, `errorBoundary`, … — is **config**. It attaches to a
56
+ node via its `use` callback, is importable, and extracts into factories that
57
+ return arrays (`withAuth()`, `withCaching()`), flattened automatically.
58
+
59
+ To decide where something can live: **does it define a URL? structure, stays in
60
+ `urls()`. Does it modify a node? config, compose freely.**
61
+
62
+ ## Passing data down the tree
63
+
64
+ Four ways to get per-request data to a segment below you, ordered safest-first.
65
+ Reach for the next rung only when the one above doesn't fit — the higher rungs
66
+ are immune to partial-revalidation staleness by construction.
67
+
68
+ 1. **A loader** (`loader()` + `useLoader()`). Loaders resolve fresh on every
69
+ pass — full renders, action revalidations, cache hits. Nothing to keep in
70
+ sync. If the data can be a loader, make it a loader.
71
+ 2. **Middleware `ctx.set()`**. Route middleware wraps every render pass,
72
+ including post-action revalidation and PE re-renders, so its variables are
73
+ never stale. Right for request-shaped context: auth, session, locale.
74
+ 3. **Handler `ctx.set()` to its own children** —
75
+ `path(handler, ..., () => [layout(...)])`. Orphan layouts and their
76
+ parallels belong to the route entry: on an action the whole entry re-runs
77
+ together by default (handler-first preserved), so the data stays consistent
78
+ with zero configuration. Right for data the page must compute anyway —
79
+ e.g. pagination, where the handler's search decides how many pages the
80
+ layout chrome renders. One rule: if you narrow the entry's revalidation
81
+ with a predicate that can return a hard `false`, put the same contract on
82
+ the entry's children too — a hard `false` on one side of a
83
+ producer/consumer pair desyncs it.
84
+ 4. **Cross-entry sharing** — an outer `layout()` entry feeding descendants.
85
+ Outer entries do NOT revalidate on actions by default (the revalidation
86
+ trace calls this `action:parent-chain-skip`), so this rung always requires
87
+ a shared revalidation contract: the same named `revalidate()` function on
88
+ the producer and every consumer. See `/layout` → "Revalidation Contracts".
89
+ Before writing one, check whether the producer can move down a rung.
90
+
91
+ The failure mode this ladder prevents: a consumer re-runs, its producer
92
+ doesn't, `ctx.get()` reads `undefined`, and fallback UI silently replaces good
93
+ UI after an action. Rungs 1–3 make that unrepresentable; rung 4 makes it a
94
+ stated, greppable contract.
95
+
96
+ ## Pick a primitive
97
+
98
+ | I need to… | Use | Skill |
99
+ | --------------------------------------- | ---------------------------------- | ----------------------- |
100
+ | render data fresh every request | `loader()` + `useLoader()` | /loader |
101
+ | cache a rendered subtree | `cache()` on a segment | /caching |
102
+ | cache one function/component's result | `"use cache"` | /use-cache |
103
+ | cache a loader's data | `loader(L, () => [cache()])` | /loader, /caching |
104
+ | re-render a segment after an action | `revalidate()` | /loader |
105
+ | mutate | `"use server"` action | /server-actions |
106
+ | debug a slow request | `debugPerformance` / telemetry | /observability |
107
+ | share config across routes | factory returning a helper array | /composability |
108
+ | compose a sub-app / module | `include()` | /route |
109
+ | modal / soft navigation | `intercept()` | /intercept |
110
+ | pre-render a route at build time | `Prerender(...)` wrapper | /prerender |
111
+ | feed live loaders from a cached shell | replayed handle + `ctx.rendered()` | /shell-manifest |
112
+ | cache the HTML shell, keep loaders live | `ppr` path option | /ppr |
113
+ | stream SSE / upgrade a WebSocket | `path.stream()` / `path.any()` | /streams-and-websockets |
114
+
115
+ ## Invariants
116
+
117
+ - `path()`/`include()` are always visible in `urls()`; config helpers are extractable.
118
+ - **Cache decides freshness; `revalidate()` decides client-update.** Orthogonal; compose.
119
+ - Loaders resolve fresh every request (even inside `cache()`) and never run twice/request.
120
+ - **The consumption-lane rule.** For every shared artifact (`cache()`,
121
+ `"use cache"`, the PPR shell): server-side handler consumption
122
+ (`await ctx.use(loader)`) yields a BAKED copy — identity reads
123
+ (`cookies()`/`headers()`) are permitted there and the capture-time value
124
+ freezes into the shared artifact (a documented footgun; see `/caching` →
125
+ "Cache purity & tainted objects"). Client-side consumption (`useLoader` in
126
+ a `"use client"` component) is the LIVE lane. DSL `loader()` segments
127
+ follow their lane machinery (live under renderable `loading()`, bake
128
+ otherwise). Pinned by semantic-matrix row PPR3.
129
+ - Inside `"use cache"`: `cookies()`/`headers()` and `ctx` side-effects
130
+ (`set`/`header`/`setTheme`/`onResponse`/`setLocationState`) throw; `ctx.use(Handle)`
131
+ is captured on miss and replayed on hit. (The non-cacheable read guard is a
132
+ separate `cache()`-boundary check — see the correctness bullet below.)
133
+ - One identity `path#export` (`functionId`/`$$id`/`actionId`); one store. Freshness
134
+ is TTL/SWR expiry plus tag-based invalidation: tag via `cache({ tags })` /
135
+ `cacheTag(...tags)`, then `updateTag(...tags)` (awaitable) or `revalidateTag(...tags)`
136
+ (background). Built-in stores index by tag.
137
+ - `useLoader` / `useHandle` / `useFetchLoader` are client-only.
138
+ - Caches are correctness-first: persistent store keys are version-segmented (no
139
+ cross-deploy drift), the forward/back cache is mutation-aware, and
140
+ `createVar({ cache: false })` throws on a **direct** read inside a `cache()`
141
+ boundary (a deliberately non-propagating guard). See `/cache-guide` →
142
+ "Correctness & invalidation".
143
+ - Nested caches: the outer cache window bounds the inner — an inner shorter TTL
144
+ only applies when the enclosing cache recomputes; put a value in a loader if it
145
+ must be fresher. See `/cache-guide` → "Combining Both".
146
+
147
+ ## Don't confuse
148
+
149
+ - `revalidate()` ≠ cache invalidation — partial-render selection vs value freshness.
150
+ - host router `.lazy()` (lazy import of a handler/sub-app) vs `.map()` (inline Response).
151
+ - `cache()` (segment, in the DSL) vs `"use cache"` (function/component directive).
152
+ - `loader()` registration (server) vs `useLoader()` consumption (client).
153
+
154
+ ### Coming from another framework (false friends)
155
+
156
+ Same words, different jobs — this is the most common source of the
157
+ `revalidate()`-is-caching misread.
158
+
159
+ | You may know | Maps to Rango axis | Watch out |
160
+ | --------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
161
+ | Next.js `export const revalidate = N` | **Axis 1** (cache) | Same word, opposite meaning. Next's `revalidate` is time-based cache expiry; Rango's `revalidate()` is **axis 2**. Use `cache({ ttl })` for the Next behavior. |
162
+ | Next.js `revalidateTag` / `updateTag` | **Axis 1** (cache) | Cache busting by tag. Tag via `cache({ tags })` / `cacheTag(...tags)`; invalidate with `updateTag(...tags)` (awaitable, read-your-own-writes) or `revalidateTag(...tags)` (background, non-blocking). Built-in stores index by tag. No `revalidatePath` (path-based busting); use tags. |
163
+ | React Router / Remix `shouldRevalidate` | **Axis 2** | This is the correct mental model for Rango's `revalidate()`. |
164
+ | HTTP `Cache-Control` / ISR | **Axis 1** | Edge/document layer — see `/document-cache`. Separate from both `cache()` and `revalidate()`. |
165
+ | Next.js PPR (partial prerendering) | HTML shell layer | Same idea, different wiring: the opt-in `ppr` path option captures at runtime (no build-time default); holes are render-defined — `loading()` subtrees plus pending promises under a consumer's own `<Suspense>`. See `/ppr`. |
166
+ | Remix/RR `loader` | live data | Like Rango loaders, fresh per request — but Rango loaders run in parallel and stream (latency overlaps first paint), and can opt into caching on demand. |
167
+
168
+ See `/cache-guide` for the axis-1 decision guide, `/loader` and `/route` for
169
+ `revalidate()` (axis 2), and `/document-cache` for the edge layer.
170
+
171
+ ## Canonical shape
172
+
173
+ ```ts
174
+ export const urlpatterns = urls(({ path, layout, loader, loading, cache, revalidate }) => [
175
+ layout(<ShopLayout />, () => [ // structure: wraps children
176
+ loader(CartLoader, () => [ // config: live data
177
+ // partial-render axis: re-run on cart actions, defer otherwise.
178
+ // ctx.isAction() matches by reference (rename-safe), not by string.
179
+ revalidate((ctx) => ctx.isAction(CartActions) || undefined),
180
+ ]),
181
+ path("/shop/:slug", ProductPage, { name: "product" }, () => [ // structure: leaf
182
+ loader(ProductLoader, () => [cache({ ttl: 60 })]), // config: cache loader DATA
183
+ loading(<ProductSkeleton />), // config
184
+ withRecs(), // composed factory (config array)
185
+ ]),
186
+ ]),
187
+ ]);
188
+ ```
189
+
190
+ One tree, both axes visible: structure (`layout`/`path`) vs config (everything
191
+ else), freshness (`cache`) vs client-update (`revalidate`). Actions are matched
192
+ by reference with `ctx.isAction(Action)` (rename-safe, where `CartActions` is an
193
+ `import * as CartActions from "./actions/cart"`); see `/typesafety` → "Stable
194
+ identity".
195
+
196
+ The predicate arg carries the action's full context, not just its identity. Match
197
+ _which_ action with `ctx.isAction(addToCart)` (rename-safe); branch on _what it
198
+ returned_ with `ctx.actionResult` — the value your `"use server"` function
199
+ returned, for outcome-conditional revalidation. The arg also exposes `actionId`
200
+ (raw `path#export`), `actionUrl`, `formData`, `method`, and `stale` (cross-tab
201
+ `_rsc_stale` signal). All are `undefined` on plain navigation (no action).
202
+
203
+ Two idioms, picked by what an _unrelated_ action should do. `ctx.isAction()`
204
+ returns a raw boolean, so combine it with `|| undefined` to **defer** ("mine,
205
+ else let the default decide": `ctx.isAction(CartActions) || undefined`) or leave
206
+ it bare to **suppress** ("mine only": `ctx.isAction(CartActions)`). Prefer the
207
+ defer form unless a sibling segment must own the unrelated-action decision.
208
+
209
+ ```ts
210
+ // re-render only when checkout actually succeeded; defer otherwise
211
+ revalidate((ctx) => (ctx.isAction(checkout) && ctx.actionResult?.ok) || undefined),
212
+ ```
213
+
214
+ **The source is the source of truth.** Structure, types, and update policy are
215
+ visible and local in the tree — read top-down, no hidden global model to hold in
216
+ your head. A snippet earns its place only if, from the code alone, you can answer:
217
+ _what URLs exist and who owns them?_ (composition), _can I trust this reference
218
+ without leaving the call site?_ (type-safety), _what re-renders after this
219
+ action?_ (partial rendering). If any answer needs another file, it isn't legible
220
+ yet.
221
+
222
+ **Reading Rango's own source.** Rango is consumed as raw TypeScript — the
223
+ `exports` map resolves `@rangojs/router` and its subpaths to `./src/*.ts` for
224
+ both types and runtime, so a consuming app bundles Rango straight from source.
225
+ Only the `./vite` plugin entry and the CLI `bin` load from `dist/`. To confirm
226
+ any runtime or type detail against an installed copy, read the resolved source
227
+ under `node_modules/@rangojs/router/src/`, not `dist/` — the runtime does not
228
+ resolve `dist/` outside `./vite`, and it may lag `src/`.
229
+
11
230
  ## Skills
12
231
 
13
- | Skill | Description |
14
- | ----------------------- | -------------------------------------------------------------------------- |
15
- | `/router-setup` | Create and configure the RSC router |
16
- | `/route` | Define routes with `urls()` and `path()` |
17
- | `/layout` | Layouts that wrap child routes |
18
- | `/loader` | Data loaders with `createLoader()` |
19
- | `/middleware` | Request processing and authentication |
20
- | `/intercept` | Modal/slide-over patterns for soft navigation |
21
- | `/parallel` | Multi-column layouts and sidebars |
22
- | `/caching` | Segment caching with memory or KV stores |
23
- | `/use-cache` | Function-level caching with `"use cache"` directive |
24
- | `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide |
25
- | `/document-cache` | Edge caching with Cache-Control headers |
26
- | `/theme` | Light/dark mode with FOUC prevention |
27
- | `/links` | URL generation: ctx.reverse, href, useHref, useMount, scopedReverse |
28
- | `/hooks` | Client-side React hooks |
29
- | `/typesafety` | Type-safe routes, params, href, and environment |
30
- | `/host-router` | Multi-app host routing with domain/subdomain patterns |
31
- | `/tailwind` | Set up Tailwind CSS v4 with `?url` imports |
32
- | `/response-routes` | JSON/text/HTML/XML/stream endpoints with `path.json()`, `path.text()` |
33
- | `/mime-routes` | Content negotiation same URL, different response types via Accept header |
34
- | `/fonts` | Load web fonts with preload hints |
35
- | `/migrate-nextjs` | Migrate a Next.js App Router project to Rango |
36
- | `/migrate-react-router` | Migrate a React Router / Remix project to Rango |
232
+ Grouped by concern — read when you need to…
233
+
234
+ **Positioning & evaluation**:
235
+
236
+ | Skill | Description |
237
+ | ------------- | ------------------------------------------------------------ |
238
+ | `/comparison` | Compare Rango with Next.js, TanStack Start, and Waku fairly. |
239
+
240
+ **Structure & routing** — shape URLs, layouts, navigation, and request processing:
241
+
242
+ | Skill | Description |
243
+ | ------------------------- | -------------------------------------------------------------------------- |
244
+ | `/router-setup` | Create and configure the RSC router |
245
+ | `/route` | Define routes with `urls()`, `path()`, and `include()` |
246
+ | `/layout` | Layouts that wrap child routes |
247
+ | `/parallel` | Multi-column layouts and sidebars |
248
+ | `/intercept` | Modal/slide-over patterns for soft navigation |
249
+ | `/middleware` | Request processing and authentication |
250
+ | `/host-router` | Multi-app host routing with domain/subdomain patterns |
251
+ | `/links` | URL generation: ctx.reverse, href, useHref, useMount, scopedReverse |
252
+ | `/response-routes` | JSON/text/HTML/XML/stream endpoints with `path.json()`, `path.text()` |
253
+ | `/api-client` | Typed client for consuming your own response-route JSON APIs (recipe) |
254
+ | `/mime-routes` | Content negotiation same URL, different response types via Accept header |
255
+ | `/streams-and-websockets` | SSE via `path.stream` and WebSocket upgrades via `path.any` |
256
+ | `/handler-use` | Attach default loaders/middleware to a handler via `handler.use` |
257
+ | `/composability` | Reusable route-helper factories (structure vs config) |
258
+
259
+ **Data & caching** — fetch, mutate, and cache:
260
+
261
+ | Skill | Description |
262
+ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263
+ | `/loader` | Data loaders with `createLoader()` and `revalidate()` |
264
+ | `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation |
265
+ | `/caching` | Segment caching with memory or KV stores |
266
+ | `/use-cache` | Function-level caching with `"use cache"` directive |
267
+ | `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide |
268
+ | `/document-cache` | Edge caching with Cache-Control headers |
269
+ | `/ppr` | PPR shell caching: cached shell served instantly, live holes resumed — a hole is a `loading()` subtree OR a pending promise under `<Suspense>` (no loader needed) |
270
+ | `/prerender` | Pre-render route segments at build time (Passthrough live fallback) |
271
+ | `/shell-manifest` | Replayed handles as cache metadata read by live loaders (frozen shell, batched live holes) |
272
+
273
+ **Client & presentation** — build the client-side UX:
274
+
275
+ | Skill | Description |
276
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
277
+ | `/hooks` | Client-side React hooks |
278
+ | `/theme` | Light/dark mode with FOUC prevention |
279
+ | `/i18n` | Locale routing with `:locale?`, resolution chains, react-intl integration |
280
+ | `/fonts` | Load web fonts with preload hints |
281
+ | `/css` | Import CSS in the Document `<head>` (`?url` + managed `precedence` links) |
282
+ | `/scripts` | Inject third-party scripts (GTM/analytics) into head/body via the `Script` handle; nonce auto-applied to document-rendered scripts |
283
+ | `/tailwind` | Set up Tailwind CSS v4 with `?url` imports |
284
+ | `/view-transitions` | React View Transitions on layouts, routes, and parallel slots |
285
+ | `/defer-hydration` | Full body HTML in the PPR shell + hydration off the critical path (gated Suspense boundary, content-as-fallback) |
286
+ | `/breadcrumbs` | Built-in Breadcrumbs handle for breadcrumb navigation |
287
+ | `/react-compiler` | Enable React Compiler (opt-in) the vite-rsc way; client-only scope |
288
+
289
+ **Observability & production health**:
290
+
291
+ | Skill | Description |
292
+ | ------------------ | ------------------------------------------------------------------------ |
293
+ | `/observability` | `debugPerformance`, `Server-Timing`, structured telemetry, tracing |
294
+ | `/bundle-analysis` | Audit your app's production bundle for server leaks and oversized chunks |
295
+ | `/debug-manifest` | Inspect route manifest structure |
296
+
297
+ **Deployment**:
298
+
299
+ | Skill | Description |
300
+ | --------- | ----------------------------------------------------------------------------------------- |
301
+ | `/vercel` | Deploy to Vercel Functions (`preset: "vercel"`), Runtime Cache, and `createVercelTracing` |
302
+
303
+ **Testing**:
304
+
305
+ | Skill | Description |
306
+ | ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
307
+ | `/testing` | Unit (loaders/middleware/reverse/components), integration (dispatch/Flight), and e2e (dev+prod parity, progressive enhancement) |
308
+
309
+ **Setup, types & migration**:
310
+
311
+ | Skill | Description |
312
+ | ----------------------- | ----------------------------------------------- |
313
+ | `/typesafety` | Type-safe routes, params, href, and environment |
314
+ | `/migrate-nextjs` | Migrate a Next.js App Router project to Rango |
315
+ | `/migrate-react-router` | Migrate a React Router / Remix project to Rango |
37
316
 
38
317
  ## Quick Start
39
318
 
@@ -89,10 +368,23 @@ Each file is classified by its contents:
89
368
  Directories are scanned recursively for `.ts`/`.tsx` files, skipping `node_modules`,
90
369
  dotfiles, and existing `.gen.` files.
91
370
 
371
+ > The two generated files are **not interchangeable surfaces**.
372
+ > `router.named-routes.gen.ts` augments the global `GeneratedRouteMap` for
373
+ > named-route typing (`Handler<"name">`, `ctx.reverse("name")`, prerender).
374
+ > Per-module `*.gen.ts` exports a local `routes` map for `useReverse(routes)`
375
+ > and explicit local handler typing (`Handler<".name", routes>`). Neither
376
+ > carries response payloads — response/MIME payload inference comes from
377
+ > `typeof router.routeMap` via `RegisteredRoutes`, not `*.named-routes.gen.ts`.
378
+ > See `/typesafety` for the full surface breakdown.
379
+
92
380
  ### Recursive includes
93
381
 
94
382
  The generator follows `include()` calls across files, resolving imports to build
95
- the full route tree. Circular includes are detected and warned about.
383
+ the full route tree. It resolves both the eager form `include("/x", patterns)`
384
+ and the code-split async form `include("/x", () => import("./x"))` — for the
385
+ latter it walks the imported module's `export default urls(...)`, including any
386
+ nested `include()`s inside it — so a code-split route group is still fully typed
387
+ (see `/composability`). Circular includes are detected and warned about.
96
388
 
97
389
  ### First-wins deduplication
98
390