@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
@@ -12,10 +12,6 @@
12
12
  import type { ResolvedSegment } from "../types.js";
13
13
  import type { RequestContext } from "../server/request-context.js";
14
14
 
15
- // ============================================================================
16
- // Segment Cache Store (low-level storage interface)
17
- // ============================================================================
18
-
19
15
  /**
20
16
  * Result from cache get() including data and revalidation status
21
17
  */
@@ -116,12 +112,6 @@ export interface SegmentCacheStore<TEnv = unknown> {
116
112
  */
117
113
  clear?(): Promise<void>;
118
114
 
119
- // ============================================================================
120
- // Document Cache Methods (optional)
121
- // ============================================================================
122
- // These methods are for caching full HTTP responses (document-level caching).
123
- // Stores that support response caching should implement these methods.
124
-
125
115
  /**
126
116
  * Get a cached Response by key.
127
117
  * Returns the response and whether it should be revalidated (SWR).
@@ -136,20 +126,16 @@ export interface SegmentCacheStore<TEnv = unknown> {
136
126
  * @param response - Response to cache (will be cloned)
137
127
  * @param ttl - Time-to-live in seconds
138
128
  * @param swr - Optional stale-while-revalidate window in seconds
129
+ * @param tags - Optional cache tags for invalidation
139
130
  */
140
131
  putResponse?(
141
132
  key: string,
142
133
  response: Response,
143
134
  ttl: number,
144
135
  swr?: number,
136
+ tags?: string[],
145
137
  ): Promise<void>;
146
138
 
147
- // ============================================================================
148
- // Function Cache Methods (optional, for "use cache" directive)
149
- // ============================================================================
150
- // These methods cache individual function/component return values.
151
- // Stores that support "use cache" should implement these methods.
152
-
153
139
  /**
154
140
  * Get a cached function result by key.
155
141
  * Returns the serialized value, optional handle data, and staleness flag.
@@ -167,6 +153,16 @@ export interface SegmentCacheStore<TEnv = unknown> {
167
153
  value: string,
168
154
  options?: CacheItemOptions,
169
155
  ): Promise<void>;
156
+
157
+ /**
158
+ * Invalidate every cache entry (segment, response, item) tagged with any of
159
+ * `tags`. Store-level primitive that the public updateTag()/revalidateTag()
160
+ * APIs delegate to. Receives ALL of one invalidation call's tags at once so
161
+ * stores can batch their work (e.g. a single CDN purge request rather than
162
+ * one per tag). Stores that do not support tags simply omit this method.
163
+ * @param tags - The cache tags to invalidate
164
+ */
165
+ invalidateTags?(tags: string[]): Promise<void>;
170
166
  }
171
167
 
172
168
  /**
@@ -175,18 +171,27 @@ export interface SegmentCacheStore<TEnv = unknown> {
175
171
  export interface CacheItemResult {
176
172
  /** RSC-serialized return value */
177
173
  value: string;
178
- /** Handle data captured during execution (breadcrumbs, metadata, etc.) */
179
- handles?: Record<string, SegmentHandleData>;
174
+ /** RSC-encoded handle data captured during execution (breadcrumbs, metadata,
175
+ * etc.). Encoded via the Flight codec so Promise/ReactNode handle values
176
+ * survive JSON-serializing stores — see handle-snapshot.ts encodeHandles. */
177
+ handles?: string;
180
178
  /** Whether the entry is stale and should be revalidated */
181
179
  shouldRevalidate: boolean;
180
+ /**
181
+ * The entry's cache tags (including runtime cacheTag() tags), surfaced on read
182
+ * so a "use cache" HIT can still contribute its tags to the request-scoped tag
183
+ * set used by document-level caching. On a hit the cached function is not
184
+ * re-run, so its runtime tags are only available here, not from re-execution.
185
+ */
186
+ tags?: string[];
182
187
  }
183
188
 
184
189
  /**
185
190
  * Options for setItem() for function-level caching ("use cache").
186
191
  */
