@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
@@ -1,13 +1,53 @@
1
1
  ---
2
2
  name: caching
3
3
  description: Configure segment caching with memory or Cloudflare KV stores in @rangojs/router
4
- argument-hint: [setup]
4
+ argument-hint: "[setup]"
5
5
  ---
6
6
 
7
7
  # Caching
8
8
 
9
9
  @rangojs/router supports segment-level caching with stale-while-revalidate (SWR) for optimal performance.
10
10
 
11
+ > SWR support is store-specific. `CFCacheStore` revalidates segment, response,
12
+ > and `"use cache"` entries in the background. `MemorySegmentCacheStore`
13
+ > supports SWR for response and `"use cache"` item entries, but its
14
+ > route-segment entries expire at TTL with no background revalidation — use
15
+ > `CFCacheStore` for real segment SWR. See `/cache-guide`.
16
+
17
+ ## cache() is Partial Prerendering (PPR)
18
+
19
+ `cache()` caches **everything except loaders**. On a cache hit, the cached
20
+ segments (layouts, route components, parallels — including any resolved
21
+ Suspense) are served from the store, and **loaders re-run fresh on every
22
+ request**, streaming their results into the same response. Loaders are the
23
+ dynamic "holes" of an otherwise-cached tree.
24
+
25
+ This means a `cache()` boundary at the document root **is** whole-document
26
+ Partial Prerendering: the static shell is cached and served instantly while
27
+ per-request/per-user data stays live — in one streamed response, no extra round
28
+ trip. The browser cannot tell the shell came from cache.
29
+
30
+ ```typescript
31
+ cache({ ttl: 60, swr: 300 }, () => [
32
+ layout(<RootLayout />), // cached shell
33
+ path("/dashboard", Dashboard, { name: "dashboard" }, () => [
34
+ loader(StatsLoader), // DYNAMIC HOLE — re-runs every request
35
+ ]),
36
+ ]);
37
+ ```
38
+
39
+ The consumer rule: **want it cached? render it inline. want it dynamic? put it
40
+ in a loader and read it with `useLoader()` in a client component.** Anything
41
+ read with `cookies()`, `headers()`, or a non-cacheable variable belongs in a
42
+ loader (loaders always run fresh). Reading it directly in a cached handler
43
+ throws; awaiting it with `ctx.use()` and rendering the result in a cached
44
+ handler silently bakes per-request data into the shared shell (see "Cache purity
45
+ & tainted objects" below).
46
+
47
+ Pre-rendering (`/prerender`) is the build-time twin: it caches the same shell at
48
+ build time instead of on first request. Both feed the segment system
49
+ identically, and loaders always run fresh at request time.
50
+
11
51
  ## Route-Level Caching with cache()
12
52
 
13
53
  Use the `cache()` DSL function to cache routes:
@@ -30,14 +70,124 @@ export const urlpatterns = urls(({ path, cache }) => [
30
70
  ## Cache Options
31
71
 
32
72
  ```typescript
33
- cache({
34
- ttl: 60, // Time-to-live in seconds (default: 60)
35
- swr: 300, // Stale-while-revalidate window (default: 300)
36
- }, () => [
37
- // Cached routes
38
- ])
73
+ cache(
74
+ {
75
+ ttl: 60, // Time-to-live in seconds (default: 60)
76
+ swr: 300, // Stale-while-revalidate window (default: 300)
77
+ },
78
+ () => [
79
+ // Cached routes
80
+ ],
81
+ );
39
82
  ```
40
83
 
84
+ ## Tag-Based Invalidation
85
+
86
+ Tag cached entries, then invalidate them on demand. Tags can be attached three ways:
87
+
88
+ ```typescript
89
+ // 1. Static tags in the cache() DSL
90
+ cache({ ttl: 300, tags: ["products"] }, () => [path("/products", List)]);
91
+
92
+ // 2. Dynamic tags (function of ctx)
93
+ cache(
94
+ { ttl: 300, tags: (ctx) => [`product:${ctx.params.id}`, "products"] },
95
+ () => [path("/products/:id", Detail)],
96
+ );
97
+
98
+ // 3. Runtime tags inside a "use cache" function
99
+ async function getProduct(id: string) {
100
+ "use cache";
101
+ cacheTag(`product:${id}`, "products"); // variadic, additive
102
+ return db.getProduct(id);
103
+ }
104
+ ```
105
+
106
+ Invalidate with one of two server-only verbs (both variadic, imported from
107
+ `@rangojs/router`):
108
+
109
+ ```typescript
110
+ // Server Action — read-your-own-writes. Await it so the action's own re-render
111
+ // (and the next navigation) sees fresh data.
112
+ async function updateProduct(formData: FormData) {
113
+ "use server";
114
+ await db.updateProduct(formData);
115
+ await updateTag("products");
116
+ }
117
+
118
+ // Route handler / webhook — background, non-blocking (waitUntil). Hard-purge:
119
+ // the next read re-renders fresh (NOT stale-while-revalidate).
120
+ export async function POST() {
121
+ "use server";
122
+ revalidateTag("products");
123
+ return new Response("ok");
124
+ }
125
+ ```
126
+
127
+ | API | Timing | Use in | Semantics |
128
+ | ------------------------ | --------------------------- | ------------------------- | ----------------------------------------------------- |
129
+ | `updateTag(...tags)` | awaitable (`Promise<void>`) | server actions | immediate; next read is fresh |
130
+ | `revalidateTag(...tags)` | background (`void`) | route handlers / webhooks | background (non-blocking); next read re-renders fresh |
131
+
132
+ Both built-in stores support tags. For `CFCacheStore`, distributed (cross-colo)
133
+ invalidation requires a `kv` namespace — the tag-invalidation markers live in
134
+ that same namespace; there is **no** separate tag-invalidation store to wire.
135
+ If no tag-capable store is configured, `updateTag`/`revalidateTag` warn and no-op.
136
+
137
+ By default `CFCacheStore` reads the KV marker on every tagged cache read
138
+ (strongest invalidation latency). To cut KV reads on hot tagged routes, set
139
+ `tagCacheTtl` (seconds) to cache each marker in the per-colo edge cache for that
140
+ window — the colo running `updateTag`/`revalidateTag` writes the fresh marker
141
+ into its own edge cache immediately (read-your-own-writes), while other colos
142
+ converge within `tagCacheTtl` (the **maximum extra cross-colo invalidation
143
+ latency** when no purge is wired). Keep it small (e.g. 30–60), or wire a purge
144
+ (below) and set it large. (Contrast `tagInvalidationTtl`, which must be _large_
145
+ — it bounds how long the KV marker itself lives and must exceed your max entry
146
+ TTL+SWR.)
147
+
148
+ To make other colos prompt without a short `tagCacheTtl`, pass `onRevalidateTag`:
149
+ each cached marker carries a namespaced Cloudflare `Cache-Tag`, and the hook is
150
+ handed exactly those tags (batched, once per `updateTag`/`revalidateTag` call) to
151
+ feed Cloudflare's purge-by-tag API — evicting the cached lookups everywhere.
152
+ Purge-by-tag is available on all plans (since April 2025), subject to per-plan
153
+ rate limits, so the batched single call matters. With a purge wired, `tagCacheTtl`
154
+ becomes a pure read-cost reducer + fallback window.
155
+
156
+ ## Named Cache Profiles
157
+
158
+ Define named profiles in `createRouter({ cacheProfiles })` so the same TTL/SWR
159
+ values can be shared across the DSL and `"use cache"` functions without repetition.
160
+ Unknown names throw at boot time.
161
+
162
+ ```typescript
163
+ // Define profiles in router
164
+ createRouter({
165
+ cacheProfiles: {
166
+ default: { ttl: 900, swr: 1800 },
167
+ short: { ttl: 60, swr: 120 },
168
+ long: { ttl: 3600, swr: 7200 },
169
+ },
170
+ });
171
+ ```
172
+
173
+ In the DSL, pass the profile's options directly to `cache()`:
174
+
175
+ ```typescript
176
+ export const urlpatterns = urls(({ path, cache }) => [
177
+ cache({ ttl: 3600, swr: 7200 }, () => [
178
+ path("/blog", BlogIndex, { name: "blog" }),
179
+ ]),
180
+
181
+ // Orphan cache boundary (covers subsequent siblings)
182
+ cache({ ttl: 60, swr: 120 }),
183
+ path("/feed", FeedPage, { name: "feed" }),
184
+ ]);
185
+ ```
186
+
187
+ The DSL `cache()` helper does NOT accept a string profile name — strings are only
188
+ valid in the `"use cache: <name>"` directive inside server functions. See
189
+ `/use-cache` for function-level caching with named profiles.
190
+
41
191
  ## Loader-Level Caching
42
192
 
43
193
  Cache individual loaders:
@@ -45,13 +195,11 @@ Cache individual loaders:
45
195
  ```typescript
46
196
  path("/product/:slug", ProductPage, { name: "product" }, () => [
47
197
  // Cache this loader's results
48
- loader(ProductLoader, () => [
49
- cache({ ttl: 300 }),
50
- ]),
198
+ loader(ProductLoader, () => [cache({ ttl: 300 })]),
51
199
 
52
200
  // This loader is not cached
53
201
  loader(CartLoader),
54
- ])
202
+ ]);
55
203
  ```
56
204
 
57
205
  ## Global Cache Configuration
@@ -60,7 +208,7 @@ Configure a cache store in the router:
60
208
 
61
209
  ```typescript
62
210
  import { createRouter } from "@rangojs/router";
63
- import { MemorySegmentCacheStore } from "@rangojs/router/rsc";
211
+ import { MemorySegmentCacheStore } from "@rangojs/router/cache";
64
212
 
65
213
  const store = new MemorySegmentCacheStore({
66
214
  defaults: { ttl: 60, swr: 300 },
@@ -83,34 +231,217 @@ const router = createRouter({
83
231
  For single-instance deployments:
84
232
 
85
233
  ```typescript
86
- import { MemorySegmentCacheStore } from "@rangojs/router/rsc";
234
+ import { MemorySegmentCacheStore } from "@rangojs/router/cache";
87
235
 
88
236
  const store = new MemorySegmentCacheStore({
89
237
  defaults: { ttl: 60, swr: 300 },
90
- maxSize: 1000, // Max entries
91
238
  });
92
239
  ```
93
240
 
94
- ### Cloudflare KV Store
241
+ ### Cloudflare Edge Cache Store
95
242
 
96
- For distributed caching on Cloudflare Workers:
243
+ For distributed caching on Cloudflare Workers using the Cache API:
97
244
 
98
245
  ```typescript
99
- import { CFCacheStore } from "@rangojs/router/cache/cf";
246
+ import { CFCacheStore } from "@rangojs/router/cache";
100
247
 
101
- const router = createRouter({
248
+ const router = createRouter<AppBindings>({
102
249
  document: Document,
103
250
  urls: urlpatterns,
104
- cache: (env) => ({
251
+ cache: (env, ctx) => ({
105
252
  store: new CFCacheStore({
106
- kv: env.Bindings.CACHE_KV,
107
- waitUntil: (fn) => env.ctx.waitUntil(fn),
253
+ ctx,
254
+ defaults: { ttl: 60, swr: 300 },
108
255
  }),
109
256
  enabled: true,
110
257
  }),
111
258
  });
112
259
  ```
113
260
 
261
+ ### With KV L2 Persistence
262
+
263
+ Add a KV namespace for global cross-colo persistence. On Cache API miss, KV is
264
+ checked and hits are promoted back to L1. Writes go to both layers.
265
+
266
+ ```typescript
267
+ import { CFCacheStore } from "@rangojs/router/cache";
268
+
269
+ const router = createRouter<AppBindings>({
270
+ document: Document,
271
+ urls: urlpatterns,
272
+ cache: (env, ctx) => ({
273
+ store: new CFCacheStore({
274
+ ctx,
275
+ kv: env.CACHE_KV, // optional KV namespace binding
276
+ defaults: { ttl: 60, swr: 300 },
277
+ }),
278
+ enabled: true,
279
+ }),
280
+ });
281
+ ```
282
+
283
+ **How the two layers work:**
284
+
285
+ | Scenario | L1 (Cache API) | L2 (KV) | Result |
286
+ | ------------ | -------------- | ------- | ----------------------------- |
287
+ | Hot request | HIT | — | Serve from L1 (fast) |
288
+ | Cold colo | MISS | HIT | Serve from KV, promote to L1 |
289
+ | First render | MISS | MISS | Render, write to both L1 + KV |
290
+
291
+ KV entries require `expirationTtl >= 60s`. Short-lived entries (< 60s total TTL)
292
+ are only cached in L1.
293
+
294
+ ### Resilience & latency budgets
295
+
296
+ Every cache read is **fail-safe**: a degraded tier never stalls or fails the
297
+ request — it degrades to the next tier (L1 → L2 → render). Three optional latency
298
+ budgets (milliseconds) bound each tier so a slow colo or KV namespace cannot pin
299
+ a request behind it:
300
+
301
+ | Option | Default | Bounds |
302
+ | --------------------- | ------- | ----------------------------------- |
303
+ | `edgeLookupTimeoutMs` | `10` | L1 `cache.match` (the lookup) |
304
+ | `edgeReadTimeoutMs` | `20` | L1 body read (CF streams it lazily) |
305
+ | `kvReadTimeoutMs` | `170` | L2 / KV read |
306
+
307
+ Set any to `0` (or a negative value) to disable that budget and always await the
308
+ read. A non-finite value (e.g. `Number(env.UNSET)`) falls back to the default.
309
+ The tag-invalidation marker reads inherit these same budgets and **fail open** on
310
+ a KV timeout — the entry is served rather than wrongly treated as invalidated.
311
+
312
+ ```typescript
313
+ new CFCacheStore({
314
+ ctx,
315
+ kv: env.CACHE_KV,
316
+ defaults: { ttl: 60, swr: 300 },
317
+ // Raise a budget only if your HEALTHY reads legitimately run slower (large
318
+ // Flight payloads, far-from-colo regions); measure the p99 first. These are
319
+ // degradation guard-rails, not tuning levers for "slow is normal here".
320
+ kvReadTimeoutMs: 250,
321
+ });
322
+ ```
323
+
324
+ Failure handling, by kind — none of these fail the request:
325
+
326
+ | Failure | Behavior |
327
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
328
+ | Transient read error (5xx/blip) | Degrade to the next tier; entry left intact |
329
+ | Read budget exceeded (timeout) | Abandon the read, degrade to the next tier |
330
+ | Corrupt / unparseable L1 entry | Reported corrupt; degrade to L2 (served if present). The L1 entry is evicted ONLY when L2 has no copy — so the evict can't race the L2→L1 promote |
331
+ | Corrupt / unparseable KV entry | Reported corrupt; evicted (self-heal) + render (no tier below it) |
332
+ | Write failure | No-op (entry simply not cached); never throws |
333
+
334
+ Each is surfaced to the router's `onError` callback (phase `"cache"`, with
335
+ `metadata.category` one of `cache-read`, `cache-corrupt`, `cache-write`,
336
+ `cache-delete`, `cache-invalidate`, `stale-revalidation`) so you can observe
337
+ cache health without affecting users.
338
+
339
+ ### Validating cache behavior with `debug`
340
+
341
+ Pass `debug` to emit one structured event per L1 read — use it to confirm on a
342
+ real deployment (via `wrangler tail`) that the store behaves as expected before
343
+ relying on it. It is intended for validation, not steady-state production.
344
+
345
+ ```typescript
346
+ new CFCacheStore({
347
+ ctx,
348
+ kv: env.CACHE_KV,
349
+ debug: true, // logs each CFCacheReadDebugEvent to the console
350
+ // ...or capture programmatically:
351
+ // debug: (event) => myTelemetry.record(event),
352
+ });
353
+ ```
354
+
355
+ Each event reports which tier answered and why (`outcome`: `l1-fresh`,
356
+ `l1-stale-revalidate`, `l1-revalidating-guarded`, `match-timeout`, `match-error`,
357
+ `body-timeout`, `body-error`, `non-200`, `tag-invalidated`, `l1-miss`, `kv-fresh`,
358
+ `kv-stale`, `kv-stale-suppressed`, `kv-miss`, `kv-timeout`, `error`), the
359
+ staleness / revalidating timestamps, and the measured per-tier durations:
360
+ `matchMs` (the L1 `match`), `markerMs` (the tag-marker resolution tail for a
361
+ tagged entry, between `matchMs` and `bodyReadMs`; absent or 0 for an untagged
362
+ entry or a per-request memo hit), and `bodyReadMs` (the L1 body read). A
363
+ persistently large `markerMs` signals a degraded KV namespace; on a healthy
364
+ deployment KV keeps markers hot in its per-colo edge cache, so it stays a few
365
+ milliseconds. `match-error` (a transient `cache.match` rejection that falls
366
+ through to L2) is kept distinct from a plain `l1-miss`.
367
+
368
+ ## Cache purity & tainted objects
369
+
370
+ A `cache()` boundary caches everything except loaders, so anything read inside a
371
+ cached handler is **frozen into the shared cache entry** and served to every
372
+ subsequent visitor. To stop one user's request-scoped data from leaking to
373
+ another, request-scoped APIs are guarded inside a cache scope:
374
+
375
+ | Inside a `cache()` boundary | Behavior |
376
+ | --------------------------------------------------------------- | --------------------------------------------------- |
377
+ | `cookies()` / `headers()` (read or write) | **throws** — request-scoped, would poison the entry |
378
+ | `ctx.header()` / `setCookie()` / `setStatus()` / `onResponse()` | **throws** — response side effects lost on a hit |
379
+ | `ctx.get(var)` where the var is `{ cache: false }` | **throws** on read |
380
+ | `ctx.set(var, value)` for a cacheable var | allowed (children are cached too) |
381
+ | Any of the above **inside a loader** | **allowed** — loaders always run fresh |
382
+
383
+ **Tainted objects.** Request-scoped objects (`ctx`, `env`, `request`) carry an
384
+ internal taint symbol so they are excluded from `"use cache"` cache keys, and
385
+ the cache scope is tracked via async-local state. Two flags back the guards:
386
+ `INSIDE_CACHE_EXEC` (set while a `"use cache"` function runs) and the `cache()`
387
+ DSL scope (`isInsideCacheScope()`). `isInsideCacheScope()` deliberately returns
388
+ `false` inside loaders — which is exactly why loaders are the dynamic holes:
389
+ they may read `cookies()`/`headers()` and re-run on every request.
390
+
391
+ The fix for "I need request data in a cached route": register a `loader()` and
392
+ **consume it with `useLoader()` in a client component**. The loader is the
393
+ dynamic hole — its data rides the fresh (never-cached) loader segment and is
394
+ rendered in the client component, so it never lands in the cached shell.
395
+
396
+ This is NOT the same as awaiting the loader in the handler. A cached handler
397
+ that does `await ctx.use(Loader)` and renders the result bakes that per-request
398
+ data straight into the shared cached segment — the loader running "fresh" does
399
+ not help, because its output was inlined into the cached parent, and `ctx.use()`
400
+ is **not** guarded. `ctx.use()` is a server-side escape hatch for non-rendered
401
+ uses (set a ctx var, make a routing decision); never render its result inside a
402
+ cached handler.
403
+
404
+ ```typescript
405
+ // WRONG — throws: cookies() read directly in a cached handler
406
+ cache({ ttl: 60 }, () => [
407
+ path("/me", () => <Profile id={cookies().get("uid")?.value} />),
408
+ ]);
409
+
410
+ // ALSO WRONG (unguarded, but leaks) — the awaited loader data is rendered into
411
+ // the cached handler, so the user's data is frozen into the shared shell.
412
+ cache({ ttl: 60 }, () => [
413
+ path(
414
+ "/me",
415
+ async (ctx) => {
416
+ const { user } = await ctx.use(MeLoader); // runs fresh…
417
+ return <Profile user={user} />; // …but inlined into the CACHED segment → leak
418
+ },
419
+ { name: "me" },
420
+ () => [loader(MeLoader)],
421
+ ),
422
+ ]);
423
+
424
+ // RIGHT — consume the loader in a CLIENT component via useLoader(). The cached
425
+ // route segment holds only the <Profile/> reference; the user data rides the
426
+ // fresh loader segment and renders client-side.
427
+
428
+ // profile.tsx (client component)
429
+ "use client";
430
+ import { useLoader } from "@rangojs/router/client";
431
+
432
+ export function Profile() {
433
+ const { user } = useLoader(MeLoader); // fresh per request; never cached
434
+ return <span>{user.name}</span>;
435
+ }
436
+
437
+ // urls — register the loader; MeLoader reads cookies() inside the loader (allowed)
438
+ cache({ ttl: 60 }, () => [
439
+ path("/me", () => <Profile />, { name: "me" }, () => [loader(MeLoader)]),
440
+ ]);
441
+ ```
442
+
443
+ See `/cache-guide` for the full decision guide and the `cache()` vs `"use cache"` comparison.
444
+
114
445
  ## Nested Cache Boundaries
115
446
 
116
447
  Override cache settings for specific sections:
@@ -124,7 +455,7 @@ cache({ ttl: 300 }, () => [
124
455
  cache({ ttl: 30 }, () => [
125
456
  path("/blog/:slug", BlogPost, { name: "blogPost" }),
126
457
  ]),
127
- ])
458
+ ]);
128
459
  ```
129
460
 
130
461
  ## Custom Cache Store
@@ -139,14 +470,15 @@ const checkoutCache = new MemorySegmentCacheStore({
139
470
  // In urls
140
471
  cache({ store: checkoutCache }, () => [
141
472
  path("/checkout", CheckoutPage, { name: "checkout" }),
142
- ])
473
+ ]);
143
474
  ```
144
475
 
145
476
  ## Complete Example
146
477
 
147
478
  ```typescript
148
479
  import { urls } from "@rangojs/router";
149
- import { MemorySegmentCacheStore } from "@rangojs/router/rsc";
480
+ import { MemorySegmentCacheStore } from "@rangojs/router/cache";
481
+ import * as CartActions from "./actions/cart";
150
482
 
151
483
  // Custom store for checkout (short TTL)
152
484
  const checkoutCache = new MemorySegmentCacheStore({
@@ -175,7 +507,7 @@ export const urlpatterns = urls(({ path, layout, cache, loader, revalidate }) =>
175
507
  path("/shop/product/:slug", ProductPage, { name: "product" }, () => [
176
508
  loader(ProductLoader, () => [cache({ ttl: 120 })]),
177
509
  loader(CartLoader, () => [
178
- revalidate(({ actionId }) => actionId?.includes("Cart") ?? false),
510
+ revalidate((ctx) => ctx.isAction(CartActions) || undefined),
179
511
  ]),
180
512
  ]),
181
513
  ]),
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: comparison
3
+ description: Compare Rango with Next.js App Router, TanStack Start, and Waku. Use when evaluating React frameworks, explaining why Rango, writing positioning or adoption guidance, answering migration questions, or checking claims about Rango's routing, loaders, caching, rendering, prefetching, safety, testing, observability, and production behavior.
4
+ ---
5
+
6
+ # Compare Rango with other React frameworks
7
+
8
+ Use the canonical [framework comparison](references/framework-comparison.md) as
9
+ the factual baseline. Read the sections relevant to the question; read the full
10
+ reference when producing an overall evaluation or editing the comparison itself.
11
+
12
+ ## Comparison rules
13
+
14
+ 1. Compare capabilities and architecture separately from ecosystem size, hiring,
15
+ integrations, and organizational familiarity.
16
+ 2. Present Rango's range clearly: start with `path()` and a component, then add
17
+ named routes, `include()`, loaders, caching, `revalidate()`, slots, intercepts,
18
+ safety, and diagnostics in the same declared route graph.
19
+ 3. Do not confuse Rango's `revalidate()` with cache invalidation. Cache APIs decide
20
+ stored-value freshness; `revalidate()` decides client render selection.
21
+ 4. Describe loaders as live-by-default RSC data slots beneath cached or prerendered
22
+ UI, not as renamed Remix or TanStack route loaders.
23
+ 5. Include production correctness where relevant: Rango State, userland client-cache
24
+ invalidation, deployment-skew recovery, tainted request context, CSP nonce
25
+ propagation, default CSRF origin checks, prefetch guards, testing, and timing.
26
+ 6. State boundaries and tradeoffs. In particular, distinguish origin checking from
27
+ token-based CSRF protection, nonce plumbing from application-owned CSP policy,
28
+ and reload-based skew recovery from Vercel deployment pinning.
29
+ 7. Credit competing frameworks where they lead. Next.js leads in ecosystem and
30
+ hiring, TanStack in search-param ergonomics and client/data devtools, and Waku
31
+ in minimal surface area.
32
+
33
+ ## Currency and evidence
34
+
35
+ - Treat Rango identifiers and behavior as source-verifiable. Check the current
36
+ package source or the relevant Rango skill when changing a precise claim.
37
+ - Competitor details age quickly. Before publishing or materially revising a claim,
38
+ verify it against current official documentation and link that primary source.
39
+ - Do not weaken a current, sourced claim merely because an older model remembers a
40
+ previous release. Check first: examples include TanStack Start's Rsbuild support,
41
+ Next.js `proxy.ts` and Cache Components, and Waku handler interceptors.
42
+ - Avoid declaring a framework universally "better." State which model fits the
43
+ application's constraints and why.
44
+
45
+ ## Output shape
46
+
47
+ Lead with the decision or differentiator. For a short answer, use the TL;DR and the
48
+ relevant framework-specific paragraph from the reference. For a decision memo,
49
+ cover the at-a-glance table, the simple-to-complex growth path, substantive runtime
50
+ differences, and the section where competitors still lead.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Rango Comparison"
3
+ short_description: "Compare Rango with other React frameworks"
4
+ default_prompt: "Use $comparison to compare Rango with Next.js, TanStack Start, and Waku."