@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,6 +1,6 @@
1
1
  ---
2
2
  name: prerender
3
- description: Pre-render route segments at build time with Prerender and passthrough fallback
3
+ description: Pre-render route segments at build time with Prerender and Passthrough live fallback
4
4
  argument-hint: [passthrough]
5
5
  ---
6
6
 
@@ -51,8 +51,14 @@ path("/blog/:slug", BlogPost, { name: "blog.post" })
51
51
 
52
52
  ### With Passthrough (live fallback for unknown params)
53
53
 
54
+ Wrap a `Prerender` definition with `Passthrough()` to add a separate live handler
55
+ for unknown params at runtime. The build handler runs at build time, the live
56
+ handler runs at request time.
57
+
54
58
  ```typescript
55
- export const ProductPage = Prerender(
59
+ import { Prerender, Passthrough } from "@rangojs/router";
60
+
61
+ export const ProductPageDef = Prerender(
56
62
  async () => {
57
63
  const top = await db.query("SELECT id FROM products WHERE featured");
58
64
  return top.map(p => ({ id: p.id }));
@@ -61,30 +67,42 @@ export const ProductPage = Prerender(
61
67
  const product = await db.query("SELECT * FROM products WHERE id = ?", ctx.params.id);
62
68
  return <Product data={product} />;
63
69
  },
64
- { passthrough: true }
70
+ { concurrency: 4 }
65
71
  );
72
+
73
+ // In route definition:
74
+ path("/products/:id", Passthrough(ProductPageDef, async (ctx) => {
75
+ const product = await ctx.env.DB.query("SELECT * FROM products WHERE id = ?", ctx.params.id);
76
+ return <Product data={product} />;
77
+ }), { name: "product" })
66
78
  ```
67
79
 
68
- ## Passthrough Mode
80
+ ## Passthrough Wrapper
69
81
 
70
- Controls whether the handler stays in the RSC server bundle after build:
82
+ `Passthrough(prerenderDef, liveHandler)` wraps a `Prerender` definition with a
83
+ separate handler for runtime fallback. The build and live handlers are separate
84
+ functions — no `ctx.build` branching needed.
71
85
 
72
- | | `passthrough: false` (default) | `passthrough: true` |
73
- |---|---|---|
74
- | Known params | Served from pre-rendered Flight payload | Served from pre-rendered Flight payload |
75
- | Unknown params | Handler evicted, no live fallback | Handler runs live at request time |
76
- | Bundle size | Handler code + imports removed | Handler code kept in RSC bundle |
77
- | `revalidate()` | Not allowed (handler gone) | Allowed (handler can re-render) |
78
- | `loading()` | Ignored (segments fully resolved) | Works for live fallback renders |
86
+ | | Plain `Prerender` (no wrapper) | `Passthrough(def, liveHandler)` |
87
+ | ------------------- | --------------------------------------- | ---------------------------------------- |
88
+ | Known params | Served from pre-rendered Flight payload | Served from pre-rendered Flight payload |
89
+ | Unknown params | Handler evicted, no live fallback | Live handler runs at request time |
90
+ | `ctx.passthrough()` | Throws (not on Passthrough route) | Skips artifact, defers to live handler |
91
+ | Bundle size | Build handler code + imports removed | Build handler evicted, live handler kept |
92
+ | `revalidate()` | Not allowed (handler gone) | Allowed (live handler can re-render) |
93
+ | `loading()` | Ignored (segments fully resolved) | Works for live fallback renders |
79
94
 
80
- ### When to use passthrough
95
+ ### When to use Passthrough
96
+
97
+ Use `Passthrough()` when:
81
98
 
82
- Use `passthrough: true` when:
83
99
  - The route has a large or open-ended param space (e.g., user profiles, product pages)
84
100
  - You want to pre-render popular/known params for speed but still serve unknown params live
85
101
  - You need `revalidate()` on the route
102
+ - The live handler needs runtime bindings (e.g., `ctx.env.DB`)
103
+
104
+ Use plain `Prerender` (no wrapper) when:
86
105
 
87
- Use `passthrough: false` (default) when:
88
106
  - All possible params are known at build time (e.g., markdown files, config-driven pages)
89
107
  - You want maximum bundle size reduction (handler code + node:fs imports removed)
90
108
  - The route uses build-only APIs (node:fs, local files) not available at runtime
