@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c

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 (392) hide show
  1. package/AGENTS.md +8 -0
  2. package/README.md +126 -16
  3. package/dist/bin/rango.js +319 -95
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +2724 -1053
  6. package/package.json +68 -14
  7. package/skills/api-client/SKILL.md +211 -0
  8. package/skills/breadcrumbs/SKILL.md +64 -2
  9. package/skills/bundle-analysis/SKILL.md +159 -0
  10. package/skills/cache-guide/SKILL.md +224 -32
  11. package/skills/caching/SKILL.md +279 -17
  12. package/skills/composability/SKILL.md +27 -3
  13. package/skills/css/SKILL.md +76 -0
  14. package/skills/debug-manifest/SKILL.md +4 -2
  15. package/skills/document-cache/SKILL.md +78 -55
  16. package/skills/handler-use/SKILL.md +11 -9
  17. package/skills/hooks/SKILL.md +243 -29
  18. package/skills/host-router/SKILL.md +83 -23
  19. package/skills/i18n/SKILL.md +276 -0
  20. package/skills/intercept/SKILL.md +68 -19
  21. package/skills/layout/SKILL.md +13 -9
  22. package/skills/links/SKILL.md +190 -23
  23. package/skills/loader/SKILL.md +235 -9
  24. package/skills/middleware/SKILL.md +18 -10
  25. package/skills/migrate-nextjs/SKILL.md +43 -19
  26. package/skills/migrate-react-router/SKILL.md +8 -2
  27. package/skills/mime-routes/SKILL.md +28 -1
  28. package/skills/observability/SKILL.md +172 -0
  29. package/skills/parallel/SKILL.md +18 -7
  30. package/skills/prerender/SKILL.md +65 -60
  31. package/skills/rango/SKILL.md +251 -24
  32. package/skills/react-compiler/SKILL.md +168 -0
  33. package/skills/response-routes/SKILL.md +115 -48
  34. package/skills/route/SKILL.md +46 -5
  35. package/skills/router-setup/SKILL.md +30 -8
  36. package/skills/scripts/SKILL.md +179 -0
  37. package/skills/server-actions/SKILL.md +775 -0
  38. package/skills/tailwind/SKILL.md +27 -3
  39. package/skills/testing/SKILL.md +130 -0
  40. package/skills/testing/bindings.md +103 -0
  41. package/skills/testing/cache-prerender.md +127 -0
  42. package/skills/testing/client-components.md +124 -0
  43. package/skills/testing/e2e-parity.md +125 -0
  44. package/skills/testing/flight.md +91 -0
  45. package/skills/testing/handles.md +129 -0
  46. package/skills/testing/loader.md +128 -0
  47. package/skills/testing/middleware.md +99 -0
  48. package/skills/testing/render-handler.md +122 -0
  49. package/skills/testing/response-routes.md +95 -0
  50. package/skills/testing/reverse-and-types.md +84 -0
  51. package/skills/testing/server-actions.md +107 -0
  52. package/skills/testing/server-tree.md +128 -0
  53. package/skills/testing/setup.md +123 -0
  54. package/skills/typesafety/SKILL.md +322 -29
  55. package/skills/use-cache/SKILL.md +57 -14
  56. package/skills/view-transitions/SKILL.md +337 -0
  57. package/src/__augment-tests__/augment.ts +81 -0
  58. package/src/__augment-tests__/augmented.check.ts +116 -0
  59. package/src/__internal.ts +0 -65
  60. package/src/browser/action-coordinator.ts +53 -36
  61. package/src/browser/action-fence.ts +47 -0
  62. package/src/browser/app-shell.ts +39 -0
  63. package/src/browser/connection-warmup.ts +134 -0
  64. package/src/browser/cookie-name.ts +140 -0
  65. package/src/browser/event-controller.ts +192 -150
  66. package/src/browser/history-state.ts +21 -0
  67. package/src/browser/index.ts +3 -3
  68. package/src/browser/invalidate-client-cache.ts +52 -0
  69. package/src/browser/navigation-bridge.ts +94 -25
  70. package/src/browser/navigation-client.ts +121 -84
  71. package/src/browser/navigation-store-handle.ts +38 -0
  72. package/src/browser/navigation-store.ts +115 -67
  73. package/src/browser/navigation-transaction.ts +9 -59
  74. package/src/browser/network-error-handler.ts +34 -7
  75. package/src/browser/partial-update.ts +147 -128
  76. package/src/browser/prefetch/cache.ts +107 -56
  77. package/src/browser/prefetch/fetch.ts +204 -34
  78. package/src/browser/prefetch/queue.ts +6 -3
  79. package/src/browser/rango-state.ts +158 -76
  80. package/src/browser/react/Link.tsx +30 -7
  81. package/src/browser/react/NavigationProvider.tsx +283 -118
  82. package/src/browser/react/ScrollRestoration.tsx +10 -6
  83. package/src/browser/react/deferred-handle-resolution.ts +75 -0
  84. package/src/browser/react/filter-segment-order.ts +66 -7
  85. package/src/browser/react/index.ts +0 -48
  86. package/src/browser/react/location-state-shared.ts +178 -8
  87. package/src/browser/react/location-state.ts +39 -14
  88. package/src/browser/react/use-action.ts +6 -15
  89. package/src/browser/react/use-handle.ts +17 -14
  90. package/src/browser/react/use-href.tsx +8 -1
  91. package/src/browser/react/use-link-status.ts +33 -8
  92. package/src/browser/react/use-navigation.ts +10 -5
  93. package/src/browser/react/use-params.ts +11 -11
  94. package/src/browser/react/use-reverse.ts +106 -0
  95. package/src/browser/react/use-router.ts +25 -3
  96. package/src/browser/react/use-search-params.ts +0 -5
  97. package/src/browser/react/use-segments.ts +11 -21
  98. package/src/browser/response-adapter.ts +99 -8
  99. package/src/browser/rsc-router.tsx +91 -24
  100. package/src/browser/scroll-restoration.ts +30 -17
  101. package/src/browser/segment-structure-assert.ts +2 -2
  102. package/src/browser/server-action-bridge.ts +214 -55
  103. package/src/browser/types.ts +80 -9
  104. package/src/browser/validate-redirect-origin.ts +43 -16
  105. package/src/build/collect-fallback-refs.ts +107 -0
  106. package/src/build/generate-manifest.ts +60 -35
  107. package/src/build/generate-route-types.ts +2 -1
  108. package/src/build/index.ts +8 -2
  109. package/src/build/prefix-tree-utils.ts +123 -0
  110. package/src/build/route-trie.ts +117 -14
  111. package/src/build/route-types/ast-route-extraction.ts +15 -8
  112. package/src/build/route-types/codegen.ts +16 -5
  113. package/src/build/route-types/include-resolution.ts +117 -23
  114. package/src/build/route-types/param-extraction.ts +6 -3
  115. package/src/build/route-types/per-module-writer.ts +22 -6
  116. package/src/build/route-types/router-processing.ts +55 -28
  117. package/src/build/route-types/scan-filter.ts +1 -1
  118. package/src/build/route-types/source-scan.ts +216 -0
  119. package/src/build/runtime-discovery.ts +9 -20
  120. package/src/cache/cache-error.ts +104 -0
  121. package/src/cache/cache-key-utils.ts +29 -13
  122. package/src/cache/cache-policy.ts +108 -34
  123. package/src/cache/cache-runtime.ts +224 -41
  124. package/src/cache/cache-scope.ts +188 -82
  125. package/src/cache/cache-tag.ts +103 -0
  126. package/src/cache/cf/cf-base64.ts +33 -0
  127. package/src/cache/cf/cf-cache-constants.ts +127 -0
  128. package/src/cache/cf/cf-cache-store.ts +1989 -378
  129. package/src/cache/cf/cf-cache-types.ts +349 -0
  130. package/src/cache/cf/cf-kv-utils.ts +46 -0
  131. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  132. package/src/cache/cf/index.ts +6 -16
  133. package/src/cache/document-cache.ts +89 -21
  134. package/src/cache/handle-snapshot.ts +70 -0
  135. package/src/cache/index.ts +10 -20
  136. package/src/cache/memory-segment-store.ts +136 -37
  137. package/src/cache/profile-registry.ts +46 -31
  138. package/src/cache/read-through-swr.ts +56 -12
  139. package/src/cache/segment-codec.ts +9 -17
  140. package/src/cache/tag-invalidation.ts +230 -0
  141. package/src/cache/types.ts +37 -100
  142. package/src/client.rsc.tsx +44 -21
  143. package/src/client.tsx +36 -61
  144. package/src/cloudflare/index.ts +11 -0
  145. package/src/cloudflare/tracing.ts +109 -0
  146. package/src/component-utils.ts +19 -0
  147. package/src/components/DefaultDocument.tsx +8 -2
  148. package/src/context-var.ts +18 -6
  149. package/src/decode-loader-results.ts +52 -0
  150. package/src/defer.ts +196 -0
  151. package/src/deps/ssr.ts +0 -1
  152. package/src/encode-kv.ts +49 -0
  153. package/src/errors.ts +30 -4
  154. package/src/escape-script.ts +52 -0
  155. package/src/handle.ts +31 -23
  156. package/src/handles/MetaTags.tsx +62 -19
  157. package/src/handles/Scripts.tsx +183 -0
  158. package/src/handles/breadcrumbs.ts +37 -8
  159. package/src/handles/is-thenable.ts +19 -0
  160. package/src/handles/meta.ts +51 -40
  161. package/src/handles/script.ts +244 -0
  162. package/src/host/cookie-handler.ts +9 -60
  163. package/src/host/errors.ts +0 -24
  164. package/src/host/index.ts +8 -2
  165. package/src/host/pattern-matcher.ts +23 -52
  166. package/src/host/router.ts +107 -99
  167. package/src/host/testing.ts +40 -27
  168. package/src/host/types.ts +37 -4
  169. package/src/host/utils.ts +1 -1
  170. package/src/href-client.ts +137 -22
  171. package/src/index.rsc.ts +96 -12
  172. package/src/index.ts +94 -14
  173. package/src/internal-debug.ts +11 -10
  174. package/src/loader-store.ts +500 -0
  175. package/src/loader.rsc.ts +20 -13
  176. package/src/loader.ts +12 -11
  177. package/src/missing-id-error.ts +68 -0
  178. package/src/outlet-context.ts +1 -1
  179. package/src/outlet-provider.tsx +1 -5
  180. package/src/prerender/param-hash.ts +16 -16
  181. package/src/prerender/store.ts +32 -37
  182. package/src/prerender.ts +61 -6
  183. package/src/redirect-origin.ts +100 -0
  184. package/src/regex-escape.ts +8 -0
  185. package/src/render-error-thrower.tsx +20 -0
  186. package/src/response-utils.ts +34 -0
  187. package/src/reverse.ts +65 -40
  188. package/src/root-error-boundary.tsx +1 -19
  189. package/src/route-content-wrapper.tsx +19 -77
  190. package/src/route-definition/dsl-helpers.ts +304 -309
  191. package/src/route-definition/helper-factories.ts +28 -140
  192. package/src/route-definition/helpers-types.ts +82 -55
  193. package/src/route-definition/index.ts +1 -2
  194. package/src/route-definition/redirect.ts +44 -11
  195. package/src/route-definition/resolve-handler-use.ts +12 -1
  196. package/src/route-definition/use-item-types.ts +29 -0
  197. package/src/route-map-builder.ts +0 -16
  198. package/src/route-types.ts +19 -46
  199. package/src/router/basename.ts +14 -0
  200. package/src/router/content-negotiation.ts +73 -25
  201. package/src/router/error-handling.ts +45 -18
  202. package/src/router/find-match.ts +44 -23
  203. package/src/router/handler-context.ts +27 -43
  204. package/src/router/instrument.ts +350 -0
  205. package/src/router/intercept-resolution.ts +39 -20
  206. package/src/router/lazy-includes.ts +10 -47
  207. package/src/router/loader-resolution.ts +155 -72
  208. package/src/router/logging.ts +0 -6
  209. package/src/router/manifest.ts +18 -29
  210. package/src/router/match-api.ts +9 -24
  211. package/src/router/match-context.ts +0 -22
  212. package/src/router/match-handlers.ts +58 -58
  213. package/src/router/match-middleware/background-revalidation.ts +40 -24
  214. package/src/router/match-middleware/cache-lookup.ts +159 -285
  215. package/src/router/match-middleware/cache-store.ts +64 -52
  216. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  217. package/src/router/match-middleware/segment-resolution.ts +0 -22
  218. package/src/router/match-pipelines.ts +1 -42
  219. package/src/router/match-result.ts +44 -74
  220. package/src/router/metrics.ts +0 -34
  221. package/src/router/middleware-types.ts +7 -134
  222. package/src/router/middleware.ts +247 -166
  223. package/src/router/navigation-snapshot.ts +0 -51
  224. package/src/router/params-util.ts +23 -0
  225. package/src/router/pattern-matching.ts +85 -94
  226. package/src/router/prefetch-cache-ttl.ts +51 -0
  227. package/src/router/prerender-match.ts +104 -65
  228. package/src/router/preview-match.ts +3 -1
  229. package/src/router/request-classification.ts +28 -62
  230. package/src/router/revalidation.ts +123 -73
  231. package/src/router/route-snapshot.ts +0 -1
  232. package/src/router/router-context.ts +3 -28
  233. package/src/router/router-interfaces.ts +83 -35
  234. package/src/router/router-options.ts +136 -5
  235. package/src/router/router-registry.ts +2 -5
  236. package/src/router/segment-resolution/fresh.ts +97 -84
  237. package/src/router/segment-resolution/helpers.ts +86 -6
  238. package/src/router/segment-resolution/loader-cache.ts +76 -39
  239. package/src/router/segment-resolution/revalidation.ts +272 -320
  240. package/src/router/segment-resolution/static-store.ts +19 -5
  241. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  242. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  243. package/src/router/segment-resolution.ts +5 -1
  244. package/src/router/segment-wrappers.ts +6 -5
  245. package/src/router/state-cookie-name.ts +33 -0
  246. package/src/router/substitute-pattern-params.ts +56 -0
  247. package/src/router/telemetry-otel.ts +161 -199
  248. package/src/router/telemetry.ts +96 -19
  249. package/src/router/timeout.ts +0 -20
  250. package/src/router/tracing.ts +206 -0
  251. package/src/router/trie-matching.ts +162 -64
  252. package/src/router/types.ts +9 -63
  253. package/src/router/url-params.ts +0 -5
  254. package/src/router.ts +110 -55
  255. package/src/rsc/handler-context.ts +3 -2
  256. package/src/rsc/handler.ts +264 -220
  257. package/src/rsc/helpers.ts +100 -6
  258. package/src/rsc/index.ts +2 -5
  259. package/src/rsc/json-route-result.ts +38 -0
  260. package/src/rsc/loader-fetch.ts +114 -38
  261. package/src/rsc/manifest-init.ts +28 -41
  262. package/src/rsc/origin-guard.ts +39 -25
  263. package/src/rsc/progressive-enhancement.ts +117 -11
  264. package/src/rsc/redirect-guard.ts +99 -0
  265. package/src/rsc/response-cache-serve.ts +238 -0
  266. package/src/rsc/response-error.ts +79 -12
  267. package/src/rsc/response-route-handler.ts +88 -188
  268. package/src/rsc/rsc-rendering.ts +98 -76
  269. package/src/rsc/runtime-warnings.ts +23 -10
  270. package/src/rsc/server-action.ts +281 -117
  271. package/src/rsc/ssr-setup.ts +16 -0
  272. package/src/rsc/transition-gate.ts +89 -0
  273. package/src/rsc/types.ts +23 -5
  274. package/src/runtime-env.ts +18 -0
  275. package/src/search-params.ts +35 -30
  276. package/src/segment-loader-promise.ts +31 -4
  277. package/src/segment-system.tsx +254 -143
  278. package/src/serialize.ts +243 -0
  279. package/src/server/context.ts +163 -51
  280. package/src/server/cookie-parse.ts +32 -0
  281. package/src/server/cookie-store.ts +80 -5
  282. package/src/server/handle-store.ts +21 -38
  283. package/src/server/loader-registry.ts +33 -42
  284. package/src/server/request-context.ts +287 -178
  285. package/src/ssr/index.tsx +21 -16
  286. package/src/static-handler.ts +10 -13
  287. package/src/testing/cache-status.ts +162 -0
  288. package/src/testing/collect-handle.ts +40 -0
  289. package/src/testing/dispatch.ts +701 -0
  290. package/src/testing/dom.entry.ts +22 -0
  291. package/src/testing/e2e/fixture.ts +188 -0
  292. package/src/testing/e2e/index.ts +128 -0
  293. package/src/testing/e2e/matchers.ts +35 -0
  294. package/src/testing/e2e/page-helpers.ts +272 -0
  295. package/src/testing/e2e/parity.ts +387 -0
  296. package/src/testing/e2e/server.ts +195 -0
  297. package/src/testing/flight-matchers.ts +97 -0
  298. package/src/testing/flight-normalize.ts +11 -0
  299. package/src/testing/flight-runtime.d.ts +57 -0
  300. package/src/testing/flight-tree.ts +682 -0
  301. package/src/testing/flight.entry.ts +52 -0
  302. package/src/testing/flight.ts +257 -0
  303. package/src/testing/generated-routes.ts +183 -0
  304. package/src/testing/index.ts +105 -0
  305. package/src/testing/internal/context.ts +371 -0
  306. package/src/testing/internal/flight-client-globals.ts +30 -0
  307. package/src/testing/internal/seed-vars.ts +54 -0
  308. package/src/testing/render-handler.ts +357 -0
  309. package/src/testing/render-route.tsx +581 -0
  310. package/src/testing/run-loader.ts +385 -0
  311. package/src/testing/run-middleware.ts +205 -0
  312. package/src/testing/run-transition-when.ts +164 -0
  313. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  314. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  315. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  316. package/src/testing/vitest-stubs/version.ts +5 -0
  317. package/src/testing/vitest.ts +305 -0
  318. package/src/theme/ThemeProvider.tsx +20 -58
  319. package/src/theme/ThemeScript.tsx +7 -9
  320. package/src/theme/constants.ts +52 -13
  321. package/src/theme/index.ts +0 -7
  322. package/src/theme/theme-context.ts +1 -5
  323. package/src/theme/theme-script.ts +22 -21
  324. package/src/theme/use-theme.ts +0 -3
  325. package/src/types/boundaries.ts +0 -35
  326. package/src/types/cache-types.ts +13 -4
  327. package/src/types/error-types.ts +30 -90
  328. package/src/types/global-namespace.ts +54 -41
  329. package/src/types/handler-context.ts +110 -62
  330. package/src/types/index.ts +3 -10
  331. package/src/types/loader-types.ts +11 -9
  332. package/src/types/request-scope.ts +112 -0
  333. package/src/types/route-config.ts +6 -50
  334. package/src/types/route-entry.ts +0 -6
  335. package/src/types/segments.ts +135 -14
  336. package/src/urls/include-helper.ts +9 -56
  337. package/src/urls/index.ts +1 -11
  338. package/src/urls/path-helper-types.ts +29 -12
  339. package/src/urls/path-helper.ts +17 -106
  340. package/src/urls/pattern-types.ts +36 -19
  341. package/src/urls/response-types.ts +22 -29
  342. package/src/urls/type-extraction.ts +58 -139
  343. package/src/urls/urls-function.ts +1 -19
  344. package/src/use-loader.tsx +292 -107
  345. package/src/vite/debug.ts +185 -0
  346. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  347. package/src/vite/discovery/discover-routers.ts +126 -85
  348. package/src/vite/discovery/discovery-errors.ts +194 -0
  349. package/src/vite/discovery/gate-state.ts +171 -0
  350. package/src/vite/discovery/prerender-collection.ts +96 -68
  351. package/src/vite/discovery/route-types-writer.ts +40 -84
  352. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  353. package/src/vite/discovery/state.ts +44 -0
  354. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  355. package/src/vite/index.ts +2 -0
  356. package/src/vite/inject-client-debug.ts +36 -0
  357. package/src/vite/plugin-types.ts +126 -8
  358. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  359. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  360. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  361. package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
  362. package/src/vite/plugins/expose-action-id.ts +48 -95
  363. package/src/vite/plugins/expose-id-utils.ts +88 -55
  364. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  365. package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
  366. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  367. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  368. package/src/vite/plugins/expose-internal-ids.ts +505 -486
  369. package/src/vite/plugins/performance-tracks.ts +26 -25
  370. package/src/vite/plugins/refresh-cmd.ts +1 -1
  371. package/src/vite/plugins/use-cache-transform.ts +73 -83
  372. package/src/vite/plugins/version-injector.ts +40 -29
  373. package/src/vite/plugins/version-plugin.ts +37 -40
  374. package/src/vite/plugins/virtual-entries.ts +39 -25
  375. package/src/vite/rango.ts +109 -118
  376. package/src/vite/router-discovery.ts +718 -119
  377. package/src/vite/utils/ast-handler-extract.ts +26 -35
  378. package/src/vite/utils/banner.ts +1 -1
  379. package/src/vite/utils/bundle-analysis.ts +10 -15
  380. package/src/vite/utils/client-chunks.ts +184 -0
  381. package/src/vite/utils/directive-prologue.ts +40 -0
  382. package/src/vite/utils/forward-user-plugins.ts +171 -0
  383. package/src/vite/utils/manifest-utils.ts +4 -59
  384. package/src/vite/utils/package-resolution.ts +20 -52
  385. package/src/vite/utils/prerender-utils.ts +54 -39
  386. package/src/vite/utils/shared-utils.ts +90 -41
  387. package/src/browser/action-response-classifier.ts +0 -99
  388. package/src/browser/react/use-client-cache.ts +0 -58
  389. package/src/browser/shallow.ts +0 -40
  390. package/src/handles/index.ts +0 -7
  391. package/src/network-error-thrower.tsx +0 -23
  392. package/src/router/middleware-cookies.ts +0 -55
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: observability
3
+ description: Debug Rango request performance with debugPerformance, Server-Timing, structured telemetry, and tracing
4
+ argument-hint:
5
+ ---
6
+
7
+ # Observability
8
+
9
+ Use this when you need to understand request latency, cache decisions,
10
+ revalidation behavior, loader overlap, or production traces.
11
+
12
+ Rango exposes two complementary observability surfaces:
13
+
14
+ 1. **Performance timeline** (`debugPerformance`) — per-request waterfall for
15
+ local or targeted debugging. It prints to the console and emits
16
+ `Server-Timing`.
17
+ 2. **Structured telemetry** (`telemetry`) — lifecycle events sent to a pluggable
18
+ sink for production monitoring, OpenTelemetry, or custom metrics.
19
+
20
+ The essentials are below. The exported `TelemetryEvent` union type
21
+ (`import type { TelemetryEvent } from "@rangojs/router"`) is the full event
22
+ contract — every event kind and its fields are typed there.
23
+
24
+ ## Performance timeline
25
+
26
+ Enable globally while debugging:
27
+
28
+ ```typescript
29
+ import { createRouter } from "@rangojs/router";
30
+
31
+ const router = createRouter({
32
+ document: Document,
33
+ urls: urlpatterns,
34
+ debugPerformance: true,
35
+ });
36
+ ```
37
+
38
+ Or enable for selected requests from middleware:
39
+
40
+ ```typescript
41
+ middleware(async (ctx, next) => {
42
+ if (ctx.url.searchParams.has("debug")) {
43
+ ctx.debugPerformance();
44
+ }
45
+ await next();
46
+ });
47
+ ```
48
+
49
+ Call `ctx.debugPerformance()` before `await next()`. The request then prints a
50
+ shared-axis waterfall and adds a `Server-Timing` header.
51
+
52
+ Read the timeline as intervals:
53
+
54
+ - `handler:total` is the whole router request.
55
+ - `render:total` / `ssr-render-html` show the render pass.
56
+ - `loader:*` rows should overlap render work. If a loader starts only after the
57
+ render bar, it is serialized latency.
58
+ - Cache, route matching, middleware pre/post, RSC serialization, and SSR phases
59
+ appear as separate spans, so the slow phase is visible without guessing.
60
+
61
+ ## Structured telemetry
62
+
63
+ Use telemetry when you want durable production events rather than a one-request
64
+ debug waterfall.
65
+
66
+ ```typescript
67
+ import { createRouter, createConsoleSink } from "@rangojs/router";
68
+
69
+ const router = createRouter({
70
+ document: Document,
71
+ urls: urlpatterns,
72
+ telemetry: createConsoleSink(),
73
+ });
74
+ ```
75
+
76
+ For OpenTelemetry — phase spans come from the `tracing` slot
77
+ (`createOTelTracing`), discrete-fact spans from the `telemetry` sink
78
+ (`createOTelSink`):
79
+
80
+ ```typescript
81
+ import {
82
+ createRouter,
83
+ createOTelTracing,
84
+ createOTelSink,
85
+ } from "@rangojs/router";
86
+ import { trace } from "@opentelemetry/api";
87
+
88
+ const tracer = trace.getTracer("my-app");
89
+
90
+ const router = createRouter({
91
+ document: Document,
92
+ urls: urlpatterns,
93
+ tracing: createOTelTracing(tracer), // request/loader/render/… phase spans
94
+ telemetry: createOTelSink(tracer), // handler errors, cache decisions, …
95
+ });
96
+ ```
97
+
98
+ On **Cloudflare Workers**, use `createCloudflareTracing` for the `tracing` slot
99
+ instead — it emits the same phases as native Cloudflare custom spans (in the
100
+ Workers trace waterfall, next to the automatic KV/D1/fetch spans), with no
101
+ `@opentelemetry/api` dependency:
102
+
103
+ ```typescript
104
+ import { createRouter } from "@rangojs/router";
105
+ import { createCloudflareTracing } from "@rangojs/router/cloudflare";
106
+
107
+ const router = createRouter({
108
+ document: Document,
109
+ urls: urlpatterns,
110
+ tracing: createCloudflareTracing(), // all phases on by default
111
+ // tracing: createCloudflareTracing({ spans: { ssr: false } }), // toggle phases
112
+ });
113
+ ```
114
+
115
+ Both factories return a `RouterTracingConfig` for the same `tracing` slot;
116
+ `telemetry` stays independent (events only, no phase spans). Phase spans:
117
+ `rango.request`, `rango.middleware`, `rango.action`, `rango.loader`,
118
+ `rango.render`, `rango.ssr` — the same phases the `debugPerformance` timeline
119
+ shows, co-emitted from one site. Off-platform (no Cloudflare tracing destination
120
+ / no OTel SDK) every span call is a transparent pass-through, so the request
121
+ behaves as if tracing were off.
122
+
123
+ Custom sinks implement `emit(event)`:
124
+
125
+ ```typescript
126
+ import { createRouter } from "@rangojs/router";
127
+
128
+ const router = createRouter({
129
+ document: Document,
130
+ urls: urlpatterns,
131
+ telemetry: {
132
+ emit(event) {
133
+ myMetrics.record(event);
134
+ },
135
+ },
136
+ });
137
+ ```
138
+
139
+ Events include `request.start/end/error`, `loader.start/end/error`,
140
+ `handler.error`, `cache.decision`, `revalidation.decision`, `request.timeout`,
141
+ and `request.origin-rejected`.
142
+
143
+ ## Debugging revalidation and stale data
144
+
145
+ When stale UI or unexpected partial renders are the question, use all three
146
+ layers together:
147
+
148
+ ```typescript
149
+ import { createConsoleSink, createRouter } from "@rangojs/router";
150
+
151
+ const router = createRouter({
152
+ document: Document,
153
+ urls: urlpatterns,
154
+ debugPerformance: true,
155
+ telemetry: createConsoleSink(),
156
+ });
157
+ ```
158
+
159
+ Then inspect:
160
+
161
+ - `revalidation.decision` telemetry to see which segment re-ran or skipped.
162
+ - cache spans / `cache.decision` events to see hit, miss, stale, and background
163
+ revalidation behavior.
164
+ - loader spans to confirm live loaders overlap the render rather than blocking
165
+ first paint.
166
+ - the `Server-Timing` header to compare local logs with browser-network timing.
167
+
168
+ ## Zero-overhead defaults
169
+
170
+ `debugPerformance` is off by default, and `telemetry` emits nothing unless a sink
171
+ is configured. Per-request `ctx.debugPerformance()` lets you turn on the
172
+ waterfall only for the route, user, or query param you are investigating.
@@ -8,9 +8,6 @@ argument-hint: [@slot-name]
8
8
 
