@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa

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 (411) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +296 -887
  3. package/dist/bin/rango.js +459 -91
  4. package/dist/testing/vitest.js +36 -2
  5. package/dist/vite/index.js +1708 -414
  6. package/package.json +35 -10
  7. package/skills/api-client/SKILL.md +211 -0
  8. package/skills/breadcrumbs/SKILL.md +82 -5
  9. package/skills/bundle-analysis/SKILL.md +2 -2
  10. package/skills/cache-guide/SKILL.md +14 -9
  11. package/skills/caching/SKILL.md +221 -12
  12. package/skills/catalog.json +271 -0
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +83 -2
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +5 -3
  19. package/skills/defer-hydration/SKILL.md +235 -0
  20. package/skills/document-cache/SKILL.md +11 -3
  21. package/skills/fonts/SKILL.md +1 -1
  22. package/skills/handler-use/SKILL.md +9 -9
  23. package/skills/hooks/SKILL.md +73 -900
  24. package/skills/hooks/data.md +273 -0
  25. package/skills/hooks/handle-and-actions.md +103 -0
  26. package/skills/hooks/navigation.md +110 -0
  27. package/skills/hooks/outlets.md +41 -0
  28. package/skills/hooks/state.md +228 -0
  29. package/skills/hooks/urls.md +135 -0
  30. package/skills/host-router/SKILL.md +84 -7
  31. package/skills/i18n/SKILL.md +1 -1
  32. package/skills/intercept/SKILL.md +51 -17
  33. package/skills/layout/SKILL.md +38 -16
  34. package/skills/links/SKILL.md +1 -1
  35. package/skills/loader/SKILL.md +48 -20
  36. package/skills/middleware/SKILL.md +11 -5
  37. package/skills/migrate-nextjs/SKILL.md +203 -20
  38. package/skills/migrate-react-router/SKILL.md +59 -675
  39. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  40. package/skills/migrate-react-router/component-migration.md +196 -0
  41. package/skills/migrate-react-router/data-and-actions.md +225 -0
  42. package/skills/migrate-react-router/route-mapping.md +271 -0
  43. package/skills/mime-routes/SKILL.md +3 -3
  44. package/skills/observability/SKILL.md +70 -5
  45. package/skills/parallel/SKILL.md +32 -8
  46. package/skills/ppr/SKILL.md +622 -0
  47. package/skills/prerender/SKILL.md +59 -28
  48. package/skills/rango/SKILL.md +124 -50
  49. package/skills/response-routes/SKILL.md +78 -46
  50. package/skills/route/SKILL.md +85 -6
  51. package/skills/router-setup/SKILL.md +41 -6
  52. package/skills/scripts/SKILL.md +179 -0
  53. package/skills/server-actions/SKILL.md +28 -3
  54. package/skills/shell-manifest/SKILL.md +185 -0
  55. package/skills/streams-and-websockets/SKILL.md +1 -1
  56. package/skills/tailwind/SKILL.md +28 -4
  57. package/skills/testing/SKILL.md +68 -654
  58. package/skills/testing/bindings.md +103 -0
  59. package/skills/testing/cache-prerender.md +127 -0
  60. package/skills/testing/client-components.md +124 -0
  61. package/skills/testing/e2e-parity.md +125 -0
  62. package/skills/testing/flight.md +91 -0
  63. package/skills/testing/handles.md +131 -0
  64. package/skills/testing/loader.md +128 -0
  65. package/skills/testing/middleware.md +99 -0
  66. package/skills/testing/render-handler.md +122 -0
  67. package/skills/testing/response-routes.md +95 -0
  68. package/skills/testing/reverse-and-types.md +85 -0
  69. package/skills/testing/server-actions.md +107 -0
  70. package/skills/testing/server-tree.md +128 -0
  71. package/skills/testing/setup.md +123 -0
  72. package/skills/theme/SKILL.md +1 -1
  73. package/skills/typesafety/SKILL.md +45 -918
  74. package/skills/typesafety/env-and-bindings.md +254 -0
  75. package/skills/typesafety/generated-files-and-cli.md +335 -0
  76. package/skills/typesafety/params-and-search.md +153 -0
  77. package/skills/typesafety/route-types.md +209 -0
  78. package/skills/use-cache/SKILL.md +47 -17
  79. package/skills/vercel/SKILL.md +128 -0
  80. package/skills/view-transitions/SKILL.md +44 -1
  81. package/src/__augment-tests__/augmented.check.ts +2 -3
  82. package/src/__internal.ts +0 -65
  83. package/src/browser/action-coordinator.ts +1 -1
  84. package/src/browser/action-fence.ts +47 -0
  85. package/src/browser/app-shell.ts +14 -27
  86. package/src/browser/connection-warmup.ts +134 -0
  87. package/src/browser/cookie-name.ts +140 -0
  88. package/src/browser/event-controller.ts +178 -100
  89. package/src/browser/invalidate-client-cache.ts +52 -0
  90. package/src/browser/logging.ts +28 -0
  91. package/src/browser/merge-segment-loaders.ts +6 -4
  92. package/src/browser/navigation-bridge.ts +81 -68
  93. package/src/browser/navigation-client.ts +115 -70
  94. package/src/browser/navigation-store-handle.ts +38 -0
  95. package/src/browser/navigation-store.ts +153 -88
  96. package/src/browser/navigation-transaction.ts +0 -32
  97. package/src/browser/network-error-handler.ts +34 -7
  98. package/src/browser/partial-update.ts +157 -144
  99. package/src/browser/prefetch/cache.ts +148 -81
  100. package/src/browser/prefetch/fetch.ts +231 -51
  101. package/src/browser/prefetch/queue.ts +25 -7
  102. package/src/browser/rango-state.ts +157 -115
  103. package/src/browser/react/Link.tsx +40 -7
  104. package/src/browser/react/NavigationProvider.tsx +140 -99
  105. package/src/browser/react/ScrollRestoration.tsx +10 -6
  106. package/src/browser/react/filter-segment-order.ts +17 -2
  107. package/src/browser/react/index.ts +0 -51
  108. package/src/browser/react/location-state-shared.ts +14 -15
  109. package/src/browser/react/location-state.ts +0 -1
  110. package/src/browser/react/use-action.ts +6 -15
  111. package/src/browser/react/use-handle.ts +0 -5
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +33 -8
  114. package/src/browser/react/use-navigation.ts +10 -5
  115. package/src/browser/react/use-params.ts +0 -2
  116. package/src/browser/react/use-router.ts +6 -4
  117. package/src/browser/react/use-search-params.ts +0 -5
  118. package/src/browser/react/use-segments.ts +0 -13
  119. package/src/browser/response-adapter.ts +74 -8
  120. package/src/browser/rsc-router.tsx +97 -22
  121. package/src/browser/scroll-restoration.ts +15 -8
  122. package/src/browser/segment-reconciler.ts +31 -21
  123. package/src/browser/server-action-bridge.ts +216 -38
  124. package/src/browser/types.ts +94 -22
  125. package/src/browser/validate-redirect-origin.ts +43 -16
  126. package/src/build/generate-manifest.ts +155 -131
  127. package/src/build/generate-route-types.ts +1 -1
  128. package/src/build/index.ts +11 -5
  129. package/src/build/prefix-tree-utils.ts +123 -0
  130. package/src/build/route-trie.ts +152 -22
  131. package/src/build/route-types/ast-route-extraction.ts +15 -8
  132. package/src/build/route-types/codegen.ts +12 -1
  133. package/src/build/route-types/include-resolution.ts +455 -61
  134. package/src/build/route-types/param-extraction.ts +6 -3
  135. package/src/build/route-types/per-module-writer.ts +15 -2
  136. package/src/build/route-types/router-processing.ts +77 -41
  137. package/src/build/route-types/source-scan.ts +105 -7
  138. package/src/build/runtime-discovery.ts +4 -1
  139. package/src/cache/cache-error.ts +104 -0
  140. package/src/cache/cache-key-utils.ts +58 -13
  141. package/src/cache/cache-policy.ts +108 -34
  142. package/src/cache/cache-runtime.ts +454 -101
  143. package/src/cache/cache-scope.ts +159 -54
  144. package/src/cache/cache-tag.ts +149 -0
  145. package/src/cache/cf/cf-base64.ts +33 -0
  146. package/src/cache/cf/cf-cache-constants.ts +127 -0
  147. package/src/cache/cf/cf-cache-store.ts +2170 -377
  148. package/src/cache/cf/cf-cache-types.ts +349 -0
  149. package/src/cache/cf/cf-kv-utils.ts +46 -0
  150. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  151. package/src/cache/cf/index.ts +6 -16
  152. package/src/cache/document-cache.ts +126 -41
  153. package/src/cache/handle-snapshot.ts +70 -0
  154. package/src/cache/index.ts +23 -20
  155. package/src/cache/memory-segment-store.ts +243 -37
  156. package/src/cache/profile-registry.ts +46 -31
  157. package/src/cache/read-through-swr.ts +56 -12
  158. package/src/cache/segment-codec.ts +13 -21
  159. package/src/cache/shell-snapshot.ts +417 -0
  160. package/src/cache/tag-invalidation.ts +230 -0
  161. package/src/cache/types.ts +194 -99
  162. package/src/cache/vercel/index.ts +11 -0
  163. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  164. package/src/client.rsc.tsx +39 -22
  165. package/src/client.tsx +28 -58
  166. package/src/cloudflare/index.ts +11 -0
  167. package/src/cloudflare/tracing.ts +108 -0
  168. package/src/component-utils.ts +19 -0
  169. package/src/components/DefaultDocument.tsx +8 -2
  170. package/src/context-var.ts +13 -1
  171. package/src/decode-loader-results.ts +18 -2
  172. package/src/defer.ts +185 -0
  173. package/src/deps/ssr.ts +0 -1
  174. package/src/encode-kv.ts +49 -0
  175. package/src/errors.ts +0 -3
  176. package/src/escape-script.ts +52 -0
  177. package/src/handle.ts +57 -40
  178. package/src/handles/MetaTags.tsx +24 -53
  179. package/src/handles/Scripts.tsx +183 -0
  180. package/src/handles/breadcrumbs.ts +35 -8
  181. package/src/handles/deferred-resolution.ts +127 -0
  182. package/src/handles/is-thenable.ts +18 -0
  183. package/src/handles/meta.ts +14 -40
  184. package/src/handles/script.ts +244 -0
  185. package/src/host/cookie-handler.ts +9 -60
  186. package/src/host/errors.ts +13 -22
  187. package/src/host/index.ts +7 -0
  188. package/src/host/pattern-matcher.ts +23 -52
  189. package/src/host/router.ts +1 -65
  190. package/src/host/testing.ts +40 -27
  191. package/src/host/types.ts +6 -2
  192. package/src/href-client.ts +7 -12
  193. package/src/index.rsc.ts +88 -8
  194. package/src/index.ts +90 -16
  195. package/src/internal-debug.ts +11 -10
  196. package/src/loader.rsc.ts +19 -9
  197. package/src/loader.ts +12 -4
  198. package/src/outlet-provider.tsx +1 -5
  199. package/src/prerender/param-hash.ts +16 -16
  200. package/src/prerender/store.ts +32 -37
  201. package/src/prerender.ts +75 -7
  202. package/src/redirect-origin.ts +114 -0
  203. package/src/regex-escape.ts +8 -0
  204. package/src/render-error-thrower.tsx +20 -0
  205. package/src/response-utils.ts +25 -0
  206. package/src/root-error-boundary.tsx +1 -19
  207. package/src/route-content-wrapper.tsx +13 -49
  208. package/src/route-definition/dsl-helpers.ts +60 -53
  209. package/src/route-definition/helper-factories.ts +0 -2
  210. package/src/route-definition/helpers-types.ts +46 -46
  211. package/src/route-definition/index.ts +1 -2
  212. package/src/route-definition/redirect.ts +44 -11
  213. package/src/route-definition/resolve-handler-use.ts +6 -1
  214. package/src/route-definition/use-item-types.ts +3 -6
  215. package/src/route-map-builder.ts +41 -20
  216. package/src/route-types.ts +0 -5
  217. package/src/router/content-negotiation.ts +58 -23
  218. package/src/router/error-handling.ts +44 -17
  219. package/src/router/find-match.ts +129 -30
  220. package/src/router/handler-context.ts +6 -1
  221. package/src/router/instrument.ts +355 -0
  222. package/src/router/intercept-resolution.ts +35 -2
  223. package/src/router/lazy-includes.ts +79 -56
  224. package/src/router/loader-resolution.ts +151 -73
  225. package/src/router/logging.ts +0 -6
  226. package/src/router/manifest.ts +74 -40
  227. package/src/router/match-api.ts +76 -52
  228. package/src/router/match-context.ts +0 -22
  229. package/src/router/match-handlers.ts +181 -178
  230. package/src/router/match-middleware/background-revalidation.ts +40 -24
  231. package/src/router/match-middleware/cache-lookup.ts +115 -194
  232. package/src/router/match-middleware/cache-store.ts +61 -50
  233. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  234. package/src/router/match-middleware/segment-resolution.ts +0 -22
  235. package/src/router/match-pipelines.ts +1 -42
  236. package/src/router/match-result.ts +36 -67
  237. package/src/router/metrics.ts +0 -34
  238. package/src/router/middleware-types.ts +0 -116
  239. package/src/router/middleware.ts +231 -120
  240. package/src/router/navigation-snapshot.ts +7 -56
  241. package/src/router/params-util.ts +23 -0
  242. package/src/router/parse-pattern.ts +115 -0
  243. package/src/router/pattern-matching.ts +99 -152
  244. package/src/router/prefetch-cache-ttl.ts +51 -0
  245. package/src/router/prefetch-limits.ts +37 -0
  246. package/src/router/prerender-match.ts +111 -66
  247. package/src/router/preview-match.ts +3 -1
  248. package/src/router/request-classification.ts +47 -42
  249. package/src/router/revalidation.ts +75 -81
  250. package/src/router/route-snapshot.ts +14 -3
  251. package/src/router/router-context.ts +6 -29
  252. package/src/router/router-interfaces.ts +70 -8
  253. package/src/router/router-options.ts +126 -4
  254. package/src/router/segment-resolution/fresh.ts +104 -80
  255. package/src/router/segment-resolution/helpers.ts +86 -6
  256. package/src/router/segment-resolution/loader-cache.ts +155 -39
  257. package/src/router/segment-resolution/loader-mask.ts +60 -0
  258. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  259. package/src/router/segment-resolution/mask-nested.ts +83 -0
  260. package/src/router/segment-resolution/revalidation.ts +215 -304
  261. package/src/router/segment-resolution/static-store.ts +19 -5
  262. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  263. package/src/router/segment-resolution/view-transition-default.ts +35 -15
  264. package/src/router/segment-resolution.ts +5 -1
  265. package/src/router/segment-wrappers.ts +6 -5
  266. package/src/router/state-cookie-name.ts +33 -0
  267. package/src/router/substitute-pattern-params.ts +54 -35
  268. package/src/router/telemetry-otel.ts +160 -200
  269. package/src/router/telemetry.ts +9 -23
  270. package/src/router/timeout.ts +0 -20
  271. package/src/router/tracing.ts +215 -0
  272. package/src/router/trie-matching.ts +171 -64
  273. package/src/router/types.ts +1 -63
  274. package/src/router/url-params.ts +13 -5
  275. package/src/router.ts +119 -48
  276. package/src/rsc/full-payload.ts +70 -0
  277. package/src/rsc/handler-context.ts +1 -0
  278. package/src/rsc/handler.ts +267 -152
  279. package/src/rsc/helpers.ts +78 -4
  280. package/src/rsc/index.ts +1 -4
  281. package/src/rsc/json-route-result.ts +38 -0
  282. package/src/rsc/loader-fetch.ts +114 -38
  283. package/src/rsc/manifest-init.ts +29 -42
  284. package/src/rsc/nonce.ts +10 -1
  285. package/src/rsc/origin-guard.ts +11 -15
  286. package/src/rsc/progressive-enhancement.ts +120 -13
  287. package/src/rsc/redirect-guard.ts +100 -0
  288. package/src/rsc/response-cache-serve.ts +238 -0
  289. package/src/rsc/response-error.ts +79 -12
  290. package/src/rsc/response-route-handler.ts +58 -141
  291. package/src/rsc/rsc-rendering.ts +492 -49
  292. package/src/rsc/runtime-warnings.ts +14 -0
  293. package/src/rsc/server-action.ts +268 -82
  294. package/src/rsc/shell-capture.ts +1190 -0
  295. package/src/rsc/shell-serve.ts +181 -0
  296. package/src/rsc/transition-gate.ts +89 -0
  297. package/src/rsc/types.ts +45 -3
  298. package/src/runtime-env.ts +18 -0
  299. package/src/search-params.ts +31 -26
  300. package/src/segment-loader-promise.ts +49 -4
  301. package/src/segment-system.tsx +260 -95
  302. package/src/server/context.ts +99 -9
  303. package/src/server/cookie-parse.ts +32 -0
  304. package/src/server/cookie-store.ts +125 -2
  305. package/src/server/handle-store.ts +21 -38
  306. package/src/server/loader-registry.ts +33 -42
  307. package/src/server/request-context.ts +379 -138
  308. package/src/ssr/index.tsx +491 -182
  309. package/src/ssr/inject-rsc-eager.ts +167 -0
  310. package/src/ssr/ssr-root.tsx +228 -0
  311. package/src/static-handler.ts +10 -13
  312. package/src/testing/cache-status.ts +44 -48
  313. package/src/testing/collect-handle.ts +14 -31
  314. package/src/testing/dispatch.ts +533 -160
  315. package/src/testing/e2e/fixture.ts +45 -11
  316. package/src/testing/e2e/index.ts +1 -22
  317. package/src/testing/e2e/matchers.ts +0 -16
  318. package/src/testing/e2e/parity.ts +85 -4
  319. package/src/testing/e2e/server.ts +12 -0
  320. package/src/testing/flight-matchers.ts +7 -14
  321. package/src/testing/flight-normalize.ts +11 -0
  322. package/src/testing/flight-runtime.d.ts +36 -0
  323. package/src/testing/flight-tree.ts +682 -0
  324. package/src/testing/flight.entry.ts +30 -0
  325. package/src/testing/flight.ts +145 -70
  326. package/src/testing/generated-routes.ts +26 -50
  327. package/src/testing/index.ts +18 -19
  328. package/src/testing/internal/context.ts +184 -68
  329. package/src/testing/internal/flight-client-globals.ts +30 -0
  330. package/src/testing/internal/seed-vars.ts +54 -0
  331. package/src/testing/render-handler.ts +357 -0
  332. package/src/testing/render-route.tsx +134 -115
  333. package/src/testing/run-loader.ts +140 -51
  334. package/src/testing/run-middleware.ts +59 -33
  335. package/src/testing/run-transition-when.ts +164 -0
  336. package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
  337. package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
  338. package/src/testing/vitest.ts +138 -16
  339. package/src/theme/ThemeProvider.tsx +56 -84
  340. package/src/theme/ThemeScript.tsx +7 -9
  341. package/src/theme/constants.ts +52 -13
  342. package/src/theme/index.ts +0 -7
  343. package/src/theme/theme-context.ts +1 -5
  344. package/src/theme/theme-script.ts +22 -21
  345. package/src/theme/use-theme.ts +0 -3
  346. package/src/types/boundaries.ts +0 -35
  347. package/src/types/cache-types.ts +13 -4
  348. package/src/types/error-types.ts +30 -90
  349. package/src/types/global-namespace.ts +15 -15
  350. package/src/types/handler-context.ts +45 -15
  351. package/src/types/index.ts +2 -10
  352. package/src/types/loader-types.ts +6 -3
  353. package/src/types/request-scope.ts +8 -22
  354. package/src/types/route-config.ts +20 -52
  355. package/src/types/route-entry.ts +0 -6
  356. package/src/types/segments.ts +100 -13
  357. package/src/urls/include-helper.ts +10 -12
  358. package/src/urls/include-provider.ts +71 -0
  359. package/src/urls/index.ts +2 -8
  360. package/src/urls/path-helper-types.ts +52 -14
  361. package/src/urls/path-helper.ts +5 -54
  362. package/src/urls/pattern-types.ts +36 -0
  363. package/src/urls/type-extraction.ts +76 -42
  364. package/src/urls/urls-function.ts +0 -14
  365. package/src/use-loader.tsx +0 -186
  366. package/src/vercel/index.ts +11 -0
  367. package/src/vercel/tracing.ts +88 -0
  368. package/src/vite/discovery/bundle-postprocess.ts +2 -1
  369. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  370. package/src/vite/discovery/discover-routers.ts +34 -43
  371. package/src/vite/discovery/discovery-errors.ts +61 -0
  372. package/src/vite/discovery/prerender-collection.ts +33 -46
  373. package/src/vite/discovery/state.ts +12 -1
  374. package/src/vite/discovery/virtual-module-codegen.ts +1 -11
  375. package/src/vite/index.ts +9 -0
  376. package/src/vite/inject-client-debug.ts +88 -0
  377. package/src/vite/plugin-types.ts +143 -10
  378. package/src/vite/plugins/cjs-to-esm.ts +8 -12
  379. package/src/vite/plugins/client-ref-dedup.ts +0 -11
  380. package/src/vite/plugins/client-ref-hashing.ts +0 -10
  381. package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
  382. package/src/vite/plugins/expose-action-id.ts +2 -73
  383. package/src/vite/plugins/expose-id-utils.ts +85 -56
  384. package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
  385. package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
  386. package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
  387. package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
  388. package/src/vite/plugins/expose-internal-ids.ts +10 -1
  389. package/src/vite/plugins/performance-tracks.ts +0 -3
  390. package/src/vite/plugins/refresh-cmd.ts +1 -1
  391. package/src/vite/plugins/use-cache-transform.ts +21 -46
  392. package/src/vite/plugins/vercel-output.ts +384 -0
  393. package/src/vite/plugins/version-injector.ts +22 -27
  394. package/src/vite/plugins/version-plugin.ts +6 -66
  395. package/src/vite/plugins/virtual-entries.ts +137 -26
  396. package/src/vite/rango.ts +146 -135
  397. package/src/vite/router-discovery.ts +189 -48
  398. package/src/vite/utils/ast-handler-extract.ts +11 -20
  399. package/src/vite/utils/bundle-analysis.ts +6 -13
  400. package/src/vite/utils/client-chunks.ts +0 -6
  401. package/src/vite/utils/directive-prologue.ts +40 -0
  402. package/src/vite/utils/forward-user-plugins.ts +0 -22
  403. package/src/vite/utils/manifest-utils.ts +4 -75
  404. package/src/vite/utils/package-resolution.ts +1 -73
  405. package/src/vite/utils/prerender-utils.ts +71 -44
  406. package/src/vite/utils/shared-utils.ts +55 -37
  407. package/src/browser/react/use-client-cache.ts +0 -58
  408. package/src/browser/shallow.ts +0 -40
  409. package/src/handles/index.ts +0 -7
  410. package/src/network-error-thrower.tsx +0 -23
  411. package/src/router/middleware-cookies.ts +0 -55