@@ -95,18 +113,60 @@ Handlers receive `BuildContext` at build time, a subset of the runtime `HandlerC
95
113
 
96
114
  ```typescript
97
115
  interface BuildContext<TParams> {
98
- params: TParams; // From getParams
99
- use: <T>(handle: Handle<T>) => (data: T) => void; // Push handle data
100
- url: URL; // Synthetic URL from pattern + params
101
- pathname: string; // Pathname from synthetic URL
102
- // NOT available: req, headers, cookies, env (throws descriptive errors)
116
+ params: TParams; // From getParams
117
+ build: true; // Always true at build time
118
+ dev: boolean; // true in Vite dev mode, false during production build
119
+ use: <T>(handle: Handle<T>) => (data: T) => void; // Push handle data
120
+ url: URL; // Synthetic URL from pattern + params
121
+ pathname: string; // Pathname from synthetic URL
122
+ searchParams: URLSearchParams; // URLSearchParams from the synthetic URL (always empty for prerender)
123
+ search: {}; // Typed search params -- always {} for prerender (no real query string)
124
+ set(key: string, value: any): void; // Set context variable (string key)
125
+ set<T>(contextVar: ContextVar<T>, value: T): void; // Set typed context variable
126
+ get(key: string): any; // Read context variable (string key)
127
+ get<T>(contextVar: ContextVar<T>): T | undefined; // Read typed context variable
128
+ reverse(
129
+ name: string,
130
+ params?: Record<string, string>,
131
+ search?: Record<string, unknown>,
132
+ ): string; // URL generation
133
+ passthrough(): PrerenderPassthroughResult; // Skip local artifact (Passthrough routes only)
134
+ env: DefaultEnv; // Available when buildEnv is configured in rango() (throws otherwise)
135
+ // NOT available: request, headers, cookies (always throw)
103
136
  }
104
137
  ```
105
138
 
139
+ Use `createVar<T>()` to share typed data from a Prerender handler to child layouts:
140
+
141
+ ```typescript
142
+ import { Prerender, createVar } from "@rangojs/router";
143
+
144
+ interface PaginationData { current: number; total: number; }
145
+ export const Pagination = createVar<PaginationData>();
146
+
147
+ export const ArticleList = Prerender<{ page: string }>(
148
+ async () => [{ page: "1" }, { page: "2" }],
149
+ async (ctx) => {
150
+ ctx.set(Pagination, { current: Number(ctx.params.page), total: 2 });
151
+ return <Articles />;
152
+ },
153
+ );
154
+ ```
155
+
106
156
  All items inside the path's use() callback (child layouts, parallels) also receive
107
157
  `BuildContext` during pre-rendering. Loaders are the exception -- they run at
108
158
  request time with full server context.
109
159
 
160
+ This is one reason prerender is a good fit for handler-first composition:
161
+ the handler and its child layouts/parallels participate in the same full
162
+ render pass, so data set with `ctx.set()` is available downstream via
163
+ `ctx.get()`.
164
+
165
+ At runtime, partial action revalidation follows a narrower rule: only
166
+ revalidated segments are recomputed. If a child segment depends on data
167
+ established by an outer handler/layout, that outer segment must also be
168
+ revalidated, or the child must load/guard the data independently.
169
+
110
170
  ## Supported Export Patterns
111
171
 
112
172
  All of the following are equivalent and fully supported by the Vite transform:
@@ -139,15 +199,17 @@ In production builds, `Prerender` exports are replaced with stubs:
139
199
  // Original
140
200
  export const BlogPost = Prerender(getParams, handler);
141
201
 
142
- // Stubbed (ships to server bundle when passthrough: false)
143
- export const BlogPost = { __brand: "prerenderHandler", $$id: "abc123#BlogPost" };
202
+ // Stubbed (all Prerender handlers are evicted)
203
+ export const BlogPost = {
204
+ __brand: "prerenderHandler",
205
+ $$id: "abc123#BlogPost",
206
+ };
144
207
  ```
145
208
 
146
- The original module and its imports (node:fs, markdown libs) are excluded from
147
- the bundle. With `passthrough: true`, the handler code stays in the RSC bundle.
209
+ All Prerender handlers are evicted in production. The live handler for
210
+ `Passthrough()` routes lives in the urls module and is not evicted.
148
211
 
149
- In client and SSR environments, ALL prerender handlers are always stubbed
150
- (passthrough only affects the RSC server bundle).
212
+ In client and SSR environments, ALL prerender handlers are always stubbed.
151
213
 
