@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.
- package/dist/bin/rango.js +39 -15
- package/dist/types/browser/event-controller.d.ts +49 -0
- package/dist/types/browser/history-state.d.ts +16 -4
- package/dist/types/browser/navigation-store.d.ts +9 -0
- package/dist/types/browser/navigation-transaction.d.ts +7 -0
- package/dist/types/browser/partial-update.d.ts +2 -1
- package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
- package/dist/types/browser/rango-state.d.ts +3 -1
- package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
- package/dist/types/browser/react/context.d.ts +25 -0
- package/dist/types/browser/react/location-state-shared.d.ts +84 -10
- package/dist/types/browser/react/location-state.d.ts +7 -2
- package/dist/types/browser/rsc-router.d.ts +13 -2
- package/dist/types/browser/types.d.ts +10 -0
- package/dist/types/build/generate-manifest.d.ts +2 -0
- package/dist/types/cache/background-task.d.ts +22 -0
- package/dist/types/cache/cache-key-utils.d.ts +50 -5
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +5 -6
- package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
- package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
- package/dist/types/cache/document-cache.d.ts +8 -5
- package/dist/types/cache/handle-snapshot.d.ts +59 -50
- package/dist/types/cache/index.d.ts +3 -1
- package/dist/types/cache/invalidation-order.d.ts +14 -0
- package/dist/types/cache/memory-segment-store.d.ts +33 -1
- package/dist/types/cache/shell-snapshot.d.ts +29 -4
- package/dist/types/cache/store-scope.d.ts +22 -0
- package/dist/types/cache/tag-invalidation.d.ts +7 -6
- package/dist/types/cache/types.d.ts +84 -24
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
- package/dist/types/client-urls/navigation.d.ts +7 -1
- package/dist/types/client-urls/optimistic-location.d.ts +10 -4
- package/dist/types/errors.d.ts +6 -0
- package/dist/types/handles/deferred-resolution.d.ts +18 -1
- package/dist/types/internal-suspense-audit.d.ts +1 -0
- package/dist/types/loader-store.d.ts +4 -8
- package/dist/types/outlet-provider.d.ts +2 -1
- package/dist/types/prerender/build-shell-capture.d.ts +8 -6
- package/dist/types/prerender/cloudflare.d.ts +49 -0
- package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
- package/dist/types/prerender/index.d.ts +12 -0
- package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
- package/dist/types/prerender/on-demand.d.ts +272 -0
- package/dist/types/prerender/producer-guard.d.ts +33 -0
- package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
- package/dist/types/prerender/warm-request.d.ts +65 -0
- package/dist/types/prerender/warm.d.ts +65 -0
- package/dist/types/prerender/writable-store.d.ts +170 -0
- package/dist/types/prerender.d.ts +27 -0
- package/dist/types/reverse.d.ts +1 -1
- package/dist/types/route-content-wrapper.d.ts +3 -1
- package/dist/types/router/error-handling.d.ts +2 -0
- package/dist/types/router/handler-context.d.ts +1 -1
- package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
- package/dist/types/router/navigation-snapshot.d.ts +8 -1
- package/dist/types/router/pattern-matching.d.ts +2 -0
- package/dist/types/router/prefetch-default.d.ts +4 -4
- package/dist/types/router/prerender-match.d.ts +23 -1
- package/dist/types/router/route-trie-builder.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +1 -0
- package/dist/types/router/router-interfaces.d.ts +32 -1
- package/dist/types/router/router-options.d.ts +39 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
- package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
- package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
- package/dist/types/router/trie-matching.d.ts +2 -0
- package/dist/types/router-versions.d.ts +39 -0
- package/dist/types/rsc/handler-context.d.ts +5 -0
- package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
- package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
- package/dist/types/rsc/shell-capture.d.ts +14 -1
- package/dist/types/rsc/shell-serve.d.ts +2 -2
- package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
- package/dist/types/rsc/types.d.ts +12 -6
- package/dist/types/segment-boundary-content.d.ts +17 -0
- package/dist/types/segment-loader-promise.d.ts +2 -6
- package/dist/types/server/build-version-table.d.ts +53 -0
- package/dist/types/server/context.d.ts +12 -4
- package/dist/types/server/handle-store.d.ts +89 -46
- package/dist/types/server/request-context.d.ts +84 -9
- package/dist/types/suspense-audit-react.d.ts +26 -0
- package/dist/types/suspense-audit.d.ts +157 -0
- package/dist/types/testing/action-form.d.ts +34 -0
- package/dist/types/testing/build-versions.d.ts +32 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +3 -1
- package/dist/types/testing/index.d.ts +6 -0
- package/dist/types/testing/internal/seed-vars.d.ts +7 -2
- package/dist/types/testing/internal/server-modules.d.ts +8 -0
- package/dist/types/testing/location-state-key.d.ts +5 -1
- package/dist/types/testing/render-route.d.ts +77 -6
- package/dist/types/testing/serve-shell-request.d.ts +55 -4
- package/dist/types/testing/shell-status.d.ts +14 -6
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/thenable-status.d.ts +6 -0
- package/dist/types/types/handler-context.d.ts +15 -5
- package/dist/types/types/route-entry.d.ts +5 -0
- package/dist/types/urls/path-helper.d.ts +18 -0
- package/dist/types/vite/discovery/build-versions.d.ts +393 -0
- package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
- package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
- package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
- package/dist/types/vite/discovery/state.d.ts +25 -0
- package/dist/types/vite/encryption-key.d.ts +18 -0
- package/dist/types/vite/inject-client-debug.d.ts +6 -25
- package/dist/types/vite/plugin-types.d.ts +27 -0
- package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
- package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
- package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
- package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
- package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
- package/dist/types/vite/router-discovery.d.ts +6 -0
- package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
- package/dist/types/vite/utils/client-chunks.d.ts +2 -0
- package/dist/types/vite/utils/compare-strings.d.ts +7 -0
- package/dist/types/vite/utils/package-resolution.d.ts +8 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
- package/dist/vite/index.js +1707 -931
- package/package.json +12 -3
- package/skills/cache-guide/SKILL.md +102 -37
- package/skills/caching/SKILL.md +56 -9
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +16 -0
- package/skills/cloudflare/SKILL.md +133 -1
- package/skills/comparison/references/framework-comparison.md +17 -13
- package/skills/deployment-caching/SKILL.md +33 -12
- package/skills/document-cache/SKILL.md +16 -14
- package/skills/hooks/SKILL.md +25 -0
- package/skills/hooks/handle-and-actions.md +5 -0
- package/skills/hooks/navigation.md +38 -0
- package/skills/hooks/state.md +213 -11
- package/skills/hooks/urls.md +40 -1
- package/skills/host-router/SKILL.md +31 -0
- package/skills/intercept/SKILL.md +6 -0
- package/skills/loader/SKILL.md +25 -5
- package/skills/middleware/SKILL.md +2 -0
- package/skills/migrate-nextjs/SKILL.md +82 -9
- package/skills/ppr/SKILL.md +150 -48
- package/skills/prerender/SKILL.md +651 -10
- package/skills/rango/SKILL.md +9 -7
- package/skills/response-routes/SKILL.md +5 -5
- package/skills/route/SKILL.md +29 -0
- package/skills/router-setup/SKILL.md +15 -1
- package/skills/testing/SKILL.md +32 -27
- package/skills/testing/cache-prerender.md +18 -17
- package/skills/testing/client-components.md +193 -8
- package/skills/testing/handles.md +2 -0
- package/skills/testing/loader.md +1 -1
- package/skills/testing/middleware.md +1 -1
- package/skills/testing/render-handler.md +18 -18
- package/skills/testing/serve-shell-request.md +167 -22
- package/skills/testing/server-actions.md +17 -17
- package/skills/testing/server-tree.md +1 -1
- package/skills/typesafety/generated-files-and-cli.md +1 -1
- package/skills/use-cache/SKILL.md +25 -10
- package/skills/vercel/SKILL.md +5 -3
- package/skills/view-transitions/SKILL.md +9 -4
- package/src/bin/rango.ts +1 -1
- package/src/browser/event-controller.ts +119 -2
- package/src/browser/history-state.ts +39 -17
- package/src/browser/link-interceptor.ts +2 -2
- package/src/browser/navigation-bridge.ts +104 -41
- package/src/browser/navigation-store.ts +26 -0
- package/src/browser/navigation-transaction.ts +24 -12
- package/src/browser/network-error-handler.ts +2 -0
- package/src/browser/partial-update.ts +58 -14
- package/src/browser/prefetch/default-strategy.ts +22 -42
- package/src/browser/rango-state.ts +3 -1
- package/src/browser/react/Link.tsx +37 -43
- package/src/browser/react/NavigationProvider.tsx +85 -1
- package/src/browser/react/context.ts +30 -0
- package/src/browser/react/location-state-shared.ts +200 -34
- package/src/browser/react/location-state.ts +28 -77
- package/src/browser/react/use-action.ts +4 -1
- package/src/browser/react/use-handle.ts +3 -0
- package/src/browser/react/use-link-status.ts +4 -3
- package/src/browser/react/use-navigation.ts +4 -1
- package/src/browser/react/use-params.ts +4 -1
- package/src/browser/react/use-pathname.ts +4 -1
- package/src/browser/react/use-search-params.ts +5 -3
- package/src/browser/react/use-segments.ts +36 -54
- package/src/browser/rsc-router.tsx +93 -31
- package/src/browser/segment-reconciler.ts +3 -3
- package/src/browser/server-action-bridge.ts +43 -7
- package/src/browser/types.ts +11 -0
- package/src/build/generate-manifest.ts +17 -0
- package/src/build/merge-full-manifests.ts +5 -0
- package/src/build/route-trie.ts +1 -0
- package/src/cache/background-task.ts +61 -0
- package/src/cache/cache-key-utils.ts +91 -12
- package/src/cache/cache-policy.ts +11 -2
- package/src/cache/cache-runtime.ts +23 -23
- package/src/cache/cache-scope.ts +48 -36
- package/src/cache/cf/cf-cache-store.ts +91 -66
- package/src/cache/cf/cf-cache-types.ts +12 -4
- package/src/cache/document-cache.ts +35 -14
- package/src/cache/handle-snapshot.ts +118 -86
- package/src/cache/index.ts +5 -0
- package/src/cache/invalidation-order.ts +53 -0
- package/src/cache/memory-segment-store.ts +52 -1
- package/src/cache/shell-snapshot.ts +52 -5
- package/src/cache/store-scope.ts +36 -0
- package/src/cache/tag-invalidation.ts +7 -5
- package/src/cache/types.ts +90 -24
- package/src/cache/vercel/vercel-cache-store.ts +68 -26
- package/src/client-urls/client-root.tsx +1 -0
- package/src/client-urls/navigation.ts +10 -0
- package/src/client-urls/optimistic-location.ts +10 -4
- package/src/client.tsx +40 -3
- package/src/errors.ts +13 -6
- package/src/handles/deferred-resolution.ts +36 -0
- package/src/internal-suspense-audit.ts +12 -0
- package/src/loader-store.ts +4 -9
- package/src/outlet-provider.tsx +25 -1
- package/src/prerender/build-shell-capture.ts +11 -9
- package/src/prerender/cloudflare.ts +130 -0
- package/src/prerender/create-prerender-trigger.ts +780 -0
- package/src/prerender/index.ts +39 -0
- package/src/prerender/memory-prerender-store.ts +115 -0
- package/src/prerender/on-demand.ts +339 -0
- package/src/prerender/producer-guard.ts +66 -0
- package/src/prerender/shell-manifest-key.ts +12 -17
- package/src/prerender/warm-request.ts +154 -0
- package/src/prerender/warm.ts +193 -0
- package/src/prerender/writable-store.ts +278 -0
- package/src/prerender.ts +29 -0
- package/src/reverse.ts +1 -1
- package/src/route-content-wrapper.tsx +107 -13
- package/src/router/error-handling.ts +12 -12
- package/src/router/find-match.ts +1 -0
- package/src/router/handler-context.ts +2 -0
- package/src/router/intercept-resolution.ts +15 -8
- package/src/router/loader-resolution.ts +2 -10
- package/src/router/manifest.ts +2 -0
- package/src/router/match-middleware/background-revalidation.ts +2 -0
- package/src/router/match-middleware/cache-lookup.ts +239 -54
- package/src/router/navigation-snapshot.ts +25 -6
- package/src/router/pattern-matching.ts +14 -0
- package/src/router/prefetch-default.ts +4 -4
- package/src/router/prerender-match.ts +460 -352
- package/src/router/request-classification.ts +15 -12
- package/src/router/route-trie-builder.ts +9 -0
- package/src/router/router-context.ts +1 -0
- package/src/router/router-interfaces.ts +35 -1
- package/src/router/router-options.ts +45 -5
- package/src/router/segment-resolution/fresh.ts +20 -19
- package/src/router/segment-resolution/loader-cache.ts +110 -77
- package/src/router/segment-resolution/revalidation.ts +34 -24
- package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
- package/src/router/trie-matching.ts +3 -0
- package/src/router-versions.ts +60 -0
- package/src/router.ts +107 -1
- package/src/rsc/full-payload.ts +6 -2
- package/src/rsc/handler-context.ts +5 -0
- package/src/rsc/handler.ts +64 -7
- package/src/rsc/progressive-enhancement.ts +5 -1
- package/src/rsc/response-cache-serve.ts +10 -4
- package/src/rsc/rsc-rendering.ts +65 -12
- package/src/rsc/shell-build-manifest.ts +17 -12
- package/src/rsc/shell-capture-constants.ts +39 -7
- package/src/rsc/shell-capture.ts +144 -45
- package/src/rsc/shell-serve.ts +12 -6
- package/src/rsc/ssr-module-loader.ts +26 -0
- package/src/rsc/types.ts +12 -6
- package/src/segment-boundary-content.ts +25 -0
- package/src/segment-loader-promise.ts +28 -38
- package/src/segment-system.tsx +117 -57
- package/src/server/build-version-table.ts +130 -0
- package/src/server/context.ts +40 -17
- package/src/server/cookie-store.ts +5 -0
- package/src/server/handle-store.ts +357 -127
- package/src/server/request-context.ts +149 -12
- package/src/ssr/ssr-root.tsx +32 -13
- package/src/suspense-audit-react.tsx +98 -0
- package/src/suspense-audit.ts +1030 -0
- package/src/testing/action-form.ts +66 -0
- package/src/testing/build-versions.ts +64 -0
- package/src/testing/dispatch.ts +6 -0
- package/src/testing/dom.entry.ts +1 -0
- package/src/testing/flight-runtime.d.ts +27 -0
- package/src/testing/flight.entry.ts +4 -0
- package/src/testing/index.ts +22 -0
- package/src/testing/internal/context.ts +2 -1
- package/src/testing/internal/seed-vars.ts +14 -2
- package/src/testing/internal/server-modules.ts +32 -0
- package/src/testing/location-state-key.ts +18 -8
- package/src/testing/render-handler.ts +2 -1
- package/src/testing/render-route.tsx +293 -46
- package/src/testing/serve-shell-request.ts +262 -37
- package/src/testing/shell-status.ts +17 -6
- package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
- package/src/testing/vitest-stubs/version.ts +4 -1
- package/src/thenable-status.ts +10 -0
- package/src/types/handler-context.ts +15 -5
- package/src/types/route-entry.ts +6 -0
- package/src/urls/path-helper.ts +69 -9
- package/src/use-loader.tsx +57 -30
- package/src/vite/discovery/build-versions.ts +1281 -0
- package/src/vite/discovery/bundle-postprocess.ts +4 -0
- package/src/vite/discovery/discover-routers.ts +15 -0
- package/src/vite/discovery/installed-packages.ts +159 -0
- package/src/vite/discovery/prerender-collection.ts +38 -1
- package/src/vite/discovery/router-versions-phase.ts +336 -0
- package/src/vite/discovery/shell-prerender-phase.ts +33 -43
- package/src/vite/discovery/state.ts +36 -1
- package/src/vite/discovery/virtual-module-codegen.ts +7 -2
- package/src/vite/encryption-key.ts +101 -9
- package/src/vite/inject-client-debug.ts +42 -20
- package/src/vite/plugin-types.ts +28 -0
- package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
- package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
- package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
- package/src/vite/plugins/expose-internal-ids.ts +12 -3
- package/src/vite/plugins/stable-rsc-output.ts +147 -0
- package/src/vite/plugins/version-plugin.ts +19 -24
- package/src/vite/plugins/version.d.ts +17 -2
- package/src/vite/plugins/virtual-entries.ts +39 -6
- package/src/vite/rango.ts +28 -3
- package/src/vite/router-discovery.ts +96 -13
- package/src/vite/utils/bundle-analysis.ts +39 -7
- package/src/vite/utils/client-chunks.ts +5 -1
- package/src/vite/utils/compare-strings.ts +9 -0
- package/src/vite/utils/package-resolution.ts +9 -0
- package/src/vite/utils/prerender-utils.ts +4 -1
- package/src/vite/utils/shared-utils.ts +1 -1
- package/dist/types/segment-content-promise.d.ts +0 -13
- 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
|
+
}
|
package/src/testing/dispatch.ts
CHANGED
|
@@ -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
|
package/src/testing/dom.entry.ts
CHANGED
|
@@ -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";
|
package/src/testing/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
*
|
|
26
|
-
*
|
|
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
|
-
|
|
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
|
-
:
|
|
50
|
+
: LOCATION_STATE_KEY_PREFIX + name;
|
|
41
51
|
} else if (!peekLocationStateKey(definition)) {
|
|
42
|
-
definition.__rsc_ls_key = `${
|
|
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
|
-
|
|
257
|
+
versions: seededVersions(opts.stateCookie),
|
|
257
258
|
cacheStore: opts.cacheStore,
|
|
258
259
|
cacheProfiles: opts.cacheProfiles,
|
|
259
260
|
themeConfig:
|