@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1c0bdfad

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 (406) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +291 -61
  3. package/dist/bin/rango.js +544 -143
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3744 -1329
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +67 -13
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +312 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +247 -23
  12. package/skills/caching/SKILL.md +322 -19
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/debug-manifest/SKILL.md +4 -2
  16. package/skills/document-cache/SKILL.md +78 -55
  17. package/skills/handler-use/SKILL.md +364 -0
  18. package/skills/hooks/SKILL.md +282 -60
  19. package/skills/host-router/SKILL.md +278 -0
  20. package/skills/i18n/SKILL.md +276 -0
  21. package/skills/intercept/SKILL.md +50 -6
  22. package/skills/layout/SKILL.md +35 -9
  23. package/skills/links/SKILL.md +249 -17
  24. package/skills/loader/SKILL.md +297 -31
  25. package/skills/middleware/SKILL.md +52 -13
  26. package/skills/migrate-nextjs/SKILL.md +584 -0
  27. package/skills/migrate-react-router/SKILL.md +771 -0
  28. package/skills/mime-routes/SKILL.md +28 -1
  29. package/skills/observability/SKILL.md +172 -0
  30. package/skills/parallel/SKILL.md +203 -7
  31. package/skills/prerender/SKILL.md +155 -111
  32. package/skills/rango/SKILL.md +251 -23
  33. package/skills/react-compiler/SKILL.md +168 -0
  34. package/skills/response-routes/SKILL.md +123 -48
  35. package/skills/route/SKILL.md +104 -9
  36. package/skills/router-setup/SKILL.md +124 -11
  37. package/skills/scripts/SKILL.md +179 -0
  38. package/skills/server-actions/SKILL.md +775 -0
  39. package/skills/streams-and-websockets/SKILL.md +283 -0
  40. package/skills/tailwind/SKILL.md +27 -3
  41. package/skills/testing/SKILL.md +125 -222
  42. package/skills/testing/bindings.md +103 -0
  43. package/skills/testing/cache-prerender.md +127 -0
  44. package/skills/testing/client-components.md +124 -0
  45. package/skills/testing/e2e-parity.md +125 -0
  46. package/skills/testing/flight.md +91 -0
  47. package/skills/testing/handles.md +129 -0
  48. package/skills/testing/loader.md +128 -0
  49. package/skills/testing/middleware.md +99 -0
  50. package/skills/testing/render-handler.md +121 -0
  51. package/skills/testing/response-routes.md +95 -0
  52. package/skills/testing/reverse-and-types.md +84 -0
  53. package/skills/testing/server-actions.md +107 -0
  54. package/skills/testing/server-tree.md +128 -0
  55. package/skills/testing/setup.md +123 -0
  56. package/skills/typesafety/SKILL.md +357 -52
  57. package/skills/use-cache/SKILL.md +46 -14
  58. package/skills/view-transitions/SKILL.md +294 -0
  59. package/src/__augment-tests__/augment.ts +81 -0
  60. package/src/__augment-tests__/augmented.check.ts +116 -0
  61. package/src/__internal.ts +67 -40
  62. package/src/bin/rango.ts +18 -0
  63. package/src/browser/action-coordinator.ts +53 -36
  64. package/src/browser/action-fence.ts +47 -0
  65. package/src/browser/app-shell.ts +39 -0
  66. package/src/browser/app-version.ts +14 -0
  67. package/src/browser/connection-warmup.ts +134 -0
  68. package/src/browser/cookie-name.ts +140 -0
  69. package/src/browser/event-controller.ts +197 -150
  70. package/src/browser/history-state.ts +21 -0
  71. package/src/browser/index.ts +3 -3
  72. package/src/browser/invalidate-client-cache.ts +52 -0
  73. package/src/browser/link-interceptor.ts +4 -0
  74. package/src/browser/navigation-bridge.ts +200 -30
  75. package/src/browser/navigation-client.ts +217 -58
  76. package/src/browser/navigation-store-handle.ts +38 -0
  77. package/src/browser/navigation-store.ts +76 -67
  78. package/src/browser/navigation-transaction.ts +18 -66
  79. package/src/browser/network-error-handler.ts +34 -7
  80. package/src/browser/partial-update.ts +187 -112
  81. package/src/browser/prefetch/cache.ts +312 -30
  82. package/src/browser/prefetch/fetch.ts +344 -47
  83. package/src/browser/prefetch/policy.ts +6 -0
  84. package/src/browser/prefetch/queue.ts +126 -20
  85. package/src/browser/prefetch/resource-ready.ts +77 -0
  86. package/src/browser/rango-state.ts +158 -76
  87. package/src/browser/react/Link.tsx +125 -18
  88. package/src/browser/react/NavigationProvider.tsx +135 -120
  89. package/src/browser/react/ScrollRestoration.tsx +10 -6
  90. package/src/browser/react/context.ts +7 -2
  91. package/src/browser/react/filter-segment-order.ts +66 -7
  92. package/src/browser/react/index.ts +0 -48
  93. package/src/browser/react/location-state-shared.ts +178 -8
  94. package/src/browser/react/location-state.ts +39 -14
  95. package/src/browser/react/use-action.ts +6 -15
  96. package/src/browser/react/use-handle.ts +23 -69
  97. package/src/browser/react/use-href.tsx +8 -1
  98. package/src/browser/react/use-link-status.ts +33 -8
  99. package/src/browser/react/use-navigation.ts +32 -7
  100. package/src/browser/react/use-params.ts +20 -10
  101. package/src/browser/react/use-reverse.ts +106 -0
  102. package/src/browser/react/use-router.ts +46 -11
  103. package/src/browser/react/use-search-params.ts +0 -5
  104. package/src/browser/react/use-segments.ts +11 -21
  105. package/src/browser/response-adapter.ts +80 -5
  106. package/src/browser/rsc-router.tsx +226 -75
  107. package/src/browser/scroll-restoration.ts +54 -42
  108. package/src/browser/segment-reconciler.ts +36 -9
  109. package/src/browser/segment-structure-assert.ts +2 -2
  110. package/src/browser/server-action-bridge.ts +619 -442
  111. package/src/browser/types.ts +115 -11
  112. package/src/browser/validate-redirect-origin.ts +43 -16
  113. package/src/build/collect-fallback-refs.ts +107 -0
  114. package/src/build/generate-manifest.ts +65 -40
  115. package/src/build/generate-route-types.ts +7 -1
  116. package/src/build/index.ts +8 -2
  117. package/src/build/prefix-tree-utils.ts +123 -0
  118. package/src/build/route-trie.ts +182 -37
  119. package/src/build/route-types/ast-route-extraction.ts +15 -8
  120. package/src/build/route-types/codegen.ts +16 -5
  121. package/src/build/route-types/include-resolution.ts +125 -24
  122. package/src/build/route-types/param-extraction.ts +6 -3
  123. package/src/build/route-types/per-module-writer.ts +22 -6
  124. package/src/build/route-types/router-processing.ts +392 -106
  125. package/src/build/route-types/scan-filter.ts +9 -2
  126. package/src/build/route-types/source-scan.ts +216 -0
  127. package/src/build/runtime-discovery.ts +9 -20
  128. package/src/cache/cache-error.ts +104 -0
  129. package/src/cache/cache-key-utils.ts +29 -13
  130. package/src/cache/cache-policy.ts +108 -34
  131. package/src/cache/cache-runtime.ts +214 -48
  132. package/src/cache/cache-scope.ts +236 -89
  133. package/src/cache/cache-tag.ts +103 -0
  134. package/src/cache/cf/cf-base64.ts +33 -0
  135. package/src/cache/cf/cf-cache-constants.ts +127 -0
  136. package/src/cache/cf/cf-cache-store.ts +2224 -171
  137. package/src/cache/cf/cf-cache-types.ts +349 -0
  138. package/src/cache/cf/cf-kv-utils.ts +46 -0
  139. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  140. package/src/cache/cf/index.ts +11 -17
  141. package/src/cache/document-cache.ts +89 -27
  142. package/src/cache/handle-snapshot.ts +70 -0
  143. package/src/cache/index.ts +11 -20
  144. package/src/cache/memory-segment-store.ts +136 -37
  145. package/src/cache/profile-registry.ts +31 -31
  146. package/src/cache/read-through-swr.ts +41 -11
  147. package/src/cache/segment-codec.ts +9 -17
  148. package/src/cache/tag-invalidation.ts +230 -0
  149. package/src/cache/taint.ts +55 -0
  150. package/src/cache/types.ts +37 -100
  151. package/src/client.rsc.tsx +45 -21
  152. package/src/client.tsx +120 -336
  153. package/src/cloudflare/index.ts +11 -0
  154. package/src/cloudflare/tracing.ts +109 -0
  155. package/src/component-utils.ts +19 -0
  156. package/src/components/DefaultDocument.tsx +8 -2
  157. package/src/context-var.ts +84 -2
  158. package/src/debug.ts +2 -2
  159. package/src/decode-loader-results.ts +52 -0
  160. package/src/defer.ts +196 -0
  161. package/src/deps/ssr.ts +0 -1
  162. package/src/encode-kv.ts +49 -0
  163. package/src/errors.ts +30 -4
  164. package/src/escape-script.ts +52 -0
  165. package/src/handle.ts +70 -22
  166. package/src/handles/MetaTags.tsx +56 -19
  167. package/src/handles/Scripts.tsx +183 -0
  168. package/src/handles/breadcrumbs.ts +95 -0
  169. package/src/handles/is-thenable.ts +19 -0
  170. package/src/handles/meta.ts +51 -40
  171. package/src/handles/script.ts +244 -0
  172. package/src/host/cookie-handler.ts +9 -60
  173. package/src/host/errors.ts +0 -24
  174. package/src/host/index.ts +8 -5
  175. package/src/host/pattern-matcher.ts +23 -52
  176. package/src/host/router.ts +107 -99
  177. package/src/host/testing.ts +40 -27
  178. package/src/host/types.ts +37 -4
  179. package/src/host/utils.ts +1 -1
  180. package/src/href-client.ts +137 -22
  181. package/src/index.rsc.ts +79 -29
  182. package/src/index.ts +149 -65
  183. package/src/internal-debug.ts +11 -10
  184. package/src/loader-store.ts +500 -0
  185. package/src/loader.rsc.ts +20 -13
  186. package/src/loader.ts +12 -11
  187. package/src/missing-id-error.ts +68 -0
  188. package/src/outlet-context.ts +1 -1
  189. package/src/outlet-provider.tsx +1 -5
  190. package/src/prerender/param-hash.ts +16 -16
  191. package/src/prerender/store.ts +63 -26
  192. package/src/prerender.ts +198 -82
  193. package/src/redirect-origin.ts +100 -0
  194. package/src/regex-escape.ts +8 -0
  195. package/src/render-error-thrower.tsx +20 -0
  196. package/src/response-utils.ts +62 -0
  197. package/src/reverse.ts +65 -15
  198. package/src/root-error-boundary.tsx +1 -19
  199. package/src/route-content-wrapper.tsx +7 -72
  200. package/src/route-definition/dsl-helpers.ts +469 -276
  201. package/src/route-definition/helper-factories.ts +29 -139
  202. package/src/route-definition/helpers-types.ts +113 -37
  203. package/src/route-definition/index.ts +3 -3
  204. package/src/route-definition/redirect.ts +53 -12
  205. package/src/route-definition/resolve-handler-use.ts +161 -0
  206. package/src/route-definition/use-item-types.ts +32 -0
  207. package/src/route-map-builder.ts +7 -17
  208. package/src/route-types.ts +37 -41
  209. package/src/router/basename.ts +14 -0
  210. package/src/router/content-negotiation.ts +164 -17
  211. package/src/router/error-handling.ts +45 -18
  212. package/src/router/find-match.ts +45 -22
  213. package/src/router/handler-context.ts +110 -39
  214. package/src/router/instrument.ts +350 -0
  215. package/src/router/intercept-resolution.ts +50 -24
  216. package/src/router/lazy-includes.ts +19 -53
  217. package/src/router/loader-resolution.ts +274 -56
  218. package/src/router/logging.ts +5 -8
  219. package/src/router/manifest.ts +49 -45
  220. package/src/router/match-api.ts +121 -205
  221. package/src/router/match-context.ts +0 -22
  222. package/src/router/match-handlers.ts +58 -58
  223. package/src/router/match-middleware/background-revalidation.ts +33 -6
  224. package/src/router/match-middleware/cache-lookup.ts +214 -263
  225. package/src/router/match-middleware/cache-store.ts +73 -33
  226. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  227. package/src/router/match-middleware/segment-resolution.ts +52 -18
  228. package/src/router/match-pipelines.ts +1 -42
  229. package/src/router/match-result.ts +104 -49
  230. package/src/router/metrics.ts +217 -26
  231. package/src/router/middleware-types.ts +24 -110
  232. package/src/router/middleware.ts +384 -197
  233. package/src/router/navigation-snapshot.ts +131 -0
  234. package/src/router/params-util.ts +23 -0
  235. package/src/router/pattern-matching.ts +148 -91
  236. package/src/router/prefetch-cache-ttl.ts +51 -0
  237. package/src/router/prerender-match.ts +199 -56
  238. package/src/router/preview-match.ts +32 -102
  239. package/src/router/request-classification.ts +276 -0
  240. package/src/router/revalidation.ts +144 -74
  241. package/src/router/route-snapshot.ts +244 -0
  242. package/src/router/router-context.ts +8 -28
  243. package/src/router/router-interfaces.ts +129 -36
  244. package/src/router/router-options.ts +185 -23
  245. package/src/router/router-registry.ts +2 -5
  246. package/src/router/segment-resolution/fresh.ts +281 -76
  247. package/src/router/segment-resolution/helpers.ts +116 -31
  248. package/src/router/segment-resolution/loader-cache.ts +63 -37
  249. package/src/router/segment-resolution/revalidation.ts +493 -391
  250. package/src/router/segment-resolution/static-store.ts +19 -5
  251. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  252. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  253. package/src/router/segment-resolution.ts +5 -1
  254. package/src/router/segment-wrappers.ts +8 -5
  255. package/src/router/state-cookie-name.ts +33 -0
  256. package/src/router/substitute-pattern-params.ts +56 -0
  257. package/src/router/telemetry-otel.ts +161 -199
  258. package/src/router/telemetry.ts +96 -19
  259. package/src/router/timeout.ts +0 -20
  260. package/src/router/tracing.ts +206 -0
  261. package/src/router/trie-matching.ts +180 -58
  262. package/src/router/types.ts +10 -63
  263. package/src/router/url-params.ts +44 -0
  264. package/src/router.ts +182 -54
  265. package/src/rsc/handler-context.ts +3 -2
  266. package/src/rsc/handler.ts +702 -460
  267. package/src/rsc/helpers.ts +168 -46
  268. package/src/rsc/index.ts +2 -25
  269. package/src/rsc/json-route-result.ts +38 -0
  270. package/src/rsc/loader-fetch.ts +127 -31
  271. package/src/rsc/manifest-init.ts +33 -42
  272. package/src/rsc/origin-guard.ts +39 -25
  273. package/src/rsc/progressive-enhancement.ts +98 -19
  274. package/src/rsc/redirect-guard.ts +99 -0
  275. package/src/rsc/response-cache-serve.ts +238 -0
  276. package/src/rsc/response-error.ts +79 -12
  277. package/src/rsc/response-route-handler.ts +99 -189
  278. package/src/rsc/rsc-rendering.ts +126 -106
  279. package/src/rsc/runtime-warnings.ts +23 -10
  280. package/src/rsc/server-action.ts +269 -114
  281. package/src/rsc/ssr-setup.ts +144 -0
  282. package/src/rsc/types.ts +34 -6
  283. package/src/runtime-env.ts +18 -0
  284. package/src/search-params.ts +49 -41
  285. package/src/segment-content-promise.ts +67 -0
  286. package/src/segment-loader-promise.ts +149 -0
  287. package/src/segment-system.tsx +281 -129
  288. package/src/serialize.ts +243 -0
  289. package/src/server/context.ts +317 -63
  290. package/src/server/cookie-parse.ts +32 -0
  291. package/src/server/cookie-store.ts +80 -5
  292. package/src/server/handle-store.ts +40 -38
  293. package/src/server/loader-registry.ts +26 -46
  294. package/src/server/request-context.ts +425 -177
  295. package/src/server.ts +6 -0
  296. package/src/ssr/index.tsx +25 -16
  297. package/src/static-handler.ts +27 -18
  298. package/src/testing/cache-status.ts +162 -0
  299. package/src/testing/collect-handle.ts +40 -0
  300. package/src/testing/dispatch.ts +701 -0
  301. package/src/testing/dom.entry.ts +22 -0
  302. package/src/testing/e2e/fixture.ts +188 -0
  303. package/src/testing/e2e/index.ts +128 -0
  304. package/src/testing/e2e/matchers.ts +35 -0
  305. package/src/testing/e2e/page-helpers.ts +272 -0
  306. package/src/testing/e2e/parity.ts +387 -0
  307. package/src/testing/e2e/server.ts +195 -0
  308. package/src/testing/flight-matchers.ts +97 -0
  309. package/src/testing/flight-normalize.ts +11 -0
  310. package/src/testing/flight-runtime.d.ts +57 -0
  311. package/src/testing/flight-tree.ts +682 -0
  312. package/src/testing/flight.entry.ts +52 -0
  313. package/src/testing/flight.ts +257 -0
  314. package/src/testing/generated-routes.ts +183 -0
  315. package/src/testing/index.ts +99 -0
  316. package/src/testing/internal/context.ts +371 -0
  317. package/src/testing/internal/flight-client-globals.ts +30 -0
  318. package/src/testing/internal/seed-vars.ts +54 -0
  319. package/src/testing/render-handler.ts +343 -0
  320. package/src/testing/render-route.tsx +581 -0
  321. package/src/testing/run-loader.ts +385 -0
  322. package/src/testing/run-middleware.ts +205 -0
  323. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  324. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  325. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  326. package/src/testing/vitest-stubs/version.ts +5 -0
  327. package/src/testing/vitest.ts +305 -0
  328. package/src/theme/ThemeProvider.tsx +20 -58
  329. package/src/theme/ThemeScript.tsx +7 -9
  330. package/src/theme/constants.ts +52 -13
  331. package/src/theme/index.ts +3 -19
  332. package/src/theme/theme-context.ts +1 -5
  333. package/src/theme/theme-script.ts +22 -21
  334. package/src/theme/use-theme.ts +0 -3
  335. package/src/types/boundaries.ts +0 -35
  336. package/src/types/cache-types.ts +17 -8
  337. package/src/types/error-types.ts +30 -90
  338. package/src/types/global-namespace.ts +54 -41
  339. package/src/types/handler-context.ts +236 -88
  340. package/src/types/index.ts +1 -10
  341. package/src/types/loader-types.ts +44 -15
  342. package/src/types/request-scope.ts +112 -0
  343. package/src/types/route-config.ts +10 -45
  344. package/src/types/route-entry.ts +19 -7
  345. package/src/types/segments.ts +37 -19
  346. package/src/urls/include-helper.ts +33 -70
  347. package/src/urls/index.ts +1 -11
  348. package/src/urls/path-helper-types.ts +58 -11
  349. package/src/urls/path-helper.ts +57 -111
  350. package/src/urls/pattern-types.ts +48 -19
  351. package/src/urls/response-types.ts +25 -22
  352. package/src/urls/type-extraction.ts +58 -139
  353. package/src/urls/urls-function.ts +1 -18
  354. package/src/use-loader.tsx +346 -89
  355. package/src/vite/debug.ts +185 -0
  356. package/src/vite/discovery/bundle-postprocess.ts +64 -91
  357. package/src/vite/discovery/discover-routers.ts +147 -88
  358. package/src/vite/discovery/discovery-errors.ts +194 -0
  359. package/src/vite/discovery/gate-state.ts +171 -0
  360. package/src/vite/discovery/prerender-collection.ts +247 -145
  361. package/src/vite/discovery/route-types-writer.ts +40 -84
  362. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  363. package/src/vite/discovery/state.ts +61 -13
  364. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  365. package/src/vite/index.ts +10 -3
  366. package/src/vite/inject-client-debug.ts +36 -0
  367. package/src/vite/plugin-types.ts +155 -65
  368. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  369. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  370. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  371. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  372. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  373. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  374. package/src/vite/plugins/expose-action-id.ts +49 -98
  375. package/src/vite/plugins/expose-id-utils.ts +96 -51
  376. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  377. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  378. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  379. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  380. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  381. package/src/vite/plugins/performance-tracks.ts +89 -0
  382. package/src/vite/plugins/refresh-cmd.ts +127 -0
  383. package/src/vite/plugins/use-cache-transform.ts +73 -83
  384. package/src/vite/plugins/version-injector.ts +21 -25
  385. package/src/vite/plugins/version-plugin.ts +46 -37
  386. package/src/vite/plugins/virtual-entries.ts +13 -18
  387. package/src/vite/rango.ts +241 -287
  388. package/src/vite/router-discovery.ts +956 -149
  389. package/src/vite/utils/ast-handler-extract.ts +26 -35
  390. package/src/vite/utils/banner.ts +4 -4
  391. package/src/vite/utils/bundle-analysis.ts +10 -15
  392. package/src/vite/utils/client-chunks.ts +184 -0
  393. package/src/vite/utils/directive-prologue.ts +40 -0
  394. package/src/vite/utils/forward-user-plugins.ts +171 -0
  395. package/src/vite/utils/manifest-utils.ts +4 -59
  396. package/src/vite/utils/package-resolution.ts +20 -52
  397. package/src/vite/utils/prerender-utils.ts +141 -34
  398. package/src/vite/utils/shared-utils.ts +92 -42
  399. package/CLAUDE.md +0 -5
  400. package/src/browser/action-response-classifier.ts +0 -99
  401. package/src/browser/react/use-client-cache.ts +0 -58
  402. package/src/browser/shallow.ts +0 -40
  403. package/src/handles/index.ts +0 -6
  404. package/src/network-error-thrower.tsx +0 -23
  405. package/src/route-definition/route-function.ts +0 -119
  406. package/src/router/middleware-cookies.ts +0 -55
