@fluojs/react 0.1.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/README.ko.md +715 -0
- package/README.md +729 -0
- package/dist/client/errors.d.ts +12 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +19 -0
- package/dist/client/hooks.d.ts +38 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +56 -0
- package/dist/client/link.d.ts +13 -0
- package/dist/client/link.d.ts.map +1 -0
- package/dist/client/link.js +39 -0
- package/dist/client/provider.d.ts +12 -0
- package/dist/client/provider.d.ts.map +1 -0
- package/dist/client/provider.js +54 -0
- package/dist/client/search-params.d.ts +17 -0
- package/dist/client/search-params.d.ts.map +1 -0
- package/dist/client/search-params.js +39 -0
- package/dist/client/snapshot.d.ts +15 -0
- package/dist/client/snapshot.d.ts.map +1 -0
- package/dist/client/snapshot.js +57 -0
- package/dist/client/store.d.ts +26 -0
- package/dist/client/store.d.ts.map +1 -0
- package/dist/client/store.js +138 -0
- package/dist/client/types.d.ts +63 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +1 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +5 -0
- package/dist/decorators.d.ts +80 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +194 -0
- package/dist/experimental/rsc-diagnostics.d.ts +13 -0
- package/dist/experimental/rsc-diagnostics.d.ts.map +1 -0
- package/dist/experimental/rsc-diagnostics.js +58 -0
- package/dist/experimental/rsc-flight-response.d.ts +15 -0
- package/dist/experimental/rsc-flight-response.d.ts.map +1 -0
- package/dist/experimental/rsc-flight-response.js +76 -0
- package/dist/experimental/rsc-manifest.d.ts +13 -0
- package/dist/experimental/rsc-manifest.d.ts.map +1 -0
- package/dist/experimental/rsc-manifest.js +88 -0
- package/dist/experimental/rsc-types.d.ts +118 -0
- package/dist/experimental/rsc-types.d.ts.map +1 -0
- package/dist/experimental/rsc-types.js +50 -0
- package/dist/experimental/rsc.d.ts +11 -0
- package/dist/experimental/rsc.d.ts.map +1 -0
- package/dist/experimental/rsc.js +8 -0
- package/dist/experimental/server-functions-client.d.ts +14 -0
- package/dist/experimental/server-functions-client.d.ts.map +1 -0
- package/dist/experimental/server-functions-client.js +120 -0
- package/dist/experimental/server-functions-configuration.d.ts +17 -0
- package/dist/experimental/server-functions-configuration.d.ts.map +1 -0
- package/dist/experimental/server-functions-configuration.js +68 -0
- package/dist/experimental/server-functions-errors.d.ts +19 -0
- package/dist/experimental/server-functions-errors.d.ts.map +1 -0
- package/dist/experimental/server-functions-errors.js +22 -0
- package/dist/experimental/server-functions-reference.d.ts +22 -0
- package/dist/experimental/server-functions-reference.d.ts.map +1 -0
- package/dist/experimental/server-functions-reference.js +76 -0
- package/dist/experimental/server-functions-serialization.d.ts +37 -0
- package/dist/experimental/server-functions-serialization.d.ts.map +1 -0
- package/dist/experimental/server-functions-serialization.js +158 -0
- package/dist/experimental/server-functions-server.d.ts +14 -0
- package/dist/experimental/server-functions-server.d.ts.map +1 -0
- package/dist/experimental/server-functions-server.js +176 -0
- package/dist/experimental/server-functions-types.d.ts +86 -0
- package/dist/experimental/server-functions-types.d.ts.map +1 -0
- package/dist/experimental/server-functions-types.js +35 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/module.d.ts +40 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +58 -0
- package/dist/render-stream.d.ts +27 -0
- package/dist/render-stream.d.ts.map +1 -0
- package/dist/render-stream.js +135 -0
- package/dist/render.d.ts +50 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +201 -0
- package/dist/server-entry.d.ts +87 -0
- package/dist/server-entry.d.ts.map +1 -0
- package/dist/server-entry.js +126 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/vite/asset-utils.d.ts +57 -0
- package/dist/vite/asset-utils.d.ts.map +1 -0
- package/dist/vite/asset-utils.js +184 -0
- package/dist/vite/create-asset-manifest.d.ts +9 -0
- package/dist/vite/create-asset-manifest.d.ts.map +1 -0
- package/dist/vite/create-asset-manifest.js +127 -0
- package/dist/vite/diagnostics.d.ts +27 -0
- package/dist/vite/diagnostics.d.ts.map +1 -0
- package/dist/vite/diagnostics.js +47 -0
- package/dist/vite/parse-manifest.d.ts +18 -0
- package/dist/vite/parse-manifest.d.ts.map +1 -0
- package/dist/vite/parse-manifest.js +168 -0
- package/dist/vite/types.d.ts +187 -0
- package/dist/vite/types.d.ts.map +1 -0
- package/dist/vite/types.js +59 -0
- package/dist/vite.d.ts +3 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { RequestAbortedError } from '@fluojs/http';
|
|
2
|
+
|
|
3
|
+
/** Minimal request abort surface used while React Web Streams are read. */
|
|
4
|
+
|
|
5
|
+
function createAbortWait(signal) {
|
|
6
|
+
if (!signal) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
if (signal.aborted) {
|
|
10
|
+
return {
|
|
11
|
+
cleanup: () => undefined,
|
|
12
|
+
promise: Promise.resolve('aborted')
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
let listener;
|
|
16
|
+
const promise = new Promise(resolve => {
|
|
17
|
+
listener = () => resolve('aborted');
|
|
18
|
+
signal.addEventListener('abort', listener, {
|
|
19
|
+
once: true
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
cleanup: () => {
|
|
24
|
+
if (listener) {
|
|
25
|
+
signal.removeEventListener('abort', listener);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
promise
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function isReactRequestAborted(source) {
|
|
32
|
+
return source.isAborted?.() === true || source.signal?.aborted === true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Throws when a request has been aborted through either supported HTTP abort surface.
|
|
37
|
+
*
|
|
38
|
+
* @param source Request abort source exposed by the active fluo request.
|
|
39
|
+
*/
|
|
40
|
+
export function throwIfReactRequestAborted(source) {
|
|
41
|
+
if (isReactRequestAborted(source)) {
|
|
42
|
+
throw new RequestAbortedError();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function readNextChunk(reader, abortSource) {
|
|
46
|
+
if (isReactRequestAborted(abortSource)) {
|
|
47
|
+
return 'aborted';
|
|
48
|
+
}
|
|
49
|
+
const abort = createAbortWait(abortSource.signal);
|
|
50
|
+
if (!abort) {
|
|
51
|
+
const next = await reader.read();
|
|
52
|
+
return isReactRequestAborted(abortSource) ? 'aborted' : next;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const next = await Promise.race([reader.read(), abort.promise]);
|
|
56
|
+
return next === 'aborted' || isReactRequestAborted(abortSource) ? 'aborted' : next;
|
|
57
|
+
} finally {
|
|
58
|
+
abort.cleanup();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Collects a React HTML stream for buffered hosts without committing partial bodies after abort.
|
|
64
|
+
*
|
|
65
|
+
* @param stream React Web Stream to collect.
|
|
66
|
+
* @param abortSource Request abort source checked before buffered response metadata is applied.
|
|
67
|
+
* @returns Fully collected HTML bytes.
|
|
68
|
+
*/
|
|
69
|
+
export async function collectReadableStream(stream, abortSource) {
|
|
70
|
+
const reader = stream.getReader();
|
|
71
|
+
const chunks = [];
|
|
72
|
+
let byteLength = 0;
|
|
73
|
+
try {
|
|
74
|
+
while (!isReactRequestAborted(abortSource)) {
|
|
75
|
+
const next = await readNextChunk(reader, abortSource);
|
|
76
|
+
if (next === 'aborted') {
|
|
77
|
+
await reader.cancel();
|
|
78
|
+
throw new RequestAbortedError();
|
|
79
|
+
}
|
|
80
|
+
if (next.done) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
chunks.push(next.value);
|
|
84
|
+
byteLength += next.value.byteLength;
|
|
85
|
+
}
|
|
86
|
+
if (isReactRequestAborted(abortSource)) {
|
|
87
|
+
await reader.cancel();
|
|
88
|
+
throw new RequestAbortedError();
|
|
89
|
+
}
|
|
90
|
+
} finally {
|
|
91
|
+
reader.releaseLock();
|
|
92
|
+
}
|
|
93
|
+
const body = new Uint8Array(byteLength);
|
|
94
|
+
let offset = 0;
|
|
95
|
+
for (const chunk of chunks) {
|
|
96
|
+
body.set(chunk, offset);
|
|
97
|
+
offset += chunk.byteLength;
|
|
98
|
+
}
|
|
99
|
+
return body;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Pipes a React HTML stream into an already committed streaming response until close or abort.
|
|
104
|
+
*
|
|
105
|
+
* @param stream React Web Stream to pipe.
|
|
106
|
+
* @param target Response stream that receives HTML chunks.
|
|
107
|
+
* @param abortSource Request abort source checked between chunks.
|
|
108
|
+
* @returns A promise that resolves when piping stops and the target is closed.
|
|
109
|
+
*/
|
|
110
|
+
export async function pipeReadableStream(stream, target, abortSource) {
|
|
111
|
+
const reader = stream.getReader();
|
|
112
|
+
try {
|
|
113
|
+
while (!isReactRequestAborted(abortSource) && !target.closed) {
|
|
114
|
+
const next = await readNextChunk(reader, abortSource);
|
|
115
|
+
if (next === 'aborted') {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
if (next.done) {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
const accepted = target.write(next.value);
|
|
122
|
+
if (!accepted) {
|
|
123
|
+
await target.waitForDrain?.();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (isReactRequestAborted(abortSource)) {
|
|
127
|
+
await reader.cancel();
|
|
128
|
+
}
|
|
129
|
+
} finally {
|
|
130
|
+
reader.releaseLock();
|
|
131
|
+
if (!target.closed) {
|
|
132
|
+
target.close();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { RequestContext } from '@fluojs/http';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { ReactAssetMap, ReactBootstrapAsset, ReactServerEntry } from './server-entry.js';
|
|
4
|
+
/** Readable stream returned by React's Web Streams SSR renderer. */
|
|
5
|
+
export type ReactReadableStream = ReadableStream<Uint8Array> & {
|
|
6
|
+
/** Promise exposed by React for callers that intentionally wait for all Suspense boundaries. */
|
|
7
|
+
readonly allReady?: Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
/** React Web Streams render options used by `renderReactResponse(...)`. */
|
|
10
|
+
export type ReactReadableStreamRenderOptions = {
|
|
11
|
+
/** Trusted build-produced asset map shared with custom server renderers and client hydration code. */
|
|
12
|
+
readonly assetMap?: ReactAssetMap;
|
|
13
|
+
/** Module script URLs or descriptors forwarded to React DOM `bootstrapModules`. */
|
|
14
|
+
readonly bootstrapModules?: readonly ReactBootstrapAsset[];
|
|
15
|
+
/** Trusted inline bootstrap script content forwarded to React DOM without serialization. */
|
|
16
|
+
readonly bootstrapScriptContent?: string;
|
|
17
|
+
/** Classic script URLs or descriptors forwarded to React DOM `bootstrapScripts`. */
|
|
18
|
+
readonly bootstrapScripts?: readonly ReactBootstrapAsset[];
|
|
19
|
+
/** Stable React `useId()` prefix shared with client hydration. */
|
|
20
|
+
readonly identifierPrefix?: string;
|
|
21
|
+
/** CSP nonce applied by React DOM to emitted bootstrap scripts. */
|
|
22
|
+
readonly nonce?: string;
|
|
23
|
+
/** Reports render errors observed by React during shell or Suspense streaming. */
|
|
24
|
+
readonly onError?: (error: unknown, errorInfo?: unknown) => void;
|
|
25
|
+
/** Abort signal propagated from the active fluo request when the adapter provides one. */
|
|
26
|
+
readonly signal?: AbortSignal;
|
|
27
|
+
};
|
|
28
|
+
/** Function compatible with `react-dom/server` `renderToReadableStream`. */
|
|
29
|
+
export type ReactReadableStreamRenderer = (node: ReactNode, options: ReactReadableStreamRenderOptions) => Promise<ReactReadableStream>;
|
|
30
|
+
/** Options for rendering a React entry into one fluo response. */
|
|
31
|
+
export type RenderReactResponseOptions = {
|
|
32
|
+
/** Applies dispatcher-owned success metadata after shell creation and before response commit. */
|
|
33
|
+
readonly applySuccessResponseMetadata?: () => void;
|
|
34
|
+
/** Test or custom renderer override. Defaults to lazy `react-dom/server` Web Streams rendering. */
|
|
35
|
+
readonly renderToReadableStream?: ReactReadableStreamRenderer;
|
|
36
|
+
};
|
|
37
|
+
/** Minimal fluo request context needed to render one React HTML response. */
|
|
38
|
+
export type ReactRenderContext = Pick<RequestContext, 'request' | 'requestId' | 'response'>;
|
|
39
|
+
/**
|
|
40
|
+
* Renders a React server entry to one fluo HTML response using Web Streams SSR.
|
|
41
|
+
*
|
|
42
|
+
* @param entry React server entry created by `createReactServerEntry(...)`.
|
|
43
|
+
* @param requestContext Active fluo request context whose response receives the HTML stream.
|
|
44
|
+
* @param options Optional custom renderer, primarily for tests and custom React DOM integration.
|
|
45
|
+
* @returns A promise that resolves when the shell stream has been fully piped or a committed streaming response observes request abort.
|
|
46
|
+
* @throws Shell render errors before any response bytes are committed.
|
|
47
|
+
* @throws RequestAbortedError before buffered responses commit when the request aborts.
|
|
48
|
+
*/
|
|
49
|
+
export declare function renderReactResponse(entry: ReactServerEntry, requestContext: ReactRenderContext, options?: RenderReactResponseOptions): Promise<void>;
|
|
50
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EAGnB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAK3B,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG;IAC7D,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,gCAAgC,GAAG;IAC7C,sGAAsG;IACtG,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,mFAAmF;IACnF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC3D,4FAA4F;IAC5F,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,oFAAoF;IACpF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC3D,kEAAkE;IAClE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACjE,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B,CAAC;AAoBF,4EAA4E;AAC5E,MAAM,MAAM,2BAA2B,GAAG,CACxC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,gCAAgC,KACtC,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC,kEAAkE;AAClE,MAAM,MAAM,0BAA0B,GAAG;IACvC,iGAAiG;IACjG,QAAQ,CAAC,4BAA4B,CAAC,EAAE,MAAM,IAAI,CAAC;IACnD,mGAAmG;IACnG,QAAQ,CAAC,sBAAsB,CAAC,EAAE,2BAA2B,CAAC;CAC/D,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC,CAAC;AAyJ5F;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,gBAAgB,EACvB,cAAc,EAAE,kBAAkB,EAClC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { collectReadableStream, pipeReadableStream, throwIfReactRequestAborted } from './render-stream.js';
|
|
2
|
+
const HTML_CONTENT_TYPE = 'text/html; charset=utf-8';
|
|
3
|
+
|
|
4
|
+
/** Readable stream returned by React's Web Streams SSR renderer. */
|
|
5
|
+
|
|
6
|
+
/** React Web Streams render options used by `renderReactResponse(...)`. */
|
|
7
|
+
|
|
8
|
+
/** Function compatible with `react-dom/server` `renderToReadableStream`. */
|
|
9
|
+
|
|
10
|
+
/** Options for rendering a React entry into one fluo response. */
|
|
11
|
+
|
|
12
|
+
/** Minimal fluo request context needed to render one React HTML response. */
|
|
13
|
+
|
|
14
|
+
function applyEntryHeaders(entry, requestContext) {
|
|
15
|
+
for (const [name, value] of Object.entries(entry.headers)) {
|
|
16
|
+
requestContext.response.setHeader(name, typeof value === 'string' ? value : [...value]);
|
|
17
|
+
}
|
|
18
|
+
requestContext.response.setHeader('Content-Type', HTML_CONTENT_TYPE);
|
|
19
|
+
}
|
|
20
|
+
function applyEntryStatus(entry, requestContext) {
|
|
21
|
+
if (entry.status !== undefined) {
|
|
22
|
+
requestContext.response.setStatus(entry.status);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (requestContext.response.statusSet !== true) {
|
|
26
|
+
requestContext.response.setStatus(200);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function cloneBootstrapDescriptor(asset) {
|
|
30
|
+
return {
|
|
31
|
+
...(asset.crossOrigin !== undefined ? {
|
|
32
|
+
crossOrigin: asset.crossOrigin
|
|
33
|
+
} : {}),
|
|
34
|
+
...(asset.integrity !== undefined ? {
|
|
35
|
+
integrity: asset.integrity
|
|
36
|
+
} : {}),
|
|
37
|
+
src: asset.src
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function cloneBootstrapAsset(asset) {
|
|
41
|
+
return typeof asset === 'string' ? asset : cloneBootstrapDescriptor(asset);
|
|
42
|
+
}
|
|
43
|
+
function cloneBootstrapAssets(assets) {
|
|
44
|
+
return assets && assets.length > 0 ? assets.map(cloneBootstrapAsset) : undefined;
|
|
45
|
+
}
|
|
46
|
+
function hasAssetMap(assetMap) {
|
|
47
|
+
return Object.keys(assetMap).length > 0;
|
|
48
|
+
}
|
|
49
|
+
function createReactReadableStreamRenderOptions(entry, requestContext, onError) {
|
|
50
|
+
return {
|
|
51
|
+
...(hasAssetMap(entry.assetMap) ? {
|
|
52
|
+
assetMap: entry.assetMap
|
|
53
|
+
} : {}),
|
|
54
|
+
...(entry.bootstrapModules.length > 0 ? {
|
|
55
|
+
bootstrapModules: entry.bootstrapModules
|
|
56
|
+
} : {}),
|
|
57
|
+
...(entry.bootstrapScriptContent !== undefined ? {
|
|
58
|
+
bootstrapScriptContent: entry.bootstrapScriptContent
|
|
59
|
+
} : {}),
|
|
60
|
+
...(entry.bootstrapScripts.length > 0 ? {
|
|
61
|
+
bootstrapScripts: entry.bootstrapScripts
|
|
62
|
+
} : {}),
|
|
63
|
+
...(entry.identifierPrefix !== undefined ? {
|
|
64
|
+
identifierPrefix: entry.identifierPrefix
|
|
65
|
+
} : {}),
|
|
66
|
+
...(entry.nonce !== undefined ? {
|
|
67
|
+
nonce: entry.nonce
|
|
68
|
+
} : {}),
|
|
69
|
+
onError,
|
|
70
|
+
...(requestContext.request.signal !== undefined ? {
|
|
71
|
+
signal: requestContext.request.signal
|
|
72
|
+
} : {})
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function createReactDomRenderOptions(options) {
|
|
76
|
+
return {
|
|
77
|
+
...(options.bootstrapModules !== undefined ? {
|
|
78
|
+
bootstrapModules: cloneBootstrapAssets(options.bootstrapModules)
|
|
79
|
+
} : {}),
|
|
80
|
+
...(options.bootstrapScriptContent !== undefined ? {
|
|
81
|
+
bootstrapScriptContent: options.bootstrapScriptContent
|
|
82
|
+
} : {}),
|
|
83
|
+
...(options.bootstrapScripts !== undefined ? {
|
|
84
|
+
bootstrapScripts: cloneBootstrapAssets(options.bootstrapScripts)
|
|
85
|
+
} : {}),
|
|
86
|
+
...(options.identifierPrefix !== undefined ? {
|
|
87
|
+
identifierPrefix: options.identifierPrefix
|
|
88
|
+
} : {}),
|
|
89
|
+
...(options.nonce !== undefined ? {
|
|
90
|
+
nonce: options.nonce
|
|
91
|
+
} : {}),
|
|
92
|
+
...(options.onError !== undefined ? {
|
|
93
|
+
onError: options.onError
|
|
94
|
+
} : {}),
|
|
95
|
+
...(options.signal !== undefined ? {
|
|
96
|
+
signal: options.signal
|
|
97
|
+
} : {})
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function createRecoverableErrorContext(errorInfo, requestContext) {
|
|
101
|
+
return {
|
|
102
|
+
...(errorInfo !== undefined ? {
|
|
103
|
+
errorInfo
|
|
104
|
+
} : {}),
|
|
105
|
+
request: requestContext.request,
|
|
106
|
+
...(requestContext.requestId !== undefined ? {
|
|
107
|
+
requestId: requestContext.requestId
|
|
108
|
+
} : {})
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function reportRecoverableError(entry, requestContext, event) {
|
|
112
|
+
const hook = entry.onRecoverableError;
|
|
113
|
+
if (!hook) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
hook(event.error, createRecoverableErrorContext(event.errorInfo, requestContext));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof Error) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function reportRecoverableErrors(entry, requestContext, events) {
|
|
125
|
+
for (const event of events) {
|
|
126
|
+
reportRecoverableError(entry, requestContext, event);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function writeReactStream(plan) {
|
|
130
|
+
const {
|
|
131
|
+
applySuccessMetadata,
|
|
132
|
+
entry,
|
|
133
|
+
pendingRecoverableErrors,
|
|
134
|
+
requestContext,
|
|
135
|
+
stream
|
|
136
|
+
} = plan;
|
|
137
|
+
const responseStream = requestContext.response.stream;
|
|
138
|
+
if (!responseStream) {
|
|
139
|
+
const body = await collectReadableStream(stream, requestContext.request);
|
|
140
|
+
throwIfReactRequestAborted(requestContext.request);
|
|
141
|
+
applySuccessMetadata();
|
|
142
|
+
reportRecoverableErrors(entry, requestContext, pendingRecoverableErrors);
|
|
143
|
+
await requestContext.response.send(body);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
applySuccessMetadata();
|
|
147
|
+
requestContext.response.committed = true;
|
|
148
|
+
responseStream.flush?.();
|
|
149
|
+
reportRecoverableErrors(entry, requestContext, pendingRecoverableErrors);
|
|
150
|
+
await pipeReadableStream(stream, responseStream, requestContext.request);
|
|
151
|
+
}
|
|
152
|
+
async function defaultRenderToReadableStream(node, options) {
|
|
153
|
+
const {
|
|
154
|
+
renderToReadableStream
|
|
155
|
+
} = await import('react-dom/server');
|
|
156
|
+
return renderToReadableStream(node, createReactDomRenderOptions(options));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Renders a React server entry to one fluo HTML response using Web Streams SSR.
|
|
161
|
+
*
|
|
162
|
+
* @param entry React server entry created by `createReactServerEntry(...)`.
|
|
163
|
+
* @param requestContext Active fluo request context whose response receives the HTML stream.
|
|
164
|
+
* @param options Optional custom renderer, primarily for tests and custom React DOM integration.
|
|
165
|
+
* @returns A promise that resolves when the shell stream has been fully piped or a committed streaming response observes request abort.
|
|
166
|
+
* @throws Shell render errors before any response bytes are committed.
|
|
167
|
+
* @throws RequestAbortedError before buffered responses commit when the request aborts.
|
|
168
|
+
*/
|
|
169
|
+
export async function renderReactResponse(entry, requestContext, options = {}) {
|
|
170
|
+
throwIfReactRequestAborted(requestContext.request);
|
|
171
|
+
const renderToReadableStream = options.renderToReadableStream ?? defaultRenderToReadableStream;
|
|
172
|
+
const pendingRecoverableErrors = [];
|
|
173
|
+
let shellReady = false;
|
|
174
|
+
const stream = await renderToReadableStream(entry.node, createReactReadableStreamRenderOptions(entry, requestContext, (error, errorInfo) => {
|
|
175
|
+
const event = errorInfo !== undefined ? {
|
|
176
|
+
error,
|
|
177
|
+
errorInfo
|
|
178
|
+
} : {
|
|
179
|
+
error
|
|
180
|
+
};
|
|
181
|
+
if (shellReady) {
|
|
182
|
+
reportRecoverableError(entry, requestContext, event);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
pendingRecoverableErrors.push(event);
|
|
186
|
+
}));
|
|
187
|
+
shellReady = true;
|
|
188
|
+
throwIfReactRequestAborted(requestContext.request);
|
|
189
|
+
const applySuccessMetadata = () => {
|
|
190
|
+
options.applySuccessResponseMetadata?.();
|
|
191
|
+
applyEntryStatus(entry, requestContext);
|
|
192
|
+
applyEntryHeaders(entry, requestContext);
|
|
193
|
+
};
|
|
194
|
+
await writeReactStream({
|
|
195
|
+
applySuccessMetadata,
|
|
196
|
+
entry,
|
|
197
|
+
pendingRecoverableErrors,
|
|
198
|
+
requestContext,
|
|
199
|
+
stream
|
|
200
|
+
});
|
|
201
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { FrameworkRequest } from '@fluojs/http';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
/** Header values applied before a React server entry starts streaming. */
|
|
4
|
+
export type ReactServerEntryHeaders = Readonly<Record<string, string | readonly string[]>>;
|
|
5
|
+
/** Build-produced asset names mapped to public URLs shared by server render and client hydration. */
|
|
6
|
+
export type ReactAssetMap = Readonly<Record<string, string>>;
|
|
7
|
+
/** Descriptor for a bootstrap script or module tag emitted by React DOM server rendering. */
|
|
8
|
+
export type ReactBootstrapScriptDescriptor = {
|
|
9
|
+
/** Public URL used for the script `src` attribute. */
|
|
10
|
+
readonly src: string;
|
|
11
|
+
/** Optional `crossorigin` attribute forwarded to the emitted script tag. */
|
|
12
|
+
readonly crossOrigin?: string;
|
|
13
|
+
/** Optional Subresource Integrity value forwarded to the emitted script tag. */
|
|
14
|
+
readonly integrity?: string;
|
|
15
|
+
};
|
|
16
|
+
/** Bootstrap asset accepted by React DOM server rendering. */
|
|
17
|
+
export type ReactBootstrapAsset = string | ReactBootstrapScriptDescriptor;
|
|
18
|
+
/** Recoverable React render error details reported after the shell can stream. */
|
|
19
|
+
export type ReactRecoverableErrorContext = {
|
|
20
|
+
/** React-provided error metadata, such as a component stack, when available. */
|
|
21
|
+
readonly errorInfo?: unknown;
|
|
22
|
+
/** Framework request being rendered. */
|
|
23
|
+
readonly request: FrameworkRequest;
|
|
24
|
+
/** Adapter-provided request id, when available. */
|
|
25
|
+
readonly requestId?: string;
|
|
26
|
+
};
|
|
27
|
+
/** Hook invoked when React reports a recoverable streaming render error. */
|
|
28
|
+
export type ReactRecoverableErrorHandler = (error: unknown, context: ReactRecoverableErrorContext) => void;
|
|
29
|
+
/** Options used to create a React server entry. */
|
|
30
|
+
export type ReactServerEntryOptions = {
|
|
31
|
+
/** Trusted build-produced asset map available to server rendering and client hydration code. */
|
|
32
|
+
readonly assetMap?: ReactAssetMap;
|
|
33
|
+
/** Module script URLs or descriptors forwarded to React DOM `bootstrapModules`. */
|
|
34
|
+
readonly bootstrapModules?: readonly ReactBootstrapAsset[];
|
|
35
|
+
/** Trusted inline bootstrap script content forwarded to React DOM without serialization. */
|
|
36
|
+
readonly bootstrapScriptContent?: string;
|
|
37
|
+
/** Classic script URLs or descriptors forwarded to React DOM `bootstrapScripts`. */
|
|
38
|
+
readonly bootstrapScripts?: readonly ReactBootstrapAsset[];
|
|
39
|
+
/** Additional response headers to apply before the HTML stream commits. */
|
|
40
|
+
readonly headers?: ReactServerEntryHeaders;
|
|
41
|
+
/** Stable React `useId()` prefix shared with client hydration. */
|
|
42
|
+
readonly identifierPrefix?: string;
|
|
43
|
+
/** CSP nonce applied by React DOM to emitted bootstrap scripts. */
|
|
44
|
+
readonly nonce?: string;
|
|
45
|
+
/** Hook for recoverable errors reported by React after the shell is ready. */
|
|
46
|
+
readonly onRecoverableError?: ReactRecoverableErrorHandler;
|
|
47
|
+
/** HTTP status to apply before streaming starts. Defaults to the current response status or `200`. */
|
|
48
|
+
readonly status?: number;
|
|
49
|
+
};
|
|
50
|
+
/** Runtime-neutral React server entry rendered to streamed HTML by `renderReactResponse(...)`. */
|
|
51
|
+
export type ReactServerEntry = {
|
|
52
|
+
/** Trusted build-produced asset map available to server rendering and client hydration code. */
|
|
53
|
+
readonly assetMap: ReactAssetMap;
|
|
54
|
+
/** Module script URLs or descriptors forwarded to React DOM `bootstrapModules`. */
|
|
55
|
+
readonly bootstrapModules: readonly ReactBootstrapAsset[];
|
|
56
|
+
/** Trusted inline bootstrap script content forwarded to React DOM without serialization. */
|
|
57
|
+
readonly bootstrapScriptContent?: string;
|
|
58
|
+
/** Classic script URLs or descriptors forwarded to React DOM `bootstrapScripts`. */
|
|
59
|
+
readonly bootstrapScripts: readonly ReactBootstrapAsset[];
|
|
60
|
+
/** Additional response headers to apply before the HTML stream commits. */
|
|
61
|
+
readonly headers: ReactServerEntryHeaders;
|
|
62
|
+
/** Stable React `useId()` prefix shared with client hydration. */
|
|
63
|
+
readonly identifierPrefix?: string;
|
|
64
|
+
/** React tree rendered with `react-dom/server` Web Streams APIs. */
|
|
65
|
+
readonly node: ReactNode;
|
|
66
|
+
/** CSP nonce applied by React DOM to emitted bootstrap scripts. */
|
|
67
|
+
readonly nonce?: string;
|
|
68
|
+
/** Hook for recoverable errors reported by React after the shell is ready. */
|
|
69
|
+
readonly onRecoverableError?: ReactRecoverableErrorHandler;
|
|
70
|
+
/** HTTP status to apply before streaming starts. Defaults to the current response status or `200`. */
|
|
71
|
+
readonly status?: number;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Creates a runtime-neutral React server entry for streamed HTML rendering.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* The entry is the stable root SSR handoff for page handlers registered through
|
|
78
|
+
* `@Router(...)` and `@Path(...)`. It forwards explicit React DOM hydration asset
|
|
79
|
+
* options only; Vite manifest discovery, client bundle generation, RSC, and Server
|
|
80
|
+
* Functions remain outside the root `@fluojs/react` contract.
|
|
81
|
+
*
|
|
82
|
+
* @param node React node tree returned by a page handler.
|
|
83
|
+
* @param options Optional status, headers, and recoverable error hook for the render.
|
|
84
|
+
* @returns A defensive React server entry snapshot suitable for `renderReactResponse(...)`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createReactServerEntry(node: ReactNode, options?: ReactServerEntryOptions): ReactServerEntry;
|
|
87
|
+
//# sourceMappingURL=server-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-entry.d.ts","sourceRoot":"","sources":["../src/server-entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { cloneElement, isValidElement } from 'react';
|
|
2
|
+
const responseWriterKey = Symbol.for('fluo.http.responseWriter');
|
|
3
|
+
|
|
4
|
+
/** Header values applied before a React server entry starts streaming. */
|
|
5
|
+
|
|
6
|
+
/** Build-produced asset names mapped to public URLs shared by server render and client hydration. */
|
|
7
|
+
|
|
8
|
+
/** Descriptor for a bootstrap script or module tag emitted by React DOM server rendering. */
|
|
9
|
+
|
|
10
|
+
/** Bootstrap asset accepted by React DOM server rendering. */
|
|
11
|
+
|
|
12
|
+
/** Recoverable React render error details reported after the shell can stream. */
|
|
13
|
+
|
|
14
|
+
/** Hook invoked when React reports a recoverable streaming render error. */
|
|
15
|
+
|
|
16
|
+
/** Options used to create a React server entry. */
|
|
17
|
+
|
|
18
|
+
/** Runtime-neutral React server entry rendered to streamed HTML by `renderReactResponse(...)`. */
|
|
19
|
+
|
|
20
|
+
function cloneAssetMap(assetMap) {
|
|
21
|
+
const cloned = {};
|
|
22
|
+
Object.setPrototypeOf(cloned, null);
|
|
23
|
+
for (const [name, url] of Object.entries(assetMap ?? {})) {
|
|
24
|
+
cloned[name] = url;
|
|
25
|
+
}
|
|
26
|
+
return cloned;
|
|
27
|
+
}
|
|
28
|
+
function cloneBootstrapAsset(asset) {
|
|
29
|
+
if (typeof asset === 'string') {
|
|
30
|
+
return asset;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...(asset.crossOrigin !== undefined ? {
|
|
34
|
+
crossOrigin: asset.crossOrigin
|
|
35
|
+
} : {}),
|
|
36
|
+
...(asset.integrity !== undefined ? {
|
|
37
|
+
integrity: asset.integrity
|
|
38
|
+
} : {}),
|
|
39
|
+
src: asset.src
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function getBootstrapAssetSource(asset) {
|
|
43
|
+
return typeof asset === 'string' ? asset : asset.src;
|
|
44
|
+
}
|
|
45
|
+
function cloneUniqueBootstrapAssets(assets) {
|
|
46
|
+
const cloned = [];
|
|
47
|
+
const seenSources = new Set();
|
|
48
|
+
for (const asset of assets ?? []) {
|
|
49
|
+
const source = getBootstrapAssetSource(asset);
|
|
50
|
+
if (seenSources.has(source)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
seenSources.add(source);
|
|
54
|
+
cloned.push(cloneBootstrapAsset(asset));
|
|
55
|
+
}
|
|
56
|
+
return cloned;
|
|
57
|
+
}
|
|
58
|
+
function cloneHeaders(headers) {
|
|
59
|
+
const cloned = {};
|
|
60
|
+
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
61
|
+
cloned[name] = typeof value === 'string' ? value : [...value];
|
|
62
|
+
}
|
|
63
|
+
return cloned;
|
|
64
|
+
}
|
|
65
|
+
function cloneNodeAssetMapProp(node, originalAssetMap, assetMapSnapshot) {
|
|
66
|
+
if (originalAssetMap === undefined || !isValidElement(node)) {
|
|
67
|
+
return node;
|
|
68
|
+
}
|
|
69
|
+
if (node.props.assetMap !== originalAssetMap) {
|
|
70
|
+
return node;
|
|
71
|
+
}
|
|
72
|
+
return cloneElement(node, {
|
|
73
|
+
assetMap: assetMapSnapshot
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a runtime-neutral React server entry for streamed HTML rendering.
|
|
79
|
+
*
|
|
80
|
+
* @remarks
|
|
81
|
+
* The entry is the stable root SSR handoff for page handlers registered through
|
|
82
|
+
* `@Router(...)` and `@Path(...)`. It forwards explicit React DOM hydration asset
|
|
83
|
+
* options only; Vite manifest discovery, client bundle generation, RSC, and Server
|
|
84
|
+
* Functions remain outside the root `@fluojs/react` contract.
|
|
85
|
+
*
|
|
86
|
+
* @param node React node tree returned by a page handler.
|
|
87
|
+
* @param options Optional status, headers, and recoverable error hook for the render.
|
|
88
|
+
* @returns A defensive React server entry snapshot suitable for `renderReactResponse(...)`.
|
|
89
|
+
*/
|
|
90
|
+
export function createReactServerEntry(node, options = {}) {
|
|
91
|
+
const assetMap = cloneAssetMap(options.assetMap);
|
|
92
|
+
const entry = {
|
|
93
|
+
assetMap,
|
|
94
|
+
bootstrapModules: cloneUniqueBootstrapAssets(options.bootstrapModules),
|
|
95
|
+
...(options.bootstrapScriptContent !== undefined ? {
|
|
96
|
+
bootstrapScriptContent: options.bootstrapScriptContent
|
|
97
|
+
} : {}),
|
|
98
|
+
bootstrapScripts: cloneUniqueBootstrapAssets(options.bootstrapScripts),
|
|
99
|
+
headers: cloneHeaders(options.headers),
|
|
100
|
+
...(options.identifierPrefix !== undefined ? {
|
|
101
|
+
identifierPrefix: options.identifierPrefix
|
|
102
|
+
} : {}),
|
|
103
|
+
node: cloneNodeAssetMapProp(node, options.assetMap, assetMap),
|
|
104
|
+
...(options.nonce !== undefined ? {
|
|
105
|
+
nonce: options.nonce
|
|
106
|
+
} : {}),
|
|
107
|
+
...(options.onRecoverableError !== undefined ? {
|
|
108
|
+
onRecoverableError: options.onRecoverableError
|
|
109
|
+
} : {}),
|
|
110
|
+
...(options.status !== undefined ? {
|
|
111
|
+
status: options.status
|
|
112
|
+
} : {})
|
|
113
|
+
};
|
|
114
|
+
Object.defineProperty(entry, responseWriterKey, {
|
|
115
|
+
enumerable: false,
|
|
116
|
+
value: async context => {
|
|
117
|
+
const {
|
|
118
|
+
renderReactResponse
|
|
119
|
+
} = await import('./render.js');
|
|
120
|
+
await renderReactResponse(entry, context.requestContext, {
|
|
121
|
+
applySuccessResponseMetadata: context.applySuccessResponseMetadata
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return entry;
|
|
126
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planning marker for the first public `@fluojs/react` package surface.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* The package manifest starts at `0.0.0` so the committed Changesets minor release intent
|
|
6
|
+
* can produce the planned `0.1.0` initial public version through the canonical release flow.
|
|
7
|
+
* This marker describes the stable root SSR MVP; Vite assets, client navigation, and
|
|
8
|
+
* RSC/server functions belong to future subpaths rather than the root package.
|
|
9
|
+
*/
|
|
10
|
+
export type ReactScaffoldPhase = '0.1.0-scaffold';
|
|
11
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|