@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.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/LICENSE +21 -0
- package/dist/cjs/cli/index.js +268 -0
- package/dist/cjs/cli/tanstackTypes.js +388 -0
- package/dist/cjs/cli.js +65 -0
- package/dist/cjs/runtime/DefaultNotFound.js +47 -0
- package/dist/cjs/runtime/basepathRewrite.js +62 -0
- package/dist/cjs/runtime/dataMutation.js +345 -0
- package/dist/cjs/runtime/hooks.js +57 -0
- package/dist/cjs/runtime/index.js +114 -0
- package/dist/cjs/runtime/lifecycle.js +125 -0
- package/dist/cjs/runtime/plugin.js +250 -0
- package/dist/cjs/runtime/plugin.node.js +304 -0
- package/dist/cjs/runtime/prefetchLink.js +55 -0
- package/dist/cjs/runtime/routeTree.js +492 -0
- package/dist/cjs/runtime/rsc/ClientSlot.js +53 -0
- package/dist/cjs/runtime/rsc/CompositeComponent.js +75 -0
- package/dist/cjs/runtime/rsc/ReplayableStream.js +141 -0
- package/dist/cjs/runtime/rsc/RscNodeRenderer.js +65 -0
- package/dist/cjs/runtime/rsc/SlotContext.js +54 -0
- package/dist/cjs/runtime/rsc/client.js +93 -0
- package/dist/cjs/runtime/rsc/createRscProxy.js +141 -0
- package/dist/cjs/runtime/rsc/index.js +42 -0
- package/dist/cjs/runtime/rsc/payloadRouter.js +211 -0
- package/dist/cjs/runtime/rsc/server.js +246 -0
- package/dist/cjs/runtime/rsc/slotUsageSanitizer.js +65 -0
- package/dist/cjs/runtime/rsc/symbols.js +72 -0
- package/dist/cjs/runtime/types.js +18 -0
- package/dist/cjs/runtime/utils.js +142 -0
- package/dist/cjs/runtime.js +58 -0
- package/dist/esm/cli/index.mjs +201 -0
- package/dist/esm/cli/tanstackTypes.mjs +341 -0
- package/dist/esm/cli.mjs +2 -0
- package/dist/esm/rslib-runtime.mjs +18 -0
- package/dist/esm/runtime/DefaultNotFound.mjs +13 -0
- package/dist/esm/runtime/basepathRewrite.mjs +28 -0
- package/dist/esm/runtime/dataMutation.mjs +305 -0
- package/dist/esm/runtime/hooks.mjs +8 -0
- package/dist/esm/runtime/index.mjs +6 -0
- package/dist/esm/runtime/lifecycle.mjs +82 -0
- package/dist/esm/runtime/plugin.mjs +214 -0
- package/dist/esm/runtime/plugin.node.mjs +268 -0
- package/dist/esm/runtime/prefetchLink.mjs +18 -0
- package/dist/esm/runtime/routeTree.mjs +452 -0
- package/dist/esm/runtime/rsc/ClientSlot.mjs +19 -0
- package/dist/esm/runtime/rsc/CompositeComponent.mjs +41 -0
- package/dist/esm/runtime/rsc/ReplayableStream.mjs +104 -0
- package/dist/esm/runtime/rsc/RscNodeRenderer.mjs +31 -0
- package/dist/esm/runtime/rsc/SlotContext.mjs +17 -0
- package/dist/esm/runtime/rsc/client.mjs +53 -0
- package/dist/esm/runtime/rsc/createRscProxy.mjs +107 -0
- package/dist/esm/runtime/rsc/index.mjs +1 -0
- package/dist/esm/runtime/rsc/payloadRouter.mjs +162 -0
- package/dist/esm/runtime/rsc/server.mjs +200 -0
- package/dist/esm/runtime/rsc/slotUsageSanitizer.mjs +31 -0
- package/dist/esm/runtime/rsc/symbols.mjs +17 -0
- package/dist/esm/runtime/types.mjs +0 -0
- package/dist/esm/runtime/utils.mjs +89 -0
- package/dist/esm/runtime.mjs +1 -0
- package/dist/esm-node/cli/index.mjs +205 -0
- package/dist/esm-node/cli/tanstackTypes.mjs +342 -0
- package/dist/esm-node/cli.mjs +3 -0
- package/dist/esm-node/rslib-runtime.mjs +19 -0
- package/dist/esm-node/runtime/DefaultNotFound.mjs +14 -0
- package/dist/esm-node/runtime/basepathRewrite.mjs +29 -0
- package/dist/esm-node/runtime/dataMutation.mjs +306 -0
- package/dist/esm-node/runtime/hooks.mjs +9 -0
- package/dist/esm-node/runtime/index.mjs +7 -0
- package/dist/esm-node/runtime/lifecycle.mjs +83 -0
- package/dist/esm-node/runtime/plugin.mjs +215 -0
- package/dist/esm-node/runtime/plugin.node.mjs +269 -0
- package/dist/esm-node/runtime/prefetchLink.mjs +19 -0
- package/dist/esm-node/runtime/routeTree.mjs +453 -0
- package/dist/esm-node/runtime/rsc/ClientSlot.mjs +20 -0
- package/dist/esm-node/runtime/rsc/CompositeComponent.mjs +42 -0
- package/dist/esm-node/runtime/rsc/ReplayableStream.mjs +105 -0
- package/dist/esm-node/runtime/rsc/RscNodeRenderer.mjs +32 -0
- package/dist/esm-node/runtime/rsc/SlotContext.mjs +18 -0
- package/dist/esm-node/runtime/rsc/client.mjs +54 -0
- package/dist/esm-node/runtime/rsc/createRscProxy.mjs +108 -0
- package/dist/esm-node/runtime/rsc/index.mjs +2 -0
- package/dist/esm-node/runtime/rsc/payloadRouter.mjs +163 -0
- package/dist/esm-node/runtime/rsc/server.mjs +201 -0
- package/dist/esm-node/runtime/rsc/slotUsageSanitizer.mjs +32 -0
- package/dist/esm-node/runtime/rsc/symbols.mjs +18 -0
- package/dist/esm-node/runtime/types.mjs +1 -0
- package/dist/esm-node/runtime/utils.mjs +90 -0
- package/dist/esm-node/runtime.mjs +2 -0
- package/dist/types/cli/index.d.ts +20 -0
- package/dist/types/cli/tanstackTypes.d.ts +11 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/runtime/DefaultNotFound.d.ts +2 -0
- package/dist/types/runtime/basepathRewrite.d.ts +8 -0
- package/dist/types/runtime/dataMutation.d.ts +29 -0
- package/dist/types/runtime/hooks.d.ts +18 -0
- package/dist/types/runtime/index.d.ts +9 -0
- package/dist/types/runtime/lifecycle.d.ts +22 -0
- package/dist/types/runtime/plugin.d.ts +17 -0
- package/dist/types/runtime/plugin.node.d.ts +17 -0
- package/dist/types/runtime/prefetchLink.d.ts +11 -0
- package/dist/types/runtime/routeTree.d.ts +11 -0
- package/dist/types/runtime/rsc/ClientSlot.d.ts +5 -0
- package/dist/types/runtime/rsc/CompositeComponent.d.ts +3 -0
- package/dist/types/runtime/rsc/ReplayableStream.d.ts +24 -0
- package/dist/types/runtime/rsc/RscNodeRenderer.d.ts +5 -0
- package/dist/types/runtime/rsc/SlotContext.d.ts +11 -0
- package/dist/types/runtime/rsc/client.d.ts +11 -0
- package/dist/types/runtime/rsc/createRscProxy.d.ts +7 -0
- package/dist/types/runtime/rsc/index.d.ts +2 -0
- package/dist/types/runtime/rsc/payloadRouter.d.ts +24 -0
- package/dist/types/runtime/rsc/server.d.ts +14 -0
- package/dist/types/runtime/rsc/slotUsageSanitizer.d.ts +2 -0
- package/dist/types/runtime/rsc/symbols.d.ts +46 -0
- package/dist/types/runtime/types.d.ts +68 -0
- package/dist/types/runtime/utils.d.ts +36 -0
- package/dist/types/runtime.d.ts +1 -0
- package/dist/types-direct/cli/index.d.ts +20 -0
- package/dist/types-direct/cli/tanstackTypes.d.ts +11 -0
- package/dist/types-direct/cli.d.ts +2 -0
- package/dist/types-direct/runtime/DefaultNotFound.d.ts +2 -0
- package/dist/types-direct/runtime/basepathRewrite.d.ts +8 -0
- package/dist/types-direct/runtime/dataMutation.d.ts +29 -0
- package/dist/types-direct/runtime/hooks.d.ts +18 -0
- package/dist/types-direct/runtime/index.d.ts +9 -0
- package/dist/types-direct/runtime/lifecycle.d.ts +22 -0
- package/dist/types-direct/runtime/plugin.d.ts +17 -0
- package/dist/types-direct/runtime/plugin.node.d.ts +17 -0
- package/dist/types-direct/runtime/prefetchLink.d.ts +11 -0
- package/dist/types-direct/runtime/routeTree.d.ts +11 -0
- package/dist/types-direct/runtime/rsc/ClientSlot.d.ts +5 -0
- package/dist/types-direct/runtime/rsc/CompositeComponent.d.ts +3 -0
- package/dist/types-direct/runtime/rsc/ReplayableStream.d.ts +24 -0
- package/dist/types-direct/runtime/rsc/RscNodeRenderer.d.ts +5 -0
- package/dist/types-direct/runtime/rsc/SlotContext.d.ts +11 -0
- package/dist/types-direct/runtime/rsc/client.d.ts +11 -0
- package/dist/types-direct/runtime/rsc/createRscProxy.d.ts +7 -0
- package/dist/types-direct/runtime/rsc/index.d.ts +2 -0
- package/dist/types-direct/runtime/rsc/payloadRouter.d.ts +24 -0
- package/dist/types-direct/runtime/rsc/server.d.ts +14 -0
- package/dist/types-direct/runtime/rsc/slotUsageSanitizer.d.ts +2 -0
- package/dist/types-direct/runtime/rsc/symbols.d.ts +46 -0
- package/dist/types-direct/runtime/types.d.ts +68 -0
- package/dist/types-direct/runtime/utils.d.ts +36 -0
- package/dist/types-direct/runtime.d.ts +1 -0
- package/package.json +126 -0
- package/rslib.config.mts +4 -0
- package/rstest.config.mts +43 -0
- package/src/cli/index.ts +388 -0
- package/src/cli/tanstackTypes.ts +503 -0
- package/src/cli.ts +2 -0
- package/src/runtime/DefaultNotFound.tsx +15 -0
- package/src/runtime/basepathRewrite.ts +59 -0
- package/src/runtime/dataMutation.tsx +517 -0
- package/src/runtime/hooks.ts +34 -0
- package/src/runtime/index.tsx +30 -0
- package/src/runtime/lifecycle.ts +150 -0
- package/src/runtime/plugin.node.tsx +534 -0
- package/src/runtime/plugin.tsx +395 -0
- package/src/runtime/prefetchLink.tsx +87 -0
- package/src/runtime/routeTree.ts +942 -0
- package/src/runtime/rsc/ClientSlot.tsx +25 -0
- package/src/runtime/rsc/CompositeComponent.tsx +65 -0
- package/src/runtime/rsc/ReplayableStream.ts +155 -0
- package/src/runtime/rsc/RscNodeRenderer.tsx +45 -0
- package/src/runtime/rsc/SlotContext.tsx +31 -0
- package/src/runtime/rsc/client.tsx +90 -0
- package/src/runtime/rsc/createRscProxy.tsx +189 -0
- package/src/runtime/rsc/index.ts +10 -0
- package/src/runtime/rsc/payloadRouter.ts +318 -0
- package/src/runtime/rsc/server.tsx +303 -0
- package/src/runtime/rsc/slotUsageSanitizer.ts +76 -0
- package/src/runtime/rsc/symbols.ts +106 -0
- package/src/runtime/ssr-shim.d.ts +12 -0
- package/src/runtime/types.ts +83 -0
- package/src/runtime/utils.tsx +161 -0
- package/src/runtime.ts +1 -0
- package/tests/router/cli.test.ts +386 -0
- package/tests/router/dataMutation.test.tsx +396 -0
- package/tests/router/prefetchLink.test.tsx +43 -0
- package/tests/router/routeTree.test.ts +502 -0
- package/tests/router/rsc.test.tsx +256 -0
- package/tests/router/tanstackTypes.test.ts +62 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsgo.json +6 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
import { useSlotContext } from './SlotContext';
|
|
5
|
+
|
|
6
|
+
export function ClientSlot({ args, slot }: { args: unknown[]; slot: string }) {
|
|
7
|
+
const context = useSlotContext();
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new Error('ClientSlot must be rendered inside CompositeComponent.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const implementation = context.implementations[slot];
|
|
13
|
+
if (typeof implementation === 'undefined') {
|
|
14
|
+
if (context.strict) {
|
|
15
|
+
throw new Error(`Missing RSC slot implementation for "${slot}".`);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (typeof implementation === 'function') {
|
|
21
|
+
return <>{implementation(...args)}</>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return <>{implementation as React.ReactNode}</>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
import { Suspense } from 'react';
|
|
5
|
+
import { SlotProvider } from './SlotContext';
|
|
6
|
+
import {
|
|
7
|
+
type AnyCompositeComponent,
|
|
8
|
+
type CompositeComponentProps,
|
|
9
|
+
RSC_PROXY_GET_TREE,
|
|
10
|
+
RSC_PROXY_PATH,
|
|
11
|
+
} from './symbols';
|
|
12
|
+
|
|
13
|
+
function EmptyFallback() {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function selectTreePath(tree: unknown, path: string[]) {
|
|
18
|
+
let current = tree;
|
|
19
|
+
for (const key of path) {
|
|
20
|
+
if (current === null || typeof current !== 'object') {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
current = (current as Record<string, unknown>)[key];
|
|
24
|
+
}
|
|
25
|
+
return current as React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function CompositeInner<TComp extends AnyCompositeComponent>({
|
|
29
|
+
slotProps,
|
|
30
|
+
src,
|
|
31
|
+
strict,
|
|
32
|
+
}: {
|
|
33
|
+
slotProps: Record<string, unknown>;
|
|
34
|
+
src: TComp;
|
|
35
|
+
strict?: boolean;
|
|
36
|
+
}) {
|
|
37
|
+
const getTree = src[RSC_PROXY_GET_TREE];
|
|
38
|
+
if (!getTree) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
'CompositeComponent src must come from createCompositeComponent().',
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const tree = selectTreePath(getTree(), src[RSC_PROXY_PATH] || []);
|
|
45
|
+
return (
|
|
46
|
+
<SlotProvider implementations={slotProps} strict={strict}>
|
|
47
|
+
{tree}
|
|
48
|
+
</SlotProvider>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function CompositeComponent<TComp extends AnyCompositeComponent>(
|
|
53
|
+
props: CompositeComponentProps<TComp>,
|
|
54
|
+
) {
|
|
55
|
+
const { children, src, strict, ...slotProps } = props;
|
|
56
|
+
return (
|
|
57
|
+
<Suspense fallback={<EmptyFallback />}>
|
|
58
|
+
<CompositeInner
|
|
59
|
+
slotProps={{ ...slotProps, children }}
|
|
60
|
+
src={src}
|
|
61
|
+
strict={strict}
|
|
62
|
+
/>
|
|
63
|
+
</Suspense>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
export interface ReplayableStreamOptions {
|
|
2
|
+
signal?: AbortSignal;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
type Waiter = {
|
|
6
|
+
promise: Promise<void>;
|
|
7
|
+
resolve: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const REPLAYABLE_STREAM_MARKER = Symbol.for(
|
|
11
|
+
'modern.tanstack.rsc.ReplayableStream',
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export class ReplayableStream<T = Uint8Array> {
|
|
15
|
+
readonly [REPLAYABLE_STREAM_MARKER] = true;
|
|
16
|
+
|
|
17
|
+
private chunks: T[] = [];
|
|
18
|
+
private done = false;
|
|
19
|
+
private error: unknown;
|
|
20
|
+
private waiter: Waiter | null = null;
|
|
21
|
+
private released = false;
|
|
22
|
+
private reader: ReadableStreamDefaultReader<T> | null = null;
|
|
23
|
+
private abortListener: (() => void) | null = null;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
private source: ReadableStream<T>,
|
|
27
|
+
private options: ReplayableStreamOptions = {},
|
|
28
|
+
) {
|
|
29
|
+
this.start();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private start() {
|
|
33
|
+
const signal = this.options.signal;
|
|
34
|
+
if (signal?.aborted) {
|
|
35
|
+
this.release();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.abortListener = () => this.release();
|
|
40
|
+
signal?.addEventListener('abort', this.abortListener, { once: true });
|
|
41
|
+
|
|
42
|
+
const reader = this.source.getReader();
|
|
43
|
+
this.reader = reader;
|
|
44
|
+
|
|
45
|
+
const pump = async () => {
|
|
46
|
+
try {
|
|
47
|
+
while (!this.released) {
|
|
48
|
+
const { done, value } = await reader.read();
|
|
49
|
+
if (done) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
this.chunks.push(value);
|
|
53
|
+
this.notify();
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (!this.released) {
|
|
57
|
+
this.error = err;
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
this.done = true;
|
|
61
|
+
this.detachAbortListener();
|
|
62
|
+
try {
|
|
63
|
+
reader.releaseLock();
|
|
64
|
+
} catch {}
|
|
65
|
+
if (this.reader === reader) {
|
|
66
|
+
this.reader = null;
|
|
67
|
+
}
|
|
68
|
+
this.notify();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
void pump();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private detachAbortListener() {
|
|
76
|
+
const signal = this.options.signal;
|
|
77
|
+
if (signal && this.abortListener) {
|
|
78
|
+
signal.removeEventListener('abort', this.abortListener);
|
|
79
|
+
}
|
|
80
|
+
this.abortListener = null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private notify() {
|
|
84
|
+
this.waiter?.resolve();
|
|
85
|
+
this.waiter = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private wait() {
|
|
89
|
+
if (this.done || this.released) {
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
if (!this.waiter) {
|
|
93
|
+
let resolve!: () => void;
|
|
94
|
+
const promise = new Promise<void>(res => {
|
|
95
|
+
resolve = res;
|
|
96
|
+
});
|
|
97
|
+
this.waiter = { promise, resolve };
|
|
98
|
+
}
|
|
99
|
+
return this.waiter.promise;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
release() {
|
|
103
|
+
if (this.released) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.released = true;
|
|
107
|
+
this.chunks = [];
|
|
108
|
+
this.detachAbortListener();
|
|
109
|
+
try {
|
|
110
|
+
void this.reader?.cancel(new Error('ReplayableStream released'));
|
|
111
|
+
} catch {}
|
|
112
|
+
this.notify();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
isReleased() {
|
|
116
|
+
return this.released;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
createReplayStream(): ReadableStream<T> {
|
|
120
|
+
if (this.released) {
|
|
121
|
+
return new ReadableStream<T>({
|
|
122
|
+
start(controller) {
|
|
123
|
+
controller.close();
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let index = 0;
|
|
129
|
+
return new ReadableStream<T>({
|
|
130
|
+
pull: async controller => {
|
|
131
|
+
while (true) {
|
|
132
|
+
if (index < this.chunks.length) {
|
|
133
|
+
controller.enqueue(this.chunks[index++]);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (this.error) {
|
|
138
|
+
controller.error(this.error);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (this.done || this.released) {
|
|
143
|
+
controller.close();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await this.wait();
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
cancel: () => {
|
|
151
|
+
// Canceling one replay reader must not cancel the shared upstream stream.
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
import { Suspense } from 'react';
|
|
5
|
+
import {
|
|
6
|
+
type AnyRenderableServerComponent,
|
|
7
|
+
RSC_PROXY_GET_TREE,
|
|
8
|
+
RSC_PROXY_PATH,
|
|
9
|
+
} from './symbols';
|
|
10
|
+
|
|
11
|
+
function EmptyFallback() {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function selectTreePath(tree: unknown, path: string[]) {
|
|
16
|
+
let current = tree;
|
|
17
|
+
for (const key of path) {
|
|
18
|
+
if (current === null || typeof current !== 'object') {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
current = (current as Record<string, unknown>)[key];
|
|
22
|
+
}
|
|
23
|
+
return current as React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function RscNodeRenderInner({ data }: { data: AnyRenderableServerComponent }) {
|
|
27
|
+
const getTree = data[RSC_PROXY_GET_TREE];
|
|
28
|
+
if (!getTree) {
|
|
29
|
+
throw new Error('Renderable RSC is missing its decoded tree getter.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return <>{selectTreePath(getTree(), data[RSC_PROXY_PATH] || [])}</>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function RscNodeRenderer({
|
|
36
|
+
data,
|
|
37
|
+
}: {
|
|
38
|
+
data: AnyRenderableServerComponent;
|
|
39
|
+
}) {
|
|
40
|
+
return (
|
|
41
|
+
<Suspense fallback={<EmptyFallback />}>
|
|
42
|
+
<RscNodeRenderInner data={data} />
|
|
43
|
+
</Suspense>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
import { createContext, use } from 'react';
|
|
5
|
+
|
|
6
|
+
export type SlotImplementations = Record<string, unknown>;
|
|
7
|
+
|
|
8
|
+
const SlotContext = createContext<{
|
|
9
|
+
implementations: SlotImplementations;
|
|
10
|
+
strict: boolean;
|
|
11
|
+
} | null>(null);
|
|
12
|
+
|
|
13
|
+
export function useSlotContext() {
|
|
14
|
+
return use(SlotContext);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function SlotProvider({
|
|
18
|
+
children,
|
|
19
|
+
implementations,
|
|
20
|
+
strict,
|
|
21
|
+
}: {
|
|
22
|
+
children?: React.ReactNode;
|
|
23
|
+
implementations: SlotImplementations;
|
|
24
|
+
strict?: boolean;
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<SlotContext value={{ implementations, strict: strict === true }}>
|
|
28
|
+
{children}
|
|
29
|
+
</SlotContext>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createSerializationAdapter } from '@tanstack/react-router';
|
|
4
|
+
import { use } from 'react';
|
|
5
|
+
import { CompositeComponent } from './CompositeComponent';
|
|
6
|
+
import { createRscProxy } from './createRscProxy';
|
|
7
|
+
import type {
|
|
8
|
+
AnyCompositeComponent,
|
|
9
|
+
RscSlotUsageEvent,
|
|
10
|
+
ServerComponentStream,
|
|
11
|
+
} from './symbols';
|
|
12
|
+
|
|
13
|
+
export type {
|
|
14
|
+
AnyCompositeComponent,
|
|
15
|
+
AnyRenderableServerComponent,
|
|
16
|
+
CompositeComponentProps,
|
|
17
|
+
} from './symbols';
|
|
18
|
+
export { CompositeComponent };
|
|
19
|
+
|
|
20
|
+
type SerializedRsc = {
|
|
21
|
+
kind: 'renderable' | 'composite';
|
|
22
|
+
stream: ReadableStream<Uint8Array>;
|
|
23
|
+
slotUsagesStream?: ReadableStream<RscSlotUsageEvent>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type ModernRscClientRuntime = typeof import('@modern-js/runtime/rsc/client');
|
|
27
|
+
|
|
28
|
+
let modernRscClientRuntimePromise: Promise<ModernRscClientRuntime> | undefined;
|
|
29
|
+
|
|
30
|
+
function loadModernRscClientRuntime() {
|
|
31
|
+
modernRscClientRuntimePromise ??= import(
|
|
32
|
+
'@modern-js/runtime/rsc/client'
|
|
33
|
+
).then(runtime => {
|
|
34
|
+
runtime.setServerCallback(runtime.callServer);
|
|
35
|
+
return runtime;
|
|
36
|
+
});
|
|
37
|
+
return modernRscClientRuntimePromise;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createTreeGetter(stream: ReadableStream<Uint8Array>) {
|
|
41
|
+
let ready = false;
|
|
42
|
+
let tree: unknown;
|
|
43
|
+
const treePromise = loadModernRscClientRuntime()
|
|
44
|
+
.then(runtime =>
|
|
45
|
+
runtime.createFromReadableStream(stream, {
|
|
46
|
+
callServer: runtime.callServer,
|
|
47
|
+
}),
|
|
48
|
+
)
|
|
49
|
+
.then(value => {
|
|
50
|
+
tree = value;
|
|
51
|
+
ready = true;
|
|
52
|
+
return value;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
if (ready) {
|
|
57
|
+
return tree;
|
|
58
|
+
}
|
|
59
|
+
return use(treePromise);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function createFromFlightStream(serialized: SerializedRsc) {
|
|
64
|
+
const streamWrapper: ServerComponentStream = {
|
|
65
|
+
createReplayStream: () => serialized.stream,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return createRscProxy(createTreeGetter(serialized.stream), {
|
|
69
|
+
renderable: serialized.kind === 'renderable',
|
|
70
|
+
slotUsagesStream: serialized.slotUsagesStream,
|
|
71
|
+
stream: streamWrapper,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const adapter = createSerializationAdapter({
|
|
76
|
+
key: '$MODERN_TANSTACK_RSC',
|
|
77
|
+
test: (_value: unknown): _value is never => false,
|
|
78
|
+
toSerializable: (): never => {
|
|
79
|
+
throw new Error('TanStack RSC data cannot be serialized on client.');
|
|
80
|
+
},
|
|
81
|
+
fromSerializable: (value: SerializedRsc): AnyCompositeComponent => {
|
|
82
|
+
return createFromFlightStream(value) as AnyCompositeComponent;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export function getTanstackRscSerializationAdapters() {
|
|
87
|
+
return [adapter];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const rscSerializationAdapter = getTanstackRscSerializationAdapters;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createElement } from 'react';
|
|
4
|
+
import { RscNodeRenderer } from './RscNodeRenderer';
|
|
5
|
+
import {
|
|
6
|
+
RENDERABLE_RSC,
|
|
7
|
+
RSC_PROXY_GET_TREE,
|
|
8
|
+
RSC_PROXY_PATH,
|
|
9
|
+
RSC_SLOT_USAGES,
|
|
10
|
+
RSC_SLOT_USAGES_STREAM,
|
|
11
|
+
type RscSlotUsageEvent,
|
|
12
|
+
SERVER_COMPONENT_STREAM,
|
|
13
|
+
type ServerComponentStream,
|
|
14
|
+
} from './symbols';
|
|
15
|
+
|
|
16
|
+
type CreateProxyOptions = {
|
|
17
|
+
getTree: () => unknown;
|
|
18
|
+
path: string[];
|
|
19
|
+
renderable: boolean;
|
|
20
|
+
slotUsages?: RscSlotUsageEvent[];
|
|
21
|
+
slotUsagesStream?: ReadableStream<RscSlotUsageEvent>;
|
|
22
|
+
stream?: ServerComponentStream;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const UNHANDLED = Symbol('modern.tanstack.rsc.unhandled');
|
|
26
|
+
|
|
27
|
+
function handleProxyTrap(
|
|
28
|
+
kind: 'get' | 'has',
|
|
29
|
+
prop: PropertyKey,
|
|
30
|
+
options: CreateProxyOptions,
|
|
31
|
+
) {
|
|
32
|
+
switch (prop) {
|
|
33
|
+
case '__SEROVAL_STREAM__':
|
|
34
|
+
case '__SEROVAL_SEQUENCE__':
|
|
35
|
+
case Symbol.iterator:
|
|
36
|
+
case Symbol.asyncIterator:
|
|
37
|
+
return kind === 'get' ? undefined : false;
|
|
38
|
+
case SERVER_COMPONENT_STREAM:
|
|
39
|
+
return kind === 'get' ? options.stream : Boolean(options.stream);
|
|
40
|
+
case RENDERABLE_RSC:
|
|
41
|
+
return kind === 'get' ? options.renderable : true;
|
|
42
|
+
case RSC_PROXY_GET_TREE:
|
|
43
|
+
return kind === 'get' ? options.getTree : true;
|
|
44
|
+
case RSC_PROXY_PATH:
|
|
45
|
+
return kind === 'get' ? options.path : true;
|
|
46
|
+
case RSC_SLOT_USAGES_STREAM:
|
|
47
|
+
return kind === 'get'
|
|
48
|
+
? options.slotUsagesStream
|
|
49
|
+
: Boolean(options.slotUsagesStream);
|
|
50
|
+
case RSC_SLOT_USAGES:
|
|
51
|
+
return kind === 'get' ? options.slotUsages : Boolean(options.slotUsages);
|
|
52
|
+
case 'then':
|
|
53
|
+
return kind === 'get' ? undefined : UNHANDLED;
|
|
54
|
+
case 'toString':
|
|
55
|
+
return kind === 'get' ? Object.prototype.toString : UNHANDLED;
|
|
56
|
+
case 'valueOf':
|
|
57
|
+
return kind === 'get' ? Object.prototype.valueOf : UNHANDLED;
|
|
58
|
+
case 'constructor':
|
|
59
|
+
return kind === 'get' ? Object : UNHANDLED;
|
|
60
|
+
default:
|
|
61
|
+
return UNHANDLED;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createRscProxyInternal(options: CreateProxyOptions): any {
|
|
66
|
+
const childCache = new Map<string, unknown>();
|
|
67
|
+
const dataProxy = options.renderable
|
|
68
|
+
? createRscProxyInternal({ ...options, renderable: false })
|
|
69
|
+
: undefined;
|
|
70
|
+
const target = options.renderable
|
|
71
|
+
? createElement(RscNodeRenderer, { data: dataProxy })
|
|
72
|
+
: {};
|
|
73
|
+
|
|
74
|
+
const getChild = (key: string) => {
|
|
75
|
+
if (!childCache.has(key)) {
|
|
76
|
+
childCache.set(
|
|
77
|
+
key,
|
|
78
|
+
createRscProxyInternal({
|
|
79
|
+
...options,
|
|
80
|
+
path: [...options.path, key],
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return childCache.get(key);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return new Proxy(target as Record<PropertyKey, unknown>, {
|
|
88
|
+
get(proxyTarget, prop) {
|
|
89
|
+
const handled = handleProxyTrap('get', prop, options);
|
|
90
|
+
if (handled !== UNHANDLED) {
|
|
91
|
+
return handled;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (options.renderable) {
|
|
95
|
+
if (prop === 'props') {
|
|
96
|
+
return proxyTarget.props;
|
|
97
|
+
}
|
|
98
|
+
if (prop === 'data') {
|
|
99
|
+
return dataProxy;
|
|
100
|
+
}
|
|
101
|
+
if (prop in proxyTarget) {
|
|
102
|
+
return proxyTarget[prop];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (typeof prop === 'symbol') {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return getChild(prop);
|
|
110
|
+
},
|
|
111
|
+
getOwnPropertyDescriptor(proxyTarget, prop) {
|
|
112
|
+
return options.renderable
|
|
113
|
+
? Object.getOwnPropertyDescriptor(proxyTarget, prop)
|
|
114
|
+
: undefined;
|
|
115
|
+
},
|
|
116
|
+
getPrototypeOf(proxyTarget) {
|
|
117
|
+
return options.renderable
|
|
118
|
+
? Object.getPrototypeOf(proxyTarget)
|
|
119
|
+
: Object.prototype;
|
|
120
|
+
},
|
|
121
|
+
has(proxyTarget, prop) {
|
|
122
|
+
const handled = handleProxyTrap('has', prop, options);
|
|
123
|
+
if (handled !== UNHANDLED) {
|
|
124
|
+
return Boolean(handled);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (options.renderable) {
|
|
128
|
+
return prop in proxyTarget || typeof prop === 'string';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return typeof prop === 'string';
|
|
132
|
+
},
|
|
133
|
+
ownKeys(proxyTarget) {
|
|
134
|
+
return options.renderable ? Reflect.ownKeys(proxyTarget) : [];
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function createRscProxy(
|
|
140
|
+
getTree: () => unknown,
|
|
141
|
+
options: {
|
|
142
|
+
renderable?: boolean;
|
|
143
|
+
slotUsages?: RscSlotUsageEvent[];
|
|
144
|
+
slotUsagesStream?: ReadableStream<RscSlotUsageEvent>;
|
|
145
|
+
stream?: ServerComponentStream;
|
|
146
|
+
} = {},
|
|
147
|
+
) {
|
|
148
|
+
const slotUsages =
|
|
149
|
+
process.env.NODE_ENV === 'development' && options.slotUsagesStream
|
|
150
|
+
? (options.slotUsages ?? [])
|
|
151
|
+
: options.slotUsages;
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
process.env.NODE_ENV === 'development' &&
|
|
155
|
+
options.slotUsagesStream &&
|
|
156
|
+
slotUsages
|
|
157
|
+
) {
|
|
158
|
+
void consumeSlotUsages(options.slotUsagesStream, slotUsages);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return createRscProxyInternal({
|
|
162
|
+
getTree,
|
|
163
|
+
path: [],
|
|
164
|
+
renderable: options.renderable === true,
|
|
165
|
+
slotUsages,
|
|
166
|
+
slotUsagesStream: options.slotUsagesStream,
|
|
167
|
+
stream: options.stream,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function consumeSlotUsages(
|
|
172
|
+
stream: ReadableStream<RscSlotUsageEvent>,
|
|
173
|
+
slotUsages: RscSlotUsageEvent[],
|
|
174
|
+
) {
|
|
175
|
+
try {
|
|
176
|
+
const reader = stream.getReader();
|
|
177
|
+
for (;;) {
|
|
178
|
+
const { value, done } = await reader.read();
|
|
179
|
+
if (done) {
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
if (value?.slot) {
|
|
183
|
+
slotUsages.push(value);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} catch {
|
|
187
|
+
// Development-only metadata should not affect rendering.
|
|
188
|
+
}
|
|
189
|
+
}
|