package/src/client.tsx CHANGED
@@ -13,7 +13,6 @@ import {
13
13
  type ClientErrorBoundaryFallbackProps,
14
14
  type ErrorInfo,
15
15
  type LoaderDefinition,
16
- type LoaderFn,
17
16
  type ResolvedSegment,
18
17
  } from "./types";
19
18
  import {
@@ -22,6 +21,83 @@ import {
22
21
  } from "./route-content-wrapper.js";
23
22
  import { OutletProvider } from "./outlet-provider.js";
24
23
  import { MountContextProvider } from "./browser/react/mount-context.js";
24
+ import { getMemoizedContentPromise } from "./segment-content-promise.js";
25
+
26
+ /**
27
+ * Render the content for a named parallel/intercept slot segment.
28
+ *
29
+ * Shared by Outlet (with `name` prop) and ParallelOutlet — both resolve a
30
+ * segment from context.parallel by slot name and then render it through the
31
+ * same layout/loader/mountPath wrapping pipeline.
32
+ */
33
+ function renderSlotContent(segment: ResolvedSegment | null): ReactNode {
34
+ if (!segment) return null;
35
+
36
+ const content: ReactNode =
37
+ segment.loading || segment.component instanceof Promise ? (
38
+ <RouteContentWrapper
39
+ content={getMemoizedContentPromise(segment.component)}
40
+ fallback={segment.loading}
41
+ segmentId={segment.id}
42
+ />
43
+ ) : (
44
+ (segment.component ?? null)
45
+ );
46
+
47
+ const hasOwnLoaders = !!(segment.loaderDataPromise && segment.loaderIds);
48
+ const loaderWrapped = hasOwnLoaders ? (
49
+ <LoaderBoundary
50
+ loaderDataPromise={segment.loaderDataPromise!}
51
+ loaderIds={segment.loaderIds!}
52
+ fallback={segment.loading}
53
+ outletKey={segment.id + "-loader"}
54
+ outletContent={null}
55
+ segment={segment}
56
+ >
57
+ {content}
58
+ </LoaderBoundary>
59
+ ) : null;
60
+
61
+ let result: ReactNode;
62
+ if (segment.layout) {
63
+ // Layout renders immediately; if loaders exist, the LoaderBoundary becomes
64
+ // the outlet content so layout's <Outlet /> suspends until loaders resolve.
65
+ result = (
66
+ <OutletProvider
67
+ content={hasOwnLoaders ? loaderWrapped : content}
68
+ segment={segment}
69
+ >
70
+ {segment.layout}
71
+ </OutletProvider>
72
+ );
73
+ } else if (hasOwnLoaders) {
74
+ // No layout but has loaders — wrap content with LoaderBoundary for useLoader context.
75
+ // Common for intercept routes that use useLoader without a custom layout.
76
+ result = loaderWrapped;
77
+ } else {
78
+ result = content;
79
+ }
80
+
81
+ if (segment.mountPath) {
82
+ return (
83
+ <MountContextProvider value={segment.mountPath}>
84
+ {result}
85
+ </MountContextProvider>
86
+ );
87
+ }
88
+
89
+ return result;
90
+ }
91
+
92
+ function useSlotSegment(
93
+ context: OutletContextValue | null,
94
+ name: `@${string}` | undefined,
95
+ ): ResolvedSegment | null {
96
+ return useMemo(() => {
97
+ if (!name || !context?.parallel) return null;
98
+ return context.parallel.find((seg) => seg.slot === name) ?? null;
99
+ }, [context, name]);
100
+ }
25
101
 
26
102
  /**
27
103
  * Outlet component - renders child content in layouts
@@ -35,6 +111,11 @@ import { MountContextProvider } from "./browser/react/mount-context.js";
35
111
  * the parallel segment with that slot name instead of the default content.
36
112
  * This is used for parallel routes and intercepting routes.
37
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
+ *
38
119
  * @param name - Optional slot name for parallel/intercept content (must start with @)
39
120
  *
40
121
  * @example
@@ -62,95 +143,10 @@ import { MountContextProvider } from "./browser/react/mount-context.js";
62
143
  */
63
144
  export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
64
145
  const context = useContext(OutletContext);
146
+ const namedSegment = useSlotSegment(context, name);
65
147
 
66
- // If name provided, render parallel/intercept content for that slot
67
148
  if (name) {
68
- const segment = context?.parallel?.find((seg) => seg.slot === name) ?? null;
69
-
70
- if (!segment) return null;
71
-
72
- // Determine the content to render
73
- let content: ReactNode;
74
- if (segment.loading || segment.component instanceof Promise) {
75
- // Use RouteContentWrapper to handle Suspense wrapping properly
76
- content = (
77
- <RouteContentWrapper
78
- content={
79
- segment.component instanceof Promise
80
- ? segment.component
81
- : Promise.resolve(segment.component)
82
- }
83
- fallback={segment.loading}
84
- segmentId={segment.id}
85
- />
86
- );
87
- } else {
88
- content = segment.component ?? null;
89
- }
90
-
91
- let result: ReactNode;
92
-
93
- // If segment has a layout, wrap appropriately
94
- if (segment.layout) {
95
- // Check if this segment has loaders that need streaming
96
- // The layout renders immediately, LoaderBoundary becomes the outlet content
97
- // When layout renders <Outlet />, it gets the LoaderBoundary which suspends
98
- if (segment.loaderDataPromise && segment.loaderIds) {
99
- const loaderAwareContent = (
100
- <LoaderBoundary
101
- loaderDataPromise={segment.loaderDataPromise}
102
- loaderIds={segment.loaderIds}
103
- fallback={segment.loading}
104
- outletKey={segment.id + "-loader"}
105
- outletContent={null}
106
- segment={segment}
107
- >
108
- {content}
109
- </LoaderBoundary>
110
- );
111
-
112
- result = (
113
- <OutletProvider content={loaderAwareContent} segment={segment}>
114
- {segment.layout}
115
- </OutletProvider>
116
- );
117
- } else {
118
- // No loaders - wrap in OutletProvider so layout can use <Outlet />
119
- result = (
120
- <OutletProvider content={content} segment={segment}>
121
- {segment.layout}
122
- </OutletProvider>
123
- );
124
- }
125
- } else if (segment.loaderDataPromise && segment.loaderIds) {
126
- // No layout but has loaders - wrap content with LoaderBoundary for useLoader context
127
- // This is common for intercept routes that use useLoader without a custom layout
128
- result = (
129
- <LoaderBoundary
130
- loaderDataPromise={segment.loaderDataPromise}
131
- loaderIds={segment.loaderIds}
132
- fallback={segment.loading}
133
- outletKey={segment.id + "-loader"}
134
- outletContent={null}
135
- segment={segment}
136
- >
137
- {content}
138
- </LoaderBoundary>
139
- );
140
- } else {
141
- result = content;
142
- }
143
-
144
- // Wrap with MountContextProvider for include() scoped parallel/intercept slots
145
- if (segment.mountPath) {
146
- return (
147
- <MountContextProvider value={segment.mountPath}>
148
- {result}
149
- </MountContextProvider>
150
- );
151
- }
152
-
153
- return result;
149
+ return renderSlotContent(namedSegment);
154
150
  }
155
151
 
156
152
  // Default: render child content
@@ -164,6 +160,7 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
164
160
 
165
161
  return content;
166
162
  }
