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

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 +293 -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 +2508 -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 +24 -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-snapshot.ts +368 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +222 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1113 -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 +173 -35
  205. package/src/index.ts +241 -73
  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 +527 -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 +897 -0
  313. package/src/rsc/shell-serve.ts +124 -0
  314. package/src/rsc/ssr-setup.ts +144 -0
  315. package/src/rsc/transition-gate.ts +89 -0
  316. package/src/rsc/types.ts +95 -12
  317. package/src/runtime-env.ts +18 -0
  318. package/src/search-params.ts +99 -82
  319. package/src/segment-content-promise.ts +67 -0
  320. package/src/segment-loader-promise.ts +149 -0
  321. package/src/segment-system.tsx +349 -134
  322. package/src/serialize.ts +243 -0
  323. package/src/server/context.ts +459 -85
  324. package/src/server/cookie-parse.ts +32 -0
  325. package/src/server/cookie-store.ts +310 -0
  326. package/src/server/fetchable-loader-store.ts +11 -6
  327. package/src/server/handle-store.ts +123 -42
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +848 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +443 -135
  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 +76 -98
  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 +44 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +280 -0
  389. package/src/urls/pattern-types.ts +160 -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
@@ -0,0 +1,611 @@
1
+ ---
2
+ name: migrate-nextjs
3
+ description: Migrate a Next.js App Router project to @rangojs/router. Use when the user asks to "migrate from Next.js", "convert Next.js to Rango", "replace Next.js", or has a Next.js app they want to port.
4
+ argument-hint: [path-to-nextjs-app]
5
+ ---
6
+
7
+ # Migrate from Next.js App Router to @rangojs/router
8
+
9
+ ## Why Rango
10
+
11
+ Common reasons to migrate:
12
+
13
+ - **Server components by default** — keep data fetching on the server without
14
+ framework-specific file conventions.
15
+ See: `/router-setup`, `/route`
16
+ - **Django-style route definition** — `urls()`, `path()`, and `layout()` make
17
+ the route tree explicit instead of spreading routing across many special files.
18
+ See: `/route`, `/layout`
19
+ - **Named routes** — reverse URLs by route name instead of hard-coding path
20
+ strings throughout the app.
21
+ See: `/links`, `/typesafety`
22
+ - **Clear execution model** — request scope, render scope, segment boundaries,
23
+ and shared `ctx` behavior are explicit in the routing model.
24
+ See: `/middleware`, `/loader`
25
+ - **Live data layer** — `createLoader()` and `loader()` keep data fresh
26
+ independently of cached UI. A route can serve cached segments while loaders
27
+ still resolve live on every request.
28
+ See: `/loader`, `/caching`, `/cache-guide`
29
+ - **Explicit caching model** — `cache()` DSL, `revalidate()`, `use cache`, and
30
+ custom cache stores make caching and revalidation behavior visible in code.
31
+ See: `/caching`, `/cache-guide`, `/use-cache`
32
+ - **Build-time rendering** — `Static()` and `Prerender()` provide explicit
33
+ build-time rendering instead of mixing rendering and caching behind conventions.
34
+ See: `/prerender`
35
+ - **Composable route tree** — layouts, includes, middleware, parallels, and
36
+ intercepts compose directly in the route definition.
37
+ See: `/composability`, `/parallel`, `/intercept`
38
+ - **Multi-router flexibility** — support multiple routers, domain routing, and
39
+ worker/edge-style deployment patterns.
40
+ See: `/host-router`
41
+
42
+ ## Migration Strategy
43
+
44
+ Work route-by-route, bottom-up. Start with leaf pages, then layouts, then middleware. Verify each route works before moving to the next.
45
+
46
+ ## 1. Project Setup
47
+
48
+ Replace Next.js tooling with Vite + Rango:
49
+
50
+ ```bash
51
+ npm remove next @next/env
52
+ npm install @rangojs/router @vitejs/plugin-react
53
+ npm install -D vite
54
+ ```
55
+
56
+ ```typescript
57
+ // vite.config.ts
58
+ import { defineConfig } from "vite";
59
+ import { rango } from "@rangojs/router/vite";
60
+
61
+ export default defineConfig({
62
+ plugins: [rango()],
63
+ });
64
+ ```
65
+
66
+ ```typescript
67
+ // src/router.tsx
68
+ import { createRouter } from "@rangojs/router";
69
+ import { Document } from "./document";
70
+ import { urlpatterns } from "./urls";
71
+
72
+ export default createRouter({
73
+ document: Document,
74
+ }).routes(urlpatterns);
75
+ ```
76
+
77
+ The Document component replaces `app/layout.tsx`'s `<html>` wrapper. See `/router-setup` for full config options.
78
+
79
+ ## 2. Route Mapping
80
+
81
+ ### File-based → URL pattern DSL
82
+
83
+ | Next.js file path | Rango equivalent |
84
+ | ------------------------------- | ---------------------------------------------------------- |
85
+ | `app/page.tsx` | `path("/", HomePage, { name: "home" })` |
86
+ | `app/about/page.tsx` | `path("/about", AboutPage, { name: "about" })` |
87
+ | `app/blog/[slug]/page.tsx` | `path("/blog/:slug", BlogPost, { name: "blogPost" })` |
88
+ | `app/shop/[...path]/page.tsx` | `path("/shop/:path+", CatchAll, { name: "shopCatchAll" })` |
89
+ | `app/docs/[[...slug]]/page.tsx` | `path("/docs/:slug*", Docs, { name: "docs" })` |
90
+
91
+ The catch-all remainder is a single string at `ctx.params.<name>` with the `/`
92
+ separators preserved — split it to recover the array Next gives you:
93
+
94
+ ```typescript
95
+ // app/docs/[[...slug]]/page.tsx -> params.slug is string[] | undefined in Next
96
+ path("/docs/:slug*", (ctx) => {
97
+ // "" for /docs, "a/b/c" for /docs/a/b/c
98
+ const slug = ctx.params.slug === "" ? [] : ctx.params.slug.split("/");
99
+ return <Docs slug={slug} />;
100
+ }, { name: "docs" });
101
+ ```
102
+
103
+ `[...path]` (required, ≥1 segment) maps to `:path+`; `[[...slug]]` (optional,
104
+ matches the bare parent too) maps to `:slug*` — which binds `""` at `/docs`.
105
+
106
+ ### Layouts
107
+
108
+ ```typescript
109
+ // Next.js: app/dashboard/layout.tsx
110
+ export default function DashboardLayout({ children }) {
111
+ return <div className="dashboard">{children}</div>;
112
+ }
113
+
114
+ // Rango:
115
+ import { Outlet } from "@rangojs/router/client";
116
+
117
+ function DashboardLayout() {
118
+ return (
119
+ <div className="dashboard">
120
+ <Outlet />
121
+ </div>
122
+ );
123
+ }
124
+
125
+ // In urls.tsx:
126
+ layout(<DashboardLayout />, () => [
127
+ path("/dashboard", DashboardIndex, { name: "dashboard" }),
128
+ path("/dashboard/settings", Settings, { name: "settings" }),
129
+ ])
130
+ ```
131
+
132
+ Key difference: Rango layouts use `<Outlet />` instead of `{children}`. Layouts are server components by default.
133
+
134
+ ### Dynamic layouts (with data)
135
+
136
+ ```typescript
137
+ // Next.js: app/dashboard/layout.tsx
138
+ export default async function DashboardLayout({ children }) {
139
+ const user = await getUser();
140
+ return <Shell user={user}>{children}</Shell>;
141
+ }
142
+
143
+ // Rango: handler function layout
144
+ layout(async (ctx) => {
145
+ const user = ctx.get("user");
146
+ return (
147
+ <Shell user={user}>
148
+ <Outlet />
149
+ </Shell>
150
+ );
151
+ }, () => [
152
+ path("/dashboard", DashboardIndex, { name: "dashboard" }),
153
+ ])
154
+ ```
155
+
156
+ ### Route groups
157
+
158
+ Next.js `app/(marketing)/page.tsx` route groups have no URL segment. In Rango, just organize with `include()`:
159
+
160
+ ```typescript
161
+ // src/urls/marketing.tsx
162
+ export const marketingPatterns = urls(({ path }) => [
163
+ path("/", LandingPage, { name: "landing" }),
164
+ path("/pricing", PricingPage, { name: "pricing" }),
165
+ ]);
166
+
167
+ // src/urls.tsx
168
+ include("/", marketingPatterns, { name: "marketing" }),
169
+ ```
170
+
171
+ Next.js code-splits each route segment automatically. Rango's eager `include()`
172
+ bundles the group into the entry chunk; to get Next-style per-section splitting,
173
+ pass an async provider so the group loads on the first request under its prefix:
174
+
175
+ ```typescript
176
+ // urls/admin.tsx: `export default adminPatterns` — loads on first /admin request
177
+ include("/admin", () => import("./urls/admin"), { name: "admin" }),
178
+ ```
179
+
180
+ Route types, `href()`, and prerender still see every route in the split group.
181
+ See `/composability`.
182
+
183
+ ### Parallel routes
184
+
185
+ In Next.js, `@sidebar` and `@main` are both named slots. In Rango, the main content
186
+ renders through `<Outlet />` (the path handler), and only extra slots use `parallel()` +
187
+ `<ParallelOutlet />`:
188
+
189
+ ```typescript
190
+ // Next.js: app/layout.tsx renders {sidebar} and {children}
191
+ // app/@sidebar/page.tsx provides the sidebar slot
192
+ // app/page.tsx provides the main content
193
+
194
+ // Rango: main content is the path handler, sidebar is a parallel slot
195
+ layout(
196
+ () => (
197
+ <div className="dashboard">
198
+ <ParallelOutlet name="@sidebar" />
199
+ <Outlet />
200
+ </div>
201
+ ),
202
+ () => [
203
+ parallel({
204
+ "@sidebar": <Sidebar />,
205
+ }),
206
+ path("/dashboard", DashboardPage, { name: "dashboard" }),
207
+ ],
208
+ )
209
+ ```
210
+
211
+ Only add `parallel()` slots for content that renders alongside the main route.
212
+ The main content always goes through `<Outlet />` via the `path()` handler.
213
+
214
+ ### Intercepting routes
215
+
216
+ ```typescript
217
+ // Next.js: app/(.)product/[id]/page.tsx
218
+ // (convention: (.) means same level, (..) parent level)
219
+
220
+ // Rango: explicit intercept in layout
221
+ layout(<ShopLayout />, () => [
222
+ path("/product/:id", ProductPage, { name: "product" }),
223
+ intercept("@modal", ".product", <ProductModal />, {
224
+ when: ({ from }) => from.pathname.startsWith("/shop"),
225
+ }),
226
+ ])
227
+ ```
228
+
229
+ ## 3. Data Fetching
230
+
231
+ ### Server component data fetching
232
+
233
+ Inline `fetch()` or direct DB calls in server components work as-is — no migration needed:
234
+
235
+ ```typescript
236
+ // Next.js:
237
+ async function ProductPage({ params }) {
238
+ const product = await fetch(`/api/products/${params.slug}`).then(r => r.json());
239
+ return <div>{product.name}</div>;
240
+ }
241
+
242
+ // Rango: same pattern, params come from ctx
243
+ const ProductPage: Handler<"product"> = async (ctx) => {
244
+ const product = await fetch(`/api/products/${ctx.params.slug}`).then(r => r.json());
245
+ return <div>{product.name}</div>;
246
+ };
247
+ ```
248
+
249
+ ### When to use createLoader
250
+
251
+ Loaders are Rango's live data layer. Use them when you need:
252
+
253
+ - **Client-side data refresh** — `useLoader()` in client components for reactive data
254
+ - **Per-loader caching** — opt in with `loader(MyLoader, () => [cache({ ttl: 60 })])`; loaders stay live by default
255
+ - **Revalidation control** — `revalidate()` targets specific segments and loaders after actions
256
+ - **Loading skeletons** — `loading()` shows a Suspense fallback while loaders resolve
257
+
258
+ ```typescript
259
+ import { createLoader } from "@rangojs/router";
260
+
261
+ export const ProductLoader = createLoader(async (ctx) => {
262
+ return await db.getProduct(ctx.params.slug);
263
+ });
264
+
265
+ // In urls:
266
+ path("/product/:slug", ProductPage, { name: "product" }, () => [
267
+ loader(ProductLoader),
268
+ loading(<ProductSkeleton />),
269
+ ])
270
+ ```
271
+
272
+ If the existing fetch pattern works and you don't need these features, leave it as-is. See `/loader` for full API.
273
+
274
+ ### generateStaticParams → Prerender + Passthrough
275
+
276
+ Plain `Prerender` only serves the listed params — unlisted params get no live
277
+ fallback in production (the handler is evicted). If the Next.js route serves
278
+ params outside the generated set at runtime, wrap with `Passthrough()`:
279
+
280
+ ```typescript
281
+ // Next.js:
282
+ export async function generateStaticParams() {
283
+ return [{ slug: "a" }, { slug: "b" }];
284
+ }
285
+
286
+ // Rango (build-only, no live fallback for unlisted params):
287
+ import { Prerender } from "@rangojs/router";
288
+
289
+ export const ProductDef = Prerender<{ slug: string }>(
290
+ async () => [{ slug: "a" }, { slug: "b" }],
291
+ async (ctx) => {
292
+ const product = await getProduct(ctx.params.slug);
293
+ return <ProductPage product={product} />;
294
+ },
295
+ );
296
+
297
+ // Rango (with live fallback — matches Next.js dynamicParams behavior):
298
+ import { Prerender, Passthrough } from "@rangojs/router";
299
+
300
+ const ProductDef = Prerender<{ slug: string }>(
301
+ async () => [{ slug: "a" }, { slug: "b" }],
302
+ async (ctx) => {
303
+ const product = await getProduct(ctx.params.slug);
304
+ if (!product) return ctx.passthrough();
305
+ return <ProductPage product={product} />;
306
+ },
307
+ );
308
+
309
+ export const Product = Passthrough(ProductDef, async (ctx) => {
310
+ const product = await getProduct(ctx.params.slug);
311
+ return <ProductPage product={product} />;
312
+ });
313
+ ```
314
+
315
+ Use `Passthrough()` whenever the Next.js route has `dynamicParams: true` (the
316
+ default) or serves an open-ended param space. See `/prerender` for full API.
317
+
318
+ ### Revalidation: two distinct axes
319
+
320
+ Next.js conflates two things under "revalidation." Rango separates them — and
321
+ tag-based cache invalidation now maps directly.
322
+
323
+ **1. Cache invalidation (bust cached values) — direct equivalent.** Tag entries
324
+ with `cache({ tags })` or, inside a `"use cache"` function, runtime
325
+ `cacheTag(...tags)`. Then invalidate by tag:
326
+
327
+ ```typescript
328
+ // Next.js Rango
329
+ // revalidateTag("products") → await updateTag("products") // in a server action: awaitable,
330
+ // // read-your-own-writes (next render is fresh)
331
+ // or revalidateTag("products") // in a route handler / webhook:
332
+ // // background, non-blocking (hard-purge)
333
+ ```
334
+
335
+ `updateTag` is awaitable and immediate; `revalidateTag` is fire-and-forget. Both
336
+ hard-purge (the next read re-renders fresh); the only difference is awaitability —
337
+ despite the Next.js name, `revalidateTag` here is NOT stale-while-revalidate.
338
+ Built-in stores (`MemorySegmentCacheStore`, `CFCacheStore`) index by tag. Next's
339
+ `revalidatePath` has no path-based equivalent — tag the relevant entries instead.
340
+
341
+ **2. Partial-render selection (which segments re-run after an action).** This is
342
+ NOT cache invalidation — it is `revalidate()`, controlling which segments
343
+ (layouts, paths, loaders, parallels) recompute during partial action
344
+ re-rendering:
345
+
346
+ ```typescript
347
+ import { updateBlog } from "./actions/blog";
348
+
349
+ // Re-run this layout when a blog action fires
350
+ layout(BlogLayout, () => [
351
+ revalidate((ctx) => ctx.isAction(updateBlog) || undefined),
352
+ path("/blog/:slug", BlogPost, { name: "blogPost" }),
353
+ ]);
354
+
355
+ // Re-run sidebar parallel when params change
356
+ parallel({ "@sidebar": BlogSidebar }, () => [
357
+ revalidate(
358
+ ({ currentParams, nextParams }) => currentParams.slug !== nextParams.slug,
359
+ ),
360
+ ]);
361
+ ```
362
+
363
+ **Server-side caching** — `cache()` DSL, loader-level `cache()`, and `"use cache"`
364
+ control what gets cached and for how long. This is separate from `revalidate()`:
365
+
366
+ ```typescript
367
+ cache({ ttl: 60, swr: 300 }, () => [
368
+ path("/blog/:slug", BlogPost, { name: "blogPost" }),
369
+ ]);
370
+ ```
371
+
372
+ The two axes compose: `updateTag()` / `revalidateTag()` bust cached values;
373
+ `revalidate()` selects which segments re-render and stream to the client after an
374
+ action.
375
+
376
+ When migrating:
377
+
378
+ - `revalidateTag(tag)` → `await updateTag(tag)` (in a server action) or
379
+ `revalidateTag(tag)` (in a route handler / webhook). Effectively 1:1.
380
+ - `revalidatePath(path)` → no path-based equivalent; tag the entries on that
381
+ route (`cache({ tags })` / `cacheTag(...)`) and invalidate by tag.
382
+ - To also force specific segments to re-render after the action (independent of
383
+ cache busting), attach a `revalidate()` rule at those segment boundaries.
384
+
385
+ ## 4. Middleware
386
+
387
+ Next.js `middleware.ts` wraps the entire request — including server actions.
388
+ The direct equivalent is `router.use()`, not the DSL `middleware()`:
389
+
390
+ ```typescript
391
+ // Next.js: middleware.ts (file-convention, wraps all requests)
392
+ import { NextResponse } from "next/server";
393
+
394
+ export function middleware(request) {
395
+ if (!request.cookies.get("session")) {
396
+ return NextResponse.redirect(new URL("/login", request.url));
397
+ }
398
+ }
399
+ export const config = { matcher: ["/dashboard/:path*"] };
400
+
401
+ // Rango: split into initialisation (global) + guard (scoped)
402
+ import { redirect, cookies } from "@rangojs/router";
403
+ import type { Middleware } from "@rangojs/router";
404
+
405
+ // Runs on every request — resolves the session for all routes
406
+ const authInit: Middleware = async (ctx, next) => {
407
+ const session = cookies().get("session")?.value;
408
+ if (session) {
409
+ const user = await verifySession(session);
410
+ ctx.set("user", user);
411
+ }
412
+ await next();
413
+ };
414
+
415
+ // Scoped guard — redirects unauthenticated users
416
+ const requireAuth: Middleware = async (ctx, next) => {
417
+ if (!ctx.get("user")) {
418
+ return redirect("/login");
419
+ }
420
+ await next();
421
+ };
422
+
423
+ const router = createRouter({})
424
+ .use(authInit) // all routes — sets ctx user
425
+ .use("/dashboard/*", requireAuth) // dashboard only — redirects
426
+ .routes(urlpatterns);
427
+ ```
428
+
429
+ **Rango has two middleware levels with different scopes:**
430
+
431
+ | | `router.use()` | `middleware()` in DSL |
432
+ | ------------------ | ------------------------------------ | ------------------------------- |
433
+ | Wraps | Entire request (actions + rendering) | Rendering only |
434
+ | Use for | Auth guards, logging, CORS | Context shaping, render headers |
435
+ | Next.js equivalent | `middleware.ts` | No direct equivalent |
436
+
437
+ Use `router.use()` for auth guards — it wraps the full request including actions.
438
+ DSL `middleware()` can also guard rendering (e.g. redirect unauthenticated users
439
+ away from a page), but it does not protect actions on that route. For full auth
440
+ coverage, prefer `router.use()`. See `/middleware`.
441
+
442
+ ## 5. Loading & Error States
443
+
444
+ ```typescript
445
+ // Next.js: app/dashboard/loading.tsx
446
+ export default function Loading() { return <Skeleton />; }
447
+
448
+ // Rango:
449
+ path("/dashboard", DashboardPage, { name: "dashboard" }, () => [
450
+ loading(<Skeleton />),
451
+ ])
452
+ ```
453
+
454
+ ```typescript
455
+ // Next.js: app/dashboard/error.tsx wraps all routes under /dashboard
456
+ "use client";
457
+ export default function Error({ error, reset }) { ... }
458
+
459
+ // Rango: errorBoundary wrapping a group of routes
460
+ layout(<DashboardLayout />, () => [
461
+ errorBoundary(({ error, reset }) => (
462
+ <div>
463
+ <h2>Something went wrong</h2>
464
+ <button onClick={reset}>Try again</button>
465
+ </div>
466
+ )),
467
+ path("/dashboard", DashboardIndex, { name: "dashboard" }),
468
+ path("/dashboard/settings", Settings, { name: "settings" }),
469
+ ])
470
+ ```
471
+
472
+ ```typescript
473
+ // Next.js: app/not-found.tsx
474
+ export default function NotFound() { ... }
475
+
476
+ // Rango (app-level — no route match, or notFound() without a boundary):
477
+ createRouter({
478
+ notFound: ({ pathname }) => <NotFoundPage pathname={pathname} />,
479
+ })
480
+
481
+ // Rango (route-level — notFoundBoundary wrapping a group of routes):
482
+ layout(<ShopLayout />, () => [
483
+ notFoundBoundary(({ notFound: info }) => (
484
+ <div>
485
+ <h2>Not Found</h2>
486
+ <p>{info.message}</p>
487
+ </div>
488
+ )),
489
+ path("/product/:slug", ProductPage, { name: "product" }),
490
+ path("/product/:slug/reviews", ReviewsPage, { name: "reviews" }),
491
+ ])
492
+ ```
493
+
494
+ Both `errorBoundary()` and `notFoundBoundary()` catch errors from all
495
+ children in their scope — handlers, loaders, and nested segments.
496
+
497
+ ## 6. Navigation
498
+
499
+ | Next.js | Rango |
500
+ | ------------------------------- | ------------------------------------------------- |
501
+ | `import Link from "next/link"` | `import { Link } from "@rangojs/router/client"` |
502
+ | `<Link href="/about">` | `<Link to="/about">` |
503
+ | `useRouter().push("/about")` | `useRouter().push("/about")` |
504
+ | `useRouter().replace("/about")` | `useRouter().replace("/about")` |
505
+ | `usePathname()` | `usePathname()` from `@rangojs/router/client` |
506
+ | `useSearchParams()` | `useSearchParams()` from `@rangojs/router/client` |
507
+ | `redirect("/login")` (server) | `redirect("/login")` from `@rangojs/router` |
508
+
509
+ ## 7. Server Actions
510
+
511
+ Server actions work the same way — `"use server"` directive, `useActionState`, form actions. No migration needed for action logic.
512
+
513
+ Key difference: in Rango, route middleware does NOT wrap action execution. Actions only see global middleware context. Use `getRequestContext()` in actions to access `ctx.set()`/`ctx.get()`.
514
+
515
+ Next.js's `revalidateTag()` maps directly: tag entries via `cache({ tags })` / `cacheTag(...)`, then invalidate. **In a server action use `await updateTag(tag)`** — it is read-your-own-writes, so the action's own re-render sees fresh data; `revalidateTag(tag)` is a background (non-blocking) hard-purge and is NOT read-your-own-writes, so reserve it for route handlers / webhooks (calling it from an action can leave that action's re-render stale). `revalidatePath()` has no path-based equivalent — tag the route's entries instead. Separately, to force specific matched segments (path/layout/parallel/intercept) and their loaders to re-render after an action, attach a `revalidate(({ actionId }) => ...)` rule to that segment or loader registration. See `/server-actions` for the full pattern (validation, error handling, file uploads), `/caching` for tag invalidation, and `/loader` for revalidation rule semantics.
516
+
517
+ ## 8. Metadata / Head
518
+
519
+ Rango uses the `Meta` handle + `<MetaTags />` client component:
520
+
521
+ ```typescript
522
+ // Next.js: export const metadata = { title: "Home" }
523
+ // Next.js: export function generateMetadata({ params }) { ... }
524
+
525
+ // Rango: Meta handle in handlers (server), MetaTags in document <head> (client)
526
+ import { Meta } from "@rangojs/router";
527
+
528
+ const HomePage: Handler<"home"> = (ctx) => {
529
+ const meta = ctx.use(Meta);
530
+ meta({ title: "Home" });
531
+ meta({ name: "description", content: "Welcome to the site" });
532
+ return <div>Home page</div>;
533
+ };
534
+ ```
535
+
536
+ Add `<MetaTags />` in the Document component's `<head>`:
537
+
538
+ ```typescript
539
+ import { MetaTags } from "@rangojs/router/client";
540
+
541
+ function Document({ children }: { children: ReactNode }) {
542
+ return (
543
+ <html>
544
+ <head>
545
+ <MetaTags />
546
+ </head>
547
+ <body>{children}</body>
548
+ </html>
549
+ );
550
+ }
551
+ ```
552
+
553
+ Later routes override earlier ones for the same meta key (deduplication).
554
+
555
+ ## 9. API Routes
556
+
557
+ ```typescript
558
+ // Next.js: app/api/users/route.ts
559
+ export async function GET(request) { ... }
560
+
561
+ // Rango: response routes
562
+ path.json("/api/users", async (ctx) => {
563
+ const users = await db.getUsers();
564
+ return users;
565
+ }, { name: "apiUsers" })
566
+
567
+ path.text("/api/health", () => "ok", { name: "apiHealth" })
568
+ ```
569
+
570
+ See `/response-routes` for full API.
571
+
572
+ ## 10. Theme / Dark Mode
573
+
574
+ If the Next.js app uses `next-themes` or a custom theme provider, replace it
575
+ with Rango's built-in theme system (FOUC prevention included):
576
+
577
+ ```typescript
578
+ const router = createRouter({
579
+ theme: true, // or { defaultTheme: "system", attribute: "class" }
580
+ });
581
+ ```
582
+
583
+ Client components use `useTheme()` to read and toggle:
584
+
585
+ ```typescript
586
+ "use client";
587
+ import { useTheme } from "@rangojs/router/theme";
588
+
589
+ function ThemeToggle() {
590
+ const { theme, setTheme } = useTheme();
591
+ return <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>{theme}</button>;
592
+ }
593
+ ```
594
+
595
+ See `/theme` for full API including system detection and cookie persistence.
596
+
597
+ ## Migration Checklist
598
+
599
+ 1. [ ] Set up Vite config with `rango()` plugin
600
+ 2. [ ] Create Document component (replaces root `<html>` layout)
601
+ 3. [ ] Create `router.tsx` with `createRouter()`
602
+ 4. [ ] Convert file-based routes to `urls()` DSL in `urls.tsx`
603
+ 5. [ ] Migrate layouts to `layout()` with `<Outlet />`
604
+ 6. [ ] Convert data fetching to `createLoader()` + `ctx.use()`
605
+ 7. [ ] Migrate `middleware.ts` to `router.use()` (auth, guards, logging)
606
+ 8. [ ] Replace `next/link` with `Link` from `@rangojs/router/client`
607
+ 9. [ ] Convert loading/error files to `loading()` / `errorBoundary()`
608
+ 10. [ ] Migrate API routes to `path.json()` / `path.text()`
609
+ 11. [ ] Update metadata to use `Meta` handle + `<MetaTags />` in document head
610
+ 12. [ ] Replace `next-themes` with `theme: true` in createRouter (see `/theme`)
611
+ 13. [ ] Run `npx rango generate src/` to generate route types