@rangojs/router 0.20.0 → 0.22.0

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 (331) hide show
  1. package/dist/bin/rango.js +39 -15
  2. package/dist/types/browser/event-controller.d.ts +49 -0
  3. package/dist/types/browser/history-state.d.ts +16 -4
  4. package/dist/types/browser/navigation-store.d.ts +9 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +7 -0
  6. package/dist/types/browser/partial-update.d.ts +2 -1
  7. package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
  8. package/dist/types/browser/rango-state.d.ts +3 -1
  9. package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
  10. package/dist/types/browser/react/context.d.ts +25 -0
  11. package/dist/types/browser/react/location-state-shared.d.ts +84 -10
  12. package/dist/types/browser/react/location-state.d.ts +7 -2
  13. package/dist/types/browser/rsc-router.d.ts +13 -2
  14. package/dist/types/browser/types.d.ts +10 -0
  15. package/dist/types/build/generate-manifest.d.ts +2 -0
  16. package/dist/types/cache/background-task.d.ts +22 -0
  17. package/dist/types/cache/cache-key-utils.d.ts +50 -5
  18. package/dist/types/cache/cache-policy.d.ts +17 -0
  19. package/dist/types/cache/cache-scope.d.ts +5 -6
  20. package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
  21. package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
  22. package/dist/types/cache/document-cache.d.ts +8 -5
  23. package/dist/types/cache/handle-snapshot.d.ts +59 -50
  24. package/dist/types/cache/index.d.ts +3 -1
  25. package/dist/types/cache/invalidation-order.d.ts +14 -0
  26. package/dist/types/cache/memory-segment-store.d.ts +33 -1
  27. package/dist/types/cache/shell-snapshot.d.ts +29 -4
  28. package/dist/types/cache/store-scope.d.ts +22 -0
  29. package/dist/types/cache/tag-invalidation.d.ts +7 -6
  30. package/dist/types/cache/types.d.ts +84 -24
  31. package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
  32. package/dist/types/client-urls/navigation.d.ts +7 -1
  33. package/dist/types/client-urls/optimistic-location.d.ts +10 -4
  34. package/dist/types/errors.d.ts +6 -0
  35. package/dist/types/handles/deferred-resolution.d.ts +18 -1
  36. package/dist/types/internal-suspense-audit.d.ts +1 -0
  37. package/dist/types/loader-store.d.ts +4 -8
  38. package/dist/types/outlet-provider.d.ts +2 -1
  39. package/dist/types/prerender/build-shell-capture.d.ts +8 -6
  40. package/dist/types/prerender/cloudflare.d.ts +49 -0
  41. package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
  42. package/dist/types/prerender/index.d.ts +12 -0
  43. package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
  44. package/dist/types/prerender/on-demand.d.ts +272 -0
  45. package/dist/types/prerender/producer-guard.d.ts +33 -0
  46. package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
  47. package/dist/types/prerender/warm-request.d.ts +65 -0
  48. package/dist/types/prerender/warm.d.ts +65 -0
  49. package/dist/types/prerender/writable-store.d.ts +170 -0
  50. package/dist/types/prerender.d.ts +27 -0
  51. package/dist/types/reverse.d.ts +1 -1
  52. package/dist/types/route-content-wrapper.d.ts +3 -1
  53. package/dist/types/router/error-handling.d.ts +2 -0
  54. package/dist/types/router/handler-context.d.ts +1 -1
  55. package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
  56. package/dist/types/router/navigation-snapshot.d.ts +8 -1
  57. package/dist/types/router/pattern-matching.d.ts +2 -0
  58. package/dist/types/router/prefetch-default.d.ts +4 -4
  59. package/dist/types/router/prerender-match.d.ts +23 -1
  60. package/dist/types/router/route-trie-builder.d.ts +8 -1
  61. package/dist/types/router/router-context.d.ts +1 -0
  62. package/dist/types/router/router-interfaces.d.ts +32 -1
  63. package/dist/types/router/router-options.d.ts +39 -5
  64. package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
  65. package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
  66. package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
  67. package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
  68. package/dist/types/router/trie-matching.d.ts +2 -0
  69. package/dist/types/router-versions.d.ts +39 -0
  70. package/dist/types/rsc/handler-context.d.ts +5 -0
  71. package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
  72. package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
  73. package/dist/types/rsc/shell-capture.d.ts +14 -1
  74. package/dist/types/rsc/shell-serve.d.ts +2 -2
  75. package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
  76. package/dist/types/rsc/types.d.ts +12 -6
  77. package/dist/types/segment-boundary-content.d.ts +17 -0
  78. package/dist/types/segment-loader-promise.d.ts +2 -6
  79. package/dist/types/server/build-version-table.d.ts +53 -0
  80. package/dist/types/server/context.d.ts +12 -4
  81. package/dist/types/server/handle-store.d.ts +89 -46
  82. package/dist/types/server/request-context.d.ts +84 -9
  83. package/dist/types/suspense-audit-react.d.ts +26 -0
  84. package/dist/types/suspense-audit.d.ts +157 -0
  85. package/dist/types/testing/action-form.d.ts +34 -0
  86. package/dist/types/testing/build-versions.d.ts +32 -0
  87. package/dist/types/testing/dom.entry.d.ts +1 -1
  88. package/dist/types/testing/flight.entry.d.ts +3 -1
  89. package/dist/types/testing/index.d.ts +6 -0
  90. package/dist/types/testing/internal/seed-vars.d.ts +7 -2
  91. package/dist/types/testing/internal/server-modules.d.ts +8 -0
  92. package/dist/types/testing/location-state-key.d.ts +5 -1
  93. package/dist/types/testing/render-route.d.ts +77 -6
  94. package/dist/types/testing/serve-shell-request.d.ts +55 -4
  95. package/dist/types/testing/shell-status.d.ts +14 -6
  96. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
  97. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  98. package/dist/types/thenable-status.d.ts +6 -0
  99. package/dist/types/types/handler-context.d.ts +15 -5
  100. package/dist/types/types/route-entry.d.ts +5 -0
  101. package/dist/types/urls/path-helper.d.ts +18 -0
  102. package/dist/types/vite/discovery/build-versions.d.ts +393 -0
  103. package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
  104. package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
  105. package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
  106. package/dist/types/vite/discovery/state.d.ts +25 -0
  107. package/dist/types/vite/encryption-key.d.ts +18 -0
  108. package/dist/types/vite/inject-client-debug.d.ts +6 -25
  109. package/dist/types/vite/plugin-types.d.ts +27 -0
  110. package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
  111. package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
  112. package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
  113. package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
  114. package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
  115. package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
  116. package/dist/types/vite/router-discovery.d.ts +6 -0
  117. package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
  118. package/dist/types/vite/utils/client-chunks.d.ts +2 -0
  119. package/dist/types/vite/utils/compare-strings.d.ts +7 -0
  120. package/dist/types/vite/utils/package-resolution.d.ts +8 -0
  121. package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
  122. package/dist/vite/index.js +1707 -931
  123. package/package.json +12 -3
  124. package/skills/cache-guide/SKILL.md +102 -37
  125. package/skills/caching/SKILL.md +56 -9
  126. package/skills/catalog.json +1 -1
  127. package/skills/client-urls/SKILL.md +16 -0
  128. package/skills/cloudflare/SKILL.md +133 -1
  129. package/skills/comparison/references/framework-comparison.md +17 -13
  130. package/skills/deployment-caching/SKILL.md +33 -12
  131. package/skills/document-cache/SKILL.md +16 -14
  132. package/skills/hooks/SKILL.md +25 -0
  133. package/skills/hooks/handle-and-actions.md +5 -0
  134. package/skills/hooks/navigation.md +38 -0
  135. package/skills/hooks/state.md +213 -11
  136. package/skills/hooks/urls.md +40 -1
  137. package/skills/host-router/SKILL.md +31 -0
  138. package/skills/intercept/SKILL.md +6 -0
  139. package/skills/loader/SKILL.md +25 -5
  140. package/skills/middleware/SKILL.md +2 -0
  141. package/skills/migrate-nextjs/SKILL.md +82 -9
  142. package/skills/ppr/SKILL.md +150 -48
  143. package/skills/prerender/SKILL.md +651 -10
  144. package/skills/rango/SKILL.md +9 -7
  145. package/skills/response-routes/SKILL.md +5 -5
  146. package/skills/route/SKILL.md +29 -0
  147. package/skills/router-setup/SKILL.md +15 -1
  148. package/skills/testing/SKILL.md +32 -27
  149. package/skills/testing/cache-prerender.md +18 -17
  150. package/skills/testing/client-components.md +193 -8
  151. package/skills/testing/handles.md +2 -0
  152. package/skills/testing/loader.md +1 -1
  153. package/skills/testing/middleware.md +1 -1
  154. package/skills/testing/render-handler.md +18 -18
  155. package/skills/testing/serve-shell-request.md +167 -22
  156. package/skills/testing/server-actions.md +17 -17
  157. package/skills/testing/server-tree.md +1 -1
  158. package/skills/typesafety/generated-files-and-cli.md +1 -1
  159. package/skills/use-cache/SKILL.md +25 -10
  160. package/skills/vercel/SKILL.md +5 -3
  161. package/skills/view-transitions/SKILL.md +9 -4
  162. package/src/bin/rango.ts +1 -1
  163. package/src/browser/event-controller.ts +119 -2
  164. package/src/browser/history-state.ts +39 -17
  165. package/src/browser/link-interceptor.ts +2 -2
  166. package/src/browser/navigation-bridge.ts +104 -41
  167. package/src/browser/navigation-store.ts +26 -0
  168. package/src/browser/navigation-transaction.ts +24 -12
  169. package/src/browser/network-error-handler.ts +2 -0
  170. package/src/browser/partial-update.ts +58 -14
  171. package/src/browser/prefetch/default-strategy.ts +22 -42
  172. package/src/browser/rango-state.ts +3 -1
  173. package/src/browser/react/Link.tsx +37 -43
  174. package/src/browser/react/NavigationProvider.tsx +85 -1
  175. package/src/browser/react/context.ts +30 -0
  176. package/src/browser/react/location-state-shared.ts +200 -34
  177. package/src/browser/react/location-state.ts +28 -77
  178. package/src/browser/react/use-action.ts +4 -1
  179. package/src/browser/react/use-handle.ts +3 -0
  180. package/src/browser/react/use-link-status.ts +4 -3
  181. package/src/browser/react/use-navigation.ts +4 -1
  182. package/src/browser/react/use-params.ts +4 -1
  183. package/src/browser/react/use-pathname.ts +4 -1
  184. package/src/browser/react/use-search-params.ts +5 -3
  185. package/src/browser/react/use-segments.ts +36 -54
  186. package/src/browser/rsc-router.tsx +93 -31
  187. package/src/browser/segment-reconciler.ts +3 -3
  188. package/src/browser/server-action-bridge.ts +43 -7
  189. package/src/browser/types.ts +11 -0
  190. package/src/build/generate-manifest.ts +17 -0
  191. package/src/build/merge-full-manifests.ts +5 -0
  192. package/src/build/route-trie.ts +1 -0
  193. package/src/cache/background-task.ts +61 -0
  194. package/src/cache/cache-key-utils.ts +91 -12
  195. package/src/cache/cache-policy.ts +11 -2
  196. package/src/cache/cache-runtime.ts +23 -23
  197. package/src/cache/cache-scope.ts +48 -36
  198. package/src/cache/cf/cf-cache-store.ts +91 -66
  199. package/src/cache/cf/cf-cache-types.ts +12 -4
  200. package/src/cache/document-cache.ts +35 -14
  201. package/src/cache/handle-snapshot.ts +118 -86
  202. package/src/cache/index.ts +5 -0
  203. package/src/cache/invalidation-order.ts +53 -0
  204. package/src/cache/memory-segment-store.ts +52 -1
  205. package/src/cache/shell-snapshot.ts +52 -5
  206. package/src/cache/store-scope.ts +36 -0
  207. package/src/cache/tag-invalidation.ts +7 -5
  208. package/src/cache/types.ts +90 -24
  209. package/src/cache/vercel/vercel-cache-store.ts +68 -26
  210. package/src/client-urls/client-root.tsx +1 -0
  211. package/src/client-urls/navigation.ts +10 -0
  212. package/src/client-urls/optimistic-location.ts +10 -4
  213. package/src/client.tsx +40 -3
  214. package/src/errors.ts +13 -6
  215. package/src/handles/deferred-resolution.ts +36 -0
  216. package/src/internal-suspense-audit.ts +12 -0
  217. package/src/loader-store.ts +4 -9
  218. package/src/outlet-provider.tsx +25 -1
  219. package/src/prerender/build-shell-capture.ts +11 -9
  220. package/src/prerender/cloudflare.ts +130 -0
  221. package/src/prerender/create-prerender-trigger.ts +780 -0
  222. package/src/prerender/index.ts +39 -0
  223. package/src/prerender/memory-prerender-store.ts +115 -0
  224. package/src/prerender/on-demand.ts +339 -0
  225. package/src/prerender/producer-guard.ts +66 -0
  226. package/src/prerender/shell-manifest-key.ts +12 -17
  227. package/src/prerender/warm-request.ts +154 -0
  228. package/src/prerender/warm.ts +193 -0
  229. package/src/prerender/writable-store.ts +278 -0
  230. package/src/prerender.ts +29 -0
  231. package/src/reverse.ts +1 -1
  232. package/src/route-content-wrapper.tsx +107 -13
  233. package/src/router/error-handling.ts +12 -12
  234. package/src/router/find-match.ts +1 -0
  235. package/src/router/handler-context.ts +2 -0
  236. package/src/router/intercept-resolution.ts +15 -8
  237. package/src/router/loader-resolution.ts +2 -10
  238. package/src/router/manifest.ts +2 -0
  239. package/src/router/match-middleware/background-revalidation.ts +2 -0
  240. package/src/router/match-middleware/cache-lookup.ts +239 -54
  241. package/src/router/navigation-snapshot.ts +25 -6
  242. package/src/router/pattern-matching.ts +14 -0
  243. package/src/router/prefetch-default.ts +4 -4
  244. package/src/router/prerender-match.ts +460 -352
  245. package/src/router/request-classification.ts +15 -12
  246. package/src/router/route-trie-builder.ts +9 -0
  247. package/src/router/router-context.ts +1 -0
  248. package/src/router/router-interfaces.ts +35 -1
  249. package/src/router/router-options.ts +45 -5
  250. package/src/router/segment-resolution/fresh.ts +20 -19
  251. package/src/router/segment-resolution/loader-cache.ts +110 -77
  252. package/src/router/segment-resolution/revalidation.ts +34 -24
  253. package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
  254. package/src/router/trie-matching.ts +3 -0
  255. package/src/router-versions.ts +60 -0
  256. package/src/router.ts +107 -1
  257. package/src/rsc/full-payload.ts +6 -2
  258. package/src/rsc/handler-context.ts +5 -0
  259. package/src/rsc/handler.ts +64 -7
  260. package/src/rsc/progressive-enhancement.ts +5 -1
  261. package/src/rsc/response-cache-serve.ts +10 -4
  262. package/src/rsc/rsc-rendering.ts +65 -12
  263. package/src/rsc/shell-build-manifest.ts +17 -12
  264. package/src/rsc/shell-capture-constants.ts +39 -7
  265. package/src/rsc/shell-capture.ts +144 -45
  266. package/src/rsc/shell-serve.ts +12 -6
  267. package/src/rsc/ssr-module-loader.ts +26 -0
  268. package/src/rsc/types.ts +12 -6
  269. package/src/segment-boundary-content.ts +25 -0
  270. package/src/segment-loader-promise.ts +28 -38
  271. package/src/segment-system.tsx +117 -57
  272. package/src/server/build-version-table.ts +130 -0
  273. package/src/server/context.ts +40 -17
  274. package/src/server/cookie-store.ts +5 -0
  275. package/src/server/handle-store.ts +357 -127
  276. package/src/server/request-context.ts +149 -12
  277. package/src/ssr/ssr-root.tsx +32 -13
  278. package/src/suspense-audit-react.tsx +98 -0
  279. package/src/suspense-audit.ts +1030 -0
  280. package/src/testing/action-form.ts +66 -0
  281. package/src/testing/build-versions.ts +64 -0
  282. package/src/testing/dispatch.ts +6 -0
  283. package/src/testing/dom.entry.ts +1 -0
  284. package/src/testing/flight-runtime.d.ts +27 -0
  285. package/src/testing/flight.entry.ts +4 -0
  286. package/src/testing/index.ts +22 -0
  287. package/src/testing/internal/context.ts +2 -1
  288. package/src/testing/internal/seed-vars.ts +14 -2
  289. package/src/testing/internal/server-modules.ts +32 -0
  290. package/src/testing/location-state-key.ts +18 -8
  291. package/src/testing/render-handler.ts +2 -1
  292. package/src/testing/render-route.tsx +293 -46
  293. package/src/testing/serve-shell-request.ts +262 -37
  294. package/src/testing/shell-status.ts +17 -6
  295. package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
  296. package/src/testing/vitest-stubs/version.ts +4 -1
  297. package/src/thenable-status.ts +10 -0
  298. package/src/types/handler-context.ts +15 -5
  299. package/src/types/route-entry.ts +6 -0
  300. package/src/urls/path-helper.ts +69 -9
  301. package/src/use-loader.tsx +57 -30
  302. package/src/vite/discovery/build-versions.ts +1281 -0
  303. package/src/vite/discovery/bundle-postprocess.ts +4 -0
  304. package/src/vite/discovery/discover-routers.ts +15 -0
  305. package/src/vite/discovery/installed-packages.ts +159 -0
  306. package/src/vite/discovery/prerender-collection.ts +38 -1
  307. package/src/vite/discovery/router-versions-phase.ts +336 -0
  308. package/src/vite/discovery/shell-prerender-phase.ts +33 -43
  309. package/src/vite/discovery/state.ts +36 -1
  310. package/src/vite/discovery/virtual-module-codegen.ts +7 -2
  311. package/src/vite/encryption-key.ts +101 -9
  312. package/src/vite/inject-client-debug.ts +42 -20
  313. package/src/vite/plugin-types.ts +28 -0
  314. package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
  315. package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
  316. package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
  317. package/src/vite/plugins/expose-internal-ids.ts +12 -3
  318. package/src/vite/plugins/stable-rsc-output.ts +147 -0
  319. package/src/vite/plugins/version-plugin.ts +19 -24
  320. package/src/vite/plugins/version.d.ts +17 -2
  321. package/src/vite/plugins/virtual-entries.ts +39 -6
  322. package/src/vite/rango.ts +28 -3
  323. package/src/vite/router-discovery.ts +96 -13
  324. package/src/vite/utils/bundle-analysis.ts +39 -7
  325. package/src/vite/utils/client-chunks.ts +5 -1
  326. package/src/vite/utils/compare-strings.ts +9 -0
  327. package/src/vite/utils/package-resolution.ts +9 -0
  328. package/src/vite/utils/prerender-utils.ts +4 -1
  329. package/src/vite/utils/shared-utils.ts +1 -1
  330. package/dist/types/segment-content-promise.d.ts +0 -13
  331. package/src/segment-content-promise.ts +0 -67