@@ -12,6 +12,12 @@
12
12
  * `toMatchFlightSnapshot` matchers (which import `vitest`) live at the separate
13
13
  * `@rangojs/router/testing/flight-matchers` subpath, so a consumer can import
14
14
  * `renderToFlightString` without taking a hard dependency on Vitest.
15
+ *
16
+ * `renderToFlightString` returns the wire STRING (for `toMatchFlight`).
17
+ * `renderServerTree` additionally deserializes it back to an inspectable React
18
+ * element tree, so you can assert typed prop fidelity across the client boundary
19
+ * (a `Date` comes back a `Date`) and detect inlined-vs-island. Serialize +
20
+ * deserialize only — no hydration/interaction (that is the e2e tier).
15
21
  */
16
22
 
17
23
  export {
@@ -20,3 +26,27 @@ export {
20
26
  assertFlightRuntimeAvailable,
21
27
  } from "./flight.js";
22
28
  export type { RenderToFlightStringOptions } from "./flight.js";
29
+
30
+ export {
31
+ renderServerTree,
32
+ findClientBoundaries,
33
+ findElements,
34
+ textContent,
35
+ assertFlightTreeRuntimeAvailable,
36
+ } from "./flight-tree.js";
37
+ export type {
38
+ RenderServerTreeOptions,
39
+ RenderServerTreeResult,
40
+ ClientBoundary,
41
+ BoundarySelector,
42
+ FoundElement,
43
+ ElementSelector,
44
+ } from "./flight-tree.js";
45
+
46
+ export { renderHandler } from "./render-handler.js";
47
+ export type {
48
+ TestableHandler,
49
+ RenderHandlerOptions,
50
+ RenderHandlerResult,
51
+ StateCookieSeed,
52
+ } from "./render-handler.js";
@@ -19,9 +19,9 @@
19
19
  * Scope / limitations (v1):
20
20
  * - Server-only / leaf trees. A tree containing a CLIENT component emits an
21
21
  * `I[...]` import row whose module id will not resolve against the empty `{}`
22
- * client manifest used here — fine for snapshotting the SHAPE of the payload,
23
- * but the client reference cannot be executed/hydrated. The interactive DOM
24
- * render (`renderServer`) is deferred (see module TODO at bottom of report).
22
+ * client manifest used here — fine for snapshotting the SHAPE of the payload.
23
+ * To inspect a client boundary's deserialized props instead, use
24
+ * `renderServerTree` (flight-tree.ts). Interactive hydration stays at the e2e tier.
25
25
  * - The vendored subpath is a private plugin-rsc path; a minor bump could move
26
26
  * it. `assertFlightRuntimeAvailable()` provides a smoke check.
27
27
  * - For stable snapshots, run under NODE_ENV=production: the production
@@ -41,16 +41,32 @@ import {
41
41
  runWithRequestContext,
42
42
  setRequestContextParams,
43
43
  } from "../server/request-context.js";
44
+ import { seedVariables, type VarsInit } from "./internal/seed-vars.js";
45
+ import { normalizeFlight } from "./flight-normalize.js";
46
+ import { resolveThemeConfig } from "../theme/constants.js";
47
+ import type { ThemeConfig } from "../theme/types.js";
48
+ import type { SegmentCacheStore } from "../cache/types.js";
49
+ import type { CacheProfile } from "../cache/profile-registry.js";
44
50
  import type { RscPayload } from "../rsc/types.js";
45
51
  import type { ResolvedSegment } from "../types.js";
46
52
 
53
+ // Re-export from the serializer-free module so this entry's public surface is
54
+ // unchanged while flight-matchers can import normalizeFlight without pulling in
55
+ // the vendored serializer (which throws outside the react-server condition).
56
+ export { normalizeFlight };
57
+
47
58
  /**
48
59
  * Options for {@link renderToFlightString}.
49
60
  */
50
61
  export interface RenderToFlightStringOptions {
51
- /** Request URL. Defaults to `http://localhost/`. */
52
- url?: string;
53
- /** Request headers (e.g. Cookie) visible to the server tree. */
62
+ /**
63
+ * The request the render runs under: a `Request`, or a URL string (absolute or
64
+ * path). Defaults to `http://localhost/`. A server component reading
65
+ * `getRequestContext()` sees this request's url/cookies. When a `Request` is
66
+ * passed, its headers are used and `headers` below is ignored.
67
+ */
68
+ request?: Request | string;
69
+ /** Request headers (e.g. Cookie) visible to the server tree (when `request` is a string). */
54
70
  headers?: HeadersInit;
55
71
  /** Env / bindings exposed as `ctx.env`. Defaults to `{}`. */
56
72
  env?: unknown;
@@ -58,15 +74,92 @@ export interface RenderToFlightStringOptions {
58
74
  params?: Record<string, string>;
59
75
  /** Matched route name (drives `ctx.routeName` and scoped reverse). */
60
76
  routeName?: string;
77
+ /**
78
+ * Route name -> pattern map enabling a SCOPED `ctx.reverse()` (like
79
+ * `renderHandler`). Without it, a server component that reverses resolves
80
+ * against the GLOBAL route map and is order-dependent on whatever router
81
+ * registered last. Pass the router-under-test's map to make reversing
82
+ * deterministic.
83
+ */
84
+ routeMap?: Record<string, string>;
85
+ /**
86
+ * Context variables visible to the rendered tree via `ctx.get(...)` — as a
87
+ * prior middleware would have set them. Seeds the SAME way the handler-test
88
+ * primitives (`runInRequestContext`/`runLoader`) do, so a server component
89
+ * that reads `getRequestContext().get(MyVar)` during render is testable.
90
+ * Object form (`{ user }`) or `[key, value]` tuples (`[[userVar, u]]`).
91
+ */
92
+ vars?: VarsInit;
93
+ /**
94
+ * Theme config in the same shape `createRouter({ theme })` takes (e.g. `true`
95
+ * or `{ themes: [...] }`). Without it `getRequestContext().theme` is `undefined`
96
+ * and `ctx.setTheme` is inert — pass one to render a server component that
97
+ * reads `ctx.theme`. Threaded into the SAME createRequestContext renderHandler
98
+ * uses, so the two Flight primitives expose theme identically.
99
+ */
100
+ theme?: ThemeConfig | true;
101
+ /**
102
+ * Cache store backing a `"use cache"` function the rendered server tree
103
+ * invokes. Without it, `registerCachedFunction` takes the uncached bypass and
104
+ * the cached path is NOT exercised. Pair with `cacheProfiles` so a
105
+ * `"use cache: profileName"` directive resolves its profile.
106
+ */
107
+ cacheStore?: SegmentCacheStore;
108
+ /** Cache profiles in the `createRouter({ cacheProfiles })` shape. */
109
+ cacheProfiles?: Record<string, CacheProfile>;
61
110
  }
62
111
 
63
112
  const DEFAULT_URL = "http://localhost/";
64
113
 
65
114
  /**
66
- * Wrap a single element in the minimal ResolvedSegment + RscPayload shape that
67
- * mirrors Rango's wire format, so the serialized output matches what a real
68
- * route segment would emit.
115
+ * True when `error` is the out-of-react-server stub thrown by index.ts's
116
+ * server-only exports (getRequestContext/cookies/headers/...) i.e. the bare
117
+ * `@rangojs/router` specifier resolved to index.ts, not index.rsc.ts, because
118
+ * the rsc Vitest project is missing the `rangoTestAliases` alias. Matches both
119
+ * substrings of `serverOnlyStubError` (index.ts) so a normal app error cannot
120
+ * over-match. Shared with render-handler.ts so the two Flight primitives report
121
+ * the same misconfiguration identically.
122
+ */
123
+ export function isServerOnlyStubError(error: unknown): boolean {
124
+ return (
125
+ error instanceof Error &&
126
+ error.message.includes("is only available from") &&
127
+ error.message.includes("react-server")
128
+ );
129
+ }
130
+
131
+ /**
132
+ * Rethrow a server tree render error. When it is the missing-rsc-alias stub
133
+ * (above), rethrow an actionable message naming `rangoTestAliases` instead of
134
+ * the opaque stub text; otherwise rethrow the original unchanged. Classify the
135
+ * ORIGINAL error before constructing the wrapper so the wrapper's `Original: ...`
136
+ * echo (which re-embeds the matched substrings) never re-triggers the predicate.
69
137
  */
138
+ function rethrowFlightRenderError(error: unknown): never {
139
+ if (isServerOnlyStubError(error)) {
140
+ throw new Error(
141
+ `The server component called a server-only API ` +
142
+ `(getRequestContext/cookies/headers/...) but "@rangojs/router" resolved to ` +
143
+ `the out-of-react-server stub. Add rangoTestAliases({ preset }) to your ` +
144
+ `vitest.rsc.config.ts \`resolve.alias\` so the bare specifier maps to ` +
145
+ `index.rsc.ts (the real react-server implementations). ` +
146
+ `Original: ${(error as Error).message}`,
147
+ );
148
+ }
149
+ throw error;
150
+ }
151
+
152
+ export function assertNoLegacyUrlOption(opts: object, fnName: string): void {
153
+ if ("url" in opts) {
154
+ throw new Error(
155
+ `${fnName}: the \`url\` option was renamed to \`request\`. Pass ` +
156
+ `{ request: "<url-or-path>" } (or a Request) instead of { url }. ` +
157
+ `The legacy \`url\` key is ignored, so the render would silently use ` +
158
+ `the default origin.`,
159
+ );
160
+ }
161
+ }
162
+
70
163
  function wrapAsPayload(element: ReactNode, pathname: string): RscPayload {
71
164
  const segment: ResolvedSegment = {
72
165
  id: "test",
@@ -98,78 +191,60 @@ export async function renderToFlightString(
98
191
  element: ReactNode,
99
192
  opts: RenderToFlightStringOptions = {},
100
193
  ): Promise<string> {
101
- const url = new URL(opts.url ?? DEFAULT_URL);
102
- const request = new Request(url, { headers: opts.headers });
194
+ assertNoLegacyUrlOption(opts, "renderToFlightString");
195
+ return serializeToFlightString(element, opts, {});
196
+ }
197
+
198
+ export async function serializeToFlightString(
199
+ element: ReactNode,
200
+ opts: RenderToFlightStringOptions,
201
+ clientManifest: unknown,
202
+ ): Promise<string> {
203
+ const request =
204
+ opts.request instanceof Request
205
+ ? opts.request
206
+ : new Request(new URL(opts.request ?? DEFAULT_URL, DEFAULT_URL), {
207
+ headers: opts.headers,
208
+ });
209
+ const url = new URL(request.url);
103
210
  const ctx = createRequestContext({
104
211
  env: opts.env ?? {},
105
212
  request,
106
213
  url,
107
- variables: {},
214
+ variables: seedVariables({}, opts.vars),
215
+ themeConfig:
216
+ opts.theme === undefined ? undefined : resolveThemeConfig(opts.theme),
217
+ cacheStore: opts.cacheStore,
218
+ cacheProfiles: opts.cacheProfiles,
108
219
  });
109
220
 
110
- const payload = wrapAsPayload(element, url.pathname);
111
-
112
- return runWithRequestContext(ctx, async () => {
113
- setRequestContextParams(opts.params ?? {}, opts.routeName);
114
- // Capture (do NOT rethrow) the first render error. The serializer calls
115
- // onError from its own scheduled work; throwing there escapes as an
116
- // unhandled rejection AND leaves the stream un-closed, so the drain below
117
- // would hang until the test times out. Production's onError returns void
118
- // (rsc-rendering.ts) so the stream completes with an error row. We mirror
119
- // that — let the stream finish — then surface the error as a clean
120
- // rejection after draining, so `await expect(...).rejects.toThrow()` works.
121
- let renderError: unknown;
122
- let didError = false;
123
- const stream = RSDServer.renderToReadableStream(
124
- payload,
125
- {},
126
- {
127
- onError(error: unknown) {
128
- if (!didError) {
129
- didError = true;
130
- renderError = error;
131
- }
132
- },
133
- },
134
- );
135
- // Drain inside the context so async components see ctx during streaming.
136
- const text = await new Response(stream).text();
137
- if (didError) throw renderError;
138
- return text;
221
+ return runWithRequestContext(ctx, () => {
222
+ setRequestContextParams(opts.params ?? {}, opts.routeName, opts.routeMap);
223
+ return serializeNodeToFlight(element, clientManifest, url.pathname);
139
224
  });
140
225
  }
141
226
 
142
- // Volatile leading reference row: `:N<timestamp>` (dev debug-info anchor).
143
- const REFERENCE_ROW_RE = /^:N[\d.]+\n/;
144
- // Absolute file:// paths embedded in dev STACK rows. The serializer emits stack
145
- // frames as `["Component","file:///abs/path.tsx",<line>,<col>,...]`, so the
146
- // path is a quoted JSON string immediately followed by `",<line>,<col>`. The
147
- // lookahead scopes the scrub to exactly that frame shape, leaving a legitimate
148
- // `file://` href in RENDERED content (e.g. `{"href":"file:///x"}`) untouched.
149
- const FILE_URL_RE = /file:\/\/[^"\\]+(?=",\d+,\d+)/g;
150
-
151
- /**
152
- * Scrub volatile bits from a Flight string so snapshots are stable across runs
153
- * and machines:
154
- * - the leading `:N<timestamp>` reference row (dev only),
155
- * - absolute `file://...` paths inside dev stack rows.
156
- *
157
- * Under NODE_ENV=production these rows are already absent; normalize is a
158
- * no-op safety net there. In dev mode it removes the machine/clock-specific
159
- * noise while leaving the rendered tree intact.
160
- */
161
- export function normalizeFlight(flight: string): string {
162
- return flight
163
- .replace(REFERENCE_ROW_RE, "")
164
- .replace(FILE_URL_RE, "file://<path>");
227
+ export async function serializeNodeToFlight(
228
+ node: ReactNode,
229
+ clientManifest: unknown,
230
+ pathname: string,
231
+ ): Promise<string> {
232
+ const payload = wrapAsPayload(node, pathname);
233
+ let renderError: unknown;
234
+ let didError = false;
235
+ const stream = RSDServer.renderToReadableStream(payload, clientManifest, {
236
+ onError(error: unknown) {
237
+ if (!didError) {
238
+ didError = true;
239
+ renderError = error;
240
+ }
241
+ },
242
+ });
243
+ const text = await new Response(stream).text();
244
+ if (didError) rethrowFlightRenderError(renderError);
245
+ return text;
165
246
  }
166
247
 
167
- /**
168
- * Smoke check that the vendored serializer subpath still resolves and exposes
169
- * `renderToReadableStream`. The vendored path is private to plugin-rsc; a minor
170
- * bump could relocate it. Call this in a test to fail loudly with a clear
171
- * message instead of an opaque import error.
172
- */
173
248
  export function assertFlightRuntimeAvailable(): void {
174
249
  if (typeof RSDServer.renderToReadableStream !== "function") {
175
250
  throw new Error(
@@ -29,17 +29,13 @@ import { isAutoGeneratedRouteName } from "../route-name.js";
29
29
  */
30
30
  interface RouterWithRouteMap {
31
31
  routeMap: Record<string, unknown>;
32
+ // findMatch is async (it may await an async include provider's import before
33
+ // the route's names are spliced into routeMap), so expandLazyIncludes awaits
34
+ // it. Typed `unknown` (not `unknown | Promise<unknown>`, which collapses to
35
+ // the same thing) — the awaited value is never read, only its side effect.
32
36
  findMatch?: (pathname: string) => unknown;
33
37
  }
34
38
 
35
- /**
36
- * Derive a best-effort concrete path from a route pattern so `findMatch` can be
37
- * invoked to expand a lazy include. `:param`, `:param(constraint)`, optional
38
- * `:param?`, and `*` are all replaced with a literal segment. A constrained
39
- * param may not match its constraint (so that one route's match fails), but
40
- * since matching ANY route in an include expands ALL of the include's routes,
41
- * a sibling route in the same include will still trigger expansion.
42
- */
43
39
  function concretePath(pattern: string): string {
44
40
  return (
45
41
  pattern
@@ -50,29 +46,27 @@ function concretePath(pattern: string): string {
50
46
  );
51
47
  }
52
48
 
53
- /**
54
- * Force-expand the router's lazy `include()`d routes into `router.routeMap`.
55
- *
56
- * All Rango includes are lazy — their child routes only populate `routeMap` when
57
- * the router first matches a path inside them (in production the build-time
58
- * manifest virtual carries the full map; in a bare test that virtual is absent).
59
- * To make the whole-app drift check work in a unit test, we trigger expansion by
60
- * calling `findMatch` on a concrete path derived from each known pattern. This is
61
- * idempotent and side-effect-free beyond populating the route map. Routers that
62
- * don't expose `findMatch` (e.g. a plain `{ routeMap }` object) are left as-is.
63
- */
64
- function expandLazyIncludes(
49
+ async function expandLazyIncludes(
65
50
  router: RouterWithRouteMap,
66
51
  patterns: Iterable<string>,
67
- ): void {
52
+ ): Promise<void> {
68
53
  const findMatch = router.findMatch;
69
54
  if (typeof findMatch !== "function") return;
70
55
  for (const pattern of patterns) {
71
56
  try {
72
- findMatch.call(router, concretePath(pattern));
73
- } catch {
74
- // A pattern that fails to match (constrained param, etc.) is fine — a
75
- // sibling route in the same include still triggers expansion.
57
+ // Await: an async include(`() => import()`) resolves its module + splices
58
+ // its routes into routeMap only after findMatch's Promise settles. Without
59
+ // the await, the routeMap read below races the import and reports the
60
+ // split-group routes as missing.
61
+ await findMatch.call(router, concretePath(pattern));
62
+ } catch (e) {
63
+ // A findMatch throw here is almost always a failing async include()
64
+ // provider (its `() => import()` rejected). Surface it — swallowed, it
65
+ // resurfaces downstream as a misleading "missing route" in the diff.
66
+ console.warn(
67
+ `[@rangojs/router] assertGeneratedRoutesMatch: could not expand include ` +
68
+ `for "${pattern}": ${(e as Error)?.message ?? String(e)}`,
69
+ );
76
70
  }
77
71
  }
78
72
  }
@@ -100,11 +94,6 @@ export interface GeneratedRoutesDiff {
100
94
  ok: boolean;
101
95
  }
102
96
 
103
- /**
104
- * Normalize a route map value to its pattern string. Route maps may carry
105
- * either a bare pattern string or a `{ path, ... }` object (for response/search
106
- * routes); compare on the `path`.
107
- */
108
97
  function patternOf(value: unknown): string {
109
98
  if (typeof value === "string") return value;
110
99
  if (
@@ -118,20 +107,13 @@ function patternOf(value: unknown): string {
118
107
  return String(value);
119
108
  }
120
109
 
121
- /**
122
- * Compute the diff between a router's runtime route map and a generated map.
123
- */
124
- export function diffGeneratedRoutes(
110
+ export async function diffGeneratedRoutes(
125
111
  router: RouterWithRouteMap,
126
112
  generatedMap?: Record<string, unknown>,
127
- ): GeneratedRoutesDiff {
113
+ ): Promise<GeneratedRoutesDiff> {
128
114
  const generated = generatedMap ?? getGlobalRouteMap();
129
115
 
130
- // Lazy `include()`d routes are absent from `routeMap` until first matched, so
131
- // expand them first (using the generated patterns to drive the matches) —
132
- // otherwise every included route is a false `missing`. No-op for plain
133
- // `{ routeMap }` objects that don't expose `findMatch`.
134
- expandLazyIncludes(
116
+ await expandLazyIncludes(
135
117
  router,
136
118
  Object.values(generated).map((v) => patternOf(v)),
137
119
  );
@@ -155,12 +137,6 @@ export function diffGeneratedRoutes(
155
137
  }
156
138
 
157
139
  for (const name of Object.keys(runtime)) {
158
- // Auto-generated internal names ($path_*/$prefix_*) live in the runtime
159
- // mergedRouteMap but are deliberately excluded from the generated
160
- // *.named-routes.gen.ts file (route-types-writer / runtime-discovery skip
161
- // them). Reporting them as `extra` would throw on a perfectly in-sync app
162
- // that simply uses an unnamed path()/include() route, so skip them here to
163
- // match exactly the surface the generator emits.
164
140
  if (isAutoGeneratedRouteName(name)) continue;
165
141
  if (!(name in generated)) {
166
142
  extra.push(name);
@@ -185,14 +161,14 @@ export function diffGeneratedRoutes(
185
161
  * import generated from "./router.named-routes.gen";
186
162
  * import { router } from "./router";
187
163
  *
188
- * assertGeneratedRoutesMatch(router, generated);
164
+ * await assertGeneratedRoutesMatch(router, generated);
189
165
  * ```
190
166
  */
191
- export function assertGeneratedRoutesMatch(
167
+ export async function assertGeneratedRoutesMatch(
192
168
  router: RouterWithRouteMap,
193
169
  generatedMap?: Record<string, unknown>,
194
- ): void {
195
- const diff = diffGeneratedRoutes(router, generatedMap);
170
+ ): Promise<void> {
171
+ const diff = await diffGeneratedRoutes(router, generatedMap);
196
172
  if (diff.ok) return;
197
173
 
198
174
  const lines: string[] = [
@@ -34,30 +34,31 @@
34
34
  * - RSC: see @rangojs/router/testing/flight
35
35
  */
36
36
 
37
- // Unit
38
37
  export { runMiddleware } from "./run-middleware.js";
39
38
  export type {
40
39
  RunMiddlewareOptions,
41
40
  RunMiddlewareResult,
42
41
  } from "./run-middleware.js";
43
- export { runLoader } from "./run-loader.js";
42
+ export { runLoader, runLoaderResult } from "./run-loader.js";
44
43
  export type {
45
44
  RunLoaderOptions,
45
+ RunLoaderResult,
46
46
  UseResolver,
47
47
  TestLoaderContext,
48
48
  } from "./run-loader.js";
49
49
 
50
- // Integration
50
+ export { runTransitionWhen } from "./run-transition-when.js";
51
+ export type {
52
+ RunTransitionWhenOptions,
53
+ RunTransitionWhenResult,
54
+ } from "./run-transition-when.js";
55
+
51
56
  export { dispatch } from "./dispatch.js";
52
57
  export type { DispatchOptions } from "./dispatch.js";
53
58
 
54
- // renderRoute lives at `@rangojs/router/testing/dom` — it pulls React, the
55
- // browser runtime, and @testing-library/react types, which this barrel keeps
56
- // out so node-only unit suites depend on none of them.
57
-
58
- // Cross-cutting: cache/prerender status
59
59
  export {
60
60
  assertCacheStatus,
61
+ assertCacheDecision,
61
62
  parseCacheHeader,
62
63
  createCacheSink,
63
64
  filterCacheDecisions,
@@ -67,11 +68,16 @@ export type {
67
68
  CacheStatusTarget,
68
69
  CacheSink,
69
70
  } from "./cache-status.js";
71
+ export type {
72
+ TelemetryEvent,
73
+ TelemetrySink,
74
+ CacheDecisionEvent,
75
+ CacheSegmentSignal,
76
+ CacheSegmentStatus,
77
+ } from "../router/telemetry.js";
70
78
 
71
- // Cross-cutting: handle collect/accumulator
72
79
  export { collectHandle } from "./collect-handle.js";
73
80
 
74
- // Cross-cutting: generated-route drift
75
81
  export {
76
82
  diffGeneratedRoutes,
77
83
  assertGeneratedRoutesMatch,
@@ -81,7 +87,6 @@ export type {
81
87
  GeneratedRouteMismatch,
82
88
  } from "./generated-routes.js";
83
89
 
84
- // Advanced: build a real RequestContext for bespoke loader/middleware setups
85
90
  export {
86
91
  createTestRequestContext,
87
92
  runInRequestContext,
@@ -91,16 +96,10 @@ export {
91
96
  export type {
92
97
  CreateTestContextOptions,
93
98
  TestRequestContext,
99
+ TestRequestContextObject,
94
100
  RunInRequestContextResult,
95
101
  VarsInit,
102
+ StateCookieSeed,
96
103
  } from "./internal/context.js";
97
104
 
98
- // The low-level context runner that enters a RequestContext (the same one the
99
- // RSC handler uses for server actions). Re-exported so a ctx built with
100
- // createTestRequestContext can be entered directly; runInRequestContext is the
101
- // one-call convenience over createTestRequestContext + runWithRequestContext.
102
105
  export { runWithRequestContext } from "../server/request-context.js";
103
-
104
- // The E2E harness is NOT re-exported here: it must be imported from
105
- // `@rangojs/router/testing/e2e` so it stays loadable in a plain Playwright
106
- // runner (this barrel pulls in router-manifest code that needs Vite virtuals).