rwsdk 1.0.0-alpha.6 → 1.0.0-alpha.7
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/lib/e2e/browser.d.mts +10 -0
- package/dist/lib/e2e/browser.mjs +107 -0
- package/dist/lib/e2e/dev.d.mts +8 -0
- package/dist/lib/e2e/dev.mjs +232 -0
- package/dist/lib/e2e/environment.d.mts +14 -0
- package/dist/lib/e2e/environment.mjs +201 -0
- package/dist/lib/e2e/index.d.mts +7 -0
- package/dist/lib/e2e/index.mjs +7 -0
- package/dist/lib/e2e/release.d.mts +56 -0
- package/dist/lib/e2e/release.mjs +537 -0
- package/dist/lib/e2e/tarball.d.mts +14 -0
- package/dist/lib/e2e/tarball.mjs +189 -0
- package/dist/lib/e2e/testHarness.d.mts +98 -0
- package/dist/lib/e2e/testHarness.mjs +393 -0
- package/dist/lib/e2e/types.d.mts +31 -0
- package/dist/lib/e2e/types.mjs +1 -0
- package/dist/lib/smokeTests/browser.mjs +3 -94
- package/dist/lib/smokeTests/development.mjs +2 -223
- package/dist/lib/smokeTests/environment.d.mts +4 -11
- package/dist/lib/smokeTests/environment.mjs +10 -158
- package/dist/lib/smokeTests/release.d.mts +2 -49
- package/dist/lib/smokeTests/release.mjs +3 -503
- package/dist/runtime/lib/injectHtmlAtMarker.d.ts +11 -0
- package/dist/runtime/lib/injectHtmlAtMarker.js +90 -0
- package/dist/runtime/lib/realtime/worker.d.ts +1 -1
- package/dist/runtime/lib/rwContext.d.ts +22 -0
- package/dist/runtime/lib/rwContext.js +1 -0
- package/dist/runtime/render/assembleDocument.d.ts +6 -0
- package/dist/runtime/render/assembleDocument.js +22 -0
- package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
- package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
- package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
- package/dist/runtime/render/normalizeActionResult.js +43 -0
- package/dist/runtime/render/preloads.d.ts +2 -2
- package/dist/runtime/render/preloads.js +2 -3
- package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
- package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
- package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
- package/dist/runtime/render/renderHtmlStream.js +31 -0
- package/dist/runtime/render/renderToRscStream.d.ts +2 -3
- package/dist/runtime/render/renderToRscStream.js +2 -41
- package/dist/runtime/render/renderToStream.d.ts +2 -1
- package/dist/runtime/render/renderToStream.js +15 -8
- package/dist/runtime/render/stylesheets.d.ts +2 -2
- package/dist/runtime/render/stylesheets.js +2 -3
- package/dist/runtime/ssrBridge.d.ts +2 -1
- package/dist/runtime/ssrBridge.js +2 -1
- package/dist/runtime/worker.d.ts +1 -0
- package/dist/runtime/worker.js +11 -6
- package/dist/vite/configPlugin.mjs +2 -2
- package/package.json +8 -4
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
- package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
- package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Preloads } from "./preloads.js";
|
|
3
|
+
import { Stylesheets } from "./stylesheets.js";
|
|
4
|
+
// Note: This is a server component, even though it doesn't have the "use server"
|
|
5
|
+
// directive. It's intended to be imported and used within the RSC render pass.
|
|
6
|
+
export const assembleDocument = ({ requestInfo, pageElement, shouldSSR, }) => {
|
|
7
|
+
// todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
|
|
8
|
+
// surface context. e.g:
|
|
9
|
+
// * we assign `user: requestInfo.clientCtx` here
|
|
10
|
+
// * user populates requestInfo.clientCtx on worker side
|
|
11
|
+
// * user can import a read only `import { clientCtx } from "rwsdk/client"`
|
|
12
|
+
// on client side
|
|
13
|
+
const clientContext = {
|
|
14
|
+
rw: {
|
|
15
|
+
ssr: shouldSSR,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
const Document = requestInfo.rw.Document;
|
|
19
|
+
return (_jsxs(Document, { ...requestInfo, children: [_jsx("script", { nonce: requestInfo.rw.nonce, dangerouslySetInnerHTML: {
|
|
20
|
+
__html: `globalThis.__RWSDK_CONTEXT = ${JSON.stringify(clientContext)}`,
|
|
21
|
+
} }), _jsx(Stylesheets, { requestInfo: requestInfo }), _jsx(Preloads, { requestInfo: requestInfo }), _jsx("div", { id: "hydrate-root", children: pageElement })] }));
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createThenableFromReadableStream: (stream: ReadableStream) => Thenable<T>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createModuleMap } from "./createModuleMap.js";
|
|
2
|
+
import ReactServerDom from "react-server-dom-webpack/client.edge";
|
|
3
|
+
const { createFromReadableStream } = ReactServerDom;
|
|
4
|
+
export const createThenableFromReadableStream = (stream) => createFromReadableStream(stream, {
|
|
5
|
+
serverConsumerManifest: {
|
|
6
|
+
moduleMap: createModuleMap(),
|
|
7
|
+
moduleLoading: null,
|
|
8
|
+
},
|
|
9
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const normalizeActionResult: (actionResult: any) => any;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// context(justinvdm, 24 Mar 2025): React flight limits chunks to 28 bytes, so we need to rechunk
|
|
2
|
+
// the stream to avoid losing data
|
|
3
|
+
function rechunkStream(stream, maxChunkSize = 28) {
|
|
4
|
+
const reader = stream.getReader();
|
|
5
|
+
return new ReadableStream({
|
|
6
|
+
async pull(controller) {
|
|
7
|
+
let buffer = new Uint8Array(0);
|
|
8
|
+
try {
|
|
9
|
+
while (true) {
|
|
10
|
+
const { done, value } = await reader.read();
|
|
11
|
+
if (done && buffer.length === 0) {
|
|
12
|
+
controller.close();
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (value) {
|
|
16
|
+
buffer = new Uint8Array([...buffer, ...value]);
|
|
17
|
+
}
|
|
18
|
+
while (buffer.length >= maxChunkSize || (done && buffer.length > 0)) {
|
|
19
|
+
const chunk = buffer.slice(0, maxChunkSize);
|
|
20
|
+
buffer = buffer.slice(maxChunkSize);
|
|
21
|
+
controller.enqueue(chunk);
|
|
22
|
+
}
|
|
23
|
+
if (done) {
|
|
24
|
+
controller.close();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
controller.error(error);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export const normalizeActionResult = (actionResult) => {
|
|
36
|
+
if (actionResult instanceof Response) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (actionResult instanceof ReadableStream) {
|
|
40
|
+
return rechunkStream(actionResult);
|
|
41
|
+
}
|
|
42
|
+
return actionResult;
|
|
43
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RequestInfo } from "../requestInfo/types.js";
|
|
2
2
|
import type { Manifest, ManifestChunk } from "../lib/manifest.js";
|
|
3
3
|
export declare function findScriptForModule(id: string, manifest: Manifest): ManifestChunk | undefined;
|
|
4
|
-
export declare const Preloads: ({ requestInfo }: {
|
|
4
|
+
export declare const Preloads: ({ requestInfo, }: {
|
|
5
5
|
requestInfo: RequestInfo;
|
|
6
|
-
}) => import("react/jsx-runtime.js").JSX.Element
|
|
6
|
+
}) => Promise<import("react/jsx-runtime.js").JSX.Element>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { use } from "react";
|
|
3
2
|
import { getManifest } from "../lib/manifest.js";
|
|
4
3
|
export function findScriptForModule(id, manifest) {
|
|
5
4
|
const visited = new Set();
|
|
@@ -27,8 +26,8 @@ export function findScriptForModule(id, manifest) {
|
|
|
27
26
|
}
|
|
28
27
|
return find(id);
|
|
29
28
|
}
|
|
30
|
-
export const Preloads = ({ requestInfo }) => {
|
|
31
|
-
const manifest =
|
|
29
|
+
export const Preloads = async ({ requestInfo, }) => {
|
|
30
|
+
const manifest = await getManifest();
|
|
32
31
|
const allScripts = new Set();
|
|
33
32
|
for (const scriptId of requestInfo.rw.scriptsToBeLoaded) {
|
|
34
33
|
const script = findScriptForModule(scriptId, manifest);
|
package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type DocumentProps } from "../lib/router.js";
|
|
2
2
|
import { type RequestInfo } from "../requestInfo/types.js";
|
|
3
|
-
export declare const
|
|
4
|
-
|
|
3
|
+
export declare const renderDocumentHtmlStream: ({ rscPayloadStream, Document, requestInfo, shouldSSR, onError, }: {
|
|
4
|
+
rscPayloadStream: ReadableStream;
|
|
5
5
|
Document: React.FC<DocumentProps>;
|
|
6
6
|
requestInfo: RequestInfo;
|
|
7
7
|
shouldSSR: boolean;
|
|
8
8
|
onError: (error: unknown) => void;
|
|
9
|
-
}) => Promise<
|
|
9
|
+
}) => Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Preloads } from "./preloads.js";
|
|
3
|
+
import { Stylesheets } from "./stylesheets.js";
|
|
4
|
+
import { renderHtmlStream, createThenableFromReadableStream, } from "rwsdk/__ssr_bridge";
|
|
5
|
+
import { injectHtmlAtMarker } from "../lib/injectHtmlAtMarker.js";
|
|
6
|
+
export const renderDocumentHtmlStream = async ({ rscPayloadStream, Document, requestInfo, shouldSSR, onError, }) => {
|
|
7
|
+
// Extract the app node from the RSC payload
|
|
8
|
+
const rscAppThenable = createThenableFromReadableStream(rscPayloadStream);
|
|
9
|
+
const { node: appNode } = (await rscAppThenable);
|
|
10
|
+
// todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
|
|
11
|
+
// surface context. e.g:
|
|
12
|
+
// * we assign `user: requestInfo.clientCtx` here
|
|
13
|
+
// * user populates requestInfo.clientCtx on worker side
|
|
14
|
+
// * user can import a read only `import { clientCtx } from "rwsdk/client"`
|
|
15
|
+
// on client side
|
|
16
|
+
const clientContext = {
|
|
17
|
+
rw: {
|
|
18
|
+
ssr: shouldSSR,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
// Create the outer document with a marker for injection
|
|
22
|
+
const documentElement = (_jsxs(Document, { ...requestInfo, children: [_jsx("script", { nonce: requestInfo.rw.nonce, dangerouslySetInnerHTML: {
|
|
23
|
+
__html: `globalThis.__RWSDK_CONTEXT = ${JSON.stringify(clientContext)}`,
|
|
24
|
+
} }), _jsx(Stylesheets, { requestInfo: requestInfo }), _jsx(Preloads, { requestInfo: requestInfo }), _jsx("div", { id: "hydrate-root", dangerouslySetInnerHTML: { __html: "<!-- RWSDK_INJECT_APP_HTML -->" } })] }));
|
|
25
|
+
const outerHtmlStream = await renderHtmlStream({
|
|
26
|
+
node: documentElement,
|
|
27
|
+
requestInfo,
|
|
28
|
+
onError,
|
|
29
|
+
identifierPrefix: "__RWSDK_DOCUMENT__",
|
|
30
|
+
});
|
|
31
|
+
const appHtmlStream = await renderHtmlStream({
|
|
32
|
+
node: appNode,
|
|
33
|
+
requestInfo,
|
|
34
|
+
onError,
|
|
35
|
+
});
|
|
36
|
+
// Stitch the streams together
|
|
37
|
+
const stitchedStream = injectHtmlAtMarker(outerHtmlStream, appHtmlStream, "<!-- RWSDK_INJECT_APP_HTML -->");
|
|
38
|
+
return stitchedStream;
|
|
39
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RequestInfo } from "../requestInfo/types.js";
|
|
2
|
+
export declare const renderHtmlStream: ({ node, identifierPrefix, requestInfo, onError, }: {
|
|
3
|
+
node: React.ReactNode;
|
|
4
|
+
requestInfo: RequestInfo;
|
|
5
|
+
onError: (error: unknown) => void;
|
|
6
|
+
identifierPrefix?: string;
|
|
7
|
+
}) => Promise<import("react-dom/server.js").ReactDOMServerReadableStream>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { renderToReadableStream } from "react-dom/server.edge";
|
|
2
|
+
export const renderHtmlStream = async ({ node, identifierPrefix, requestInfo, onError, }) => {
|
|
3
|
+
return await renderToReadableStream(node, {
|
|
4
|
+
nonce: requestInfo.rw.nonce,
|
|
5
|
+
identifierPrefix,
|
|
6
|
+
onError(error, { componentStack }) {
|
|
7
|
+
try {
|
|
8
|
+
if (!error) {
|
|
9
|
+
error = new Error(`A falsy value was thrown during rendering: ${String(error)}.`);
|
|
10
|
+
}
|
|
11
|
+
const message = error
|
|
12
|
+
? (error.stack ?? error.message ?? error)
|
|
13
|
+
: error;
|
|
14
|
+
const wrappedMessage = `${message}\n\nComponent stack:${componentStack}`;
|
|
15
|
+
if (error instanceof Error) {
|
|
16
|
+
const wrappedError = new Error(wrappedMessage);
|
|
17
|
+
wrappedError.stack = error.stack;
|
|
18
|
+
error = wrappedError;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
error = new Error(wrappedMessage);
|
|
22
|
+
error.stack = componentStack;
|
|
23
|
+
}
|
|
24
|
+
onError(error);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
onError(error);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
};
|
|
@@ -1,46 +1,7 @@
|
|
|
1
1
|
import { renderToReadableStream as baseRenderToRscStream } from "react-server-dom-webpack/server.edge";
|
|
2
2
|
import { createClientManifest } from "./createClientManifest.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function rechunkStream(stream, maxChunkSize = 28) {
|
|
6
|
-
const reader = stream.getReader();
|
|
7
|
-
return new ReadableStream({
|
|
8
|
-
async pull(controller) {
|
|
9
|
-
let buffer = new Uint8Array(0);
|
|
10
|
-
try {
|
|
11
|
-
while (true) {
|
|
12
|
-
const { done, value } = await reader.read();
|
|
13
|
-
if (done && buffer.length === 0) {
|
|
14
|
-
controller.close();
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
if (value) {
|
|
18
|
-
buffer = new Uint8Array([...buffer, ...value]);
|
|
19
|
-
}
|
|
20
|
-
while (buffer.length >= maxChunkSize || (done && buffer.length > 0)) {
|
|
21
|
-
const chunk = buffer.slice(0, maxChunkSize);
|
|
22
|
-
buffer = buffer.slice(maxChunkSize);
|
|
23
|
-
controller.enqueue(chunk);
|
|
24
|
-
}
|
|
25
|
-
if (done) {
|
|
26
|
-
controller.close();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
controller.error(error);
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
export const renderToRscStream = (app) => {
|
|
38
|
-
const { node, onError } = app;
|
|
39
|
-
let { actionResult } = app;
|
|
40
|
-
if (actionResult instanceof ReadableStream) {
|
|
41
|
-
actionResult = rechunkStream(actionResult);
|
|
42
|
-
}
|
|
43
|
-
return baseRenderToRscStream({ node, actionResult }, createClientManifest(), {
|
|
3
|
+
export const renderToRscStream = ({ input, onError, }) => {
|
|
4
|
+
return baseRenderToRscStream(input, createClientManifest(), {
|
|
44
5
|
onError,
|
|
45
6
|
});
|
|
46
7
|
};
|
|
@@ -2,8 +2,9 @@ import { ReactElement, FC } from "react";
|
|
|
2
2
|
import { DocumentProps } from "../lib/router";
|
|
3
3
|
export interface RenderToStreamOptions {
|
|
4
4
|
Document?: FC<DocumentProps>;
|
|
5
|
+
ssr?: boolean;
|
|
5
6
|
injectRSCPayload?: boolean;
|
|
6
7
|
onError?: (error: unknown) => void;
|
|
7
8
|
}
|
|
8
9
|
export declare const IdentityDocument: FC<DocumentProps>;
|
|
9
|
-
export declare const renderToStream: (element: ReactElement, { Document, injectRSCPayload: shouldInjectRSCPayload, onError, }?: RenderToStreamOptions) => Promise<ReadableStream>;
|
|
10
|
+
export declare const renderToStream: (element: ReactElement, { ssr: shouldSSR, Document, injectRSCPayload: shouldInjectRSCPayload, onError, }?: RenderToStreamOptions) => Promise<ReadableStream>;
|
|
@@ -1,27 +1,34 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { renderToRscStream } from "./renderToRscStream";
|
|
3
|
-
import { transformRscToHtmlStream } from "./transformRscToHtmlStream";
|
|
4
3
|
import { requestInfo } from "../requestInfo/worker";
|
|
5
4
|
import { injectRSCPayload } from "rsc-html-stream/server";
|
|
5
|
+
import { renderDocumentHtmlStream } from "./renderDocumentHtmlStream";
|
|
6
6
|
export const IdentityDocument = ({ children }) => (_jsx(_Fragment, { children: children }));
|
|
7
|
-
export const renderToStream = async (element, { Document = IdentityDocument, injectRSCPayload: shouldInjectRSCPayload =
|
|
7
|
+
export const renderToStream = async (element, { ssr: shouldSSR = true, Document = IdentityDocument, injectRSCPayload: shouldInjectRSCPayload = true, onError = () => { }, } = {}) => {
|
|
8
8
|
let rscStream = renderToRscStream({
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
input: {
|
|
10
|
+
node: element,
|
|
11
|
+
actionResult: undefined,
|
|
12
|
+
},
|
|
11
13
|
onError,
|
|
12
14
|
});
|
|
15
|
+
let injectRSCStream;
|
|
13
16
|
if (shouldInjectRSCPayload) {
|
|
14
17
|
const [rscPayloadStream1, rscPayloadStream2] = rscStream.tee();
|
|
15
18
|
rscStream = rscPayloadStream1;
|
|
16
|
-
|
|
19
|
+
injectRSCStream = injectRSCPayload(rscPayloadStream2, {
|
|
17
20
|
nonce: requestInfo.rw.nonce,
|
|
18
|
-
})
|
|
21
|
+
});
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
let htmlStream = await renderDocumentHtmlStream({
|
|
24
|
+
rscPayloadStream: rscStream,
|
|
22
25
|
Document,
|
|
23
26
|
requestInfo,
|
|
27
|
+
shouldSSR,
|
|
24
28
|
onError,
|
|
25
29
|
});
|
|
30
|
+
if (injectRSCStream) {
|
|
31
|
+
htmlStream = htmlStream.pipeThrough(injectRSCStream);
|
|
32
|
+
}
|
|
26
33
|
return htmlStream;
|
|
27
34
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type RequestInfo } from "../requestInfo/types.js";
|
|
2
|
-
export declare const Stylesheets: ({ requestInfo }: {
|
|
2
|
+
export declare const Stylesheets: ({ requestInfo, }: {
|
|
3
3
|
requestInfo: RequestInfo;
|
|
4
|
-
}) => import("react/jsx-runtime.js").JSX.Element
|
|
4
|
+
}) => Promise<import("react/jsx-runtime.js").JSX.Element>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { use } from "react";
|
|
3
2
|
import { getManifest } from "../lib/manifest.js";
|
|
4
3
|
const findCssForModule = (scriptId, manifest) => {
|
|
5
4
|
const css = new Set();
|
|
@@ -22,8 +21,8 @@ const findCssForModule = (scriptId, manifest) => {
|
|
|
22
21
|
inner(scriptId);
|
|
23
22
|
return Array.from(css);
|
|
24
23
|
};
|
|
25
|
-
export const Stylesheets = ({ requestInfo }) => {
|
|
26
|
-
const manifest =
|
|
24
|
+
export const Stylesheets = async ({ requestInfo, }) => {
|
|
25
|
+
const manifest = await getManifest();
|
|
27
26
|
const allStylesheets = new Set();
|
|
28
27
|
for (const scriptId of requestInfo.rw.scriptsToBeLoaded) {
|
|
29
28
|
const css = findCssForModule(scriptId, manifest);
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { renderHtmlStream } from "./render/renderHtmlStream";
|
|
2
|
+
export { createThenableFromReadableStream } from "./render/createThenableFromReadableStream";
|
|
2
3
|
export { ssrLoadModule, ssrGetModuleExport, ssrWebpackRequire, } from "./imports/ssr";
|
|
@@ -7,5 +7,6 @@
|
|
|
7
7
|
// import it through this bridge module, using the bare import path
|
|
8
8
|
// `rwsdk/__ssr_bridge`. We have bundler logic (ssrBridgePlugin) that looks out
|
|
9
9
|
// for imports to it.
|
|
10
|
-
export {
|
|
10
|
+
export { renderHtmlStream } from "./render/renderHtmlStream";
|
|
11
|
+
export { createThenableFromReadableStream } from "./render/createThenableFromReadableStream";
|
|
11
12
|
export { ssrLoadModule, ssrGetModuleExport, ssrWebpackRequire, } from "./imports/ssr";
|
package/dist/runtime/worker.d.ts
CHANGED
package/dist/runtime/worker.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { renderDocumentHtmlStream } from "./render/renderDocumentHtmlStream";
|
|
3
|
+
import { normalizeActionResult } from "./render/normalizeActionResult";
|
|
3
4
|
import { renderToRscStream } from "./render/renderToRscStream";
|
|
4
5
|
import { rscActionHandler } from "./register/worker";
|
|
5
6
|
import { injectRSCPayload } from "rsc-html-stream/server";
|
|
@@ -8,6 +9,7 @@ import { getRequestInfo, runWithRequestInfo, runWithRequestInfoOverrides, } from
|
|
|
8
9
|
import { defineRoutes } from "./lib/router";
|
|
9
10
|
import { generateNonce } from "./lib/utils";
|
|
10
11
|
import { ssrWebpackRequire } from "./imports/worker";
|
|
12
|
+
export * from "./requestInfo/types";
|
|
11
13
|
export const defineApp = (routes) => {
|
|
12
14
|
return {
|
|
13
15
|
fetch: async (request, env, cf) => {
|
|
@@ -79,12 +81,14 @@ export const defineApp = (routes) => {
|
|
|
79
81
|
status: 500,
|
|
80
82
|
});
|
|
81
83
|
}
|
|
82
|
-
const actionResult = requestInfo.rw.actionResult;
|
|
84
|
+
const actionResult = normalizeActionResult(requestInfo.rw.actionResult);
|
|
83
85
|
const pageElement = createPageElement(requestInfo, Page);
|
|
84
86
|
const { rscPayload: shouldInjectRSCPayload } = rw;
|
|
85
87
|
let rscPayloadStream = renderToRscStream({
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
input: {
|
|
89
|
+
node: pageElement,
|
|
90
|
+
actionResult,
|
|
91
|
+
},
|
|
88
92
|
onError,
|
|
89
93
|
});
|
|
90
94
|
if (isRSCRequest) {
|
|
@@ -104,11 +108,12 @@ export const defineApp = (routes) => {
|
|
|
104
108
|
nonce: rw.nonce,
|
|
105
109
|
});
|
|
106
110
|
}
|
|
107
|
-
let html = await
|
|
108
|
-
|
|
111
|
+
let html = await renderDocumentHtmlStream({
|
|
112
|
+
rscPayloadStream: rscPayloadStream,
|
|
109
113
|
Document: rw.Document,
|
|
110
114
|
requestInfo: requestInfo,
|
|
111
115
|
onError,
|
|
116
|
+
shouldSSR: rw.ssr,
|
|
112
117
|
});
|
|
113
118
|
if (injectRSCPayloadStream) {
|
|
114
119
|
html = html.pipeThrough(injectRSCPayloadStream);
|
|
@@ -148,8 +148,8 @@ export const configPlugin = ({ silent, projectRootDir, workerEntryPathname, clie
|
|
|
148
148
|
// original `export` statement from the bundle to prevent syntax
|
|
149
149
|
// errors.
|
|
150
150
|
inlineDynamicImports: true,
|
|
151
|
-
banner: `export const {
|
|
152
|
-
footer: `return {
|
|
151
|
+
banner: `export const { renderHtmlStream, ssrWebpackRequire, ssrGetModuleExport, createThenableFromReadableStream } = (function() {`,
|
|
152
|
+
footer: `return { renderHtmlStream, ssrWebpackRequire, ssrGetModuleExport, createThenableFromReadableStream };\n})();`,
|
|
153
153
|
},
|
|
154
154
|
plugins: [
|
|
155
155
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rwsdk",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.7",
|
|
4
4
|
"description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -90,6 +90,10 @@
|
|
|
90
90
|
"./realtime/durableObject": {
|
|
91
91
|
"types": "./dist/runtime/lib/realtime/durableObject.d.ts",
|
|
92
92
|
"default": "./dist/runtime/lib/realtime/durableObject.js"
|
|
93
|
+
},
|
|
94
|
+
"./e2e": {
|
|
95
|
+
"types": "./dist/lib/e2e/index.d.mts",
|
|
96
|
+
"default": "./dist/lib/e2e/index.mjs"
|
|
93
97
|
}
|
|
94
98
|
},
|
|
95
99
|
"keywords": [
|
|
@@ -159,9 +163,9 @@
|
|
|
159
163
|
},
|
|
160
164
|
"peerDependencies": {
|
|
161
165
|
"@cloudflare/vite-plugin": "^1.12.4",
|
|
162
|
-
"react": "19.2.0-canary-3fb190f7-20250908",
|
|
163
|
-
"react-dom": "19.2.0-canary-3fb190f7-20250908",
|
|
164
|
-
"react-server-dom-webpack": "19.2.0-canary-3fb190f7-20250908",
|
|
166
|
+
"react": "19.2.0-canary-3fb190f7-20250908 <20.0.0",
|
|
167
|
+
"react-dom": "19.2.0-canary-3fb190f7-20250908 <20.0.0",
|
|
168
|
+
"react-server-dom-webpack": ">=19.2.0-canary-3fb190f7-20250908 <20.0.0",
|
|
165
169
|
"vite": "^6.2.6 || 7.x",
|
|
166
170
|
"wrangler": "^4.35.0"
|
|
167
171
|
},
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { use } from "react";
|
|
3
|
-
import { renderToReadableStream } from "react-dom/server.edge";
|
|
4
|
-
import { Preloads } from "./preloads.js";
|
|
5
|
-
import { Stylesheets } from "./stylesheets.js";
|
|
6
|
-
export const renderRscThenableToHtmlStream = async ({ thenable, Document, requestInfo, shouldSSR, onError, }) => {
|
|
7
|
-
const Component = () => {
|
|
8
|
-
const RscApp = () => {
|
|
9
|
-
const node = use(thenable).node;
|
|
10
|
-
return (_jsxs(_Fragment, { children: [_jsx(Stylesheets, { requestInfo: requestInfo }), _jsx(Preloads, { requestInfo: requestInfo }), _jsx("div", { id: "hydrate-root", children: node })] }));
|
|
11
|
-
};
|
|
12
|
-
// todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
|
|
13
|
-
// surface context. e.g:
|
|
14
|
-
// * we assign `user: requestInfo.clientCtx` here
|
|
15
|
-
// * user populates requestInfo.clientCtx on worker side
|
|
16
|
-
// * user can import a read only `import { clientCtx } from "rwsdk/client"`
|
|
17
|
-
// on client side
|
|
18
|
-
const clientContext = {
|
|
19
|
-
rw: {
|
|
20
|
-
ssr: shouldSSR,
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
return (_jsxs(Document, { ...requestInfo, children: [_jsx("script", { nonce: requestInfo.rw.nonce, dangerouslySetInnerHTML: {
|
|
24
|
-
__html: `globalThis.__RWSDK_CONTEXT = ${JSON.stringify(clientContext)}`,
|
|
25
|
-
} }), _jsx(RscApp, {})] }));
|
|
26
|
-
};
|
|
27
|
-
return await renderToReadableStream(_jsx(Component, {}), {
|
|
28
|
-
nonce: requestInfo.rw.nonce,
|
|
29
|
-
onError(error, { componentStack }) {
|
|
30
|
-
try {
|
|
31
|
-
if (!error) {
|
|
32
|
-
error = new Error(`A falsy value was thrown during rendering: ${String(error)}.`);
|
|
33
|
-
}
|
|
34
|
-
const message = error
|
|
35
|
-
? (error.stack ?? error.message ?? error)
|
|
36
|
-
: error;
|
|
37
|
-
const wrappedMessage = `${message}\n\nComponent stack:${componentStack}`;
|
|
38
|
-
if (error instanceof Error) {
|
|
39
|
-
const wrappedError = new Error(wrappedMessage);
|
|
40
|
-
wrappedError.stack = error.stack;
|
|
41
|
-
error = wrappedError;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
error = new Error(wrappedMessage);
|
|
45
|
-
error.stack = componentStack;
|
|
46
|
-
}
|
|
47
|
-
onError(error);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
onError(error);
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { DocumentProps } from "../lib/router";
|
|
2
|
-
import { RequestInfo } from "../requestInfo/types";
|
|
3
|
-
export declare const transformRscToHtmlStream: ({ stream, Document, requestInfo, onError, }: {
|
|
4
|
-
stream: ReadableStream;
|
|
5
|
-
Document: React.FC<DocumentProps>;
|
|
6
|
-
requestInfo: RequestInfo;
|
|
7
|
-
onError: (error: unknown) => void;
|
|
8
|
-
}) => Promise<import("react-dom/server.js").ReactDOMServerReadableStream>;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { createModuleMap } from "./createModuleMap.js";
|
|
2
|
-
import ReactServerDom from "react-server-dom-webpack/client.edge";
|
|
3
|
-
import { renderRscThenableToHtmlStream } from "rwsdk/__ssr_bridge";
|
|
4
|
-
const { createFromReadableStream } = ReactServerDom;
|
|
5
|
-
export const transformRscToHtmlStream = ({ stream, Document, requestInfo, onError, }) => {
|
|
6
|
-
const thenable = createFromReadableStream(stream, {
|
|
7
|
-
serverConsumerManifest: {
|
|
8
|
-
moduleMap: createModuleMap(),
|
|
9
|
-
moduleLoading: null,
|
|
10
|
-
},
|
|
11
|
-
});
|
|
12
|
-
return renderRscThenableToHtmlStream({
|
|
13
|
-
thenable,
|
|
14
|
-
Document,
|
|
15
|
-
requestInfo,
|
|
16
|
-
shouldSSR: requestInfo.rw.ssr,
|
|
17
|
-
onError,
|
|
18
|
-
});
|
|
19
|
-
};
|