163
+
167
164
  /**
168
165
  * ParallelOutlet component - renders content for a named parallel slot
169
166
  *
@@ -171,6 +168,9 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
171
168
  * is wrapped in Suspense with the loading component as fallback.
172
169
  * This enables streaming and navigation loading states for parallels.
173
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
+ *
174
174
  * @param name - The slot name (must start with @, e.g., "@modal", "@sidebar")
175
175
  *
176
176
  * @example
@@ -188,101 +188,15 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
188
188
  */
189
189
  export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
190
190
  const context = useContext(OutletContext);
191
- const segment = useMemo(() => {
192
- if (!context?.parallel) return null;
193
- return context.parallel.find((seg) => seg.slot === name) ?? null;
194
- }, [context, name]);
195
-
196
- if (!segment) return null;
191
+ const segment = useSlotSegment(context, name);
197
192
 
198
- // Determine the content to render
199
- let content: ReactNode;
200
- if (segment.loading || segment.component instanceof Promise) {
201
- // Use RouteContentWrapper to handle Suspense wrapping properly
202
- content = (
203
- <RouteContentWrapper
204
- content={
205
- segment.component instanceof Promise
206
- ? segment.component
207
- : Promise.resolve(segment.component)
208
- }
209
- fallback={segment.loading}
210
- segmentId={segment.id}
211
- />
212
- );
213
- } else {
214
- content = segment.component ?? null;
215
- }
216
-
217
- let result: ReactNode;
218
-
219
- // If segment has a layout, wrap appropriately
220
- if (segment.layout) {
221
- // Check if this segment has loaders that need streaming
222
- // The layout renders immediately, LoaderBoundary becomes the outlet content
223
- if (segment.loaderDataPromise && segment.loaderIds) {
224
- const loaderAwareContent = (
225
- <LoaderBoundary
226
- loaderDataPromise={segment.loaderDataPromise}
227
- loaderIds={segment.loaderIds}
228
- fallback={segment.loading}
229
- outletKey={segment.id + "-loader"}
230
- outletContent={null}
231
- segment={segment}
232
- >
233
- {content}
234
- </LoaderBoundary>
235
- );
236
-
237
- result = (
238
- <OutletProvider content={loaderAwareContent} segment={segment}>
239
- {segment.layout}
240
- </OutletProvider>
241
- );
242
- } else {
243
- // No loaders - wrap in OutletProvider so layout can use <Outlet />
244
- result = (
245
- <OutletProvider content={content} segment={segment}>
246
- {segment.layout}
247
- </OutletProvider>
248
- );
249
- }
250
- } else if (segment.loaderDataPromise && segment.loaderIds) {
251
- // No layout but has loaders - wrap content with LoaderBoundary for useLoader context
252
- // This is common for intercept routes that use useLoader without a custom layout
253
- result = (
254
- <LoaderBoundary
255
- loaderDataPromise={segment.loaderDataPromise}
256
- loaderIds={segment.loaderIds}
257
- fallback={segment.loading}
258
- outletKey={segment.id + "-loader"}
259
- outletContent={null}
260
- segment={segment}
261
- >
262
- {content}
263
- </LoaderBoundary>
264
- );
265
- } else {
266
- result = content;
267
- }
268
-
269
- // Wrap with MountContextProvider for include() scoped parallel/intercept slots
270
- if (segment.mountPath) {
271
- return (
272
- <MountContextProvider value={segment.mountPath}>
273
- {result}
274
- </MountContextProvider>
275
- );
276
- }
277
-
278
- return result;
193
+ return renderSlotContent(segment);
279
194
  }
