diff --git a/packages/actors/src/client.tsx b/packages/actors/src/client.tsx index f3b804e..7365e78 100644 --- a/packages/actors/src/client.tsx +++ b/packages/actors/src/client.tsx @@ -1,11 +1,13 @@ "use client"; import { useEffect, useState } from "react"; -// TODO: remove this hack once dependency de-dupe works -function rsc() { - // @ts-ignore - return globalThis.rsc as typeof import("@vitejs/plugin-rsc/rsc"); -} +const createFromReadableStream = import.meta.env.SSR + ? () => { + throw new Error("createFromReadableStream is not available in SSR"); + } + : await import("@vitejs/plugin-rsc/browser").then( + (m) => m.createFromReadableStream + ); type ClientComponentProps = { children: React.ReactNode; @@ -20,27 +22,29 @@ export function ClientComponent({ }: ClientComponentProps) { const [component, setComponent] = useState(null); - useEffect(() => { - const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; - const ws = new WebSocket( - `${protocol}//${window.location.host}/${actorName}/${id}` - ); - - ws.addEventListener("message", async (event) => { - const data = event.data as Blob; - const bytes = await data.arrayBuffer(); - const stream = new ReadableStream({ - start(controller) { - controller.enqueue(new Uint8Array(bytes)); - controller.close(); - }, + if (!import.meta.env.SSR) { + useEffect(() => { + const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; + const ws = new WebSocket( + `${protocol}//${window.location.host}/${actorName}/${id}` + ); + + ws.addEventListener("message", async (event) => { + const data = event.data as Blob; + const bytes = await data.arrayBuffer(); + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new Uint8Array(bytes)); + controller.close(); + }, + }); + const created = await createFromReadableStream(stream); + setComponent((created as any).root); }); - const created = await rsc().createFromReadableStream(stream); - setComponent((created as any).root); - }); - return () => ws.close(); - }, []); + return () => ws.close(); + }, []); + } if (!component) { return children; diff --git a/packages/actors/src/index.tsx b/packages/actors/src/index.tsx index 7a66763..6bb1956 100644 --- a/packages/actors/src/index.tsx +++ b/packages/actors/src/index.tsx @@ -2,17 +2,15 @@ import * as React from "react"; import { ActorState, Actor as CfActor, getActor } from "@cloudflare/actors"; import { isValidElement } from "react"; import { observedSymbol } from "./observed.js"; +import { + createFromReadableStream, + renderToReadableStream, +} from "@vitejs/plugin-rsc/rsc"; export * from "@cloudflare/actors"; export * from "./observed.js"; -// TODO: remove this hack once dependency de-dupe works -function rsc() { - // @ts-ignore - return globalThis.rsc as typeof import("@vitejs/plugin-rsc/rsc"); -} - type RSCPayload = { root: React.ReactNode }; export class Actor extends CfActor { @@ -27,7 +25,7 @@ export class Actor extends CfActor { props: Record ): Promise<[ReadableStream, boolean]> { const Component = (this[name as keyof this] as any).bind(this); - const rscStream = rsc().renderToReadableStream({ + const rscStream = renderToReadableStream({ root: , }); // @ts-ignore @@ -77,7 +75,7 @@ async function internalComponent, Env>( await stub.setIdentifier(props.name ?? "default"); const rscStream = await stub.__rscStream("Component", rest); - const payload = await rsc().createFromReadableStream( + const payload = await createFromReadableStream( rscStream[0] as ReadableStream ); diff --git a/packages/actors/src/observed.tsx b/packages/actors/src/observed.tsx index ca9a339..e225aaa 100644 --- a/packages/actors/src/observed.tsx +++ b/packages/actors/src/observed.tsx @@ -1,12 +1,6 @@ -import * as React from "react"; import { Actor } from "./index.js"; import { type JSX } from "react"; - -// TODO: remove this hack once dependency de-dupe works -function rsc() { - // @ts-ignore - return globalThis.rsc as typeof import("@vitejs/plugin-rsc/rsc"); -} +import { renderToReadableStream } from "@vitejs/plugin-rsc/rsc"; type ClassMethodDecorator = ( value: (...args: Args) => Return, @@ -27,7 +21,7 @@ export function Observed( self["onPersist"] = async () => { const ret = await value.apply(this); - const stream = rsc().renderToReadableStream({ + const stream = renderToReadableStream({ root: ret, }); diff --git a/packages/core/src/client.tsx b/packages/core/src/client.tsx index 8ffec3b..9b674ab 100644 --- a/packages/core/src/client.tsx +++ b/packages/core/src/client.tsx @@ -8,9 +8,6 @@ import * as ReactDOMClient from "react-dom/client"; import type { RscPayload } from "./server.js"; import { ErrorBoundary, ErrorFallback } from "./error-handling/browser.js"; -// @ts-expect-error -globalThis.rsc = ReactClient; - export async function main() { // stash `setPayload` function to trigger re-rendering // from outside of `BrowserRoot` component (e.g. server function call, navigation, hmr) diff --git a/packages/core/src/server.tsx b/packages/core/src/server.tsx index 96307c3..53c87c6 100644 --- a/packages/core/src/server.tsx +++ b/packages/core/src/server.tsx @@ -13,9 +13,6 @@ import { CloudflareEnv } from "./index.js"; import { internalContext } from "./internal-context.js"; import { env } from "cloudflare:workers"; -// @ts-expect-error -globalThis.rsc = ReactServer; - export interface Context { cloudflare: { env: CloudflareEnv;