@@ -0,0 +1,66 @@
1
+ /**
2
+ * createActionForm: the FormData a browser posts when a form is submitted
3
+ * before hydration (no JavaScript), for a server action.
4
+ *
5
+ * React's server render writes hidden fields on the form so the POST names its
6
+ * action and carries the bound state; decodeAction / decodeFormState read them
7
+ * back. A test cannot render those fields (react-dom/server does not load under
8
+ * the react-server condition), so this builds the same wire format and
9
+ * registers the action where the plugin-rsc stub's decodeAction resolves it,
10
+ * tagged as a server reference like a built "use server" export so the bound
11
+ * action keeps its `$$id` (onError's actionId, the action phase label).
12
+ * Post the result with `serveShellRequest(router, url, { form })`.
13
+ */
14
+
15
+ import * as RSDServer from "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge";
16
+ import { registerServerModule } from "./internal/server-modules.js";
17
+
18
+ /** Options for {@link createActionForm}. */
19
+ export interface CreateActionFormOptions {
20
+ /**
21
+ * The action's reference id, `<module id>#<export name>`, as the build gives
22
+ * a "use server" export. Any unique string of that shape in a test.
23
+ */
24
+ id: string;
25
+ /**
26
+ * A `useActionState` form (default): the action runs as `(prevState, formData)`
27
+ * and the POST carries the state key. false: a plain `<form action={fn}>`
28
+ * ($ACTION_ID_), the action runs as `(formData)`.
29
+ */
30
+ useActionState?: boolean;
31
+ /** The form's own fields, as the person filled them. */
32
+ fields?: Record<string, string>;
33
+ }
34
+
35
+ /**
36
+ * Build a no-JS form POST body for `action` and register it so a handler
37
+ * decodes it back to `action`.
38
+ */
39
+ export function createActionForm(
40
+ action: (...args: any[]) => unknown,
41
+ options: CreateActionFormOptions,
42
+ ): FormData {
43
+ const { id, useActionState = true, fields = {} } = options;
44
+ const [moduleId, name] = id.split("#");
45
+ if (!moduleId || !name) {
46
+ throw new Error(
47
+ `createActionForm: id must be "<module id>#<export name>", received ${JSON.stringify(id)}`,
48
+ );
49
+ }
50
+ registerServerModule(moduleId, {
51
+ [name]: RSDServer.registerServerReference(action, moduleId, name),
52
+ });
53
+
54
+ const form = new FormData();
55
+ if (useActionState) {
56
+ form.set("$ACTION_REF_1", "");
57
+ form.set("$ACTION_1:0", JSON.stringify({ id, bound: "$@1" }));
58
+ // The state the hook held when the form was submitted: its initial null.
59
+ form.set("$ACTION_1:1", "[null]");
60
+ form.set("$ACTION_KEY", "k0");
61
+ } else {
62
+ form.set(`$ACTION_ID_${id}`, "");
63
+ }
64
+ for (const [field, value] of Object.entries(fields)) form.set(field, value);
65
+ return form;
66
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Testing primitive for the per-router cache versions.
3
+ *
4
+ * A production build gives every `createRouter()` a data version and a
5
+ * document version (docs/design/per-app-cache-version.md) and ships them as a
6
+ * table in the `@rangojs/router:version` module. A test has no build, so the
7
+ * module is a stub and every router serves unversioned. `setBuildVersions`
8
+ * installs a table in its place; the request handler, `dispatch` and the cache
9
+ * stores then resolve versions through the same code path the built app uses.
10
+ * Calling it again is a deploy: the next request is served by a handler that
11
+ * resolved the new versions.
12
+ */
13
+ import { installRouterVersionsTable } from "../server/build-version-table.js";
14
+ import {
15
+ DEFAULT_ROUTER_VERSIONS_KEY,
16
+ type RouterVersions,
17
+ type RouterVersionsTable,
18
+ } from "../router-versions.js";
19
+
20
+ export type { RouterVersions };
21
+
22
+ /**
23
+ * The versions a simulated build ships.
24
+ *
25
+ * - `data` and `document`: the versions of every router the test does not list
26
+ * under `routers` (the build's whole-build pair).
27
+ * - `routers`: versions per router id (`router.id`), as a host-router build
28
+ * computes them.
29
+ */
30
+ export interface BuildVersions extends RouterVersions {
31
+ routers?: Record<string, RouterVersions>;
32
+ }
33
+
34
+ /**
35
+ * Install the cache versions of a simulated build, or remove them with no
36
+ * argument (routers serve unversioned again, the default in tests).
37
+ *
38
+ * A router that sets `createRouter({ version })` keeps that value for both
39
+ * versions, exactly as in production.
40
+ *
41
+ * @example A deploy that changes the client only keeps cached data
42
+ * ```ts
43
+ * setBuildVersions({ data: "d1", document: "h1" });
44
+ * await serveShellRequest(router, "/product/1", { cacheStore });
45
+ * setBuildVersions({ data: "d1", document: "h2" });
46
+ * const { shellStatus } = await serveShellRequest(router, "/product/1", {
47
+ * cacheStore,
48
+ * });
49
+ * // shellStatus is "MISS": the stored HTML was keyed by document version h1.
50
+ * ```
51
+ */
52
+ export function setBuildVersions(versions?: BuildVersions): void {
53
+ if (!versions) {
54
+ installRouterVersionsTable(undefined);
55
+ return;
56
+ }
57
+ const table: Record<string, readonly [string, string]> = {
58
+ [DEFAULT_ROUTER_VERSIONS_KEY]: [versions.data, versions.document],
59
+ };
60
+ for (const [routerId, pair] of Object.entries(versions.routers ?? {})) {
61
+ table[routerId] = [pair.data, pair.document];
62
+ }
63
+ installRouterVersionsTable(table satisfies RouterVersionsTable);
64
+ }
@@ -106,6 +106,7 @@ import {
106
106
  runWithRequestContext,
107
107
  setRequestContextParams,
108
108
  } from "../server/request-context.js";
109
+ import { resolveRouterVersions } from "../server/build-version-table.js";
109
110
  import { executeMiddleware, matchMiddleware } from "../router/middleware.js";
110
111
  import type {
111
112
  MiddlewareEntry,
@@ -166,6 +167,8 @@ import { applyStreamIdleTimeout } from "../rsc/stream-idle.js";
166
167
  interface DispatchableRouter<TEnv> {
167
168
  id?: string;
168
169
  routerId?: string;
170
+ /** createRouter({ version }), the consumer-set version. */
171
+ version?: string;
169
172
  routeMap: Record<string, unknown>;
170
173
  middleware: MiddlewareEntry<TEnv>[];
171
174
  onError?: OnErrorCallback<TEnv>;
@@ -411,6 +414,9 @@ export async function dispatch<TEnv = any>(
411
414
  cacheStore,
412
415
  searchParamsFilter,
413
416
  cacheProfiles: router.cacheProfiles,
417
+ // The store keys with the router's versions, resolved as the production
418
+ // handler resolves them (rsc/handler.ts).
419
+ versions: resolveRouterVersions(routerId, router.version),
414
420
  });
415
421
  // Wire background error reporting so cache degradation (reportCacheError ->
416
422
  // _reportBackgroundError) reaches the router's onError, mirroring the production
@@ -18,5 +18,6 @@ export type {
18
18
  RenderRouteOptions,
19
19
  TestRouterHandle,
20
20
  RenderRouteResult,
21
+ RenderRouteHydrateResult,
21
22
  HandleDataSeed,
22
23
  } from "./render-route.js";
@@ -22,6 +22,33 @@ declare module "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge" {
22
22
  /** Server-side temporary-reference set (a WeakMap). */
23
23
  export function createTemporaryReferenceSet(): unknown;
24
24
 
25
+ /** Resolve a no-JS form POST to its action, bound to the form data; null for a plain form. */
26
+ export function decodeAction(
27
+ body: FormData,
28
+ serverManifest: unknown,
29
+ ): Promise<unknown> | null;
30
+
31
+ /** Decode an action's encoded arguments; rejects for a raw form body. */
32
+ export function decodeReply(
33
+ body: string | FormData,
34
+ serverManifest: unknown,
35
+ options?: object,
36
+ ): Promise<unknown>;
37
+
38
+ /** The useActionState form state of a no-JS POST, or null. */
39
+ export function decodeFormState(
40
+ actionResult: unknown,
41
+ body: FormData,
42
+ serverManifest: unknown,
43
+ ): Promise<unknown>;
44
+
45
+ /** Tag a function as a server reference; `$$id` becomes `${id}#${exportName}`. */
46
+ export function registerServerReference<T>(
47
+ reference: T,
48
+ id: string,
49
+ exportName: string,
50
+ ): T;
51
+
25
52
  /**
26
53
  * Tag a value as a client reference. Mutates `impl` in place (defining
27
54
  * `$$typeof`/`$$id`/`$$async`) and returns it, so a server tree that imports
@@ -56,6 +56,9 @@ export type {
56
56
  StateCookieSeed,
57
57
  } from "./render-handler.js";
58
58
 
59
+ export { createActionForm } from "./action-form.js";
60
+ export type { CreateActionFormOptions } from "./action-form.js";
61
+
59
62
  export {
60
63
  serveShellRequest,
61
64
  resetShellTestState,
@@ -63,4 +66,5 @@ export {
63
66
  export type {
64
67
  ServeShellRequestOptions,
65
68
  ServeShellRequestResult,
69
+ ShellRequestHandles,
66
70
  } from "./serve-shell-request.js";
@@ -61,6 +61,25 @@ export type { RunClientRevalidateOptions } from "./run-client-revalidate.js";
61
61
  export { dispatch } from "./dispatch.js";
62
62
  export type { DispatchOptions } from "./dispatch.js";
63
63
 
64
+ // On-demand prerender: an in-memory WritablePrerenderStore fake for unit-testing
65
+ // code that calls router.prerender(). Dep-free (a plain Map), so it stays on this
66
+ // React/RSC-free barrel. See @rangojs/router/prerender for the store contracts.
67
+ export { createMemoryPrerenderStore } from "../prerender/memory-prerender-store.js";
68
+ export type {
69
+ MemoryPrerenderStore,
70
+ MemoryPrerenderStoreOptions,
71
+ } from "../prerender/memory-prerender-store.js";
72
+ export type {
73
+ WritablePrerenderStore,
74
+ PrerenderStoredEntry,
75
+ PrerenderKey,
76
+ } from "../prerender/writable-store.js";
77
+ export type {
78
+ PrerenderConfig,
79
+ PrerenderResult,
80
+ PrerenderWarmCaches,
81
+ } from "../prerender/on-demand.js";
82
+
64
83
  export {
65
84
  assertCacheStatus,
66
85
  assertCacheDecision,
@@ -98,6 +117,9 @@ export type {
98
117
  ShellStatusTarget,
99
118
  } from "./shell-status.js";
100
119
 
120
+ export { setBuildVersions } from "./build-versions.js";
121
+ export type { BuildVersions, RouterVersions } from "./build-versions.js";
122
+
101
123
  export { collectHandle } from "./collect-handle.js";
102
124
 
103
125
  export { withLocationStateKey } from "./location-state-key.js";
@@ -19,6 +19,7 @@ import { normalizeBasename } from "../../router/basename.js";
19
19
  import {
20
20
  seedVariables,
21
21
  resolveSeededStateCookieName,
22
+ seededVersions,
22
23
  type VarsInit,
23
24
  type StateCookieSeed,
24
25
  } from "./seed-vars.js";
@@ -164,7 +165,7 @@ export function createTestRequestContext<TEnv>(
164
165
  cacheStore: opts.cacheStore,
165
166
  cacheProfiles: opts.cacheProfiles,
166
167
  stateCookieName,
167
- version: opts.stateCookie?.version,
168
+ versions: seededVersions(opts.stateCookie),
168
169
  });
169
170
  if (opts.basename !== undefined)
170
171
  ctx._basename = normalizeBasename(opts.basename);
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { contextSet, type ContextVar } from "../../context-var.js";
10
10
  import { resolveStateCookieName } from "../../router/state-cookie-name.js";
11
+ import { uniformVersions, type RouterVersions } from "../../router-versions.js";
11
12
 
12
13
  export interface StateCookieSeed {
13
14
  /**
@@ -22,8 +23,10 @@ export interface StateCookieSeed {
22
23
  */
23
24
  routerId?: string;
24
25
  /**
25
- * Build version used as the rotated value's prefix (`{version}:{timestamp}`).
26
- * Defaults to `"0"` (resolved inside createRequestContext).
26
+ * The app's version, as `createRouter({ version })` would set it: the
27
+ * rotated value's prefix (`{version}:{timestamp}`) and the version a cache
28
+ * store keys with. Defaults to `"0"` for the prefix (resolved inside
29
+ * createRequestContext) and to the build versions for the store.
27
30
  */
28
31
  version?: string;
29
32
  }
@@ -32,6 +35,15 @@ export function resolveSeededStateCookieName(seed?: StateCookieSeed): string {
32
35
  return resolveStateCookieName(seed?.prefix, seed?.routerId ?? "router_0");
33
36
  }
34
37
 
38
+ /** The request context's `versions` for a seeded `stateCookie.version`. */
39
+ export function seededVersions(
40
+ seed?: StateCookieSeed,
41
+ ): RouterVersions | undefined {
42
+ return seed?.version === undefined
43
+ ? undefined
44
+ : uniformVersions(seed.version);
45
+ }
46
+
35
47
  export type VarsInit =
36
48
  | Record<string, unknown>
37
49
  | ReadonlyArray<readonly [ContextVar<unknown> | string, unknown]>;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Server reference modules a test registered, by the module id the action's
3
+ * `$$id` (`<module id>#<export name>`) names. The plugin-rsc stub's module
4
+ * loader reads this when decodeAction resolves a no-JS form POST to its action.
5
+ */
6
+ const SERVER_MODULES: Map<string, Record<string, unknown>> = new Map();
7
+
8
+ /**
9
+ * Register `exports` as the server module `id`, merging into an earlier call.
10
+ * The module object is kept and mutated: the stub's require is memoized per
11
+ * id, so a later export must appear on the object it first returned.
12
+ */
13
+ export function registerServerModule(
14
+ id: string,
15
+ exports: Record<string, unknown>,
16
+ ): void {
17
+ const existing = SERVER_MODULES.get(id);
18
+ if (existing) Object.assign(existing, exports);
19
+ else SERVER_MODULES.set(id, { ...exports });
20
+ }
21
+
22
+ /** The module a decoded server reference resolves to. */
23
+ export function loadServerModule(id: string): Record<string, unknown> {
24
+ const found = SERVER_MODULES.get(id);
25
+ if (!found) {
26
+ throw new Error(
27
+ `plugin-rsc stub: server reference module "${id}" is not loadable in a test. ` +
28
+ "Build the form with createActionForm() so its action is registered.",
29
+ );
30
+ }
31
+ return found;
32
+ }
@@ -1,12 +1,11 @@
1
1
  import {
2
+ isLocationStateKey,
3
+ LOCATION_STATE_KEY_PREFIX,
4
+ LOCATION_STATE_KEY_SUFFIX_SEPARATOR,
2
5
  peekLocationStateKey,
3
6
  type LocationStateDefinition,
4
7
  } from "../browser/react/location-state-shared.js";
5
8
 
6
- // Typed history.state slots must carry this prefix: buildHistoryState spreads
7
- // only `__rsc_ls_*` keys onto history.state (history-state.ts).
8
- const KEY_PREFIX = "__rsc_ls_";
9
-
10
9
  let syntheticKeyCounter = 0;
11
10
 
12
11
  /**
@@ -16,11 +15,15 @@ let syntheticKeyCounter = 0;
16
15
  * use (`MyState(value)`, `useLocationState(MyState)`, `.read()`).
17
16
  *
18
17
  * - `withLocationStateKey(GridState, "GridState")` sets `__rsc_ls_GridState`
19
- * (a name that already starts with `__rsc_ls_` is used as-is).
18
+ * (a name that already starts with `__rsc_ls_` is used as-is). A definition
19
+ * with `clearOnReload` appends its suffix, so read the stored key back from
20
+ * `GridState.__rsc_ls_key` (`__rsc_ls_GridState~r`).
20
21
  * - Without a name, a key already set (by the plugin or an earlier call) is
21
22
  * kept; otherwise the definition gets a synthetic `__rsc_ls_test_<n>` key,
22
23
  * which then stays on it.
23
24
  *
25
+ * A name may not contain "~": that separator is reserved for the suffix.
26
+ *
24
27
  * Returns the definition.
25
28
  *
26
29
  * @example
@@ -35,11 +38,18 @@ export function withLocationStateKey<
35
38
  TDefinition extends LocationStateDefinition<any, any>,
36
39
  >(definition: TDefinition, name?: string): TDefinition {
37
40
  if (name !== undefined) {
38
- definition.__rsc_ls_key = name.startsWith(KEY_PREFIX)
41
+ if (name.includes(LOCATION_STATE_KEY_SUFFIX_SEPARATOR)) {
42
+ throw new Error(
43
+ `withLocationStateKey: "${name}" contains "${LOCATION_STATE_KEY_SUFFIX_SEPARATOR}", ` +
44
+ "which is reserved for the key suffix of the `clearOnReload` option. " +
45
+ "Pass the name without a suffix; the definition appends its own.",
46
+ );
47
+ }
48
+ definition.__rsc_ls_key = isLocationStateKey(name)
39
49
  ? name
40
- : KEY_PREFIX + name;
50
+ : LOCATION_STATE_KEY_PREFIX + name;
41
51
  } else if (!peekLocationStateKey(definition)) {
42
- definition.__rsc_ls_key = `${KEY_PREFIX}test_${syntheticKeyCounter++}`;
52
+ definition.__rsc_ls_key = `${LOCATION_STATE_KEY_PREFIX}test_${syntheticKeyCounter++}`;
43
53
  }
44
54
  return definition;
45
55
  }
@@ -37,6 +37,7 @@ import type { LoaderDefinition } from "../types.js";
37
37
  import {
38
38
  seedVariables,
39
39
  resolveSeededStateCookieName,
40
+ seededVersions,
40
41
  type VarsInit,
41
42
  type StateCookieSeed,
42
43
  } from "./internal/seed-vars.js";
@@ -253,7 +254,7 @@ export async function renderHandler<TEnv = any>(
253
254
  variables: seedVariables({}, opts.vars),
254
255
  build: opts.build,
255
256
  stateCookieName,
256
- version: opts.stateCookie?.version,
257
+ versions: seededVersions(opts.stateCookie),
257
258
  cacheStore: opts.cacheStore,
258
259
  cacheProfiles: opts.cacheProfiles,
259
260
  themeConfig: