UNPKG

@trpc/server

Version:

The tRPC server library

1 lines • 26.4 kB
{"version":3,"file":"ws-C7ZOJJeA.mjs","names":["callTRPCProcedure"],"sources":["../src/adapters/wsEncoder.ts","../src/adapters/ws.ts"],"sourcesContent":["/**\n * Encoder for WebSocket wire format.\n * Encodes outgoing messages and decodes incoming messages.\n *\n * @example\n * ```ts\n * const customEncoder: Encoder = {\n * encode: (data) => myFormat.stringify(data),\n * decode: (data) => myFormat.parse(data),\n * };\n * ```\n */\nexport interface Encoder {\n /** Encode data for transmission over the wire */\n encode(data: unknown): string | Uint8Array<ArrayBuffer>;\n /** Decode data received from the wire */\n decode(data: string | ArrayBuffer | Uint8Array): unknown;\n}\n\n/**\n * Default JSON encoder - used when no encoder is specified.\n * This maintains backwards compatibility with existing behavior.\n */\nexport const jsonEncoder: Encoder = {\n encode: (data) => JSON.stringify(data),\n decode: (data) => {\n if (typeof data !== 'string') {\n throw new Error(\n 'jsonEncoder received binary data. JSON uses text frames. ' +\n 'Use a binary encoder for binary data.',\n );\n }\n return JSON.parse(data);\n },\n};\n","import type { IncomingMessage } from 'http';\nimport type ws from 'ws';\nimport type {\n AnyRouter,\n CreateContextCallback,\n inferRouterContext,\n} from '../@trpc/server';\nimport {\n callTRPCProcedure,\n getErrorShape,\n getTRPCErrorFromUnknown,\n transformTRPCResponse,\n TRPCError,\n} from '../@trpc/server';\nimport type { TRPCRequestInfo } from '../@trpc/server/http';\nimport { type BaseHandlerOptions } from '../@trpc/server/http';\nimport { parseTRPCMessage } from '../@trpc/server/rpc';\n// @trpc/server/rpc\nimport type {\n TRPCClientOutgoingMessage,\n TRPCConnectionParamsMessage,\n TRPCReconnectNotification,\n TRPCResponseMessage,\n TRPCResultMessage,\n} from '../@trpc/server/rpc';\nimport { parseConnectionParamsFromUnknown } from '../http';\nimport { isObservable, observableToAsyncIterable } from '../observable';\n// eslint-disable-next-line no-restricted-imports\nimport {\n isAsyncIterable,\n isObject,\n isTrackedEnvelope,\n run,\n type MaybePromise,\n} from '../unstable-core-do-not-import';\n// eslint-disable-next-line no-restricted-imports\nimport type { Result } from '../unstable-core-do-not-import';\n// eslint-disable-next-line no-restricted-imports\nimport { iteratorResource } from '../unstable-core-do-not-import/stream/utils/asyncIterable';\nimport { Unpromise } from '../vendor/unpromise';\nimport { createURL, type NodeHTTPCreateContextFnOptions } from './node-http';\nimport type { Encoder } from './wsEncoder';\nimport { jsonEncoder } from './wsEncoder';\n\n/**\n * Importing ws causes a build error\n * @see https://github.com/trpc/trpc/pull/5279\n */\nconst WEBSOCKET_OPEN = 1; /* ws.WebSocket.OPEN */\n\n/**\n * @public\n */\nexport type CreateWSSContextFnOptions = NodeHTTPCreateContextFnOptions<\n IncomingMessage,\n ws.WebSocket\n>;\n\n/**\n * @public\n */\nexport type CreateWSSContextFn<TRouter extends AnyRouter> = (\n opts: CreateWSSContextFnOptions,\n) => MaybePromise<inferRouterContext<TRouter>>;\n\nexport type WSConnectionHandlerOptions<TRouter extends AnyRouter> =\n BaseHandlerOptions<TRouter, IncomingMessage> &\n CreateContextCallback<\n inferRouterContext<TRouter>,\n CreateWSSContextFn<TRouter>\n >;\n\n/**\n * Web socket server handler\n */\nexport type WSSHandlerOptions<TRouter extends AnyRouter> =\n WSConnectionHandlerOptions<TRouter> & {\n wss: ws.WebSocketServer;\n prefix?: string;\n keepAlive?: {\n /**\n * Enable heartbeat messages\n * @default false\n */\n enabled: boolean;\n /**\n * Heartbeat interval in milliseconds\n * @default 30_000\n */\n pingMs?: number;\n /**\n * Terminate the WebSocket if no pong is received after this many milliseconds\n * @default 5_000\n */\n pongWaitMs?: number;\n };\n /**\n * Disable responding to ping messages from the client\n * **Not recommended** - this is mainly used for testing\n * @default false\n */\n dangerouslyDisablePong?: boolean;\n /**\n * Custom encoder for wire encoding (e.g. custom binary formats)\n * @default jsonEncoder\n */\n experimental_encoder?: Encoder;\n };\n\nexport function getWSConnectionHandler<TRouter extends AnyRouter>(\n opts: WSSHandlerOptions<TRouter>,\n) {\n const { createContext, router } = opts;\n const { transformer } = router._def._config;\n const encoder = opts.experimental_encoder ?? jsonEncoder;\n\n return (client: ws.WebSocket, req: IncomingMessage) => {\n type Context = inferRouterContext<TRouter>;\n type ContextResult = Result<Context>;\n\n const clientSubscriptions = new Map<number | string, AbortController>();\n const abortController = new AbortController();\n\n if (opts.keepAlive?.enabled) {\n const { pingMs, pongWaitMs } = opts.keepAlive;\n handleKeepAlive(client, pingMs, pongWaitMs);\n }\n\n function respond(untransformedJSON: TRPCResponseMessage) {\n client.send(\n encoder.encode(\n transformTRPCResponse(router._def._config, untransformedJSON),\n ),\n );\n }\n\n async function createCtxPromise(\n getConnectionParams: () => TRPCRequestInfo['connectionParams'],\n ): Promise<ContextResult> {\n try {\n return await run(async (): Promise<ContextResult> => {\n ctx = await createContext?.({\n req,\n res: client,\n info: {\n connectionParams: getConnectionParams(),\n calls: [],\n isBatchCall: false,\n accept: null,\n type: 'unknown',\n signal: abortController.signal,\n url: null,\n },\n });\n\n return {\n ok: true,\n value: ctx,\n };\n });\n } catch (cause) {\n const error = getTRPCErrorFromUnknown(cause);\n opts.onError?.({\n error,\n path: undefined,\n type: 'unknown',\n ctx,\n req,\n input: undefined,\n });\n respond({\n id: null,\n error: getErrorShape({\n config: router._def._config,\n error,\n type: 'unknown',\n path: undefined,\n input: undefined,\n ctx,\n }),\n });\n\n // close in next tick\n (globalThis.setImmediate ?? globalThis.setTimeout)(() => {\n client.close();\n });\n return {\n ok: false,\n error,\n };\n }\n }\n\n let ctx: Context | undefined = undefined;\n\n /**\n * promise for initializing the context\n *\n * - the context promise will be created immediately on connection if no connectionParams are expected\n * - if connection params are expected, they will be created once received\n */\n let ctxPromise =\n createURL(req).searchParams.get('connectionParams') === '1'\n ? null\n : createCtxPromise(() => null);\n\n function handleRequest(msg: TRPCClientOutgoingMessage, batchIndex: number) {\n const { id, jsonrpc } = msg;\n\n if (id === null) {\n const error = getTRPCErrorFromUnknown(\n new TRPCError({\n code: 'PARSE_ERROR',\n message: '`id` is required',\n }),\n );\n opts.onError?.({\n error,\n path: undefined,\n type: 'unknown',\n ctx,\n req,\n input: undefined,\n });\n respond({\n id,\n jsonrpc,\n error: getErrorShape({\n config: router._def._config,\n error,\n type: 'unknown',\n path: undefined,\n input: undefined,\n ctx,\n }),\n });\n return;\n }\n if (msg.method === 'subscription.stop') {\n clientSubscriptions.get(id)?.abort();\n return;\n }\n const { path, lastEventId } = msg.params;\n let { input } = msg.params;\n const type = msg.method;\n\n if (lastEventId !== undefined) {\n if (isObject(input)) {\n input = {\n ...input,\n lastEventId: lastEventId,\n };\n } else {\n input ??= {\n lastEventId: lastEventId,\n };\n }\n }\n run(async () => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const res = await ctxPromise!; // asserts context has been set\n if (!res.ok) {\n throw res.error;\n }\n\n const abortController = new AbortController();\n const result = await callTRPCProcedure({\n router,\n path,\n getRawInput: async () => input,\n ctx,\n type,\n signal: abortController.signal,\n batchIndex,\n });\n\n const isIterableResult =\n isAsyncIterable(result) || isObservable(result);\n\n if (type !== 'subscription') {\n if (isIterableResult) {\n throw new TRPCError({\n code: 'UNSUPPORTED_MEDIA_TYPE',\n message: `Cannot return an async iterable or observable from a ${type} procedure with WebSockets`,\n });\n }\n // send the value as data if the method is not a subscription\n respond({\n id,\n jsonrpc,\n result: {\n type: 'data',\n data: result,\n },\n });\n return;\n }\n\n if (!isIterableResult) {\n throw new TRPCError({\n message: `Subscription ${path} did not return an observable or a AsyncGenerator`,\n code: 'INTERNAL_SERVER_ERROR',\n });\n }\n\n /* istanbul ignore next -- @preserve */\n if (client.readyState !== WEBSOCKET_OPEN) {\n // if the client got disconnected whilst initializing the subscription\n // no need to send stopped message if the client is disconnected\n\n return;\n }\n\n /* istanbul ignore next -- @preserve */\n if (clientSubscriptions.has(id)) {\n // duplicate request ids for client\n\n throw new TRPCError({\n message: `Duplicate id ${id}`,\n code: 'BAD_REQUEST',\n });\n }\n\n const iterable = isObservable(result)\n ? observableToAsyncIterable(result, abortController.signal)\n : result;\n\n run(async () => {\n await using iterator = iteratorResource(iterable);\n\n const abortPromise = new Promise<'abort'>((resolve) => {\n abortController.signal.onabort = () => resolve('abort');\n });\n // We need those declarations outside the loop for garbage collection reasons. If they\n // were declared inside, they would not be freed until the next value is present.\n let next:\n | null\n | TRPCError\n | Awaited<\n typeof abortPromise | ReturnType<(typeof iterator)['next']>\n >;\n let result: null | TRPCResultMessage<unknown>['result'];\n\n while (true) {\n next = await Unpromise.race([\n iterator.next().catch(getTRPCErrorFromUnknown),\n abortPromise,\n ]);\n\n if (next === 'abort') {\n await iterator.return?.();\n break;\n }\n if (next instanceof Error) {\n const error = getTRPCErrorFromUnknown(next);\n opts.onError?.({ error, path, type, ctx, req, input });\n respond({\n id,\n jsonrpc,\n error: getErrorShape({\n config: router._def._config,\n error,\n type,\n path,\n input,\n ctx,\n }),\n });\n break;\n }\n if (next.done) {\n break;\n }\n\n result = {\n type: 'data',\n data: next.value,\n };\n\n if (isTrackedEnvelope(next.value)) {\n const [id, data] = next.value;\n result.id = id;\n result.data = {\n id,\n data,\n };\n }\n\n respond({\n id,\n jsonrpc,\n result,\n });\n\n // free up references for garbage collection\n next = null;\n result = null;\n }\n\n respond({\n id,\n jsonrpc,\n result: {\n type: 'stopped',\n },\n });\n clientSubscriptions.delete(id);\n }).catch((cause) => {\n clientSubscriptions.delete(id);\n const error = getTRPCErrorFromUnknown(cause);\n opts.onError?.({ error, path, type, ctx, req, input });\n respond({\n id,\n jsonrpc,\n error: getErrorShape({\n config: router._def._config,\n error,\n type,\n path,\n input,\n ctx,\n }),\n });\n abortController.abort();\n });\n clientSubscriptions.set(id, abortController);\n\n respond({\n id,\n jsonrpc,\n result: {\n type: 'started',\n },\n });\n }).catch((cause) => {\n // procedure threw an error\n const error = getTRPCErrorFromUnknown(cause);\n opts.onError?.({ error, path, type, ctx, req, input });\n respond({\n id,\n jsonrpc,\n error: getErrorShape({\n config: router._def._config,\n error,\n type,\n path,\n input,\n ctx,\n }),\n });\n });\n }\n client.on('message', (rawData, isBinary) => {\n // Handle PING/PONG as text regardless of encoder\n if (!isBinary) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n const msgStr = rawData.toString();\n if (msgStr === 'PONG') {\n return;\n }\n if (msgStr === 'PING') {\n if (!opts.dangerouslyDisablePong) {\n client.send('PONG');\n }\n return;\n }\n }\n\n // Convert rawData to a format our encoder accepts\n // ws gives us Buffer for both text and binary frames\n if (!Buffer.isBuffer(rawData)) {\n const error = new TRPCError({\n code: 'UNPROCESSABLE_CONTENT',\n message: 'Unexpected WebSocket message format',\n });\n respond({\n id: null,\n error: getErrorShape({\n config: router._def._config,\n error,\n type: 'unknown',\n path: undefined,\n input: undefined,\n ctx,\n }),\n });\n return;\n }\n const data: string | Uint8Array = isBinary\n ? rawData\n : rawData.toString('utf8');\n\n if (!ctxPromise) {\n // If the ctxPromise wasn't created immediately, we're expecting the first message to be a TRPCConnectionParamsMessage\n ctxPromise = createCtxPromise(() => {\n let msg;\n try {\n msg = encoder.decode(data) as TRPCConnectionParamsMessage;\n\n if (!isObject(msg)) {\n throw new Error('Message was not an object');\n }\n } catch (cause) {\n throw new TRPCError({\n code: 'PARSE_ERROR',\n message: `Malformed TRPCConnectionParamsMessage`,\n cause,\n });\n }\n\n const connectionParams = parseConnectionParamsFromUnknown(msg.data);\n\n return connectionParams;\n });\n return;\n }\n\n const parsedMsgs = run(() => {\n try {\n const msgJSON: unknown = encoder.decode(data);\n const msgs: unknown[] = Array.isArray(msgJSON) ? msgJSON : [msgJSON];\n\n return msgs.map((raw) => parseTRPCMessage(raw, transformer));\n } catch (cause) {\n const error = new TRPCError({\n code: 'PARSE_ERROR',\n cause,\n });\n\n respond({\n id: null,\n error: getErrorShape({\n config: router._def._config,\n error,\n type: 'unknown',\n path: undefined,\n input: undefined,\n ctx,\n }),\n });\n\n return [];\n }\n });\n\n parsedMsgs.map((msg, index) => handleRequest(msg, index));\n });\n\n // WebSocket errors should be handled, as otherwise unhandled exceptions will crash Node.js.\n // This line was introduced after the following error brought down production systems:\n // \"RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear\"\n // Here is the relevant discussion: https://github.com/websockets/ws/issues/1354#issuecomment-774616962\n client.on('error', (cause) => {\n opts.onError?.({\n ctx,\n error: getTRPCErrorFromUnknown(cause),\n input: undefined,\n path: undefined,\n type: 'unknown',\n req,\n });\n });\n\n client.once('close', () => {\n for (const sub of clientSubscriptions.values()) {\n sub.abort();\n }\n clientSubscriptions.clear();\n abortController.abort();\n });\n };\n}\n\n/**\n * Handle WebSocket keep-alive messages\n */\nexport function handleKeepAlive(\n client: ws.WebSocket,\n pingMs = 30_000,\n pongWaitMs = 5_000,\n) {\n let timeout: NodeJS.Timeout | undefined = undefined;\n let ping: NodeJS.Timeout | undefined = undefined;\n\n const schedulePing = () => {\n const scheduleTimeout = () => {\n timeout = setTimeout(() => {\n client.terminate();\n }, pongWaitMs);\n };\n ping = setTimeout(() => {\n client.send('PING');\n\n scheduleTimeout();\n }, pingMs);\n };\n\n const onMessage = () => {\n clearTimeout(ping);\n clearTimeout(timeout);\n\n schedulePing();\n };\n\n client.on('message', onMessage);\n\n client.on('close', () => {\n clearTimeout(ping);\n clearTimeout(timeout);\n });\n\n schedulePing();\n}\n\nexport function applyWSSHandler<TRouter extends AnyRouter>(\n opts: WSSHandlerOptions<TRouter>,\n) {\n const encoder = opts.experimental_encoder ?? jsonEncoder;\n const onConnection = getWSConnectionHandler(opts);\n opts.wss.on('connection', (client, req) => {\n if (opts.prefix && !req.url?.startsWith(opts.prefix)) {\n return;\n }\n\n onConnection(client, req);\n });\n\n return {\n broadcastReconnectNotification: () => {\n const response: TRPCReconnectNotification = {\n id: null,\n method: 'reconnect',\n };\n const data = encoder.encode(response);\n for (const client of opts.wss.clients) {\n if (client.readyState === WEBSOCKET_OPEN) {\n client.send(data);\n }\n }\n },\n };\n}\n\nexport type { Encoder } from './wsEncoder';\nexport { jsonEncoder } from './wsEncoder';\n"],"mappings":"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"}