187
192
  export interface CacheItemOptions {
188
- /** Handle data to store alongside the value */
189
- handles?: Record<string, SegmentHandleData>;
193
+ /** RSC-encoded handle data to store alongside the value (see encodeHandles). */
194
+ handles?: string;
190
195
  /** Time-to-live in seconds */
191
196
  ttl?: number;
192
197
  /** Stale-while-revalidate window in seconds */
@@ -227,16 +232,18 @@ export interface SerializedSegmentData {
227
232
  export interface CachedEntryData {
228
233
  /** Serialized segments for this entry */
229
234
  segments: SerializedSegmentData[];
230
- /** Handle data keyed by segment ID */
231
- handles: Record<string, SegmentHandleData>;
235
+ /** RSC-encoded handle data keyed by segment ID. Encoded via the Flight codec
236
+ * (see handle-snapshot.ts encodeHandles) so Promise/ReactNode handle values
237
+ * round-trip through JSON-serializing stores instead of being flattened. */
238
+ handles: string;
232
239
  /** Expiration timestamp (ms since epoch) */
233
240
  expiresAt: number;
241
+ /** Cache tags for invalidation */
242
+ tags?: string[];
243
+ /** Timestamp (ms since epoch) when tags were attached, for distributed invalidation */
244
+ taggedAt?: number;
234
245
  }
235
246
 
236
- // ============================================================================
237
- // Cache Configuration
238
- // ============================================================================
239
-
240
247
  /**
241
248
  * Default cache options applied to all cache() boundaries.
242
249
  * Individual cache() calls can override any of these values.
@@ -252,91 +259,21 @@ export interface CacheDefaults {
252
259
  /**
253
260
  * Default time-to-live in seconds.
254
261
  * After TTL expires, cached entry is considered stale.
262
+ * Must be a finite, non-negative number; an invalid value (NaN/Infinity/
263
+ * negative) falls back to the default at read time.
255
264
  */
256
265
  ttl?: number;
257
266
  /**
258
267
  * Default stale-while-revalidate window in seconds.
259
268
  * During SWR window, stale content is served while revalidating in background.
269
+ * Must be a finite, non-negative number; an invalid value (NaN/Infinity/
270
+ * negative) falls back to the default at read time.
260
271
  */
261
272
  swr?: number;
262
273
  }
263
274
 
264
- /**
265
- * Cache configuration for RSC handler
266
- */
267
- export interface CacheConfig {
268
- /** Cache store implementation (includes defaults) */
269
- store: SegmentCacheStore;
270
- /** Enable/disable caching (default: true) */
271
- enabled?: boolean;
272
- }
273
-
274
- /**
275
- * Cache configuration - can be static or a function receiving env
276
- */
277
- export type CacheConfigOrFactory<TEnv> =
278
- | CacheConfig
279
- | ((env: TEnv) => CacheConfig);
280
-
281
- // ============================================================================
282
- // Segment Cache Provider (request-level interface)
283
- // ============================================================================
284
-
285
275
  /**
286
276
  * Handle data for a single segment
287
277
  * Structure: { handleName: [values...] }
288
278
  */
289
279
  export type SegmentHandleData = Record<string, unknown[]>;
290
-
291
- /**
292
- * Result from cache get() including segments and their handle data
293
- * Each entry can produce multiple segments (main + parallels)
294
- */
295
- export interface CachedEntryResult {
296
- /** All segments for this entry (main segment + parallels) */
297
- segments: ResolvedSegment[];
298
- /** Handle data keyed by segment ID */
299
- handles: Record<string, SegmentHandleData>;
300
- }
301
-
302
- /**
303
- * Segment cache provider interface
304
- *
305
- * Used by router to check/store segment cache during matching.
306
- * Accessed via request context - if not present, caching is disabled.
307
- *
308
- * @internal Not currently implemented - CacheScope is used directly.
309
- * Reserved for future extensibility.
310
- */
311
- export interface SegmentCacheProvider {
312
- /** Whether caching is enabled for this request */
313
- readonly enabled: boolean;
314
-
315
- /**
316
- * Get cached segments and restore handles/loaders.
317
- *
318
- * Combines cache get with handle replay and loader data restoration.
319
- * Returns tuple of [segments, segmentIds] if cache hit, null if miss or disabled.
320
- *
321
- * @param cacheKey - Cache key to look up
322
- * @param params - Route params for cache key generation
323
- * @param loaderPromises - Map to restore loader data into
324
- * @returns Tuple of [segments, segmentIds] or null if miss
325
- */
326
- restore(
327
- cacheKey: string,
328
- params: Record<string, string>,
329
- loaderPromises: Map<string, Promise<any>>,
330
- ): Promise<[ResolvedSegment[], string[]] | null>;
331
-
332
- /**
333
- * Cache entry with automatic handle collection (non-blocking).
334
- *
335
- * Schedules caching via waitUntil - handles are collected after they settle.
336
- * Validates segments have actual components before caching.
337
- *
338
- * @param cacheKey - The cache key to store under
339
- * @param segments - All resolved segments for this entry
340
- */
341
- cacheEntry(cacheKey: string, segments: ResolvedSegment[]): void;
342
- }
@@ -14,60 +14,81 @@
14
14
  export {
15
15
  Outlet,
16
16
  ParallelOutlet,
17
- OutletProvider,
18
17
  useOutlet,
19
18
  useLoader,
20
19
  ErrorBoundary,
21
20
  type ErrorBoundaryProps,
22
21
  } from "./client.js";
23
22
 
24
- // Re-export the server's createLoader for RSC context
25
- // This version includes the actual loader function
23
+ export {
24
+ useFetchLoader,
25
+ useRefreshLoaders,
26
+ type LoadFunction,
27
+ type UseLoaderResult,
28
+ type UseFetchLoaderResult,
29
+ type UseLoaderOptions,
30
+ } from "./use-loader.js";
31
+
26
32
  export { createLoader } from "./route-definition.js";
27
33
 
28
- // Re-export Link component (can be used in server components)
34
+ // "use client" hooks the default ./client entry exports. They are client
35
+ // references in the RSC graph, identical in kind to useHref/useReverse/
36
+ // useHandle already forwarded below; forward them so the RSC client entry's
37
+ // hook surface matches the default entry. useNavigation/useAction stay omitted
38
+ // (they drive client-only navigation/action state — see note below).
39
+ export { useRouter } from "./browser/react/use-router.js";
40
+ export { usePathname } from "./browser/react/use-pathname.js";
41
+ export { useSearchParams } from "./browser/react/use-search-params.js";
42
+ export { useParams } from "./browser/react/use-params.js";
43
+ // CSP nonce for userland head-script components (analytics/GTM/inline init);
44
+ // forwarded so the RSC client entry's hook surface matches the default entry.
45
+ export { useNonce } from "./browser/react/nonce-context.js";
46
+ export { useMount } from "./browser/react/use-mount.js";
47
+ export {
48
+ useSegments,
49
+ type SegmentsState,
50
+ } from "./browser/react/use-segments.js";
51
+ export {
52
+ useLinkStatus,
53
+ type LinkStatus,
54
+ } from "./browser/react/use-link-status.js";
55
+ export { useScrollRestoration } from "./browser/react/ScrollRestoration.js";
56
+
29
57
  export {
30
58
  Link,
31
59
  type LinkProps,
32
60
  type PrefetchStrategy,
33
61
  } from "./browser/react/Link.js";
34
62
 
35
- // Re-export ScrollRestoration (can be used in server components)
36
63
  export {
37
64
  ScrollRestoration,
38
65
  type ScrollRestorationProps,
39
66
  } from "./browser/react/ScrollRestoration.js";
40
67
 
41
- // Re-export NavigationProvider (needed for setup)
42
68
  export {
43
69
  NavigationProvider,
44
70
  type NavigationProviderProps,
45
71
  } from "./browser/react/NavigationProvider.js";
46
72
 
47
- // Re-export href function (can be used in server components)
48
73
  export { href } from "./href-client.js";
49
74
 
50
- // Mount context re-exports (useMount is client-only, but MountContext can be referenced)
51
75
  export { MountContext } from "./browser/react/mount-context.js";
52
76
 
53
- // Note: useNavigation, useAction, useClientCache are NOT re-exported here
54
- // because they use client-side state and should only be used in client components
77
+ // useNavigation and useAction are NOT re-exported here because they use client-side state
55
78
 
56
- // Handle API - for accumulating data across route segments
57
- // Works in both RSC and client contexts
58
79
  export { createHandle, isHandle, type Handle } from "./handle.js";
59
80
 
60
- // Built-in handles
61
- // Meta handle works in RSC context
62
81
  export { Meta } from "./handles/meta.js";
63
- // MetaTags is a "use client" component that can be imported from RSC
64
82
  export { MetaTags } from "./handles/MetaTags.js";
65
83
  export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
66
- // Breadcrumbs handle works in RSC context
84
+ export {
85
+ Script,
86
+ type ScriptConfig,
87
+ type ScriptAttributes,
88
+ } from "./handles/script.js";
89
+ export { Scripts } from "./handles/Scripts.js";
67
90
  export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
68
91
 
69
- // Location state - createLocationState works in RSC (just creates definition)
70
- // useLocationState is NOT exported here as it uses client hooks
71
92
  export {
72
93
  createLocationState,
73
94
  type LocationStateDefinition,
@@ -75,11 +96,13 @@ export {
75
96
  type LocationStateOptions,
76
97
  } from "./browser/react/location-state-shared.js";
77
98
 
78
- // Re-export useHref - it's a "use client" hook
79
99
  export { useHref } from "./browser/react/use-href.js";
80
100
 
81
- // Re-export useHandle - it's a "use client" hook
101
+ export { useReverse } from "./browser/react/use-reverse.js";
102
+
82
103
  export { useHandle } from "./browser/react/use-handle.js";
104
+ // Type a deferred-aware consumer narrows: an accumulated entry may be a Promise
105
+ // (a `ctx.use(Handle).defer()` slot) until it resolves.
106
+ export type { DeferredHandleEntry } from "./defer.js";
83
107
 
84
- // Re-export useLocationState - it's a "use client" hook
85
108
  export { useLocationState } from "./browser/react/location-state.js";
package/src/client.tsx CHANGED
@@ -111,6 +111,11 @@ function useSlotSegment(
111
111
  * the parallel segment with that slot name instead of the default content.
112
112
  * This is used for parallel routes and intercepting routes.
113
113
  *
114
+ * For a named slot, `<Outlet name="@x" />` is equivalent to
115
+ * `<ParallelOutlet name="@x" />` — both run the same resolution + wrapping
116
+ * pipeline. Convention: use bare `<Outlet />` for default content and
117
+ * `<ParallelOutlet name="@x" />` for named slots.
118
+ *
114
119
  * @param name - Optional slot name for parallel/intercept content (must start with @)
115
120
  *
116
121
  * @example
@@ -163,6 +168,9 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
163
168
  * is wrapped in Suspense with the loading component as fallback.
164
169
  * This enables streaming and navigation loading states for parallels.
165
170
  *
171
+ * Equivalent to `<Outlet name="@x" />` for a named slot; ParallelOutlet
172
+ * requires `name` and is named-slot-only, which reads clearer at the call site.
173
+ *
166
174
  * @param name - The slot name (must start with @, e.g., "@modal", "@sidebar")
167
175
  *
168
176
  * @example
@@ -186,10 +194,9 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
186
194
  }
187
195
 
188
196
  // OutletProvider is defined in outlet-provider.tsx to break a circular
189
- // dependency between client.tsx and route-content-wrapper.tsx.
190
- // Imported at the top of this file for local use in Outlet/ParallelOutlet,
191
- // and re-exported here for backwards compatibility.
192
- export { OutletProvider };
197
+ // dependency between client.tsx and route-content-wrapper.tsx. It is imported
198
+ // at the top of this file for local use in Outlet/ParallelOutlet only; it is an
199
+ // internal component and is intentionally not part of the public ./client API.
193
200
 
194
201
  /**
195
202
  * Hook to access outlet content programmatically
@@ -210,10 +217,10 @@ export function useOutlet(): ReactNode {
210
217
  return context?.content ?? null;
211
218
  }
212
219
 
213
- // Loader hooks - re-exported from dedicated file
214
220
  export {
215
221
  useLoader,
216
222
  useFetchLoader,
223
+ useRefreshLoaders,
217
224
  type LoadFunction,
218
225
  type UseLoaderResult,
219
226
  type UseFetchLoaderResult,
@@ -328,79 +335,75 @@ export class ErrorBoundary extends Component<
328
335
  }
329
336
  }
330
337
 
331
- // ============================================================================
332
- // Re-exports from browser/react for convenience
333
- // These are the most commonly used client-side navigation utilities
334
- // ============================================================================
335
-
336
- // Navigation hooks
337
338
  export { useNavigation } from "./browser/react/use-navigation.js";
338
339
  export { useRouter } from "./browser/react/use-router.js";
339
340
  export { usePathname } from "./browser/react/use-pathname.js";
340
341
  export { useSearchParams } from "./browser/react/use-search-params.js";
341
342
  export { useParams } from "./browser/react/use-params.js";
343
+ // CSP nonce for the active request, for userland components that inject their
344
+ // own <script>/<style> into the document head (analytics, GTM, inline init).
345
+ // Returns the nonce during SSR and undefined in the browser; render the tag
346
+ // server-side so the nonce lands in the SSR HTML and hydration stays clean.
347
+ export { useNonce } from "./browser/react/nonce-context.js";
342
348
  export type {
343
349
  RouterInstance,
344
350
  RouterNavigateOptions,
345
351
  ReadonlyURLSearchParams,
352
+ ActionState,
353
+ ActionLifecycleState,
346
354
  } from "./browser/types.js";
347
355
 
348
- // Action state tracking hook
349
356
  export {
350
357
  useAction,
351
358
  type ServerActionFunction,
352
359
  } from "./browser/react/use-action.js";
353
360
 
354
- // Segments state hook
355
361
  export {
356
362
  useSegments,
357
363
  type SegmentsState,
358
364
  } from "./browser/react/use-segments.js";
359
365
 
360
- // Client cache controls hook
361
- export {
362
- useClientCache,
363
- type ClientCacheControls,
364
- } from "./browser/react/use-client-cache.js";
365
-
366
- // Provider
367
366
  export {
368
367
  NavigationProvider,
369
368
  type NavigationProviderProps,
370
369
  } from "./browser/react/NavigationProvider.js";
371
370
 
372
- // Link component
373
371
  export {
374
372
  Link,
375
373
  type LinkProps,
376
374
  type PrefetchStrategy,
377
375
  type StateOrGetter,
376
+ type LinkState,
378
377
  } from "./browser/react/Link.js";
379
378
 
380
- // Link status hook
381
379
  export {
382
380
  useLinkStatus,
383
381
  type LinkStatus,
384
382
  } from "./browser/react/use-link-status.js";
385
383
 
386
- // Scroll restoration
387
384
  export {
388
385
  ScrollRestoration,
389
386
  useScrollRestoration,
390
387
  type ScrollRestorationProps,
391
388
  } from "./browser/react/ScrollRestoration.js";
392
389
 
393
- // Handle data hook (client-side only — createHandle/isHandle are server APIs from the root export)
394
390
  export { type Handle } from "./handle.js";
395
391
  export { useHandle } from "./browser/react/use-handle.js";
392
+ // Type a deferred-aware consumer narrows: an accumulated entry may be a Promise
393
+ // (a `ctx.use(Handle).defer()` slot) until it resolves.
394
+ export type { DeferredHandleEntry } from "./defer.js";
396
395
 
397
- // Built-in handles
398
396
  export { Meta } from "./handles/meta.js";
399
397
  export { MetaTags } from "./handles/MetaTags.js";
400
398
  export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
399
+ export {
400
+ Script,
401
+ type ScriptConfig,
402
+ type ScriptAttributes,
403
+ } from "./handles/script.js";
404
+ export { Scripts } from "./handles/Scripts.js";
401
405
  export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
402
406
 
403
- // Location state - type-safe navigation state
404
407
  export {
405
408
  createLocationState,
406
409
  useLocationState,
@@ -409,47 +412,19 @@ export {
409
412
  type LocationStateOptions,
410
413
  } from "./browser/react/location-state.js";
411
414
 
412
- // Type-safe href for client-side path validation
413
- export {
414
- href,
415
- type ValidPaths,
416
- type PatternToPath,
417
- type PathResponse,
418
- } from "./href-client.js";
419
-
420
- // Response envelope types for consuming JSON response routes
421
- export type { ResponseEnvelope, ResponseError } from "./urls.js";
415
+ // Ambient Rango.Path / Rango.PathResponse types (declared in href-client.ts)
416
+ export { href, type PatternToPath } from "./href-client.js";
422
417
 
423
- /**
424
- * Type guard for checking if a response envelope contains an error.
425
- *
426
- * @example
427
- * ```typescript
428
- * const result: ResponseEnvelope<Product> = await fetch(url).then(r => r.json());
429
- * if (isResponseError(result)) {
430
- * console.log(result.error.message, result.error.code);
431
- * return;
432
- * }
433
- * result.data // fully typed as Product
434
- * ```
435
- */
436
- export function isResponseError<T>(
437
- result: import("./urls.js").ResponseEnvelope<T>,
438
- ): result is import("./urls.js").ResponseEnvelope<T> & {
439
- error: import("./urls.js").ResponseError;
440
- } {
441
- return result.error !== undefined;
442
- }
418
+ // RFC 9457 error type for JSON response routes
419
+ export type { ProblemDetails } from "./urls.js";
443
420
 
444
- // Mount context for include() scoped components
445
421
  export { useMount } from "./browser/react/use-mount.js";
446
422
  export { MountContext } from "./browser/react/mount-context.js";
447
423
 
448
- // Mount-aware href hook - auto-prefixes paths with include() mount
449
424
  export { useHref } from "./browser/react/use-href.js";
450
425
 
451
- // Type-safe scoped reverse function for scopedReverse<typeof patterns>()
452
- export type { ScopedReverseFunction } from "./reverse.js";
426
+ export { useReverse } from "./browser/react/use-reverse.js";
427
+
428
+ export type { ScopedReverseFunction, LocalReverseFunction } from "./reverse.js";
453
429
 
454
- // Loader definition type - for typing loader props in client components
455
430
  export type { LoaderDefinition } from "./types.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Cloudflare preset integration surface for @rangojs/router.
3
+ *
4
+ * Imported via `@rangojs/router/cloudflare`. Cloudflare-only helpers live here
5
+ * so Node/other-platform consumers never pull them in.
6
+ */
7
+
8
+ export {
9
+ createCloudflareTracing,
10
+ type CloudflareTracingOptions,
11
+ } from "./tracing.js";
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Cloudflare custom-spans integration.
3
+ *
4
+ * Bridges the router's performance phases (request, middleware, action,
5
+ * loaders, handler, render, ssr) onto Cloudflare Workers custom spans so they show
6
+ * up in the trace waterfall and OpenTelemetry exports next to the platform's
7
+ * automatic spans (KV reads, D1 queries, fetch calls), with correct nesting.
8
+ *
9
+ * Usage (Cloudflare preset only):
10
+ *
11
+ * import { createRouter } from "@rangojs/router";
12
+ * import { createCloudflareTracing } from "@rangojs/router/cloudflare";
13
+ *
14
+ * export const router = createRouter({
15
+ * tracing: createCloudflareTracing(),
16
+ * });
17
+ *
18
+ * The runner reads `executionContext.tracing` (the same object as
19
+ * `import { tracing } from "cloudflare:workers"`) at call time. It is therefore
20
+ * dependency-free: no `cloudflare:workers` import and no hard dependency on
21
+ * `@cloudflare/workers-types`, matching the convention in cache/cf. When the
22
+ * worker is not running on a tracing-enabled Cloudflare runtime — Node, dev
23
+ * without a tracing destination, an older runtime — `executionContext.tracing`
24
+ * is undefined and every span call falls through to the work directly, so the
25
+ * request behaves exactly as if tracing were off. Whether spans are actually
26
+ * recorded is governed by the `observability`/tracing block in wrangler config.
27
+ *
28
+ * Span duration note: enterSpan ends a span when its callback's returned value
29
+ * (or promise) settles. For the streaming phases (request/render/ssr) that is at
30
+ * stream CONSTRUCTION, not body-drain. Instrumentation is best-effort and never
31
+ * wraps or buffers the response body, so it cannot regress streaming or latency.
32
+ * A loader/Suspense child that resolves mid-stream therefore keeps a rango.loader
33
+ * span that can extend past its render parent — overlapping spans are valid. Uses
34
+ * only the typed enterSpan API; spans bound work up to stream-handoff, matching
35
+ * the co-emitted perf metric.
36
+ */
37
+
38
+ import { _getRequestContext } from "../server/request-context.js";
39
+ import {
40
+ type RouterTracingConfig,
41
+ type SpanRunner,
42
+ type TracePhaseToggles,
43
+ NOOP_TRACE_SPAN,
44
+ } from "../router/tracing.js";
45
+
46
+ /**
47
+ * Minimal local view of Cloudflare's `Span`. Declared here (not imported from
48
+ * `@cloudflare/workers-types`) to avoid a hard type dependency.
49
+ */
50
+ interface CloudflareSpan {
51
+ readonly isTraced: boolean;
52
+ setAttribute(key: string, value?: boolean | number | string): void;
53
+ }
54
+
55
+ /** Minimal local view of Cloudflare's `Tracing` (only enterSpan is used). */
56
+ interface CloudflareTracing {
57
+ enterSpan<T>(name: string, callback: (span: CloudflareSpan) => T): T;
58
+ }
59
+
60
+ /** Options for createCloudflareTracing. */
61
+ export interface CloudflareTracingOptions {
62
+ /** Master switch. Defaults to true. */
63
+ enabled?: boolean;
64
+ /** Per-phase span toggles. Omitted phases default to enabled. */
65
+ spans?: TracePhaseToggles;
66
+ }
67
+
68
+ /**
69
+ * Resolve the per-request Cloudflare tracer from the active execution context.
70
+ * Returns undefined off-Cloudflare or when tracing is not enabled for the
71
+ * worker, in which case the caller runs the work without a span.
72
+ */
73
+ function getRequestTracer(): CloudflareTracing | undefined {
74
+ const executionContext = _getRequestContext()?.executionContext as
75
+ | { tracing?: CloudflareTracing }
76
+ | undefined;
77
+ const tracing = executionContext?.tracing;
78
+ return tracing && typeof tracing.enterSpan === "function"
79
+ ? tracing
80
+ : undefined;
81
+ }
82
+
83
+ const cloudflareSpanRunner: SpanRunner = (name, fn) => {
84
+ const tracer = getRequestTracer();
85
+ if (!tracer) return fn(NOOP_TRACE_SPAN);
86
+ // enterSpan runs the callback now and ends the span when its return value
87
+ // (or returned promise) settles; spans nest by JS async context. fn's param
88
+ // is the narrower TraceSpan, so the wider CloudflareSpan satisfies it directly.
89
+ return tracer.enterSpan(name, fn);
90
+ };
91
+
92
+ /**
93
+ * Create the tracing config for a Cloudflare router. Pass the result to
94
+ * `createRouter({ tracing })`. Spans are emitted for the request, middleware,
95
+ * action, loaders, handler, render, and ssr phases; pass `spans` to turn
96
+ * individual phases off.
97
+ *
98
+ * @see createOTelTracing (`@rangojs/router`) for the same slot on any platform
99
+ * with an OpenTelemetry SDK.
100
+ */
101
+ export function createCloudflareTracing(
102
+ options: CloudflareTracingOptions = {},
103
+ ): RouterTracingConfig {
104
+ return {
105
+ runner: cloudflareSpanRunner,
106
+ enabled: options.enabled ?? true,
107
+ spans: options.spans,
108
+ };
109
+ }