280
195
 
281
196
  // OutletProvider is defined in outlet-provider.tsx to break a circular
282
- // dependency between client.tsx and route-content-wrapper.tsx.
283
- // Imported at the top of this file for local use in Outlet/ParallelOutlet,
284
- // and re-exported here for backwards compatibility.
285
- 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.
286
200
 
287
201
  /**
288
202
  * Hook to access outlet content programmatically
@@ -303,113 +217,16 @@ export function useOutlet(): ReactNode {
303
217
  return context?.content ?? null;
304
218
  }
305
219
 
306
- // Loader hooks - re-exported from dedicated file
307
220
  export {
308
221
  useLoader,
309
222
  useFetchLoader,
223
+ useRefreshLoaders,
310
224
  type LoadFunction,
311
225
  type UseLoaderResult,
312
226
  type UseFetchLoaderResult,
313
227
  type UseLoaderOptions,
314
228
  } from "./use-loader.js";
315
229
 
316
- /**
317
- * Hook to access all loader data in the current context
318
- *
319
- * Returns a record of all loader data available in the current outlet context
320
- * and all parent contexts. Useful for debugging or when you need access to
321
- * multiple loaders.
322
- *
323
- * @returns Record of loader name to data, or empty object if no loaders
324
- *
325
- * @example
326
- * ```tsx
327
- * "use client";
328
- * import { useLoaderData } from "rsc-router/client";
329
- *
330
- * export function DebugPanel() {
331
- * const loaderData = useLoaderData();
332
- * return <pre>{JSON.stringify(loaderData, null, 2)}</pre>;
333
- * }
334
- * ```
335
- */
336
- export function useLoaderData(): Record<string, any> {
337
- const context = useContext(OutletContext);
338
-
339
- // Collect all loader data from the context chain
340
- // Child loaders override parent loaders with the same name
341
- const result: Record<string, any> = {};
342
- const stack: OutletContextValue[] = [];
343
-
344
- // Build stack from current to root
345
- let current: OutletContextValue | null | undefined = context;
346
- while (current) {
347
- stack.push(current);
348
- current = current.parent;
349
- }
350
-
351
- // Apply from root to current (so children override parents)
352
- for (let i = stack.length - 1; i >= 0; i--) {
353
- const ctx = stack[i];
354
- if (ctx.loaderData) {
355
- Object.assign(result, ctx.loaderData);
356
- }
357
- }
358
-
359
- return result;
360
- }
361
-
362
- /**
363
- * Client-safe createLoader factory
364
- *
365
- * Creates a loader definition that can be used with useLoader().
366
- * This is the client-side version that only stores the $$id - the function
367
- * is ignored since loaders only execute on the server.
368
- *
369
- * The $$id is injected by the exposeLoaderId Vite plugin. In most cases,
370
- * you should import the loader directly from the server file rather than
371
- * creating a reference manually.
372
- *
373
- * @param fn - Loader function (ignored on client, kept for API compatibility)
374
- * @param _fetchable - Optional fetchable flag (ignored on client)
375
- * @param __injectedId - $$id injected by Vite plugin
376
- *
377
- * @example
378
- * ```tsx
379
- * "use client";
380
- * import { useLoader } from "rsc-router/client";
381
- * import { CartLoader } from "../loaders/cart"; // Import from server file
382
- *
383
- * export function CartIcon() {
384
- * const cart = useLoader(CartLoader);
385
- * return <span>Cart ({cart?.items.length ?? 0})</span>;
386
- * }
387
- * ```
388
- */
389
- // Overload 1: With function only (not fetchable)
390
- export function createLoader<T>(
391
- fn: LoaderFn<T, Record<string, string | undefined>, any>,
392
- ): LoaderDefinition<Awaited<T>, Record<string, string | undefined>>;
393
-
394
- // Overload 2: With function and fetchable flag
395
- export function createLoader<T>(
396
- fn: LoaderFn<T, Record<string, string | undefined>, any>,
397
- fetchable: true,
398
- ): LoaderDefinition<Awaited<T>, Record<string, string | undefined>>;
399
-
400
- // Implementation - function is ignored at runtime on client
401
- // The $$id is injected by Vite plugin as hidden third parameter
402
- export function createLoader(
403
- _fn: LoaderFn<any, Record<string, string | undefined>, any>,
404
- _fetchable?: true,
405
- __injectedId?: string,
406
- ): LoaderDefinition<any, Record<string, string | undefined>> {
407
- return {
408
- __brand: "loader",
409
- $$id: __injectedId || "",
410
- };
411
- }
412
-
413
230
  /**
414
231
  * Props for the ErrorBoundary component
415
232
  */