9
9
  Parallel routes render multiple components simultaneously in named slots.
10
10
 
11
- Canonical semantics reference:
12
- [docs/execution-model.md](../../docs/internal/execution-model.md)
13
-
14
11
  ## Basic Parallel Routes
15
12
 
16
13
  ```typescript
@@ -237,6 +234,8 @@ A slot's `loading()` (whether from `handler.use` or explicit) makes that slot an
237
234
 
238
235
  The `parallel` mount site has the narrowest allow-list for `handler.use` items — slots cannot bring their own middleware or layout, only `revalidate`, `loader`, `loading`, `errorBoundary`, `notFoundBoundary`, and `transition`. See [skills/handler-use](../handler-use/SKILL.md) for the full table and merge rules.
239
236
 
237
+ `transition` is allowed in the slot allow-list, but slot-level rendering does **not** currently apply a `<ViewTransition>` wrapper — only the layout/route wraps take effect at render time. For a modal-only morph today, use an element-level React `<ViewTransition>` inside the slot's component. The reverse direction is the useful guarantee: a layout-level `transition()` fires when the layout's default outlet content changes but **not** when a `<ParallelOutlet />` mounts new content (modal opens are not subtree updates of the layout VT). See [skills/view-transitions](../view-transitions/SKILL.md) for the wrap rules and the intercept caveat.
238
+
240
239
  ### Two scopes for explicit `use`: shared (broadcast) and slot-local
241
240
 
242
241
  `parallel({...slots}, () => [...use])` runs the shared `use()` callback **once per slot** ([dsl-helpers.ts](../../src/route-definition/dsl-helpers.ts)) — items in that callback land on every slot's entry. That's the right behavior for the items the parallel allow-list permits and that accumulate (`loader`, `revalidate`, `errorBoundary`, `notFoundBoundary`, `transition`). (Slots cannot bring `middleware` or `layout` — see the allowed-types note above.)
@@ -331,6 +330,8 @@ parallel({
331
330
  Control when parallel routes revalidate:
332
331
 
333
332
  ```typescript
