From ada6e7782344ce2b5f5ed6f2fea48a242811cd03 Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:38:23 +0530 Subject: [PATCH] fix(server-runtime,server-sdk): accept plain JSON from external WebSocket clients (#1234) --- packages/server-runtime/src/index.ts | 17 ++++++++++++++++- packages/server-sdk/src/client.ts | 10 ++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/server-runtime/src/index.ts b/packages/server-runtime/src/index.ts index a8e8e2126..ddf00d343 100644 --- a/packages/server-runtime/src/index.ts +++ b/packages/server-runtime/src/index.ts @@ -257,7 +257,22 @@ export function setupApp(options?: { // superjson.parse here instead of message.json() (which uses JSON.parse). // Using JSON.parse on a superjson-encoded string returns the wrapper object // { json: {...}, meta: {...} } with type=undefined, which breaks all event routing. - event = parse(message.text()) + // + // However, external clients may send plain JSON (not superjson-encoded). + // superjson.parse on plain JSON returns undefined since there is no `json` wrapper key. + // In that case, fall back to JSON.parse so external clients can interoperate. + const text = message.text() + const parsed = parse(text) + const potentialEvent = (parsed && typeof parsed === 'object' && 'type' in parsed) + ? parsed + : JSON.parse(text) + + if (!potentialEvent || typeof potentialEvent !== 'object' || !('type' in potentialEvent)) { + send(peer, RESPONSES.error('invalid event format', instanceId)) + return + } + + event = potentialEvent as WebSocketEvent } catch (err) { const errorMessage = err instanceof Error ? err.message : String(err) diff --git a/packages/server-sdk/src/client.ts b/packages/server-sdk/src/client.ts index 9e9afeaba..fad7027bf 100644 --- a/packages/server-sdk/src/client.ts +++ b/packages/server-sdk/src/client.ts @@ -262,8 +262,14 @@ export class Client { private async handleMessage(event: MessageEvent) { try { - const data = superjson.parse | undefined>(event.data as string) - if (!data) { + // Try superjson first (used by SDK clients), fall back to plain JSON + // for external clients that send standard JSON-encoded messages. + const raw = event.data as string + const parsed = superjson.parse | undefined>(raw) + const data = (parsed && typeof parsed === 'object' && 'type' in parsed) + ? parsed + : JSON.parse(raw) as WebSocketEvent + if (!data || typeof data !== 'object' || !('type' in data)) { console.warn('Received empty message') return }