@@ -518,80 +335,75 @@ export class ErrorBoundary extends Component<
518
335
  }
519
336
  }
520
337
 
521
- // ============================================================================
522
- // Re-exports from browser/react for convenience
523
- // These are the most commonly used client-side navigation utilities
524
- // ============================================================================
525
-
526
- // Navigation hooks
527
338
  export { useNavigation } from "./browser/react/use-navigation.js";
528
339
  export { useRouter } from "./browser/react/use-router.js";
529
340
  export { usePathname } from "./browser/react/use-pathname.js";
530
341
  export { useSearchParams } from "./browser/react/use-search-params.js";
531
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";
532
348
  export type {
533
349
  RouterInstance,
534
350
  RouterNavigateOptions,
535
351
  ReadonlyURLSearchParams,
352
+ ActionState,
353
+ ActionLifecycleState,
536
354
  } from "./browser/types.js";
537
355
 
538
- // Action state tracking hook
539
356
  export {
540
357
  useAction,
541
358
  type ServerActionFunction,
542
359
  } from "./browser/react/use-action.js";
543
360
 
544
- // Segments state hook
545
361
  export {
546
362
  useSegments,
547
363
  type SegmentsState,
548
364
  } from "./browser/react/use-segments.js";
549
365
 
550
- // Client cache controls hook
551
- export {
552
- useClientCache,
553
- type ClientCacheControls,
554
- } from "./browser/react/use-client-cache.js";
555
-
556
- // Provider
557
366
  export {
558
367
  NavigationProvider,
559
368
  type NavigationProviderProps,
560
369
  } from "./browser/react/NavigationProvider.js";
