diff --git a/code/core/src/channels/parse-event.ts b/code/core/src/channels/parse-event.ts new file mode 100644 index 000000000000..f54fb4f0e0d6 --- /dev/null +++ b/code/core/src/channels/parse-event.ts @@ -0,0 +1,15 @@ +/// +import { global } from '@storybook/global'; + +import { isJSON, parse } from 'telejson'; + +/** + * Deserialize a raw channel message using telejson. Returns the parsed object when the message is + * a JSON string, otherwise returns it as-is. Uses `global.CHANNEL_OPTIONS` for parse options so + * all transports apply the same settings. + */ +export function parseEvent(data: unknown): any { + return typeof data === 'string' && isJSON(data) + ? parse(data, global.CHANNEL_OPTIONS || {}) + : data; +} diff --git a/code/core/src/channels/postmessage/index.ts b/code/core/src/channels/postmessage/index.ts index 9498e31a28b1..7f6097784193 100644 --- a/code/core/src/channels/postmessage/index.ts +++ b/code/core/src/channels/postmessage/index.ts @@ -4,7 +4,7 @@ import * as EVENTS from 'storybook/internal/core-events'; import { global } from '@storybook/global'; -import { isJSON, parse, stringify } from 'telejson'; +import { stringify } from 'telejson'; import invariant from 'tiny-invariant'; import type { @@ -14,6 +14,7 @@ import type { ChannelTransport, Config, } from '../types'; +import { parseEvent } from '../parse-event'; import { getEventSourceUrl } from './getEventSourceUrl'; const { document, location } = global; @@ -195,7 +196,7 @@ export class PostMessageTransport implements ChannelTransport { try { const { data } = rawEvent; const { key, event, refId } = - typeof data === 'string' && isJSON(data) ? parse(data, global.CHANNEL_OPTIONS || {}) : data; + parseEvent(data); if (key === KEY) { const pageString = diff --git a/code/core/src/channels/websocket/index.ts b/code/core/src/channels/websocket/index.ts index a1a06c9f6131..1688e21f642c 100644 --- a/code/core/src/channels/websocket/index.ts +++ b/code/core/src/channels/websocket/index.ts @@ -3,10 +3,11 @@ import * as EVENTS from 'storybook/internal/core-events'; import { global } from '@storybook/global'; -import { isJSON, parse, stringify } from 'telejson'; +import { stringify } from 'telejson'; import invariant from 'tiny-invariant'; import type { ChannelHandler, ChannelTransport, Config } from '../types'; +import { parseEvent } from '../parse-event'; const { WebSocket } = global; @@ -49,7 +50,7 @@ export class WebsocketTransport implements ChannelTransport { this.flush(); }; this.socket.onmessage = ({ data }) => { - const event = typeof data === 'string' && isJSON(data) ? parse(data) : data; + const event = parseEvent(data); invariant(this.handler, 'WebsocketTransport handler should be set'); this.handler(event); if (event.type === 'ping') { diff --git a/code/core/src/core-server/utils/get-server-channel.ts b/code/core/src/core-server/utils/get-server-channel.ts index a338f5e2e298..f86624c8b50b 100644 --- a/code/core/src/core-server/utils/get-server-channel.ts +++ b/code/core/src/core-server/utils/get-server-channel.ts @@ -3,9 +3,10 @@ import type { IncomingMessage } from 'node:http'; import type { ChannelHandler } from 'storybook/internal/channels'; import { Channel, HEARTBEAT_INTERVAL } from 'storybook/internal/channels'; -import { isJSON, parse, stringify } from 'telejson'; +import { stringify } from 'telejson'; import WebSocket, { WebSocketServer } from 'ws'; +import { parseEvent } from '../../channels/parse-event'; import { logger } from '../../node-logger'; import { UniversalStore } from '../../shared/universal-store'; import { type HostValidationOptions, isValidHost } from './getHostValidationMiddleware'; @@ -64,7 +65,7 @@ export class ServerChannelTransport { this.socket.on('connection', (wss) => { wss.on('message', (raw) => { const data = raw.toString(); - const event = typeof data === 'string' && isJSON(data) ? parse(data, {}) : data; + const event = parseEvent(data); this.handler?.(event); }); });