rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
69 lines (68 loc) • 3.21 kB
JavaScript
import { env } from "cloudflare:workers";
import { route } from "../../runtime/entries/router";
import { runWithRequestInfo } from "../../runtime/requestInfo/worker";
import { SyncedStateServer } from "./server.mjs";
import { setIdentityInUrl } from "./identity.mjs";
import { DEFAULT_SYNCED_STATE_PATH } from "../constants.mjs";
export { SyncedStateServer };
const DEFAULT_HIBERNATION_STATE_NAME = "syncedStateHibernation";
// context(justinvdm, 19 Jun 2026): The route hands the browser's WebSocket
// directly to the DO and exits. Room resolution and identity extraction happen
// once at upgrade time in the worker; the DO then uses the captured identity to
// transform keys internally via registerKeyHandler.
export const syncedStateRoutes = (getNamespace, options = {}) => {
const basePath = options.basePath ?? DEFAULT_SYNCED_STATE_PATH;
const durableObjectName = options.durableObjectName ?? DEFAULT_HIBERNATION_STATE_NAME;
const forwardRequest = async (request, requestInfo) => {
if (request.headers.get("Upgrade") !== "websocket") {
return new Response("Expected WebSocket", { status: 400 });
}
const namespace = getNamespace(env);
SyncedStateServer.registerNamespace(namespace, durableObjectName);
const identityExtractor = SyncedStateServer.getIdentityExtractor();
const roomHandler = SyncedStateServer.getRoomHandler();
const idParam = requestInfo.params?.id;
let resolvedRoomName;
try {
if (roomHandler) {
resolvedRoomName = await runWithRequestInfo(requestInfo, async () => await roomHandler(idParam, requestInfo));
}
else {
resolvedRoomName = idParam ?? durableObjectName;
}
}
catch (error) {
const message = error instanceof Error ? error.message : "Room resolution failed";
return new Response(message, { status: 500 });
}
const id = namespace.idFromName(resolvedRoomName);
const stub = namespace.get(id);
let identity = undefined;
try {
if (identityExtractor) {
identity = await runWithRequestInfo(requestInfo, async () => await identityExtractor(requestInfo));
}
}
catch (error) {
const message = error instanceof Error ? error.message : "Identity extraction failed";
return new Response(message, { status: 500 });
}
const doUrl = new URL(request.url);
doUrl.searchParams.set("clientId", crypto.randomUUID());
try {
setIdentityInUrl(identity, doUrl);
}
catch (error) {
const message = error instanceof Error ? error.message : "Invalid identity";
return new Response(message, { status: 500 });
}
const doRequest = new Request(doUrl.toString(), {
headers: request.headers,
});
return stub.fetch(doRequest);
};
return [
route(basePath, (requestInfo) => forwardRequest(requestInfo.request, requestInfo)),
route(basePath + "/:id", (requestInfo) => forwardRequest(requestInfo.request, requestInfo)),
];
};