561
370
 
562
- // Link component
563
371
  export {
564
372
  Link,
565
373
  type LinkProps,
566
374
  type PrefetchStrategy,
567
375
  type StateOrGetter,
376
+ type LinkState,
568
377
  } from "./browser/react/Link.js";
569
378
 
570
- // Link status hook
571
379
  export {
572
380
  useLinkStatus,
573
381
  type LinkStatus,
574
382
  } from "./browser/react/use-link-status.js";
575
383
 
576
- // Scroll restoration
577
384
  export {
578
385
  ScrollRestoration,
579
386
  useScrollRestoration,
580
387
  type ScrollRestorationProps,
581
388
  } from "./browser/react/ScrollRestoration.js";
582
389
 
583
- // Handle API - for accumulating data across route segments
584
- export { createHandle, isHandle, type Handle } from "./handle.js";
585
-
586
- // Handle data hook
390
+ export { type Handle } from "./handle.js";
587
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";
588
395
 
589
- // Built-in handles
590
396
  export { Meta } from "./handles/meta.js";
591
397
  export { MetaTags } from "./handles/MetaTags.js";
592
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";
405
+ export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
593
406
 
594
- // Location state - type-safe navigation state
595
407
  export {
596
408
  createLocationState,
597
409
  useLocationState,
@@ -600,47 +412,19 @@ export {
600
412
  type LocationStateOptions,
601
413
  } from "./browser/react/location-state.js";
602
414
 
603
- // Type-safe href for client-side path validation
604
- export {
605
- href,
606
- type ValidPaths,
607
- type PatternToPath,
608
- type PathResponse,
609
- } from "./href-client.js";
610
-
611
- // Response envelope types for consuming JSON response routes
612
- 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";
613
417
 
614
- /**
615
- * Type guard for checking if a response envelope contains an error.
616
- *
617
- * @example
618
- * ```typescript
619
- * const result: ResponseEnvelope<Product> = await fetch(url).then(r => r.json());
620
- * if (isResponseError(result)) {
621
- * console.log(result.error.message, result.error.code);
622
- * return;
623
- * }
624
- * result.data // fully typed as Product
625
- * ```
626
- */
627
- export function isResponseError<T>(
628
- result: import("./urls.js").ResponseEnvelope<T>,
629
- ): result is import("./urls.js").ResponseEnvelope<T> & {
630
- error: import("./urls.js").ResponseError;
631
- } {
632
- return result.error !== undefined;
633
- }
418
+ // RFC 9457 error type for JSON response routes
419
+ export type { ProblemDetails } from "./urls.js";
634
420
 
635
- // Mount context for include() scoped components
636
421
  export { useMount } from "./browser/react/use-mount.js";
637
422
  export { MountContext } from "./browser/react/mount-context.js";
638
423
 
639
- // Mount-aware href hook - auto-prefixes paths with include() mount
640
424
  export { useHref } from "./browser/react/use-href.js";
641
425
 
642
- // Type-safe scoped reverse function for scopedReverse<typeof patterns>()
643
- export type { ScopedReverseFunction } from "./reverse.js";
426
+ export { useReverse } from "./browser/react/use-reverse.js";
427
+
428
+ export type { ScopedReverseFunction, LocalReverseFunction } from "./reverse.js";
644
429
 
645
- // Loader definition type - for typing loader props in client components
646
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";