152
214
  ## Sub-use Semantics
153
215
 
@@ -181,22 +243,36 @@ path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
181
243
 
182
244
  ## Interaction with DSL Items
183
245
 
184
- | DSL item | Behavior with Prerender |
185
- |----------------|--------------------------------------|
186
- | `loader()` | Live at runtime, bundled normally. Use `cache()` for caching. |
187
- | `revalidate()` | Not allowed without passthrough. Allowed with passthrough. |
188
- | `cache()` | Orthogonal -- use on parent layouts and loaders. |
189
- | `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live. |
190
- | `parallel()` | Parallel slots inside path are pre-rendered. |
191
- | `middleware()` | Skipped during pre-render (no request). Runs at request time for loaders. |
192
- | `loading()` | Ignored without passthrough. Works for live fallback with passthrough. |
193
- | `intercept()` | Not pre-rendered (intercepts are navigation-triggered). |
246
+ | DSL item | Behavior with Prerender |
247
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
248
+ | `loader()` | Live at runtime, bundled normally. Use `cache()` for caching. |
249
+ | `revalidate()` | Not allowed without Passthrough. Allowed with Passthrough. |
250
+ | `cache()` | Orthogonal -- use on parent layouts and loaders. |
251
+ | `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live. |
252
+ | `parallel()` | Parallel slots inside path are pre-rendered. |
253
+ | `middleware()` | Skipped during pre-render (no request). Runs at request time for loaders. |
254
+ | `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough. |
255
+ | `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when` config conditions are skipped at build time (all intercepts are pre-rendered unconditionally). |
256
+
257
+ When Passthrough revalidation is enabled, remember that revalidation is
258
+ still partial: opting a child segment into revalidation does not
259
+ implicitly re-run outer prerender-derived handlers/layouts.
194
260
 
195
261
  ## Dev Mode
196
262
 
197
- In dev mode, `Prerender` is a normal handler. Routes render live
198
- on every request. No stubbing, no build-time pre-rendering. The handler runs
199
- with full runtime context (not BuildContext).
263
+ In dev mode there is no production-style prerender build pass and no handler
264
+ stubbing.
265
+
266
+ **Node.js dev server** — `Prerender` acts as a normal handler. Routes render
267
+ live on every request with full runtime context (`ctx.build === false`).
268
+
269
+ **Non-Node runtimes (Cloudflare workerd, Deno workers)** — Handlers that
270
+ depend on Node APIs (e.g. `node:fs`) cannot run in-process. The Vite plugin
271
+ can intercept these requests and resolve them via the `/__rsc_prerender`
272
+ endpoint, which runs `matchForPrerender` in a Node.js temp server. In this
273
+ path the handler receives `BuildContext` (`ctx.build === true`) and segments
274
+ are resolved identically to production prerendering, then served on-demand.
275
+ This only applies when `__PRERENDER_DEV_URL` is set by the plugin.
200
276
 
201
277
  ## Storage Layout
202
278
 
@@ -212,41 +288,249 @@ dist/static/__<hash>/
212
288
  _.flight # static route, no params
213
289
  ```
214
290
 
