@real-router/rsc-server-plugin 0.2.1 → 0.2.3

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.
@@ -1,41 +0,0 @@
1
- import { ALLOWED_RSC_MODES } from "./constants";
2
-
3
- import type { RscSsrMode } from "./types";
4
- import type { State } from "@real-router/types";
5
-
6
- /**
7
- * Returns the SSR mode resolved by `rsc-server-plugin` for the current state.
8
- * Falls back to `"full"` when the route has no plugin entry.
9
- *
10
- * Read this from `entry-server.tsx` to branch on full vs client-only:
11
- * - `"full"` — render the Server Component tree, pipe Flight stream.
12
- * - `"client-only"` — ship shell HTML and let the client fetch via its own mechanism.
13
- *
14
- * The mode is written to `state.context.ssrRscMode` by the plugin's `start`
15
- * interceptor for every route registered in the loaders map.
16
- *
17
- * Defensive read: if `state.context.ssrRscMode` was set to something outside
18
- * `ALLOWED_RSC_MODES` by a TS-cast bypass or a foreign writer, the function
19
- * collapses it to `"full"` rather than returning the bad value. Without this
20
- * guard, a downstream `mode === "full"` branch would silently misbehave for
21
- * `0`, `false`, `""`, `null`, or any unknown string.
22
- *
23
- * The read itself is wrapped in `try/catch` — a foreign writer that installs
24
- * a throwing getter (`Object.defineProperty(ctx, "ssrRscMode", { get() { throw … } })`)
25
- * cannot break the contract. The function NEVER throws, no matter how
26
- * adversarial the context shape. `"full"` is the safe default for any error.
27
- */
28
- export function getSsrRscMode(state: State): RscSsrMode {
29
- let raw: unknown;
30
-
31
- try {
32
- raw = (state.context as { ssrRscMode?: unknown }).ssrRscMode;
33
- } catch {
34
- return "full";
35
- }
36
-
37
- return typeof raw === "string" &&
38
- ALLOWED_RSC_MODES.includes(raw as RscSsrMode)
39
- ? (raw as RscSsrMode)
40
- : "full";
41
- }
package/src/index.ts DELETED
@@ -1,28 +0,0 @@
1
- export type {
2
- RscActionResult,
3
- RscLoaderFn,
4
- RscLoaderFactoryMap,
5
- RscLoaderFnFactory,
6
- RscPayload,
7
- RscRouteEntry,
8
- RscSsrMode,
9
- SsrLoaderContext,
10
- } from "./types";
11
-
12
- export { rscServerPluginFactory } from "./factory";
13
-
14
- export { rscActionPluginFactory } from "./actionFactory";
15
-
16
- export { buildRscPayload } from "./buildRscPayload";
17
-
18
- export { getSsrRscMode } from "./getSsrRscMode";
19
-
20
- export { invalidate } from "./invalidate";
21
-
22
- declare module "@real-router/types" {
23
- interface StateContext {
24
- rsc?: import("react").ReactNode;
25
- rscAction?: import("./types").RscActionResult;
26
- ssrRscMode?: import("./types").RscSsrMode;
27
- }
28
- }
package/src/invalidate.ts DELETED
@@ -1,36 +0,0 @@
1
- import { markStale } from "./shared-ssr";
2
-
3
- import type { Router } from "@real-router/types";
4
-
5
- /**
6
- * Mark the `"rsc"` namespace as stale on the given router. The next
7
- * navigation (including a same-route reload) re-runs the RSC loader for the
8
- * destination route and overwrites `state.context.rsc` (and the mode marker)
9
- * via the plugin's `subscribeLeave` listener.
10
- *
11
- * Honest fire-and-forget semantics — returns `void`. The flag is consumed in
12
- * the awaited LEAVE_APPROVE phase of the next navigation, so subscribers see
13
- * a fresh `ReactNode` when the navigation completes. Behaviour during an
14
- * in-flight transition: the current transition completes unchanged; the flag
15
- * is read by the *following* navigation. This keeps the invariant
16
- * "one transition = one `state.context` snapshot" intact.
17
- *
18
- * Composability through the existing core API:
19
- *
20
- * ```ts
21
- * // Fire-and-forget: stale until the user navigates somewhere
22
- * invalidate(router, "rsc");
23
- *
24
- * // Explicit await — pair with a same-route reload
25
- * invalidate(router, "rsc");
26
- * await router.navigate(state.name, state.params, { reload: true });
27
- * ```
28
- *
29
- * Surgical alternative to `router.navigate({ reload: true })` for multi-
30
- * namespace routes: only the `"rsc"` namespace re-runs; a side-by-side
31
- * `ssr-data-plugin` keeps its cached `state.context.data` on this transition
32
- * unless its own `invalidate()` was also called.
33
- */
34
- export function invalidate(router: Router, namespace: "rsc"): void {
35
- markStale(router, namespace);
36
- }
package/src/types.ts DELETED
@@ -1,122 +0,0 @@
1
- import type {
2
- SsrLoaderFn,
3
- SsrLoaderFnFactory,
4
- SsrMode,
5
- SsrRouteEntry,
6
- } from "./shared-ssr";
7
- import type { DefaultDependencies } from "@real-router/types";
8
- import type { ReactNode } from "react";
9
-
10
- export { type SsrLoaderContext } from "./shared-ssr";
11
-
12
- /**
13
- * SSR mode subset supported by `rsc-server-plugin`.
14
- *
15
- * `"data-only"` is intentionally excluded — RSC has no concept of "data
16
- * without component" (the Flight payload IS the data + component). Using
17
- * `"data-only"` with `rscServerPluginFactory` is a configuration error and
18
- * is rejected at factory time.
19
- */
20
- export type RscSsrMode = Exclude<SsrMode, "data-only">;
21
-
22
- /**
23
- * Compiled RSC loader signature.
24
- *
25
- * Receives the resolved route's `params` and returns a `ReactNode` (a Server
26
- * Component element, sync or async). Synchronous return is permitted because
27
- * many Server Components are synchronous — wrapping them in `Promise.resolve`
28
- * would be ceremonial.
29
- */
30
- export type RscLoaderFn = SsrLoaderFn<ReactNode>;
31
-
32
- /**
33
- * Factory function for creating RSC loaders.
34
- *
35
- * Receives the router instance and a dependency getter (same pattern as
36
- * `DataLoaderFnFactory`/`GuardFnFactory`). Factory runs once at
37
- * `usePlugin()` time; the returned loader is cached.
38
- *
39
- * @template Dependencies - Router dependency map for typed `getDependency()`.
40
- * Defaults to `DefaultDependencies`. Pass your app's dependency interface
41
- * for type-safe DI: `RscLoaderFnFactory<AppDependencies>`.
42
- */
43
- export type RscLoaderFnFactory<
44
- Dependencies extends DefaultDependencies = DefaultDependencies,
45
- > = SsrLoaderFnFactory<ReactNode, Dependencies>;
46
-
47
- /**
48
- * Per-route entry: either a loader factory (short form) or
49
- * `{ ssr?, loader? }` object form. Mode defaults to `"full"`.
50
- *
51
- * Allowed `ssr` values for RSC: `"full"` | `"client-only"` (and the
52
- * `true` / `false` aliases). `"data-only"` is rejected at factory time.
53
- *
54
- * Function form `(state) => RscSsrMode` is resolved per-navigation,
55
- * **before** the mode is written to context.
56
- */
57
- export type RscRouteEntry<
58
- Dependencies extends DefaultDependencies = DefaultDependencies,
59
- > = SsrRouteEntry<ReactNode, RscSsrMode, Dependencies>;
60
-
61
- /**
62
- * Map of route name → entry (factory or `{ ssr?, loader? }`).
63
- *
64
- * Pass to `rscServerPluginFactory()`. Keys are route names (e.g. `"users.profile"`);
65
- * values are factory or object-form route entries.
66
- */
67
- export type RscLoaderFactoryMap<
68
- Dependencies extends DefaultDependencies = DefaultDependencies,
69
- > = Record<string, RscRouteEntry<Dependencies>>;
70
-
71
- /**
72
- * Server Action result published by `rscActionPluginFactory` to
73
- * `state.context.rscAction`. Consumers read either field at render
74
- * time. Both are optional — typical flows write one or the other:
75
- *
76
- * - `returnValue` — set when the action was invoked via the hydrated
77
- * client path (`setServerCallback` → `loadServerAction` →
78
- * `decodeReply` in the RSC entry). Threaded back into
79
- * `useActionState` on the client.
80
- * - `formState` — set when the action was invoked via progressive
81
- * enhancement (`<form action={fn}>` POST without JS) and decoded
82
- * via `decodeAction(formData)` + `decodeFormState(result, formData)`.
83
- *
84
- * Both type parameters default to `unknown` to keep the plugin
85
- * runtime-only — consumers narrow them at the call site.
86
- */
87
- export interface RscActionResult<TReturn = unknown, TFormState = unknown> {
88
- returnValue?: { ok: boolean; data: TReturn };
89
- formState?: TFormState;
90
- }
91
-
92
- /**
93
- * Canonical Flight payload shape for RSC apps that ship Server Actions.
94
- *
95
- * The pipeline serializes this object via the bundler's RSC stream
96
- * renderer (e.g. `@vitejs/plugin-rsc/rsc.renderToReadableStream`); the
97
- * SSR + browser entries deserialize the same shape and thread
98
- * `returnValue`/`formState` into `useActionState`.
99
- *
100
- * Apps without Server Actions can use `RscPayload` with all generics
101
- * defaulted, or just type their payload as `{ root: ReactNode }`.
102
- *
103
- * Type parameters:
104
- * - `TReturn` — narrowed shape of `returnValue.data` (e.g. mutation
105
- * confirmation). Defaults to `unknown`.
106
- * - `TFormState` — narrowed shape of `formState`. Defaults to
107
- * `unknown` so the plugin stays free of `react-dom/client` import
108
- * (which carries the canonical `ReactFormState` type). Consumers
109
- * narrow at call site:
110
- *
111
- * ```ts
112
- * import type { ReactFormState } from "react-dom/client";
113
- * type AppPayload = RscPayload<{ id: string }, ReactFormState>;
114
- * ```
115
- */
116
- export interface RscPayload<
117
- TReturn = unknown,
118
- TFormState = unknown,
119
- > extends RscActionResult<TReturn, TFormState> {
120
- /** Server Component tree to render. */
121
- root: ReactNode;
122
- }