333
+ import * as CartActions from "./actions/cart";
334
+
334
335
  parallel(
335
336
  {
336
337
  "@cart": () => <CartSummary />,
@@ -338,7 +339,7 @@ parallel(
338
339
  () => [
339
340
  loader(CartLoader),
340
341
  // Revalidate when cart actions occur
341
- revalidate(({ actionId }) => actionId?.includes("Cart") ?? false),
342
+ revalidate((ctx) => ctx.isAction(CartActions) || undefined),
342
343
  ]
343
344
  )
344
345
  ```
@@ -347,6 +348,13 @@ Revalidating only the parallel does not re-run outer handlers/layouts.
347
348
  If the slot reads `ctx.get()` data established above it, opt the outer
348
349
  segment into revalidation as well.
349
350
 
351
+ A `revalidate()` callback may return a hard `boolean`, a soft
352
+ `{ defaultShouldRevalidate }` object, or nothing (`void` / `null` /
353
+ `undefined`) to defer to the next revalidator. See
354
+ [loader/SKILL.md#revalidate-return-shapes](../loader/SKILL.md#revalidate-return-shapes)
355
+ for the full contract — it's the same across `loader()`, `path()`,
356
+ `layout()`, `parallel()`, and `intercept()`.
357
+
350
358
  ### Revalidation Contracts for Parallel Dependencies
351
359
 
352
360
  Prefer named revalidation contracts shared by both the upstream producer and
@@ -354,8 +362,10 @@ the parallel consumer:
354
362
 
355
363
  ```typescript
356
364
  // revalidation-contracts.ts
357
- export const revalidateCartData = ({ actionId }) =>
358
- actionId?.includes("src/actions/cart.ts#") ?? false;
365
+ import * as CartActions from "./actions/cart";
366
+
367
+ export const revalidateCartData = (ctx) =>
368
+ ctx.isAction(CartActions) || undefined;
359
369
 
360
370
  layout(CartLayout, () => [
361
371
  revalidate(revalidateCartData), // producer reruns
@@ -423,6 +433,7 @@ function MyLayout() {
423
433
  ```typescript
424
434
  import { urls } from "@rangojs/router";
425
435
  import { Outlet, ParallelOutlet } from "@rangojs/router/client";
436
+ import * as CartActions from "./actions/cart";
426
437
 
427
438
  function ShopLayout() {
428
439
  return (
@@ -473,7 +484,7 @@ export const shopPatterns = urls(({
473
484
  () => [
474
485
  loader(CartLoader),
475
486
  loading(<CartSkeleton />),
476
- revalidate(({ actionId }) => actionId?.includes("Cart") ?? false),
487
+ revalidate((ctx) => ctx.isAction(CartActions) || undefined),
477
488
  ]
478
489
  ),
479
490
 
@@ -11,9 +11,6 @@ deserialization path, same segment system. The worker handles every request --
11
11
  there are NO static .html or .rsc files served from assets. The worker reads
12
12
  pre-computed Flight payloads instead of executing handler code.
13
13
 
14
- Canonical semantics reference:
15
- [docs/execution-model.md](../../docs/internal/execution-model.md)
16
-
17
14
  ## API: Prerender
18
15
 
19
16
  ### Static Route (no params)
@@ -122,6 +119,8 @@ interface BuildContext<TParams> {
122
119
  use: <T>(handle: Handle<T>) => (data: T) => void; // Push handle data
123
120
  url: URL; // Synthetic URL from pattern + params
124
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)
125
124
  set(key: string, value: any): void; // Set context variable (string key)
126
125
  set<T>(contextVar: ContextVar<T>, value: T): void; // Set typed context variable
127
126
  get(key: string): any; // Read context variable (string key)
@@ -244,16 +243,16 @@ path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
244
243
 
245
244
  ## Interaction with DSL Items
246
245
 
247
- | DSL item | Behavior with Prerender |
248
- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
249
- | `loader()` | Live at runtime, bundled normally. Use `cache()` for caching. |
250
- | `revalidate()` | Not allowed without Passthrough. Allowed with Passthrough. |
251
- | `cache()` | Orthogonal -- use on parent layouts and loaders. |
252
- | `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live. |
253
- | `parallel()` | Parallel slots inside path are pre-rendered. |
254
- | `middleware()` | Skipped during pre-render (no request). Runs at request time for loaders. |
255
- | `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough. |
256
- | `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when()` conditions are skipped at build time (all intercepts are pre-rendered unconditionally). |
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). |
257
256
 
258
257
  When Passthrough revalidation is enabled, remember that revalidation is
259
258
  still partial: opting a child segment into revalidation does not
@@ -346,14 +345,31 @@ export const TocSidebar = Static(() => {
346
345
 
347
346
  ### Error behavior at build time
348
347
 
349
- | Handler outcome | Effect |
350
- | --------------------------- | ----------------------------------------------------- |
351
- | JSX / `null` | Normal prerender entry, log OK |
352
- | `return ctx.passthrough()` | Skip entry, log PASS, continue (Passthrough routes) |
353
- | `throw new Skip("reason")` | Skip entry, log SKIP, continue with remaining entries |
354
- | `throw new Error("reason")` | Log FAIL, stop ALL pre-rendering, fail the build |
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
355
 
356
- Both error types propagate to the router's `onError` callback with phase
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
357
373
  `"prerender"` or `"static"`.
358
374
 
359
375
  ### Build logs
@@ -361,21 +377,23 @@ Both error types propagate to the router's `onError` callback with phase
361
377
  The build produces per-URL timing logs:
362
378
 
363
379
  ```
364
- [rsc-router] Pre-rendering 12 URL(s) (concurrency: 4)...
365
- [rsc-router] OK /articles/hello (42ms)
366
- [rsc-router] PASS /articles/remote-only (5ms) - live fallback
367
- [rsc-router] SKIP /articles/draft-post (3ms) - Article is a draft
368
- [rsc-router] Pre-render complete: 11 done, 1 skipped (1204ms total)
369
-
370
- [rsc-router] Rendering 3 static handler(s)...
371
- [rsc-router] OK DocsLayout (28ms)
372
- [rsc-router] SKIP TocSidebar (1ms) - Not ready
373
- [rsc-router] Static render complete: 2 done, 1 skipped (120ms total)
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)
374
390
  ```
375
391
 
376
- A `FAIL` line is logged per-URL when a handler throws a non-Skip error. The
377
- error is re-thrown immediately, so no summary line is printed — the build
378
- stops at the first failure.
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.
379
397
 
380
398
  ### Dev mode behavior
381
399
 
@@ -466,9 +484,9 @@ export const Product = Passthrough(ProductDef, async (ctx) => {
466
484
  Passthrough entries are logged distinctly:
467
485
 
468
486
  ```
469
- [rsc-router] OK /blog/a (42ms)
470
- [rsc-router] PASS /blog/b (3ms) - live fallback
471
- [rsc-router] OK /blog/c (38ms)
487
+ [rango] OK /blog/a (42ms)
488
+ [rango] PASS /blog/b (3ms) - live fallback
489
+ [rango] OK /blog/c (38ms)
472
490
  ```
473
491
 
474
492
  ## Edge Cases and Constraints
@@ -591,12 +609,12 @@ At runtime, the cache-lookup middleware checks `ctx.isIntercept`:
591
609
  (filtered by `namespace?.startsWith("intercept:")`) and sets up slots.
592
610
  - **Direct navigation**: looks up `paramHash` (no suffix). Standard prerender path.
593
611
  - **Intercept miss (no `/i` entry)**: falls through to the normal pipeline so
594
- intercept-resolution middleware runs live. This handles `when()` conditions
612
+ intercept-resolution middleware runs live. This handles `when` config conditions
595
613
  that prevented pre-rendering.
596
614
 
597
- The `when()` callback receives an `InterceptSelectorContext` with `from.pathname`
615
+ The `when` config selector receives an `InterceptSelectorContext` with `from.pathname`
598
616
  which is unknown at build time. All intercepts are pre-rendered unconditionally;
599
- `when()` is evaluated at runtime by the intercept-resolution middleware.
617
+ `when` is evaluated at runtime by the intercept-resolution middleware.
600
618
 
601
619
  ### Example: Pre-rendered route with intercept
602
620
 
@@ -615,10 +633,13 @@ layout(ShopLayout, () => [
615
633
 
616
634
  // Intercept detail from shop index into a modal.
617
635
  // At build time, this is resolved and stored under the /i key.
618
- intercept("@modal", ".detail", <ProductModal />, () => [
619
- when(({ from }) => from.pathname === "/shop"),
620
- loader(ProductLoader),
621
- ]),
636
+ intercept(
637
+ "@modal",
638
+ ".detail",
639
+ <ProductModal />,
640
+ { when: ({ from }) => from.pathname === "/shop" },
641
+ () => [loader(ProductLoader)],
642
+ ),
622
643
  ])
623
644
  ```
624
645
 
@@ -640,16 +661,7 @@ At runtime, the cache-lookup middleware uses these flags:
640
661
 
641
662
  ## Contributor Checklist
642
663
 
643
- Before changing prerender behavior, read these docs and run these tests.
644
-
645
- ### Docs to re-read
646
-
647
- - [Prerender API design](../../docs/prerender-api-design.md) -- canonical
648
- architecture: build-time flow, runtime flow, storage, Passthrough, intercept
649
- - [Execution model](../../docs/internal/execution-model.md) -- handler-first
650
- ordering, middleware scope, context visibility rules
651
- - [Semantic change checklist](../../docs/internal/semantic-change-checklist.md)
652
- -- gate for any change to execution semantics
664
+ Before changing prerender behavior, run these tests.
653
665
 
654
666
  ### Tests to run
655
667
 
@@ -676,10 +688,3 @@ pnpm --filter @rangojs/router exec playwright test handler-first
676
688
  dev/build-only and do not need a production counterpart.
677
689
  - Behavioral assertions (rendered content, loader freshness, Passthrough
678
690
  fallback, intercept variant selection) must work in the production build.
679
-
680
- ## Maintenance References
681
-
682
- - [Stability next steps plan](../../docs/internal/stability-next-steps-plan.md)
683
- -- completed parity and cleanup pass (reference for decisions made)
684
- - [Test quality baseline](../../docs/internal/test-quality-baseline.md) --
685
- measured test inventory, sleep debt, production coverage gaps