@tanstack/react-start-rsc 0.0.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/esm/ClientSlot.js +19 -0
- package/dist/esm/ClientSlot.js.map +1 -0
- package/dist/esm/CompositeComponent.js +93 -0
- package/dist/esm/CompositeComponent.js.map +1 -0
- package/dist/esm/ReplayableStream.js +147 -0
- package/dist/esm/ReplayableStream.js.map +1 -0
- package/dist/esm/RscNodeRenderer.js +46 -0
- package/dist/esm/RscNodeRenderer.js.map +1 -0
- package/dist/esm/ServerComponentTypes.js +22 -0
- package/dist/esm/ServerComponentTypes.js.map +1 -0
- package/dist/esm/SlotContext.js +30 -0
- package/dist/esm/SlotContext.js.map +1 -0
- package/dist/esm/awaitLazyElements.js +41 -0
- package/dist/esm/awaitLazyElements.js.map +1 -0
- package/dist/esm/createCompositeComponent.js +205 -0
- package/dist/esm/createCompositeComponent.js.map +1 -0
- package/dist/esm/createCompositeComponent.stub.js +15 -0
- package/dist/esm/createCompositeComponent.stub.js.map +1 -0
- package/dist/esm/createRscProxy.js +138 -0
- package/dist/esm/createRscProxy.js.map +1 -0
- package/dist/esm/createServerComponentFromStream.js +74 -0
- package/dist/esm/createServerComponentFromStream.js.map +1 -0
- package/dist/esm/entry/rsc.js +21 -0
- package/dist/esm/entry/rsc.js.map +1 -0
- package/dist/esm/flight.js +56 -0
- package/dist/esm/flight.js.map +1 -0
- package/dist/esm/flight.rsc.js +2 -0
- package/dist/esm/flight.stub.js +15 -0
- package/dist/esm/flight.stub.js.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.rsc.js +6 -0
- package/dist/esm/plugin/vite.js +172 -0
- package/dist/esm/plugin/vite.js.map +1 -0
- package/dist/esm/reactSymbols.js +8 -0
- package/dist/esm/reactSymbols.js.map +1 -0
- package/dist/esm/renderServerComponent.js +58 -0
- package/dist/esm/renderServerComponent.js.map +1 -0
- package/dist/esm/renderServerComponent.stub.js +16 -0
- package/dist/esm/renderServerComponent.stub.js.map +1 -0
- package/dist/esm/serialization.client.js +21 -0
- package/dist/esm/serialization.client.js.map +1 -0
- package/dist/esm/serialization.server.js +121 -0
- package/dist/esm/serialization.server.js.map +1 -0
- package/dist/esm/slotUsageSanitizer.js +33 -0
- package/dist/esm/slotUsageSanitizer.js.map +1 -0
- package/dist/esm/src/ClientSlot.d.ts +5 -0
- package/dist/esm/src/CompositeComponent.d.ts +28 -0
- package/dist/esm/src/ReplayableStream.d.ts +76 -0
- package/dist/esm/src/RscNodeRenderer.d.ts +7 -0
- package/dist/esm/src/ServerComponentTypes.d.ts +99 -0
- package/dist/esm/src/SlotContext.d.ts +21 -0
- package/dist/esm/src/awaitLazyElements.d.ts +17 -0
- package/dist/esm/src/createCompositeComponent.d.ts +32 -0
- package/dist/esm/src/createCompositeComponent.stub.d.ts +9 -0
- package/dist/esm/src/createRscProxy.d.ts +18 -0
- package/dist/esm/src/createServerComponentFromStream.d.ts +24 -0
- package/dist/esm/src/entry/rsc.d.ts +7 -0
- package/dist/esm/src/flight.d.ts +41 -0
- package/dist/esm/src/flight.rsc.d.ts +17 -0
- package/dist/esm/src/flight.stub.d.ts +8 -0
- package/dist/esm/src/index.d.ts +7 -0
- package/dist/esm/src/index.rsc.d.ts +6 -0
- package/dist/esm/src/plugin/vite.d.ts +9 -0
- package/dist/esm/src/reactSymbols.d.ts +3 -0
- package/dist/esm/src/renderServerComponent.d.ts +33 -0
- package/dist/esm/src/renderServerComponent.stub.d.ts +9 -0
- package/dist/esm/src/rscSsrHandler.d.ts +24 -0
- package/dist/esm/src/serialization.client.d.ts +11 -0
- package/dist/esm/src/serialization.server.d.ts +10 -0
- package/dist/esm/src/slotUsageSanitizer.d.ts +1 -0
- package/dist/esm/src/types.d.ts +13 -0
- package/dist/plugin/entry/rsc.tsx +23 -0
- package/package.json +108 -0
- package/src/ClientSlot.tsx +34 -0
- package/src/CompositeComponent.tsx +165 -0
- package/src/ReplayableStream.ts +249 -0
- package/src/RscNodeRenderer.tsx +76 -0
- package/src/ServerComponentTypes.ts +226 -0
- package/src/SlotContext.tsx +42 -0
- package/src/awaitLazyElements.ts +91 -0
- package/src/createCompositeComponent.stub.ts +20 -0
- package/src/createCompositeComponent.ts +338 -0
- package/src/createRscProxy.tsx +294 -0
- package/src/createServerComponentFromStream.ts +105 -0
- package/src/entry/rsc.tsx +23 -0
- package/src/entry/virtual-modules.d.ts +12 -0
- package/src/flight.rsc.ts +17 -0
- package/src/flight.stub.ts +15 -0
- package/src/flight.ts +68 -0
- package/src/global.d.ts +75 -0
- package/src/index.rsc.ts +25 -0
- package/src/index.ts +26 -0
- package/src/plugin/vite.ts +241 -0
- package/src/reactSymbols.ts +6 -0
- package/src/renderServerComponent.stub.ts +26 -0
- package/src/renderServerComponent.ts +110 -0
- package/src/rscSsrHandler.ts +39 -0
- package/src/serialization.client.ts +43 -0
- package/src/serialization.server.ts +193 -0
- package/src/slotUsageSanitizer.ts +62 -0
- package/src/types.ts +15 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { RSC_SLOT_USAGES_STREAM, SERVER_COMPONENT_STREAM } from "./ServerComponentTypes.js";
|
|
2
|
+
import { ReplayableStream } from "./ReplayableStream.js";
|
|
3
|
+
import { sanitizeSlotArgs } from "./slotUsageSanitizer.js";
|
|
4
|
+
import { ClientSlot } from "./ClientSlot.js";
|
|
5
|
+
import { createElement } from "react";
|
|
6
|
+
import { renderToReadableStream } from "virtual:tanstack-rsc-runtime";
|
|
7
|
+
import { getRequest } from "@tanstack/start-server-core";
|
|
8
|
+
import { getStartContext } from "@tanstack/start-storage-context";
|
|
9
|
+
//#region src/createCompositeComponent.ts
|
|
10
|
+
/**
|
|
11
|
+
* Creates a composite server component with slot support.
|
|
12
|
+
*
|
|
13
|
+
* Supports returning:
|
|
14
|
+
* - A ReactNode directly
|
|
15
|
+
* - An object structure with ReactNodes: accessed as `src.Foo`
|
|
16
|
+
* - Nested structures: accessed as `src.x.Bar`
|
|
17
|
+
*
|
|
18
|
+
* Props that are functions become slots - they render as ClientSlot placeholders
|
|
19
|
+
* in the RSC output, filled in by the consumer with actual implementations.
|
|
20
|
+
*
|
|
21
|
+
* The returned value is NOT directly renderable. Use `<CompositeComponent src={...} />`.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const src = await createCompositeComponent((props) => (
|
|
26
|
+
* <div>
|
|
27
|
+
* <header>{props.header('Dashboard')}</header>
|
|
28
|
+
* <main>{props.children}</main>
|
|
29
|
+
* </div>
|
|
30
|
+
* ))
|
|
31
|
+
*
|
|
32
|
+
* // In route component
|
|
33
|
+
* return (
|
|
34
|
+
* <CompositeComponent src={src} header={(title) => <h1>{title}</h1>}>
|
|
35
|
+
* <p>Main content</p>
|
|
36
|
+
* </CompositeComponent>
|
|
37
|
+
* )
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
async function createCompositeComponent(component) {
|
|
41
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
42
|
+
const slotUsagesEmitter = isDev ? createReadableStreamEmitter() : null;
|
|
43
|
+
const { proxy: proxyProps } = createSlotProxy({ onSlotCall: slotUsagesEmitter ? (name, args) => {
|
|
44
|
+
const sanitizedArgs = sanitizeSlotArgs(args);
|
|
45
|
+
slotUsagesEmitter.emit({
|
|
46
|
+
slot: name,
|
|
47
|
+
args: sanitizedArgs.length ? sanitizedArgs : void 0
|
|
48
|
+
});
|
|
49
|
+
} : void 0 });
|
|
50
|
+
async function ServerComponentWrapper() {
|
|
51
|
+
return component(proxyProps);
|
|
52
|
+
}
|
|
53
|
+
const flightStream = renderToReadableStream(createElement(ServerComponentWrapper));
|
|
54
|
+
const isRouterRequest = getStartContext({ throwIfNotFound: false })?.handlerType === "router";
|
|
55
|
+
const ssrHandler = globalThis.__RSC_SSR__;
|
|
56
|
+
if (isRouterRequest && ssrHandler) {
|
|
57
|
+
const signal = getRequest().signal;
|
|
58
|
+
const stream = new ReplayableStream(flightStream, { signal });
|
|
59
|
+
const decoded = await ssrHandler.decode(stream);
|
|
60
|
+
slotUsagesEmitter?.close();
|
|
61
|
+
return ssrHandler.createCompositeProxy(stream, decoded, slotUsagesEmitter?.stream);
|
|
62
|
+
}
|
|
63
|
+
return createCompositeHandle(isDev && slotUsagesEmitter ? wrapReadableStream(flightStream, {
|
|
64
|
+
onDone: () => {
|
|
65
|
+
slotUsagesEmitter.close();
|
|
66
|
+
},
|
|
67
|
+
onCancel: () => {
|
|
68
|
+
slotUsagesEmitter.close();
|
|
69
|
+
},
|
|
70
|
+
onError: () => {
|
|
71
|
+
slotUsagesEmitter.close();
|
|
72
|
+
}
|
|
73
|
+
}) : flightStream, { slotUsagesStream: slotUsagesEmitter?.stream });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Creates a composite handle for server function responses.
|
|
77
|
+
* No proxy needed - the client will decode and create its own proxy.
|
|
78
|
+
*/
|
|
79
|
+
function createCompositeHandle(flightStream, options) {
|
|
80
|
+
const streamWrapper = { createReplayStream: () => flightStream };
|
|
81
|
+
const stub = function CompositeComponentStub() {
|
|
82
|
+
throw new Error("CompositeComponent from server function cannot be rendered on server. It should be serialized and sent to the client.");
|
|
83
|
+
};
|
|
84
|
+
stub[SERVER_COMPONENT_STREAM] = streamWrapper;
|
|
85
|
+
if (options?.slotUsagesStream) stub[RSC_SLOT_USAGES_STREAM] = options.slotUsagesStream;
|
|
86
|
+
return stub;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Proxy that turns property access into ClientSlot renders.
|
|
90
|
+
* Also tracks accessed slot names for devtools.
|
|
91
|
+
*/
|
|
92
|
+
function createSlotProxy(options) {
|
|
93
|
+
const cache = /* @__PURE__ */ new Map();
|
|
94
|
+
return { proxy: new Proxy({}, { get(_target, prop) {
|
|
95
|
+
if (prop === "then" || typeof prop !== "string") return void 0;
|
|
96
|
+
if (prop === "children") {
|
|
97
|
+
options?.onSlotCall?.("children", []);
|
|
98
|
+
return createElement(ClientSlot, {
|
|
99
|
+
slot: "children",
|
|
100
|
+
args: []
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
let fn = cache.get(prop);
|
|
104
|
+
if (!fn) {
|
|
105
|
+
fn = (...args) => {
|
|
106
|
+
options?.onSlotCall?.(prop, args);
|
|
107
|
+
return createElement(ClientSlot, {
|
|
108
|
+
slot: prop,
|
|
109
|
+
args
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
cache.set(prop, fn);
|
|
113
|
+
}
|
|
114
|
+
return fn;
|
|
115
|
+
} }) };
|
|
116
|
+
}
|
|
117
|
+
function createReadableStreamEmitter() {
|
|
118
|
+
let closed = false;
|
|
119
|
+
const queue = [];
|
|
120
|
+
let controller = null;
|
|
121
|
+
const stream = new ReadableStream({
|
|
122
|
+
start(ctrl) {
|
|
123
|
+
controller = ctrl;
|
|
124
|
+
for (const value of queue) try {
|
|
125
|
+
ctrl.enqueue(value);
|
|
126
|
+
} catch {}
|
|
127
|
+
queue.length = 0;
|
|
128
|
+
if (closed) try {
|
|
129
|
+
ctrl.close();
|
|
130
|
+
} catch {}
|
|
131
|
+
},
|
|
132
|
+
cancel() {
|
|
133
|
+
closed = true;
|
|
134
|
+
controller = null;
|
|
135
|
+
queue.length = 0;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const emit = (value) => {
|
|
139
|
+
if (closed) return;
|
|
140
|
+
if (!controller) {
|
|
141
|
+
queue.push(value);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
controller.enqueue(value);
|
|
146
|
+
} catch {}
|
|
147
|
+
};
|
|
148
|
+
const close = () => {
|
|
149
|
+
if (closed) return;
|
|
150
|
+
closed = true;
|
|
151
|
+
if (controller) {
|
|
152
|
+
try {
|
|
153
|
+
controller.close();
|
|
154
|
+
} catch {}
|
|
155
|
+
controller = null;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
stream,
|
|
160
|
+
emit,
|
|
161
|
+
close
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function wrapReadableStream(source, handlers) {
|
|
165
|
+
const reader = source.getReader();
|
|
166
|
+
let finished = false;
|
|
167
|
+
const finish = () => {
|
|
168
|
+
if (finished) return;
|
|
169
|
+
finished = true;
|
|
170
|
+
handlers.onDone?.();
|
|
171
|
+
try {
|
|
172
|
+
reader.releaseLock();
|
|
173
|
+
} catch {}
|
|
174
|
+
};
|
|
175
|
+
return new ReadableStream({
|
|
176
|
+
async pull(controller) {
|
|
177
|
+
try {
|
|
178
|
+
const { value, done } = await reader.read();
|
|
179
|
+
if (done) {
|
|
180
|
+
controller.close();
|
|
181
|
+
finish();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
controller.enqueue(value);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
try {
|
|
187
|
+
controller.error(err);
|
|
188
|
+
} catch {}
|
|
189
|
+
handlers.onError?.();
|
|
190
|
+
finish();
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
async cancel(reason) {
|
|
194
|
+
handlers.onCancel?.();
|
|
195
|
+
try {
|
|
196
|
+
await reader.cancel(reason);
|
|
197
|
+
} catch {}
|
|
198
|
+
finish();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { createCompositeComponent };
|
|
204
|
+
|
|
205
|
+
//# sourceMappingURL=createCompositeComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createCompositeComponent.js","names":[],"sources":["../../src/createCompositeComponent.ts"],"sourcesContent":["import { createElement } from 'react'\nimport { renderToReadableStream } from 'virtual:tanstack-rsc-runtime'\nimport { getRequest } from '@tanstack/start-server-core'\nimport { getStartContext } from '@tanstack/start-storage-context'\nimport { sanitizeSlotArgs } from './slotUsageSanitizer'\n\nimport { ReplayableStream } from './ReplayableStream'\nimport { ClientSlot } from './ClientSlot'\nimport {\n RSC_SLOT_USAGES_STREAM,\n SERVER_COMPONENT_STREAM,\n} from './ServerComponentTypes'\nimport type {\n AnyCompositeComponent,\n CompositeComponentResult,\n RscSlotUsageEvent,\n ServerComponentStream,\n ValidateCompositeComponent,\n} from './ServerComponentTypes'\n\nimport './rscSsrHandler' // Import for global declaration side effect\n\n/**\n * Creates a composite server component with slot support.\n *\n * Supports returning:\n * - A ReactNode directly\n * - An object structure with ReactNodes: accessed as `src.Foo`\n * - Nested structures: accessed as `src.x.Bar`\n *\n * Props that are functions become slots - they render as ClientSlot placeholders\n * in the RSC output, filled in by the consumer with actual implementations.\n *\n * The returned value is NOT directly renderable. Use `<CompositeComponent src={...} />`.\n *\n * @example\n * ```tsx\n * const src = await createCompositeComponent((props) => (\n * <div>\n * <header>{props.header('Dashboard')}</header>\n * <main>{props.children}</main>\n * </div>\n * ))\n *\n * // In route component\n * return (\n * <CompositeComponent src={src} header={(title) => <h1>{title}</h1>}>\n * <p>Main content</p>\n * </CompositeComponent>\n * )\n * ```\n */\nexport async function createCompositeComponent<TComp>(\n component: ValidateCompositeComponent<TComp>,\n): Promise<CompositeComponentResult<TComp>> {\n const isDev = process.env.NODE_ENV === 'development'\n\n // Dev-only: stream slot usage events (slot + raw args)\n const slotUsagesEmitter = isDev\n ? createReadableStreamEmitter<RscSlotUsageEvent>()\n : null\n\n // Create a wrapper component that will be rendered inside React's Flight context.\n // This ensures React.cache works properly since the component is called during\n // renderToReadableStream's render phase, not before it.\n const { proxy: proxyProps } = createSlotProxy<{}>({\n onSlotCall: slotUsagesEmitter\n ? (name, args) => {\n const sanitizedArgs = sanitizeSlotArgs(args)\n slotUsagesEmitter.emit({\n slot: name,\n args: sanitizedArgs.length ? sanitizedArgs : undefined,\n })\n }\n : undefined,\n })\n\n // Wrapper that renders the user's component inside Flight render context\n async function ServerComponentWrapper() {\n return (component as React.FC)(proxyProps)\n }\n\n // Render using createElement so React calls our component during Flight rendering\n // This is critical for React.cache to work - the component must be invoked\n // during renderToReadableStream's execution, not before\n const flightStream = renderToReadableStream(\n createElement(ServerComponentWrapper),\n )\n\n // Check if this is an SSR request (router) or a direct server function call\n const ctx = getStartContext({ throwIfNotFound: false })\n const isRouterRequest = ctx?.handlerType === 'router'\n const ssrHandler = globalThis.__RSC_SSR__\n\n // SSR path: buffer stream for replay, pre-decode for synchronous rendering\n if (isRouterRequest && ssrHandler) {\n const signal = getRequest().signal\n const stream = new ReplayableStream(flightStream, { signal })\n\n // Pre-decode during loader phase for synchronous SSR rendering\n const decoded = await ssrHandler.decode(stream)\n\n // For SSR we know decode fully consumed the Flight stream.\n slotUsagesEmitter?.close()\n\n const proxy = ssrHandler.createCompositeProxy(\n stream,\n decoded,\n slotUsagesEmitter?.stream,\n )\n return proxy as CompositeComponentResult<TComp>\n }\n\n // Server function call path:\n // The serialization adapter will stream to the client.\n const monitoredFlightStream =\n isDev && slotUsagesEmitter\n ? wrapReadableStream(flightStream, {\n onDone: () => {\n slotUsagesEmitter.close()\n },\n onCancel: () => {\n slotUsagesEmitter.close()\n },\n onError: () => {\n slotUsagesEmitter.close()\n },\n })\n : flightStream\n\n return createCompositeHandle(monitoredFlightStream, {\n slotUsagesStream: slotUsagesEmitter?.stream,\n }) as CompositeComponentResult<TComp>\n}\n\n/**\n * Creates a composite handle for server function responses.\n * No proxy needed - the client will decode and create its own proxy.\n */\nfunction createCompositeHandle(\n flightStream: ReadableStream<Uint8Array>,\n options?: {\n slotUsagesStream?: ReadableStream<RscSlotUsageEvent>\n },\n): AnyCompositeComponent {\n // Simple single-use stream wrapper. For server function calls, the stream\n // is consumed exactly once by the serialization adapter for transport.\n const streamWrapper: ServerComponentStream = {\n createReplayStream: () => flightStream,\n }\n\n // Create a stub function with the stream attached for serialization.\n // This will never be rendered directly - it goes through serialization\n // which extracts the stream and sends it to the client.\n const stub = function CompositeComponentStub(): never {\n throw new Error(\n 'CompositeComponent from server function cannot be rendered on server. ' +\n 'It should be serialized and sent to the client.',\n )\n }\n\n ;(stub as any)[SERVER_COMPONENT_STREAM] = streamWrapper\n // Note: RENDERABLE_RSC is not set (or implicitly false), indicating this is a composite component\n\n if (options?.slotUsagesStream) {\n ;(stub as any)[RSC_SLOT_USAGES_STREAM] = options.slotUsagesStream\n }\n\n return stub as unknown as AnyCompositeComponent\n}\n\n/**\n * Base slot props type - functions that become ClientSlot placeholders\n */\ninterface SlotPropsBase {\n [key: string]:\n | ((...args: Array<any>) => React.ReactNode)\n | React.ReactNode\n | undefined\n children?: React.ReactNode\n}\n\ninterface SlotProxyResult<TSlotProps extends object> {\n proxy: TSlotProps & SlotPropsBase\n}\n\n/**\n * Proxy that turns property access into ClientSlot renders.\n * Also tracks accessed slot names for devtools.\n */\nfunction createSlotProxy<TSlotProps extends object>(options?: {\n onSlotCall?: (name: string, args: Array<any>) => void\n}): SlotProxyResult<TSlotProps> {\n const cache = new Map<string, (...args: Array<any>) => React.ReactNode>()\n\n const proxy = new Proxy({} as TSlotProps & SlotPropsBase, {\n get(_target, prop) {\n if (prop === 'then' || typeof prop !== 'string') return undefined\n\n if (prop === 'children') {\n options?.onSlotCall?.('children', [])\n return createElement(ClientSlot, { slot: 'children', args: [] })\n }\n\n let fn = cache.get(prop)\n if (!fn) {\n fn = (...args: Array<any>) => {\n options?.onSlotCall?.(prop, args)\n return createElement(ClientSlot, { slot: prop, args })\n }\n cache.set(prop, fn)\n }\n return fn\n },\n })\n\n return {\n proxy,\n }\n}\n\nfunction createReadableStreamEmitter<T>(): {\n stream: ReadableStream<T>\n emit: (value: T) => void\n close: () => void\n} {\n let closed = false\n const queue: Array<T> = []\n let controller: ReadableStreamDefaultController<T> | null = null\n\n const stream = new ReadableStream<T>({\n start(ctrl) {\n controller = ctrl\n for (const value of queue) {\n try {\n ctrl.enqueue(value)\n } catch {\n // Ignore\n }\n }\n queue.length = 0\n if (closed) {\n try {\n ctrl.close()\n } catch {\n // Ignore\n }\n }\n },\n cancel() {\n closed = true\n controller = null\n queue.length = 0\n },\n })\n\n const emit = (value: T) => {\n if (closed) return\n if (!controller) {\n queue.push(value)\n return\n }\n try {\n controller.enqueue(value)\n } catch {\n // Ignore\n }\n }\n\n const close = () => {\n if (closed) return\n closed = true\n if (controller) {\n try {\n controller.close()\n } catch {\n // Ignore\n }\n controller = null\n }\n }\n\n return { stream, emit, close }\n}\n\nfunction wrapReadableStream<T>(\n source: ReadableStream<T>,\n handlers: {\n onDone?: () => void\n onCancel?: () => void\n onError?: () => void\n },\n): ReadableStream<T> {\n const reader = source.getReader()\n let finished = false\n\n const finish = () => {\n if (finished) return\n finished = true\n handlers.onDone?.()\n try {\n reader.releaseLock()\n } catch {\n // Ignore\n }\n }\n\n return new ReadableStream<T>({\n async pull(controller) {\n try {\n const { value, done } = await reader.read()\n if (done) {\n controller.close()\n finish()\n return\n }\n controller.enqueue(value)\n } catch (err) {\n try {\n controller.error(err)\n } catch {\n // Ignore\n }\n handlers.onError?.()\n finish()\n }\n },\n async cancel(reason) {\n handlers.onCancel?.()\n try {\n await reader.cancel(reason)\n } catch {\n // Ignore\n }\n finish()\n },\n })\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/createCompositeComponent.stub.ts
|
|
2
|
+
/**
|
|
3
|
+
* Client stub for createCompositeComponent.
|
|
4
|
+
*
|
|
5
|
+
* This function should never be called at runtime on the client.
|
|
6
|
+
* It exists only to satisfy bundler imports in client bundles.
|
|
7
|
+
* The real implementation only runs inside server functions.
|
|
8
|
+
*/
|
|
9
|
+
function createCompositeComponent(_component) {
|
|
10
|
+
throw new Error("createCompositeComponent cannot be called on the client. This function should only be called inside a server function or route loader.");
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { createCompositeComponent };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=createCompositeComponent.stub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createCompositeComponent.stub.js","names":[],"sources":["../../src/createCompositeComponent.stub.ts"],"sourcesContent":["import type {\n CompositeComponentResult,\n ValidateCompositeComponent,\n} from './ServerComponentTypes'\n\n/**\n * Client stub for createCompositeComponent.\n *\n * This function should never be called at runtime on the client.\n * It exists only to satisfy bundler imports in client bundles.\n * The real implementation only runs inside server functions.\n */\nexport function createCompositeComponent<TComp>(\n _component: ValidateCompositeComponent<TComp>,\n): Promise<CompositeComponentResult<TComp>> {\n throw new Error(\n 'createCompositeComponent cannot be called on the client. ' +\n 'This function should only be called inside a server function or route loader.',\n )\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,yBACd,YAC0C;AAC1C,OAAM,IAAI,MACR,yIAED"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { RENDERABLE_RSC, RSC_PROXY_GET_TREE, RSC_PROXY_PATH, RSC_SLOT_USAGES, RSC_SLOT_USAGES_STREAM, SERVER_COMPONENT_CSS_HREFS, SERVER_COMPONENT_JS_PRELOADS, SERVER_COMPONENT_STREAM } from "./ServerComponentTypes.js";
|
|
3
|
+
import { RscNodeRenderer } from "./RscNodeRenderer.js";
|
|
4
|
+
import { createElement } from "react";
|
|
5
|
+
//#region src/createRscProxy.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Creates a recursive Proxy for RSC data.
|
|
8
|
+
*
|
|
9
|
+
* If `renderable: true`, returns a React element that can be rendered as `{data}`.
|
|
10
|
+
* The element also has proxy-like behavior for nested access like `data.foo.bar`.
|
|
11
|
+
*
|
|
12
|
+
* If `renderable: false` (default), the proxy is NOT directly renderable and
|
|
13
|
+
* must be used with `<CompositeComponent src={...} />`.
|
|
14
|
+
*/
|
|
15
|
+
function createRscProxy(getTree, options = {}) {
|
|
16
|
+
if (options.renderable) return createRenderableElement(getTree, [], options.stream, options.cssHrefs, options.jsPreloads);
|
|
17
|
+
let slotUsages = void 0;
|
|
18
|
+
if (process.env.NODE_ENV === "development" && typeof window !== "undefined" && options.slotUsagesStream) {
|
|
19
|
+
slotUsages = [];
|
|
20
|
+
consumeSlotUsages(options.slotUsagesStream, slotUsages);
|
|
21
|
+
}
|
|
22
|
+
return createRscProxyWithPath(getTree, [], options.stream, options.cssHrefs, options.jsPreloads, slotUsages, options.slotUsagesStream);
|
|
23
|
+
}
|
|
24
|
+
var UNHANDLED = Symbol("tanstack.rsc.proxy.unhandled");
|
|
25
|
+
function handleProxyTrap(kind, prop, options) {
|
|
26
|
+
switch (prop) {
|
|
27
|
+
case "__SEROVAL_STREAM__":
|
|
28
|
+
case "__SEROVAL_SEQUENCE__": return kind === "get" ? void 0 : false;
|
|
29
|
+
case Symbol.iterator:
|
|
30
|
+
case Symbol.asyncIterator: return kind === "get" ? void 0 : false;
|
|
31
|
+
case SERVER_COMPONENT_STREAM: return kind === "get" ? options.stream : options.stream !== void 0;
|
|
32
|
+
case SERVER_COMPONENT_CSS_HREFS: return kind === "get" ? options.cssHrefs : options.cssHrefs !== void 0;
|
|
33
|
+
case SERVER_COMPONENT_JS_PRELOADS: return kind === "get" ? options.jsPreloads : options.jsPreloads !== void 0;
|
|
34
|
+
case RSC_PROXY_GET_TREE: return kind === "get" ? options.getTree : true;
|
|
35
|
+
case RSC_PROXY_PATH: return kind === "get" ? options.path : true;
|
|
36
|
+
case RENDERABLE_RSC: return kind === "get" ? options.renderable : true;
|
|
37
|
+
case RSC_SLOT_USAGES: return kind === "get" ? options.slotUsages : options.slotUsages !== void 0;
|
|
38
|
+
case RSC_SLOT_USAGES_STREAM: return kind === "get" ? options.slotUsagesStream : options.slotUsagesStream !== void 0;
|
|
39
|
+
case "then": return kind === "get" ? void 0 : UNHANDLED;
|
|
40
|
+
case "toString": return kind === "get" ? Object.prototype.toString : UNHANDLED;
|
|
41
|
+
case "valueOf": return kind === "get" ? Object.prototype.valueOf : UNHANDLED;
|
|
42
|
+
case "constructor": return kind === "get" ? Object : UNHANDLED;
|
|
43
|
+
}
|
|
44
|
+
if (typeof prop === "symbol") return kind === "get" ? void 0 : false;
|
|
45
|
+
return UNHANDLED;
|
|
46
|
+
}
|
|
47
|
+
function createRscProxyInternal(options) {
|
|
48
|
+
const childCache = /* @__PURE__ */ new Map();
|
|
49
|
+
const getChild = (key) => {
|
|
50
|
+
const cached = childCache.get(key);
|
|
51
|
+
if (cached) return cached;
|
|
52
|
+
const next = createRscProxyInternal({
|
|
53
|
+
...options,
|
|
54
|
+
path: [...options.path, key]
|
|
55
|
+
});
|
|
56
|
+
childCache.set(key, next);
|
|
57
|
+
return next;
|
|
58
|
+
};
|
|
59
|
+
const dataProxy = options.renderable ? createRscProxyInternal({
|
|
60
|
+
...options,
|
|
61
|
+
renderable: false
|
|
62
|
+
}) : void 0;
|
|
63
|
+
const proxyTarget = options.renderable ? createElement(RscNodeRenderer, { data: dataProxy }) : {};
|
|
64
|
+
return new Proxy(proxyTarget, {
|
|
65
|
+
get(target, prop) {
|
|
66
|
+
const handled = handleProxyTrap("get", prop, options);
|
|
67
|
+
if (handled !== UNHANDLED) return handled;
|
|
68
|
+
if (options.renderable) {
|
|
69
|
+
if (prop === "props") return target.props;
|
|
70
|
+
if (prop === "data") return dataProxy;
|
|
71
|
+
if (prop in target) return target[prop];
|
|
72
|
+
}
|
|
73
|
+
return getChild(String(prop));
|
|
74
|
+
},
|
|
75
|
+
has(target, prop) {
|
|
76
|
+
const handled = handleProxyTrap("has", prop, options);
|
|
77
|
+
if (handled !== UNHANDLED) return handled;
|
|
78
|
+
if (options.renderable) {
|
|
79
|
+
if (prop in target) return true;
|
|
80
|
+
if (typeof prop === "string") return true;
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
},
|
|
85
|
+
getPrototypeOf(target) {
|
|
86
|
+
return options.renderable ? Object.getPrototypeOf(target) : Object.prototype;
|
|
87
|
+
},
|
|
88
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
89
|
+
return options.renderable ? Object.getOwnPropertyDescriptor(target, prop) : void 0;
|
|
90
|
+
},
|
|
91
|
+
ownKeys(target) {
|
|
92
|
+
return options.renderable ? Reflect.ownKeys(target) : [];
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates a React element that's also a Proxy for nested access.
|
|
98
|
+
* This is used by renderable RSCs so they work as both {data} and {data.foo.bar}.
|
|
99
|
+
*/
|
|
100
|
+
function createRenderableElement(getTree, path, stream, cssHrefs, jsPreloads) {
|
|
101
|
+
return createRscProxyInternal({
|
|
102
|
+
getTree,
|
|
103
|
+
path,
|
|
104
|
+
stream,
|
|
105
|
+
cssHrefs,
|
|
106
|
+
jsPreloads,
|
|
107
|
+
renderable: true,
|
|
108
|
+
slotUsages: void 0,
|
|
109
|
+
slotUsagesStream: void 0
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function createRscProxyWithPath(getTree, path, stream, cssHrefs, jsPreloads, slotUsages, slotUsagesStream) {
|
|
113
|
+
return createRscProxyInternal({
|
|
114
|
+
getTree,
|
|
115
|
+
path,
|
|
116
|
+
stream,
|
|
117
|
+
cssHrefs,
|
|
118
|
+
jsPreloads,
|
|
119
|
+
renderable: false,
|
|
120
|
+
slotUsages,
|
|
121
|
+
slotUsagesStream
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async function consumeSlotUsages(stream, slotUsages) {
|
|
125
|
+
try {
|
|
126
|
+
const reader = stream.getReader();
|
|
127
|
+
for (;;) {
|
|
128
|
+
const { value, done } = await reader.read();
|
|
129
|
+
if (done) break;
|
|
130
|
+
if (!value.slot) continue;
|
|
131
|
+
slotUsages.push(value);
|
|
132
|
+
}
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { createRscProxy };
|
|
137
|
+
|
|
138
|
+
//# sourceMappingURL=createRscProxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createRscProxy.js","names":[],"sources":["../../src/createRscProxy.tsx"],"sourcesContent":["'use client'\n\nimport { createElement } from 'react'\nimport { RscNodeRenderer } from './RscNodeRenderer'\nimport {\n RENDERABLE_RSC,\n RSC_PROXY_GET_TREE,\n RSC_PROXY_PATH,\n RSC_SLOT_USAGES,\n RSC_SLOT_USAGES_STREAM,\n SERVER_COMPONENT_CSS_HREFS,\n SERVER_COMPONENT_JS_PRELOADS,\n SERVER_COMPONENT_STREAM,\n} from './ServerComponentTypes'\nimport type { RscSlotUsageEvent } from './ServerComponentTypes'\n\nexport interface RscProxyOptions {\n stream?: unknown // The stream to attach for serialization\n cssHrefs?: ReadonlySet<string> // CSS hrefs collected from the RSC stream\n jsPreloads?: ReadonlySet<string> // JS hrefs collected from the RSC stream\n renderable?: boolean // If true, proxy masquerades as React element\n slotUsagesStream?: ReadableStream<RscSlotUsageEvent> // Dev only: slot usage event stream\n}\n\n/**\n * Creates a recursive Proxy for RSC data.\n *\n * If `renderable: true`, returns a React element that can be rendered as `{data}`.\n * The element also has proxy-like behavior for nested access like `data.foo.bar`.\n *\n * If `renderable: false` (default), the proxy is NOT directly renderable and\n * must be used with `<CompositeComponent src={...} />`.\n */\nexport function createRscProxy<T>(\n getTree: () => T,\n options: RscProxyOptions = {},\n): any {\n if (options.renderable) {\n // For renderable mode, create a real React element with proxy access\n return createRenderableElement(\n getTree,\n [],\n options.stream,\n options.cssHrefs,\n options.jsPreloads,\n )\n }\n let slotUsages: Array<RscSlotUsageEvent> | undefined = undefined\n\n if (\n process.env.NODE_ENV === 'development' &&\n typeof window !== 'undefined' &&\n options.slotUsagesStream\n ) {\n slotUsages = []\n void consumeSlotUsages(options.slotUsagesStream, slotUsages)\n }\n\n // Non-renderable mode: plain proxy\n return createRscProxyWithPath(\n getTree,\n [],\n options.stream,\n options.cssHrefs,\n options.jsPreloads,\n slotUsages,\n options.slotUsagesStream,\n )\n}\n\n/**\n * Creates a React element that's also a Proxy for nested access.\n * This is used by renderable RSCs so they work as both {data} and {data.foo.bar}.\n */\ntype CreateProxyOptions = {\n getTree: () => unknown\n path: Array<string>\n stream: unknown | undefined\n cssHrefs: ReadonlySet<string> | undefined\n jsPreloads: ReadonlySet<string> | undefined\n renderable: boolean\n slotUsages: Array<RscSlotUsageEvent> | undefined\n slotUsagesStream: ReadableStream<RscSlotUsageEvent> | undefined\n}\n\nconst UNHANDLED = Symbol('tanstack.rsc.proxy.unhandled')\n\nfunction handleProxyTrap(\n kind: 'get' | 'has',\n prop: PropertyKey,\n options: CreateProxyOptions,\n): unknown | boolean | typeof UNHANDLED {\n switch (prop) {\n // Seroval (>=1.5) uses string sentinels and `in` checks for internal types.\n // These proxies must never look like streams/sequences.\n case '__SEROVAL_STREAM__':\n case '__SEROVAL_SEQUENCE__':\n return kind === 'get' ? undefined : false\n\n // Seroval >=1.5 also checks iterability via `Symbol.iterator in value`.\n case Symbol.iterator:\n case Symbol.asyncIterator:\n return kind === 'get' ? undefined : false\n\n // Our proxy branding/properties\n case SERVER_COMPONENT_STREAM:\n return kind === 'get' ? options.stream : options.stream !== undefined\n case SERVER_COMPONENT_CSS_HREFS:\n return kind === 'get' ? options.cssHrefs : options.cssHrefs !== undefined\n case SERVER_COMPONENT_JS_PRELOADS:\n return kind === 'get'\n ? options.jsPreloads\n : options.jsPreloads !== undefined\n case RSC_PROXY_GET_TREE:\n return kind === 'get' ? options.getTree : true\n case RSC_PROXY_PATH:\n return kind === 'get' ? options.path : true\n case RENDERABLE_RSC:\n return kind === 'get' ? options.renderable : true\n case RSC_SLOT_USAGES:\n return kind === 'get'\n ? options.slotUsages\n : options.slotUsages !== undefined\n\n case RSC_SLOT_USAGES_STREAM:\n return kind === 'get'\n ? options.slotUsagesStream\n : options.slotUsagesStream !== undefined\n\n // Avoid promise-like checks\n case 'then':\n return kind === 'get' ? undefined : UNHANDLED\n\n // Avoid breaking primitive coercion (eg String(proxy)).\n // Without these, nested-selection proxies can shadow these keys and\n // cause `Cannot convert object to primitive value` errors.\n case 'toString':\n return kind === 'get' ? Object.prototype.toString : UNHANDLED\n case 'valueOf':\n return kind === 'get' ? Object.prototype.valueOf : UNHANDLED\n case 'constructor':\n return kind === 'get' ? Object : UNHANDLED\n }\n\n // Non-renderable proxies claim all string keys exist for nested selection,\n // but symbol presence checks should be accurate.\n if (typeof prop === 'symbol') {\n return kind === 'get' ? undefined : false\n }\n\n return UNHANDLED\n}\n\nfunction createRscProxyInternal(options: CreateProxyOptions): any {\n // Per-proxy cache so repeated property access is referentially stable.\n const childCache = new Map<string, any>()\n\n const getChild = (key: string) => {\n const cached = childCache.get(key)\n if (cached) return cached\n\n const next = createRscProxyInternal({\n ...options,\n path: [...options.path, key],\n })\n childCache.set(key, next)\n return next\n }\n\n const dataProxy = options.renderable\n ? createRscProxyInternal({ ...options, renderable: false })\n : undefined\n\n // Use a React element (renderable) or plain object (non-renderable) as Proxy target.\n const proxyTarget = options.renderable\n ? createElement(RscNodeRenderer, { data: dataProxy })\n : ({} as any)\n\n return new Proxy(proxyTarget, {\n get(target, prop) {\n const handled = handleProxyTrap('get', prop, options)\n if (handled !== UNHANDLED) return handled\n\n if (options.renderable) {\n // Proxy invariants: if target has a non-configurable, read-only property,\n // we must return the *exact* value for that property.\n if (prop === 'props') {\n return target.props\n }\n\n if (prop === 'data') {\n return dataProxy\n }\n\n if (prop in target) {\n return target[prop]\n }\n }\n\n return getChild(String(prop))\n },\n\n has(target, prop) {\n const handled = handleProxyTrap('has', prop, options)\n if (handled !== UNHANDLED) return handled as boolean\n\n if (options.renderable) {\n if (prop in target) return true\n if (typeof prop === 'string') return true\n return false\n }\n\n // Allow any property access for nested selection.\n return true\n },\n\n getPrototypeOf(target) {\n return options.renderable\n ? Object.getPrototypeOf(target)\n : Object.prototype\n },\n\n getOwnPropertyDescriptor(target, prop) {\n return options.renderable\n ? Object.getOwnPropertyDescriptor(target, prop)\n : undefined\n },\n\n ownKeys(target) {\n return options.renderable ? Reflect.ownKeys(target) : []\n },\n })\n}\n\n/**\n * Creates a React element that's also a Proxy for nested access.\n * This is used by renderable RSCs so they work as both {data} and {data.foo.bar}.\n */\nfunction createRenderableElement(\n getTree: () => unknown,\n path: Array<string>,\n stream: unknown | undefined,\n cssHrefs: ReadonlySet<string> | undefined,\n jsPreloads: ReadonlySet<string> | undefined,\n): any {\n return createRscProxyInternal({\n getTree,\n path,\n stream,\n cssHrefs,\n jsPreloads,\n renderable: true,\n slotUsages: undefined,\n slotUsagesStream: undefined,\n })\n}\n\nfunction createRscProxyWithPath(\n getTree: () => unknown,\n path: Array<string>,\n stream: unknown | undefined,\n cssHrefs: ReadonlySet<string> | undefined,\n jsPreloads: ReadonlySet<string> | undefined,\n slotUsages: Array<RscSlotUsageEvent> | undefined,\n slotUsagesStream: ReadableStream<RscSlotUsageEvent> | undefined,\n): any {\n return createRscProxyInternal({\n getTree,\n path,\n stream,\n cssHrefs,\n jsPreloads,\n renderable: false,\n slotUsages,\n slotUsagesStream,\n })\n}\n\nasync function consumeSlotUsages(\n stream: ReadableStream<RscSlotUsageEvent>,\n slotUsages: Array<RscSlotUsageEvent>,\n): Promise<void> {\n try {\n const reader = stream.getReader()\n for (;;) {\n const { value, done } = await reader.read()\n if (done) break\n if (!value.slot) continue\n slotUsages.push(value)\n }\n } catch {\n // Ignore - dev-only best effort\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAiCA,SAAgB,eACd,SACA,UAA2B,EAAE,EACxB;AACL,KAAI,QAAQ,WAEV,QAAO,wBACL,SACA,EAAE,EACF,QAAQ,QACR,QAAQ,UACR,QAAQ,WACT;CAEH,IAAI,aAAmD,KAAA;AAEvD,KAAA,QAAA,IAAA,aAC2B,iBACzB,OAAO,WAAW,eAClB,QAAQ,kBACR;AACA,eAAa,EAAE;AACV,oBAAkB,QAAQ,kBAAkB,WAAW;;AAI9D,QAAO,uBACL,SACA,EAAE,EACF,QAAQ,QACR,QAAQ,UACR,QAAQ,YACR,YACA,QAAQ,iBACT;;AAkBH,IAAM,YAAY,OAAO,+BAA+B;AAExD,SAAS,gBACP,MACA,MACA,SACsC;AACtC,SAAQ,MAAR;EAGE,KAAK;EACL,KAAK,uBACH,QAAO,SAAS,QAAQ,KAAA,IAAY;EAGtC,KAAK,OAAO;EACZ,KAAK,OAAO,cACV,QAAO,SAAS,QAAQ,KAAA,IAAY;EAGtC,KAAK,wBACH,QAAO,SAAS,QAAQ,QAAQ,SAAS,QAAQ,WAAW,KAAA;EAC9D,KAAK,2BACH,QAAO,SAAS,QAAQ,QAAQ,WAAW,QAAQ,aAAa,KAAA;EAClE,KAAK,6BACH,QAAO,SAAS,QACZ,QAAQ,aACR,QAAQ,eAAe,KAAA;EAC7B,KAAK,mBACH,QAAO,SAAS,QAAQ,QAAQ,UAAU;EAC5C,KAAK,eACH,QAAO,SAAS,QAAQ,QAAQ,OAAO;EACzC,KAAK,eACH,QAAO,SAAS,QAAQ,QAAQ,aAAa;EAC/C,KAAK,gBACH,QAAO,SAAS,QACZ,QAAQ,aACR,QAAQ,eAAe,KAAA;EAE7B,KAAK,uBACH,QAAO,SAAS,QACZ,QAAQ,mBACR,QAAQ,qBAAqB,KAAA;EAGnC,KAAK,OACH,QAAO,SAAS,QAAQ,KAAA,IAAY;EAKtC,KAAK,WACH,QAAO,SAAS,QAAQ,OAAO,UAAU,WAAW;EACtD,KAAK,UACH,QAAO,SAAS,QAAQ,OAAO,UAAU,UAAU;EACrD,KAAK,cACH,QAAO,SAAS,QAAQ,SAAS;;AAKrC,KAAI,OAAO,SAAS,SAClB,QAAO,SAAS,QAAQ,KAAA,IAAY;AAGtC,QAAO;;AAGT,SAAS,uBAAuB,SAAkC;CAEhE,MAAM,6BAAa,IAAI,KAAkB;CAEzC,MAAM,YAAY,QAAgB;EAChC,MAAM,SAAS,WAAW,IAAI,IAAI;AAClC,MAAI,OAAQ,QAAO;EAEnB,MAAM,OAAO,uBAAuB;GAClC,GAAG;GACH,MAAM,CAAC,GAAG,QAAQ,MAAM,IAAI;GAC7B,CAAC;AACF,aAAW,IAAI,KAAK,KAAK;AACzB,SAAO;;CAGT,MAAM,YAAY,QAAQ,aACtB,uBAAuB;EAAE,GAAG;EAAS,YAAY;EAAO,CAAC,GACzD,KAAA;CAGJ,MAAM,cAAc,QAAQ,aACxB,cAAc,iBAAiB,EAAE,MAAM,WAAW,CAAC,GAClD,EAAE;AAEP,QAAO,IAAI,MAAM,aAAa;EAC5B,IAAI,QAAQ,MAAM;GAChB,MAAM,UAAU,gBAAgB,OAAO,MAAM,QAAQ;AACrD,OAAI,YAAY,UAAW,QAAO;AAElC,OAAI,QAAQ,YAAY;AAGtB,QAAI,SAAS,QACX,QAAO,OAAO;AAGhB,QAAI,SAAS,OACX,QAAO;AAGT,QAAI,QAAQ,OACV,QAAO,OAAO;;AAIlB,UAAO,SAAS,OAAO,KAAK,CAAC;;EAG/B,IAAI,QAAQ,MAAM;GAChB,MAAM,UAAU,gBAAgB,OAAO,MAAM,QAAQ;AACrD,OAAI,YAAY,UAAW,QAAO;AAElC,OAAI,QAAQ,YAAY;AACtB,QAAI,QAAQ,OAAQ,QAAO;AAC3B,QAAI,OAAO,SAAS,SAAU,QAAO;AACrC,WAAO;;AAIT,UAAO;;EAGT,eAAe,QAAQ;AACrB,UAAO,QAAQ,aACX,OAAO,eAAe,OAAO,GAC7B,OAAO;;EAGb,yBAAyB,QAAQ,MAAM;AACrC,UAAO,QAAQ,aACX,OAAO,yBAAyB,QAAQ,KAAK,GAC7C,KAAA;;EAGN,QAAQ,QAAQ;AACd,UAAO,QAAQ,aAAa,QAAQ,QAAQ,OAAO,GAAG,EAAE;;EAE3D,CAAC;;;;;;AAOJ,SAAS,wBACP,SACA,MACA,QACA,UACA,YACK;AACL,QAAO,uBAAuB;EAC5B;EACA;EACA;EACA;EACA;EACA,YAAY;EACZ,YAAY,KAAA;EACZ,kBAAkB,KAAA;EACnB,CAAC;;AAGJ,SAAS,uBACP,SACA,MACA,QACA,UACA,YACA,YACA,kBACK;AACL,QAAO,uBAAuB;EAC5B;EACA;EACA;EACA;EACA;EACA,YAAY;EACZ;EACA;EACD,CAAC;;AAGJ,eAAe,kBACb,QACA,YACe;AACf,KAAI;EACF,MAAM,SAAS,OAAO,WAAW;AACjC,WAAS;GACP,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO,MAAM;AAC3C,OAAI,KAAM;AACV,OAAI,CAAC,MAAM,KAAM;AACjB,cAAW,KAAK,MAAM;;SAElB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { awaitLazyElements } from "./awaitLazyElements.js";
|
|
3
|
+
import { createRscProxy } from "./createRscProxy.js";
|
|
4
|
+
import { use } from "react";
|
|
5
|
+
import { trackPostProcessPromise } from "@tanstack/start-client-core";
|
|
6
|
+
import { createFromReadableStream } from "virtual:tanstack-rsc-browser-decode";
|
|
7
|
+
//#region src/createServerComponentFromStream.ts
|
|
8
|
+
/**
|
|
9
|
+
* Creates a renderable RSC proxy from a raw Flight stream.
|
|
10
|
+
* Client-side only - used by the client serialization adapter for `renderServerComponent`.
|
|
11
|
+
*
|
|
12
|
+
* Returns a Proxy that:
|
|
13
|
+
* - Can be rendered directly as `{data}` in JSX
|
|
14
|
+
* - Supports nested access: `{data.foo.bar}`
|
|
15
|
+
* - Masquerades as a React element
|
|
16
|
+
*/
|
|
17
|
+
function createRenderableFromStream(stream) {
|
|
18
|
+
const { getTree, streamWrapper, cssHrefs } = setupStreamDecode(stream);
|
|
19
|
+
return createRscProxy(getTree, {
|
|
20
|
+
stream: streamWrapper,
|
|
21
|
+
cssHrefs,
|
|
22
|
+
renderable: true
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a composite RSC proxy from a raw Flight stream.
|
|
27
|
+
* Client-side only - used by the client serialization adapter for `createCompositeComponent`.
|
|
28
|
+
*
|
|
29
|
+
* Returns a Proxy that:
|
|
30
|
+
* - NOT directly renderable
|
|
31
|
+
* - Supports nested access: `src.foo.bar`
|
|
32
|
+
* - Must be rendered via `<CompositeComponent src={...} />`
|
|
33
|
+
*/
|
|
34
|
+
function createCompositeFromStream(stream, options) {
|
|
35
|
+
const { getTree, streamWrapper, cssHrefs } = setupStreamDecode(stream);
|
|
36
|
+
return createRscProxy(getTree, {
|
|
37
|
+
stream: streamWrapper,
|
|
38
|
+
cssHrefs,
|
|
39
|
+
renderable: false,
|
|
40
|
+
slotUsagesStream: options?.slotUsagesStream
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Shared stream decode setup for both renderable and composite.
|
|
45
|
+
*/
|
|
46
|
+
function setupStreamDecode(stream) {
|
|
47
|
+
const decodeThenable = createFromReadableStream(stream);
|
|
48
|
+
const cssHrefs = /* @__PURE__ */ new Set();
|
|
49
|
+
let cachedTree = void 0;
|
|
50
|
+
let cacheReady = false;
|
|
51
|
+
const transformedTreePromise = Promise.resolve(decodeThenable).then(async (result) => {
|
|
52
|
+
await awaitLazyElements(result, (href) => {
|
|
53
|
+
cssHrefs.add(href);
|
|
54
|
+
});
|
|
55
|
+
cachedTree = result;
|
|
56
|
+
cacheReady = true;
|
|
57
|
+
return result;
|
|
58
|
+
});
|
|
59
|
+
trackPostProcessPromise(transformedTreePromise);
|
|
60
|
+
const streamWrapper = { createReplayStream: () => stream };
|
|
61
|
+
const getTree = () => {
|
|
62
|
+
if (cacheReady) return cachedTree;
|
|
63
|
+
return use(transformedTreePromise);
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
getTree,
|
|
67
|
+
streamWrapper,
|
|
68
|
+
cssHrefs
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { createCompositeFromStream, createRenderableFromStream };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=createServerComponentFromStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createServerComponentFromStream.js","names":[],"sources":["../../src/createServerComponentFromStream.ts"],"sourcesContent":["'use client'\n\nimport { use } from 'react'\nimport { trackPostProcessPromise } from '@tanstack/start-client-core'\nimport { createFromReadableStream as browserDecode } from 'virtual:tanstack-rsc-browser-decode'\n\nimport { awaitLazyElements } from './awaitLazyElements'\nimport { createRscProxy } from './createRscProxy'\nimport type {\n AnyCompositeComponent,\n RscSlotUsageEvent,\n ServerComponentStream,\n} from './ServerComponentTypes'\n\n/**\n * Creates a renderable RSC proxy from a raw Flight stream.\n * Client-side only - used by the client serialization adapter for `renderServerComponent`.\n *\n * Returns a Proxy that:\n * - Can be rendered directly as `{data}` in JSX\n * - Supports nested access: `{data.foo.bar}`\n * - Masquerades as a React element\n */\nexport function createRenderableFromStream(\n stream: ReadableStream<Uint8Array>,\n): any {\n const { getTree, streamWrapper, cssHrefs } = setupStreamDecode(stream)\n\n return createRscProxy(getTree, {\n stream: streamWrapper,\n cssHrefs,\n renderable: true,\n })\n}\n\n/**\n * Creates a composite RSC proxy from a raw Flight stream.\n * Client-side only - used by the client serialization adapter for `createCompositeComponent`.\n *\n * Returns a Proxy that:\n * - NOT directly renderable\n * - Supports nested access: `src.foo.bar`\n * - Must be rendered via `<CompositeComponent src={...} />`\n */\nexport function createCompositeFromStream(\n stream: ReadableStream<Uint8Array>,\n options?: {\n slotUsagesStream?: ReadableStream<RscSlotUsageEvent>\n },\n): AnyCompositeComponent {\n const { getTree, streamWrapper, cssHrefs } = setupStreamDecode(stream)\n\n return createRscProxy(getTree, {\n stream: streamWrapper,\n cssHrefs,\n renderable: false,\n slotUsagesStream: options?.slotUsagesStream,\n })\n}\n\n/**\n * Shared stream decode setup for both renderable and composite.\n */\nfunction setupStreamDecode(stream: ReadableStream<Uint8Array>): {\n getTree: () => unknown\n streamWrapper: ServerComponentStream\n cssHrefs: Set<string> | undefined\n} {\n // Start decoding eagerly during deserialization\n const decodeThenable = browserDecode(stream)\n const cssHrefs = new Set<string>()\n\n // Synchronous cache for the decoded tree.\n let cachedTree: unknown = undefined\n let cacheReady = false\n\n // Promise for the tree with lazy elements awaited.\n const transformedTreePromise = Promise.resolve(decodeThenable).then(\n async (result) => {\n await awaitLazyElements(result, (href) => {\n cssHrefs.add(href)\n })\n cachedTree = result\n cacheReady = true\n return result\n },\n )\n\n // Track the lazy element loading - prevents flash\n trackPostProcessPromise(transformedTreePromise)\n\n const streamWrapper: ServerComponentStream = {\n createReplayStream: () => stream,\n }\n\n const getTree = () => {\n if (cacheReady) return cachedTree\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return use(transformedTreePromise)\n }\n\n return { getTree, streamWrapper, cssHrefs }\n}\n// Legacy export for backwards compatibility during migration\nexport const createServerComponentFromStream = createCompositeFromStream\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,SAAgB,2BACd,QACK;CACL,MAAM,EAAE,SAAS,eAAe,aAAa,kBAAkB,OAAO;AAEtE,QAAO,eAAe,SAAS;EAC7B,QAAQ;EACR;EACA,YAAY;EACb,CAAC;;;;;;;;;;;AAYJ,SAAgB,0BACd,QACA,SAGuB;CACvB,MAAM,EAAE,SAAS,eAAe,aAAa,kBAAkB,OAAO;AAEtE,QAAO,eAAe,SAAS;EAC7B,QAAQ;EACR;EACA,YAAY;EACZ,kBAAkB,SAAS;EAC5B,CAAC;;;;;AAMJ,SAAS,kBAAkB,QAIzB;CAEA,MAAM,iBAAiB,yBAAc,OAAO;CAC5C,MAAM,2BAAW,IAAI,KAAa;CAGlC,IAAI,aAAsB,KAAA;CAC1B,IAAI,aAAa;CAGjB,MAAM,yBAAyB,QAAQ,QAAQ,eAAe,CAAC,KAC7D,OAAO,WAAW;AAChB,QAAM,kBAAkB,SAAS,SAAS;AACxC,YAAS,IAAI,KAAK;IAClB;AACF,eAAa;AACb,eAAa;AACb,SAAO;GAEV;AAGD,yBAAwB,uBAAuB;CAE/C,MAAM,gBAAuC,EAC3C,0BAA0B,QAC3B;CAED,MAAM,gBAAgB;AACpB,MAAI,WAAY,QAAO;AAEvB,SAAO,IAAI,uBAAuB;;AAGpC,QAAO;EAAE;EAAS;EAAe;EAAU"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { renderToReadableStream } from "virtual:tanstack-rsc-runtime";
|
|
2
|
+
import { getServerFnById } from "#tanstack-start-server-fn-resolver";
|
|
3
|
+
//#region src/entry/rsc.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Shared RSC (React Server Components) entry point.
|
|
6
|
+
*
|
|
7
|
+
* This file exports the functions needed for the active RSC environment:
|
|
8
|
+
* - getServerFnById: Resolves server functions by their encoded ID
|
|
9
|
+
* - render: Renders a React node to an RSC Flight stream
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Renders a React node to an RSC Flight stream.
|
|
13
|
+
* Used internally for streaming server component output.
|
|
14
|
+
*/
|
|
15
|
+
function render(node) {
|
|
16
|
+
return renderToReadableStream(node);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { getServerFnById, render };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=rsc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rsc.js","names":[],"sources":["../../../src/entry/rsc.tsx"],"sourcesContent":["/**\n * Shared RSC (React Server Components) entry point.\n *\n * This file exports the functions needed for the active RSC environment:\n * - getServerFnById: Resolves server functions by their encoded ID\n * - render: Renders a React node to an RSC Flight stream\n */\n\nimport { renderToReadableStream } from 'virtual:tanstack-rsc-runtime'\nimport type React from 'react'\n\n// Re-export getServerFnById from the virtual module which handles both dev and production\n// In dev: dynamic import with base64url-decoded file path\n// In production: manifest-based lookup with bundled chunks\nexport { getServerFnById } from '#tanstack-start-server-fn-resolver'\n\n/**\n * Renders a React node to an RSC Flight stream.\n * Used internally for streaming server component output.\n */\nexport function render(node: React.ReactNode): ReadableStream<Uint8Array> {\n return renderToReadableStream(node)\n}\n"],"mappings":";;;;;;;;;;;;;;AAoBA,SAAgB,OAAO,MAAmD;AACxE,QAAO,uBAAuB,KAAK"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createClientOnlyFn, createIsomorphicFn } from "@tanstack/start-fn-stubs";
|
|
2
|
+
//#region src/flight.ts
|
|
3
|
+
/**
|
|
4
|
+
* Low-level Flight stream APIs for decoding RSC streams.
|
|
5
|
+
*
|
|
6
|
+
* These functions provide direct access to RSC Flight stream decoding,
|
|
7
|
+
* allowing advanced use cases like:
|
|
8
|
+
* - Server functions returning raw Flight Response
|
|
9
|
+
* - API routes streaming Flight payloads
|
|
10
|
+
* - Custom Flight stream handling via RawStream
|
|
11
|
+
*
|
|
12
|
+
* `createFromReadableStream` works in both SSR and browser contexts.
|
|
13
|
+
* `createFromFetch` is browser-only.
|
|
14
|
+
*
|
|
15
|
+
* NOTE: Dynamic imports keep decode initialisation runtime-specific. The
|
|
16
|
+
* concrete implementation comes from bundler-owned virtual modules.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Decode a Flight stream into React elements.
|
|
20
|
+
* Works in both SSR and browser contexts.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* const rawStream = await getRscRawStream()
|
|
25
|
+
* const tree = await createFromReadableStream(rawStream)
|
|
26
|
+
* return <>{tree}</>
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
var createFromReadableStream = createIsomorphicFn().server(async (stream) => {
|
|
30
|
+
const { createFromReadableStream: decode } = await import("virtual:tanstack-rsc-ssr-decode");
|
|
31
|
+
return decode(stream);
|
|
32
|
+
}).client(async (stream) => {
|
|
33
|
+
const { createFromReadableStream: decode } = await import("virtual:tanstack-rsc-browser-decode");
|
|
34
|
+
return decode(stream);
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Decode a Flight stream from a fetch Response.
|
|
38
|
+
* Browser only - will throw if called on the server.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* // From server function returning raw Response
|
|
43
|
+
* const tree = await createFromFetch(getFlightResponse())
|
|
44
|
+
*
|
|
45
|
+
* // From API route
|
|
46
|
+
* const tree = await createFromFetch(fetch('/api/rsc-flight'))
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
var createFromFetch = createClientOnlyFn(async (fetchPromise) => {
|
|
50
|
+
const { createFromFetch: decode } = await import("virtual:tanstack-rsc-browser-decode");
|
|
51
|
+
return decode(fetchPromise);
|
|
52
|
+
});
|
|
53
|
+
//#endregion
|
|
54
|
+
export { createFromFetch, createFromReadableStream };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=flight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flight.js","names":[],"sources":["../../src/flight.ts"],"sourcesContent":["/**\n * Low-level Flight stream APIs for decoding RSC streams.\n *\n * These functions provide direct access to RSC Flight stream decoding,\n * allowing advanced use cases like:\n * - Server functions returning raw Flight Response\n * - API routes streaming Flight payloads\n * - Custom Flight stream handling via RawStream\n *\n * `createFromReadableStream` works in both SSR and browser contexts.\n * `createFromFetch` is browser-only.\n *\n * NOTE: Dynamic imports keep decode initialisation runtime-specific. The\n * concrete implementation comes from bundler-owned virtual modules.\n */\n\nimport {\n createClientOnlyFn,\n createIsomorphicFn,\n} from '@tanstack/start-fn-stubs'\n\n/**\n * Decode a Flight stream into React elements.\n * Works in both SSR and browser contexts.\n *\n * @example\n * ```tsx\n * const rawStream = await getRscRawStream()\n * const tree = await createFromReadableStream(rawStream)\n * return <>{tree}</>\n * ```\n */\nexport const createFromReadableStream = createIsomorphicFn()\n .server(\n async (stream: ReadableStream<Uint8Array>): Promise<React.ReactNode> => {\n const { createFromReadableStream: decode } =\n await import('virtual:tanstack-rsc-ssr-decode')\n return decode(stream) as React.ReactNode\n },\n )\n .client(\n async (stream: ReadableStream<Uint8Array>): Promise<React.ReactNode> => {\n const { createFromReadableStream: decode } =\n await import('virtual:tanstack-rsc-browser-decode')\n return decode(stream)\n },\n )\n\n/**\n * Decode a Flight stream from a fetch Response.\n * Browser only - will throw if called on the server.\n *\n * @example\n * ```tsx\n * // From server function returning raw Response\n * const tree = await createFromFetch(getFlightResponse())\n *\n * // From API route\n * const tree = await createFromFetch(fetch('/api/rsc-flight'))\n * ```\n */\nexport const createFromFetch = createClientOnlyFn(\n async (fetchPromise: Promise<Response>): Promise<React.ReactNode> => {\n const { createFromFetch: decode } =\n await import('virtual:tanstack-rsc-browser-decode')\n return decode(fetchPromise)\n },\n)\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,2BAA2B,oBAAoB,CACzD,OACC,OAAO,WAAiE;CACtE,MAAM,EAAE,0BAA0B,WAChC,MAAM,OAAO;AACf,QAAO,OAAO,OAAO;EAExB,CACA,OACC,OAAO,WAAiE;CACtE,MAAM,EAAE,0BAA0B,WAChC,MAAM,OAAO;AACf,QAAO,OAAO,OAAO;EAExB;;;;;;;;;;;;;;AAeH,IAAa,kBAAkB,mBAC7B,OAAO,iBAA8D;CACnE,MAAM,EAAE,iBAAiB,WACvB,MAAM,OAAO;AACf,QAAO,OAAO,aAAa;EAE9B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/flight.stub.ts
|
|
2
|
+
/**
|
|
3
|
+
* Client stub for renderToReadableStream.
|
|
4
|
+
*
|
|
5
|
+
* This function should never be called at runtime on the client.
|
|
6
|
+
* It exists only to provide types for bundler imports in client bundles.
|
|
7
|
+
* The real implementation only runs inside RSC context (server functions).
|
|
8
|
+
*/
|
|
9
|
+
function renderToReadableStream(_node) {
|
|
10
|
+
throw new Error("renderToReadableStream cannot be called on the client. This function should only be called inside RSC context (server functions).");
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { renderToReadableStream };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=flight.stub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flight.stub.js","names":[],"sources":["../../src/flight.stub.ts"],"sourcesContent":["/**\n * Client stub for renderToReadableStream.\n *\n * This function should never be called at runtime on the client.\n * It exists only to provide types for bundler imports in client bundles.\n * The real implementation only runs inside RSC context (server functions).\n */\nexport function renderToReadableStream(\n _node: React.ReactNode,\n): ReadableStream<Uint8Array> {\n throw new Error(\n 'renderToReadableStream cannot be called on the client. ' +\n 'This function should only be called inside RSC context (server functions).',\n )\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,uBACd,OAC4B;AAC5B,OAAM,IAAI,MACR,oIAED"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SERVER_COMPONENT_CSS_HREFS } from "./ServerComponentTypes.js";
|
|
2
|
+
import { renderServerComponent } from "./renderServerComponent.stub.js";
|
|
3
|
+
import { createCompositeComponent } from "./createCompositeComponent.stub.js";
|
|
4
|
+
import { CompositeComponent } from "./CompositeComponent.js";
|
|
5
|
+
import { createFromFetch, createFromReadableStream } from "./flight.js";
|
|
6
|
+
import { renderToReadableStream } from "./flight.stub.js";
|
|
7
|
+
export { CompositeComponent, SERVER_COMPONENT_CSS_HREFS, createCompositeComponent, createFromFetch, createFromReadableStream, renderServerComponent, renderToReadableStream };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CompositeComponent } from "./CompositeComponent.js";
|
|
2
|
+
import { createFromFetch, createFromReadableStream } from "./flight.js";
|
|
3
|
+
import { renderServerComponent } from "./renderServerComponent.js";
|
|
4
|
+
import { createCompositeComponent } from "./createCompositeComponent.js";
|
|
5
|
+
import { renderToReadableStream } from "./flight.rsc.js";
|
|
6
|
+
export { CompositeComponent, createCompositeComponent, createFromFetch, createFromReadableStream, renderServerComponent, renderToReadableStream };
|