291
+ ## Concurrency
292
+
293
+ Prerender handlers can specify how many param sets render in parallel:
294
+
295
+ ```typescript
296
+ export const BlogPost = Prerender(
297
+ async () => posts.map(p => ({ slug: p.slug })),
298
+ async (ctx) => <PostPage slug={ctx.params.slug} />,
299
+ { concurrency: 4 },
300
+ );
301
+ ```
302
+
303
+ Default is `1` (sequential). Only `Prerender` supports concurrency; `Static` handlers
304
+ always render sequentially.
305
+
306
+ ## Skipping Entries with Skip
307
+
308
+ Throw `Skip` inside a Prerender or Static handler to skip an individual entry
309
+ without failing the build:
310
+
311
+ ```typescript
312
+ import { Prerender, Skip } from "@rangojs/router";
313
+
314
+ export const BlogPost = Prerender(
315
+ async () => [{ slug: "published" }, { slug: "draft" }],
316
+ async (ctx) => {
317
+ if (ctx.params.slug === "draft") {
318
+ throw new Skip("Draft articles are not pre-rendered");
319
+ }
320
+ return <PostPage slug={ctx.params.slug} />;
321
+ },
322
+ );
323
+
324
+ // Wrap with Passthrough to serve skipped params live at runtime
325
+ export const BlogPost = Passthrough(BlogPostDef, async (ctx) => {
326
+ if (ctx.params.slug === "draft") {
327
+ throw new Skip("Draft articles are not pre-rendered");
328
+ }
329
+ return <PostPage slug={ctx.params.slug} />;
330
+ });
331
+ ```
332
+
333
+ Skipped entries are excluded from the build output. With `Passthrough()`,
334
+ the live handler serves skipped params at request time.
335
+
336
+ `Skip` also works in `Static` handlers:
337
+
338
+ ```typescript
339
+ import { Static, Skip } from "@rangojs/router";
340
+
341
+ export const TocSidebar = Static(() => {
342
+ throw new Skip("Not ready for pre-rendering");
343
+ });
344
+ ```
345
+
346
+ ### Error behavior at build time
347
+
348
+ When a render throws a non-`Skip` error, it is **surfaced to the build** — never
349
+ baked into a frozen error page served as a 200 (issue #587). What happens next is
350
+ controlled by `prerender.onError` in your `rango()` options:
351
+
352
+ ```ts
353
+ rango({ prerender: { onError: "warn" } }); // default is "fail"
354
+ ```
355
+
356
+ | Handler outcome | `onError: "fail"` (default) | `onError: "warn"` |
357
+ | --------------------------- | -------------------------------------------- | -------------------------------- |
358
+ | JSX / `null` | Normal prerender entry, log OK | Normal prerender entry, log OK |
359
+ | `return ctx.passthrough()` | Skip entry, log PASS (Passthrough routes) | Skip entry, log PASS |
360
+ | `throw new Skip("reason")` | Skip entry, log SKIP, continue | Skip entry, log SKIP, continue |
361
+ | `throw new Error("reason")` | Log FAIL, stop ALL pre-rendering, fail build | Log WARN, skip the URL, continue |
362
+
363
+ With `"warn"` the errored entry is logged and left un-baked (never served as a baked
364
+ 200 error page). `"warn"` is a build-unblock, not a runtime contract: the route falls
365
+ through to normal resolution — it may render live (its handler is still bundled) or
366
+ 404 (once other baked entries trigger prerender handler eviction), so the outcome
367
+ depends on the rest of the build, and a skipped `Static()` handler's evicted code can
368
+ surface as an error. For DEFINED runtime behavior reach for `Passthrough()` (a live
369
+ fallback) or `throw new Skip()` (an intentional skip — works in the render fn, not
370
+ only `getParams()`); otherwise prefer the default `"fail"`.
371
+
372
+ Both `Skip` and hard errors propagate to the router's `onError` callback with phase
373
+ `"prerender"` or `"static"`.
374
+
375
+ ### Build logs
376
+
377
+ The build produces per-URL timing logs:
378
+
379
+ ```
380
+ [rango] Pre-rendering 12 URL(s) (concurrency: 4)...
381
+ [rango] OK /articles/hello (42ms)
382
+ [rango] PASS /articles/remote-only (5ms) - live fallback
383
+ [rango] SKIP /articles/draft-post (3ms) - Article is a draft
384
+ [rango] Pre-render complete: 11 done, 1 skipped (1204ms total)
385
+
386
+ [rango] Rendering 3 static handler(s)...
387
+ [rango] OK DocsLayout (28ms)
388
+ [rango] SKIP TocSidebar (1ms) - Not ready
389
+ [rango] Static render complete: 2 done, 1 skipped (120ms total)
390
+ ```
391
+
392
+ A `FAIL` line is logged per-URL when a handler throws a non-Skip error (with the
393
+ default `prerender.onError: "fail"`). The error is re-thrown immediately, so no
394
+ summary line is printed — the build stops at the first failure. Under
395
+ `prerender.onError: "warn"` the same case logs a `WARN` line, skips that URL, and
396
+ the build continues.
397
+
398
+ ### Dev mode behavior
399
+
400
+ **Node.js dev server** — `Skip` behaves like a regular runtime error because
401
+ the handler runs live with `ctx.build === false`.
402
+
403
+ **Non-Node runtimes using `/__rsc_prerender`** — `Skip` participates in the
404
+ on-demand prerender path, so build-style skip logic does run for that request.
405
+ The dev prerender endpoint treats it like a prerender miss and the request
406
+ falls back according to normal dev/runtime behavior.
407
+
408
+ ## Per-Param Passthrough with ctx.passthrough()
409
+
410
+ On routes wrapped with `Passthrough()`, the build handler can return
411
+ `ctx.passthrough()` to skip writing a local prerender artifact for a specific
412
+ param set. At runtime, the missing entry falls through to the live handler.
413
+
414
+ ```typescript
415
+ export const BlogPostDef = Prerender(
416
+ async () => [{ slug: "a" }, { slug: "b" }, { slug: "c" }],
417
+ async (ctx) => {
418
+ const post = await getPost(ctx.params.slug);
419
+ if (!post) return ctx.passthrough();
420
+ return <article>{post.content}</article>;
421
+ },
422
+ );
423
+
424
+ export const BlogPost = Passthrough(BlogPostDef, async (ctx) => {
425
+ const post = await getPost(ctx.params.slug);
426
+ return <article>{post.content}</article>;
427
+ });
428
+ ```
429
+
430
+ ### Semantics
431
+
432
+ - JSX or `null` from the build handler produces a normal prerender entry.
433
+ - `ctx.passthrough()` returns a sentinel that signals "no local artifact".
434
+ The build skips the manifest entry for that param set.
435
+ - `ctx.passthrough()` on a route not wrapped with `Passthrough()` throws.
436
+ - `ctx.passthrough()` at runtime (`ctx.build === false`) also throws.
437
+ It is a build-time-only control flow.
438
+ - `getParams()` still enumerates the param set; the build handler decides
439
+ per-param whether to produce an artifact or defer to the live handler.
440
+
441
+ ### Difference from Skip
442
+
443
+ | Mechanism | Effect on build | Runtime behavior |
444
+ | ------------------- | ---------------------- | ------------------------------------------------------ |
445
+ | `throw new Skip()` | Skips entry, logs SKIP | No artifact, no live fallback unless Passthrough route |
446
+ | `ctx.passthrough()` | Skips entry, logs PASS | Always defers to live handler (requires Passthrough) |
447
+
448
+ Use `ctx.passthrough()` when you want the live handler to run at request time
449
+ for specific params. Use `Skip` when you want to exclude params entirely.
450
+
451
+ ### Use case: Remote storage
452
+
453
+ `ctx.passthrough()` enables a pattern where build-time data is stored in a
454
+ remote KV store instead of the local prerender manifest. The build handler
455
+ pre-computes data during `getParams`, pushes it to KV, then calls
456
+ `ctx.passthrough()` so the local build skips the artifact. At runtime,
457
+ the Passthrough live handler reads from KV:
458
+
459
+ ```typescript
460
+ export const ProductDef = Prerender(
461
+ async () => {
462
+ const products = await db.getFeaturedProducts();
463
+ for (const p of products) {
464
+ await kv.put(`product:${p.id}`, await renderProduct(p));
465
+ }
466
+ return products.map(p => ({ id: p.id }));
467
+ },
468
+ async (ctx) => {
469
+ // At build time: skip local artifact, data is in KV
470
+ return ctx.passthrough();
471
+ },
472
+ );
473
+
474
+ export const Product = Passthrough(ProductDef, async (ctx) => {
475
+ // At runtime: read from KV, fall back to DB
476
+ const cached = await kv.get(`product:${ctx.params.id}`);
477
+ if (cached) return cached;
478
+ return <Product data={await ctx.env.DB.getProduct(ctx.params.id)} />;
479
+ });
480
+ ```
481
+
482
+ ### Build logs
483
+
484
+ Passthrough entries are logged distinctly:
485
+
486
+ ```
487
+ [rango] OK /blog/a (42ms)
488
+ [rango] PASS /blog/b (3ms) - live fallback
489
+ [rango] OK /blog/c (38ms)
490
+ ```
491
+
215
492
  ## Edge Cases and Constraints
216
493
 
217
494
  ### Loaders are always live
495
+
218
496
  Loaders on pre-rendered routes run at request time. They are bundled normally
219
497
  and need `cache()` for caching. Do not use build-only APIs in loaders.
220
498
 
221
499
  ### Handle data is frozen
500
+
222
501
  Handle values pushed via `ctx.use()` during pre-rendering are baked into the
223
502
  Flight payload. They do not update at request time.
224
503
 
225
504
  ### Server actions work normally
505
+
226
506
  Actions do not re-render the B segment. The pre-rendered handler output stays
227
- frozen. Loaders are live and can be revalidated by actions. With `passthrough: true`
228
- and `revalidate()`, the handler itself can re-render live.
507
+ frozen. Loaders are live and can be revalidated by actions. With `Passthrough()`
508
+ and `revalidate()`, the live handler can re-render.
229
509
 
230
510
  ### Empty getParams
511
+
231
512
  If `getParams` returns an empty array, no Flight payloads are written. No error.
232
513
 
233
514
  ### Route name is required
515
+
234
516
  Routes using `Prerender` must have a `name` in path options.
235
517
  The name is used as the storage key for Flight payloads.
236
518
 
237
- ### No revalidate without passthrough
238
- Using `revalidate()` with `passthrough: false` produces a build-time warning.
519
+ ### No revalidate without Passthrough
520
+
521
+ Using `revalidate()` without `Passthrough()` produces a build-time warning.
239
522
  The handler is evicted -- there is nothing to re-render.
240
523
 
241
- ### loading() is ignored without passthrough
524
+ ### loading() is ignored without Passthrough
525
+
242
526
  Pre-rendered segments are fully resolved at build time and never suspend.
243
- With `passthrough: true`, `loading()` works for live fallback renders.
527
+ With `Passthrough()`, `loading()` works for live fallback renders.
244
528
 
245
529
  ## Complete Example
246
530
 
247
531
  ```typescript
248
532
  // pages/guides-handler.tsx
249
- import { Prerender } from "@rangojs/router";
533
+ import { Prerender, Passthrough } from "@rangojs/router";
250
534
  import { Link } from "@rangojs/router/client";
251
535
  import { href } from "../router.js";
252
536
 
@@ -255,7 +539,7 @@ const knownGuides: Record<string, string> = {
255
539
  caching: "Caching Guide",
256
540
  };
257
541
 
258
- export const GuidesDetail = Prerender<{ slug: string }>(
542
+ export const GuidesDetailDef = Prerender<{ slug: string }>(
259
543
  async () => Object.keys(knownGuides).map((slug) => ({ slug })),
260
544
  async (ctx) => {
261
545
  const title = knownGuides[ctx.params.slug] ?? `Guide: ${ctx.params.slug}`;
@@ -271,9 +555,23 @@ export const GuidesDetail = Prerender<{ slug: string }>(
271
555
  </div>
272
556
  );
273
557
  },
274
- { passthrough: true },
275
558
  );
276
559
 
560
+ export const GuidesDetail = Passthrough(GuidesDetailDef, async (ctx) => {
561
+ const title = knownGuides[ctx.params.slug] ?? `Guide: ${ctx.params.slug}`;
562
+ return (
563
+ <div>
564
+ <h1>{title}</h1>
565
+ <p>Slug: {ctx.params.slug}</p>
566
+ <nav>
567
+ <Link to={href("guides.detail", { slug: "routing" })}>Routing</Link>
568
+ {" | "}
569
+ <Link to={href("guides.detail", { slug: "dynamic-test" })}>Dynamic</Link>
570
+ </nav>
571
+ </div>
572
+ );
573
+ });
574
+
277
575
  // pages/guides.tsx
278
576
  import { urls } from "@rangojs/router";
279
577
  import { GuidesDetail } from "./guides-handler.js";
@@ -283,23 +581,110 @@ export const guidesPatterns = urls(({ path }) => [
283
581
  ]);
284
582
 
285
583
  // urls.tsx
286
- import { urls, include } from "@rangojs/router";
584
+ import { urls } from "@rangojs/router";
287
585
  import { guidesPatterns } from "./pages/guides.js";
288
586
 
289
- export const urlpatterns = urls(({ path }) => [
587
+ export const urlpatterns = urls(({ path, include }) => [
290
588
  path("/", HomePage, { name: "home" }),
291
589
  include("/guides", guidesPatterns, { name: "guides" }),
292
590
  ]);
293
591
  ```
294
592
 
593
+ ## Interaction with intercept()
594
+
595
+ When a pre-rendered route is also the target of an `intercept()`, the build system
596
+ resolves the intercept handler at build time and stores a combined entry (main
597
+ segments + intercept segments) under an `/i`-suffixed key alongside the main entry:
598
+
599
+ ```
600
+ prerender store keys:
601
+ "blog.post/a1b2c3" -> main segments (full page)
602
+ "blog.post/a1b2c3/i" -> main segments + intercept segments (modal variant)
603
+ ```
604
+
605
+ At runtime, the cache-lookup middleware checks `ctx.isIntercept`:
606
+
607
+ - **Intercept navigation**: looks up `paramHash/i` first. If found, yields
608
+ the combined entry. `handleCacheHitIntercept()` extracts intercept segments
609
+ (filtered by `namespace?.startsWith("intercept:")`) and sets up slots.
610
+ - **Direct navigation**: looks up `paramHash` (no suffix). Standard prerender path.
611
+ - **Intercept miss (no `/i` entry)**: falls through to the normal pipeline so
612
+ intercept-resolution middleware runs live. This handles `when` config conditions
613
+ that prevented pre-rendering.
614
+
615
+ The `when` config selector receives an `InterceptSelectorContext` with `from.pathname`
616
+ which is unknown at build time. All intercepts are pre-rendered unconditionally;
617
+ `when` is evaluated at runtime by the intercept-resolution middleware.
618
+
619
+ ### Example: Pre-rendered route with intercept
620
+
621
+ ```typescript
622
+ // Route handler is pre-rendered at build time
623
+ export const ProductDetail = Prerender(
624
+ async () => [{ slug: "shoes" }, { slug: "jacket" }],
625
+ async (ctx) => <ProductPage slug={ctx.params.slug} />,
626
+ );
627
+
628
+ // urls.tsx
629
+ layout(ShopLayout, () => [
630
+ path("/:slug", ProductDetail, { name: "detail" }, () => [
631
+ loader(ProductLoader),
632
+ ]),
633
+
634
+ // Intercept detail from shop index into a modal.
635
+ // At build time, this is resolved and stored under the /i key.
636
+ intercept(
637
+ "@modal",
638
+ ".detail",
639
+ <ProductModal />,
640
+ { when: ({ from }) => from.pathname === "/shop" },
641
+ () => [loader(ProductLoader)],
642
+ ),
643
+ ])
644
+ ```
645
+
646
+ Both `ProductPage` (main) and `ProductModal` (intercept) are frozen at build time.
647
+ Loaders run fresh at request time for both variants.
648
+
295
649
  ## Trie Flags
296
650
 
297
651
  Pre-rendered routes set flags on the route trie leaf at build time:
298
652
 
299
653
  - `pr: true` -- route has pre-rendered B segment data
300
- - `pt: true` -- passthrough mode (handler available for live fallback)
654
+ - `pt: true` -- route wrapped with `Passthrough()` (live handler available)
301
655
 
302
656
  At runtime, the cache-lookup middleware uses these flags:
657
+
303
658
  - `pr + hit` -- serve pre-rendered Flight payload
304
- - `pr + pt + miss` -- fall through to live handler (handler kept in bundle)
659
+ - `pr + pt + miss` -- fall through to Passthrough live handler
305
660
  - `pr + miss` (no pt) -- fall through (handler stubbed, no live render)
661
+
662
+ ## Contributor Checklist
663
+
664
+ Before changing prerender behavior, run these tests.
665
+
666
+ ### Tests to run
667
+
668
+ ```bash
669
+ # Core prerender e2e (Passthrough, eviction, loaders, sub-use, intercept)
670
+ pnpm --filter @rangojs/router exec playwright test prerender
671
+
672
+ # Prerender-specific unit test
673
+ pnpm --filter @rangojs/router run test:unit -- prerender-passthrough
674
+
675
+ # Semantic matrix (prerender rows cover intercept + ctx propagation)
676
+ pnpm --filter @rangojs/router exec playwright test semantic-matrix
677
+
678
+ # Handler-first (ctx.set/get visibility with prerender handlers)
679
+ pnpm --filter @rangojs/router exec playwright test handler-first
680
+ ```
681
+
682
+ ### Dev-only vs build-parity
683
+
684
+ - Prerender e2e tests run against a real production build by default (the
685
+ fixture builds the test app). Dev-mode prerender behavior is tested via
686
+ `/__rsc_prerender` endpoint tests and node.js dev-server fallback.
687
+ - Log-based assertions (build output lines, debug cache logs) are inherently
688
+ dev/build-only and do not need a production counterpart.
689
+ - Behavioral assertions (rendered content, loader freshness, Passthrough
690
+ fallback, intercept variant selection) must work in the production build.