@trpc/client
Version:
1 lines • 47.1 kB
Source Map (JSON)
{"version":3,"file":"wsLink-Bm5LKL6z.mjs","names":[],"sources":["../src/links/wsLink/wsClient/encoder.ts","../src/links/wsLink/wsClient/options.ts","../src/links/internals/urlWithConnectionParams.ts","../src/links/wsLink/wsClient/utils.ts","../src/links/wsLink/wsClient/requestManager.ts","../src/links/wsLink/wsClient/wsConnection.ts","../src/links/wsLink/wsClient/wsClient.ts","../src/links/wsLink/createWsClient.ts","../src/links/wsLink/wsLink.ts"],"sourcesContent":["import type { Encoder } from '@trpc/server/adapters/ws';\n\nexport type { Encoder };\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 { UrlOptionsWithConnectionParams } from '../../internals/urlWithConnectionParams';\nimport type { Encoder } from './encoder';\n\nexport interface WebSocketClientOptions extends UrlOptionsWithConnectionParams {\n /**\n * Ponyfill which WebSocket implementation to use\n */\n WebSocket?: typeof WebSocket;\n /**\n * The number of milliseconds before a reconnect is attempted.\n * @default {@link exponentialBackoff}\n */\n retryDelayMs?: (attemptIndex: number) => number;\n /**\n * Triggered when a WebSocket connection is established\n */\n onOpen?: () => void;\n /**\n * Triggered when a WebSocket connection encounters an error\n */\n onError?: (evt?: Event) => void;\n /**\n * Triggered when a WebSocket connection is closed\n */\n onClose?: (cause?: { code?: number }) => void;\n /**\n * Lazy mode will close the WebSocket automatically after a period of inactivity (no messages sent or received and no pending requests)\n */\n lazy?: {\n /**\n * Enable lazy mode\n * @default false\n */\n enabled: boolean;\n /**\n * Close the WebSocket after this many milliseconds\n * @default 0\n */\n closeMs: number;\n };\n /**\n * Send ping messages to the server and kill the connection if no pong message is returned\n */\n keepAlive?: {\n /**\n * @default false\n */\n enabled: boolean;\n /**\n * Send a ping message every this many milliseconds\n * @default 5_000\n */\n intervalMs?: number;\n /**\n * Close the WebSocket after this many milliseconds if the server does not respond\n * @default 1_000\n */\n pongTimeoutMs?: number;\n };\n /**\n * Custom encoder for wire encoding (e.g. custom binary formats)\n * @default jsonEncoder\n */\n experimental_encoder?: Encoder;\n}\n\n/**\n * Default options for lazy WebSocket connections.\n * Determines whether the connection should be established lazily and defines the delay before closure.\n */\nexport type LazyOptions = Required<NonNullable<WebSocketClientOptions['lazy']>>;\nexport const lazyDefaults: LazyOptions = {\n enabled: false,\n closeMs: 0,\n};\n\n/**\n * Default options for the WebSocket keep-alive mechanism.\n * Configures whether keep-alive is enabled and specifies the timeout and interval for ping-pong messages.\n */\nexport type KeepAliveOptions = Required<\n NonNullable<WebSocketClientOptions['keepAlive']>\n>;\nexport const keepAliveDefaults: KeepAliveOptions = {\n enabled: false,\n pongTimeoutMs: 1_000,\n intervalMs: 5_000,\n};\n\n/**\n * Calculates a delay for exponential backoff based on the retry attempt index.\n * The delay starts at 0 for the first attempt and doubles for each subsequent attempt,\n * capped at 30 seconds.\n */\nexport const exponentialBackoff = (attemptIndex: number) => {\n return attemptIndex === 0 ? 0 : Math.min(1000 * 2 ** attemptIndex, 30000);\n};\n","import { type TRPCRequestInfo } from '@trpc/server/http';\n\n/**\n * Get the result of a value or function that returns a value\n * It also optionally accepts typesafe arguments for the function\n */\nexport const resultOf = <T, TArgs extends any[]>(\n value: T | ((...args: TArgs) => T),\n ...args: TArgs\n): T => {\n return typeof value === 'function'\n ? (value as (...args: TArgs) => T)(...args)\n : value;\n};\n\n/**\n * A value that can be wrapped in callback\n */\nexport type CallbackOrValue<T> = T | (() => T | Promise<T>);\n\nexport interface UrlOptionsWithConnectionParams {\n /**\n * The URL to connect to (can be a function that returns a URL)\n */\n url: CallbackOrValue<string>;\n\n /**\n * Connection params that are available in `createContext()`\n * - For `wsLink`/`wsClient`, these are sent as the first message\n * - For `httpSubscriptionLink`, these are serialized as part of the URL under the `connectionParams` query\n */\n connectionParams?: CallbackOrValue<TRPCRequestInfo['connectionParams']>;\n}\n","import type {\n TRPCConnectionParamsMessage,\n TRPCRequestInfo,\n} from '@trpc/server/unstable-core-do-not-import';\nimport type {\n CallbackOrValue,\n UrlOptionsWithConnectionParams,\n} from '../../internals/urlWithConnectionParams';\nimport { resultOf } from '../../internals/urlWithConnectionParams';\nimport type { Encoder } from './encoder';\n\nexport class TRPCWebSocketClosedError extends Error {\n constructor(opts: { message: string; cause?: unknown }) {\n super(opts.message, {\n cause: opts.cause,\n });\n this.name = 'TRPCWebSocketClosedError';\n Object.setPrototypeOf(this, TRPCWebSocketClosedError.prototype);\n }\n}\n\n/**\n * Utility class for managing a timeout that can be started, stopped, and reset.\n * Useful for scenarios where the timeout duration is reset dynamically based on events.\n */\nexport class ResettableTimeout {\n private timeout: ReturnType<typeof setTimeout> | undefined;\n\n constructor(\n private readonly onTimeout: () => void,\n private readonly timeoutMs: number,\n ) {}\n\n /**\n * Resets the current timeout, restarting it with the same duration.\n * Does nothing if no timeout is active.\n */\n public reset() {\n if (!this.timeout) return;\n\n clearTimeout(this.timeout);\n this.timeout = setTimeout(this.onTimeout, this.timeoutMs);\n }\n\n public start() {\n clearTimeout(this.timeout);\n this.timeout = setTimeout(this.onTimeout, this.timeoutMs);\n }\n\n public stop() {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n}\n\n// Ponyfill for Promise.withResolvers https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers\nexport function withResolvers<T>() {\n let resolve: (value: T | PromiseLike<T>) => void;\n let reject: (reason?: any) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return { promise, resolve: resolve!, reject: reject! };\n}\n\n/**\n * Resolves a WebSocket URL and optionally appends connection parameters.\n *\n * If connectionParams are provided, appends 'connectionParams=1' query parameter.\n */\nexport async function prepareUrl(urlOptions: UrlOptionsWithConnectionParams) {\n const url = await resultOf(urlOptions.url);\n\n if (!urlOptions.connectionParams) return url;\n\n // append `?connectionParams=1` when connection params are used\n const prefix = url.includes('?') ? '&' : '?';\n const connectionParams = `${prefix}connectionParams=1`;\n\n return url + connectionParams;\n}\n\nexport async function buildConnectionMessage(\n connectionParams: CallbackOrValue<TRPCRequestInfo['connectionParams']>,\n encoder: Encoder,\n) {\n const message: TRPCConnectionParamsMessage = {\n method: 'connectionParams',\n data: await resultOf(connectionParams),\n };\n\n return encoder.encode(message);\n}\n","import type { AnyTRPCRouter, inferRouterError } from '@trpc/server';\nimport type { Observer } from '@trpc/server/observable';\nimport type {\n TRPCClientOutgoingMessage,\n TRPCResponseMessage,\n} from '@trpc/server/unstable-core-do-not-import';\nimport type { TRPCClientError } from '../../../TRPCClientError';\nimport { withResolvers } from './utils';\n\nexport type TCallbacks = Observer<\n TRPCResponseMessage<unknown, inferRouterError<AnyTRPCRouter>>,\n TRPCClientError<AnyTRPCRouter>\n>;\n\ntype MessageId = string;\ntype MessageIdLike = string | number | null;\n\n/**\n * Represents a WebSocket request managed by the RequestManager.\n * Combines the network message, a utility promise (`end`) that mirrors the lifecycle\n * handled by `callbacks`, and a set of state monitoring callbacks.\n */\ninterface Request {\n message: TRPCClientOutgoingMessage;\n end: Promise<void>;\n callbacks: TCallbacks;\n}\n\n/**\n * Manages WebSocket requests, tracking their lifecycle and providing utility methods\n * for handling outgoing and pending requests.\n *\n * - **Outgoing requests**: Requests that are queued and waiting to be sent.\n * - **Pending requests**: Requests that have been sent and are in flight awaiting a response.\n * For subscriptions, multiple responses may be received until the subscription is closed.\n */\nexport class RequestManager {\n /**\n * Stores requests that are outgoing, meaning they are registered but not yet sent over the WebSocket.\n */\n private outgoingRequests = new Array<Request & { id: MessageId }>();\n\n /**\n * Stores requests that are pending (in flight), meaning they have been sent over the WebSocket\n * and are awaiting responses. For subscriptions, this includes requests\n * that may receive multiple responses.\n */\n private pendingRequests: Record<MessageId, Request> = {};\n\n /**\n * Registers a new request by adding it to the outgoing queue and setting up\n * callbacks for lifecycle events such as completion or error.\n *\n * @param message - The outgoing message to be sent.\n * @param callbacks - Callback functions to observe the request's state.\n * @returns A cleanup function to manually remove the request.\n */\n public register(message: TRPCClientOutgoingMessage, callbacks: TCallbacks) {\n const { promise: end, resolve } = withResolvers<void>();\n\n this.outgoingRequests.push({\n id: String(message.id),\n message,\n end,\n callbacks: {\n next: callbacks.next,\n complete: () => {\n callbacks.complete();\n resolve();\n },\n error: (e) => {\n callbacks.error(e);\n resolve();\n },\n },\n });\n\n return () => {\n this.delete(message.id);\n callbacks.complete();\n resolve();\n };\n }\n\n /**\n * Deletes a request from both the outgoing and pending collections, if it exists.\n */\n public delete(messageId: MessageIdLike) {\n if (messageId === null) return;\n\n this.outgoingRequests = this.outgoingRequests.filter(\n ({ id }) => id !== String(messageId),\n );\n delete this.pendingRequests[String(messageId)];\n }\n\n /**\n * Moves all outgoing requests to the pending state and clears the outgoing queue.\n *\n * The caller is expected to handle the actual sending of the requests\n * (e.g., sending them over the network) after this method is called.\n *\n * @returns The list of requests that were transitioned to the pending state.\n */\n public flush() {\n const requests = this.outgoingRequests;\n this.outgoingRequests = [];\n\n for (const request of requests) {\n this.pendingRequests[request.id] = request;\n }\n return requests;\n }\n\n /**\n * Retrieves all currently pending requests, which are in flight awaiting responses\n * or handling ongoing subscriptions.\n */\n public getPendingRequests() {\n return Object.values(this.pendingRequests);\n }\n\n /**\n * Retrieves a specific pending request by its message ID.\n */\n public getPendingRequest(messageId: MessageIdLike) {\n if (messageId === null) return null;\n\n return this.pendingRequests[String(messageId)];\n }\n\n /**\n * Retrieves all outgoing requests, which are waiting to be sent.\n */\n public getOutgoingRequests() {\n return this.outgoingRequests;\n }\n\n /**\n * Retrieves all requests, both outgoing and pending, with their respective states.\n *\n * @returns An array of all requests with their state (\"outgoing\" or \"pending\").\n */\n public getRequests() {\n return [\n ...this.getOutgoingRequests().map((request) => ({\n state: 'outgoing' as const,\n message: request.message,\n end: request.end,\n callbacks: request.callbacks,\n })),\n ...this.getPendingRequests().map((request) => ({\n state: 'pending' as const,\n message: request.message,\n end: request.end,\n callbacks: request.callbacks,\n })),\n ];\n }\n\n /**\n * Checks if there are any pending requests, including ongoing subscriptions.\n */\n public hasPendingRequests() {\n return this.getPendingRequests().length > 0;\n }\n\n /**\n * Checks if there are any pending subscriptions\n */\n public hasPendingSubscriptions() {\n return this.getPendingRequests().some(\n (request) => request.message.method === 'subscription',\n );\n }\n\n /**\n * Checks if there are any outgoing requests waiting to be sent.\n */\n public hasOutgoingRequests() {\n return this.outgoingRequests.length > 0;\n }\n}\n","import { behaviorSubject } from '@trpc/server/observable';\nimport type { UrlOptionsWithConnectionParams } from '../../internals/urlWithConnectionParams';\nimport type { Encoder } from './encoder';\nimport { buildConnectionMessage, prepareUrl, withResolvers } from './utils';\n\n/**\n * Opens a WebSocket connection asynchronously and returns a promise\n * that resolves when the connection is successfully established.\n * The promise rejects if an error occurs during the connection attempt.\n */\nfunction asyncWsOpen(ws: WebSocket) {\n const { promise, resolve, reject } = withResolvers<void>();\n\n ws.addEventListener('open', () => {\n ws.removeEventListener('error', reject);\n resolve();\n });\n ws.addEventListener('error', reject);\n\n return promise;\n}\n\ninterface PingPongOptions {\n /**\n * The interval (in milliseconds) between \"PING\" messages.\n */\n intervalMs: number;\n\n /**\n * The timeout (in milliseconds) to wait for a \"PONG\" response before closing the connection.\n */\n pongTimeoutMs: number;\n}\n\n/**\n * Sets up a periodic ping-pong mechanism to keep the WebSocket connection alive.\n *\n * - Sends \"PING\" messages at regular intervals defined by `intervalMs`.\n * - If a \"PONG\" response is not received within the `pongTimeoutMs`, the WebSocket is closed.\n * - The ping timer resets upon receiving any message to maintain activity.\n * - Automatically starts the ping process when the WebSocket connection is opened.\n * - Cleans up timers when the WebSocket is closed.\n *\n * @param ws - The WebSocket instance to manage.\n * @param options - Configuration options for ping-pong intervals and timeouts.\n */\nfunction setupPingInterval(\n ws: WebSocket,\n { intervalMs, pongTimeoutMs }: PingPongOptions,\n) {\n let pingTimeout: ReturnType<typeof setTimeout> | undefined;\n let pongTimeout: ReturnType<typeof setTimeout> | undefined;\n\n function start() {\n pingTimeout = setTimeout(() => {\n ws.send('PING');\n pongTimeout = setTimeout(() => {\n ws.close();\n }, pongTimeoutMs);\n }, intervalMs);\n }\n\n function reset() {\n clearTimeout(pingTimeout);\n start();\n }\n\n function pong() {\n clearTimeout(pongTimeout);\n reset();\n }\n\n ws.addEventListener('open', start);\n ws.addEventListener('message', ({ data }) => {\n clearTimeout(pingTimeout);\n start();\n\n if (data === 'PONG') {\n pong();\n }\n });\n ws.addEventListener('close', () => {\n clearTimeout(pingTimeout);\n clearTimeout(pongTimeout);\n });\n}\n\nexport interface WebSocketConnectionOptions {\n WebSocketPonyfill?: typeof WebSocket;\n urlOptions: UrlOptionsWithConnectionParams;\n keepAlive: PingPongOptions & {\n enabled: boolean;\n };\n encoder: Encoder;\n}\n\n/**\n * Manages a WebSocket connection with support for reconnection, keep-alive mechanisms,\n * and observable state tracking.\n */\nexport class WsConnection {\n static connectCount = 0;\n public id = ++WsConnection.connectCount;\n\n private readonly WebSocketPonyfill: typeof WebSocket;\n private readonly urlOptions: UrlOptionsWithConnectionParams;\n private readonly keepAliveOpts: WebSocketConnectionOptions['keepAlive'];\n private readonly encoder: Encoder;\n public readonly wsObservable = behaviorSubject<WebSocket | null>(null);\n\n constructor(opts: WebSocketConnectionOptions) {\n this.WebSocketPonyfill = opts.WebSocketPonyfill ?? WebSocket;\n if (!this.WebSocketPonyfill) {\n throw new Error(\n \"No WebSocket implementation found - you probably don't want to use this on the server, but if you do you need to pass a `WebSocket`-ponyfill\",\n );\n }\n\n this.urlOptions = opts.urlOptions;\n this.keepAliveOpts = opts.keepAlive;\n this.encoder = opts.encoder;\n }\n\n public get ws() {\n return this.wsObservable.get();\n }\n\n private set ws(ws) {\n this.wsObservable.next(ws);\n }\n\n /**\n * Checks if the WebSocket connection is open and ready to communicate.\n */\n public isOpen(): this is { ws: WebSocket } {\n return (\n !!this.ws &&\n this.ws.readyState === this.WebSocketPonyfill.OPEN &&\n !this.openPromise\n );\n }\n\n /**\n * Checks if the WebSocket connection is closed or in the process of closing.\n */\n public isClosed(): this is { ws: WebSocket } {\n return (\n !!this.ws &&\n (this.ws.readyState === this.WebSocketPonyfill.CLOSING ||\n this.ws.readyState === this.WebSocketPonyfill.CLOSED)\n );\n }\n\n /**\n * Manages the WebSocket opening process, ensuring that only one open operation\n * occurs at a time. Tracks the ongoing operation with `openPromise` to avoid\n * redundant calls and ensure proper synchronization.\n *\n * Sets up the keep-alive mechanism and necessary event listeners for the connection.\n *\n * @returns A promise that resolves once the WebSocket connection is successfully opened.\n */\n private openPromise: Promise<void> | null = null;\n public async open() {\n if (this.openPromise) return this.openPromise;\n\n this.id = ++WsConnection.connectCount;\n const wsPromise = prepareUrl(this.urlOptions).then(\n (url) => new this.WebSocketPonyfill(url),\n );\n this.openPromise = wsPromise.then(async (ws) => {\n this.ws = ws;\n\n // Set binaryType to handle both text and binary messages consistently\n ws.binaryType = 'arraybuffer';\n\n // Setup ping listener\n ws.addEventListener('message', function ({ data }) {\n if (data === 'PING') {\n this.send('PONG');\n }\n });\n\n if (this.keepAliveOpts.enabled) {\n setupPingInterval(ws, this.keepAliveOpts);\n }\n\n ws.addEventListener('close', () => {\n if (this.ws === ws) {\n this.ws = null;\n }\n });\n\n await asyncWsOpen(ws);\n\n if (this.urlOptions.connectionParams) {\n ws.send(\n await buildConnectionMessage(\n this.urlOptions.connectionParams,\n this.encoder,\n ),\n );\n }\n });\n\n try {\n await this.openPromise;\n } finally {\n this.openPromise = null;\n }\n }\n\n /**\n * Closes the WebSocket connection gracefully.\n * Waits for any ongoing open operation to complete before closing.\n */\n public async close() {\n try {\n await this.openPromise;\n } finally {\n this.ws?.close();\n }\n }\n}\n\n/**\n * Provides a backward-compatible representation of the connection state.\n */\nexport function backwardCompatibility(connection: WsConnection) {\n if (connection.isOpen()) {\n return {\n id: connection.id,\n state: 'open',\n ws: connection.ws,\n } as const;\n }\n\n if (connection.isClosed()) {\n return {\n id: connection.id,\n state: 'closed',\n ws: connection.ws,\n } as const;\n }\n\n if (!connection.ws) {\n return null;\n }\n\n return {\n id: connection.id,\n state: 'connecting',\n ws: connection.ws,\n } as const;\n}\n","import type { AnyTRPCRouter } from '@trpc/server';\nimport type { BehaviorSubject } from '@trpc/server/observable';\nimport { behaviorSubject, observable } from '@trpc/server/observable';\nimport type {\n CombinedDataTransformer,\n TRPCClientIncomingMessage,\n TRPCClientIncomingRequest,\n TRPCClientOutgoingMessage,\n TRPCResponseMessage,\n} from '@trpc/server/unstable-core-do-not-import';\nimport {\n run,\n sleep,\n transformResult,\n} from '@trpc/server/unstable-core-do-not-import';\nimport { TRPCClientError } from '../../../TRPCClientError';\nimport type { TRPCConnectionState } from '../../internals/subscriptions';\nimport type { Operation, OperationResultEnvelope } from '../../types';\nimport type { Encoder } from './encoder';\nimport { jsonEncoder } from './encoder';\nimport type { WebSocketClientOptions } from './options';\nimport { exponentialBackoff, keepAliveDefaults, lazyDefaults } from './options';\nimport type { TCallbacks } from './requestManager';\nimport { RequestManager } from './requestManager';\nimport { ResettableTimeout, TRPCWebSocketClosedError } from './utils';\nimport { backwardCompatibility, WsConnection } from './wsConnection';\n\n/**\n * A WebSocket client for managing TRPC operations, supporting lazy initialization,\n * reconnection, keep-alive, and request management.\n */\nexport class WsClient {\n /**\n * Observable tracking the current connection state, including errors.\n */\n public readonly connectionState: BehaviorSubject<\n TRPCConnectionState<TRPCClientError<AnyTRPCRouter>>\n >;\n\n private allowReconnect = false;\n private requestManager = new RequestManager();\n private readonly activeConnection: WsConnection;\n private readonly reconnectRetryDelay: (attemptIndex: number) => number;\n private inactivityTimeout: ResettableTimeout;\n private readonly callbacks: Pick<\n WebSocketClientOptions,\n 'onOpen' | 'onClose' | 'onError'\n >;\n private readonly lazyMode: boolean;\n private readonly encoder: Encoder;\n\n constructor(opts: WebSocketClientOptions) {\n this.encoder = opts.experimental_encoder ?? jsonEncoder;\n // Initialize callbacks, connection parameters, and options.\n this.callbacks = {\n onOpen: opts.onOpen,\n onClose: opts.onClose,\n onError: opts.onError,\n };\n\n const lazyOptions = {\n ...lazyDefaults,\n ...opts.lazy,\n };\n\n // Set up inactivity timeout for lazy connections.\n this.inactivityTimeout = new ResettableTimeout(() => {\n if (\n this.requestManager.hasOutgoingRequests() ||\n this.requestManager.hasPendingRequests()\n ) {\n this.inactivityTimeout.reset();\n return;\n }\n\n this.close().catch(() => null);\n }, lazyOptions.closeMs);\n\n // Initialize the WebSocket connection.\n this.activeConnection = new WsConnection({\n WebSocketPonyfill: opts.WebSocket,\n urlOptions: opts,\n keepAlive: {\n ...keepAliveDefaults,\n ...opts.keepAlive,\n },\n encoder: this.encoder,\n });\n this.activeConnection.wsObservable.subscribe({\n next: (ws) => {\n if (!ws) return;\n this.setupWebSocketListeners(ws);\n },\n });\n this.reconnectRetryDelay = opts.retryDelayMs ?? exponentialBackoff;\n\n this.lazyMode = lazyOptions.enabled;\n\n this.connectionState = behaviorSubject<\n TRPCConnectionState<TRPCClientError<AnyTRPCRouter>>\n >({\n type: 'state',\n state: lazyOptions.enabled ? 'idle' : 'connecting',\n error: null,\n });\n\n // Automatically open the connection if lazy mode is disabled.\n if (!this.lazyMode) {\n this.open().catch(() => null);\n }\n }\n\n /**\n * Opens the WebSocket connection. Handles reconnection attempts and updates\n * the connection state accordingly.\n */\n private async open() {\n this.allowReconnect = true;\n if (this.connectionState.get().state === 'idle') {\n this.connectionState.next({\n type: 'state',\n state: 'connecting',\n error: null,\n });\n }\n\n try {\n await this.activeConnection.open();\n } catch (error) {\n this.reconnect(\n new TRPCWebSocketClosedError({\n message: 'Initialization error',\n cause: error,\n }),\n );\n return this.reconnecting;\n }\n }\n\n /**\n * Closes the WebSocket connection and stops managing requests.\n * Ensures all outgoing and pending requests are properly finalized.\n */\n public async close() {\n this.allowReconnect = false;\n this.inactivityTimeout.stop();\n\n const requestsToAwait: Promise<void>[] = [];\n for (const request of this.requestManager.getRequests()) {\n if (request.message.method === 'subscription') {\n request.callbacks.complete();\n } else if (request.state === 'outgoing') {\n request.callbacks.error(\n TRPCClientError.from(\n new TRPCWebSocketClosedError({\n message: 'Closed before connection was established',\n }),\n ),\n );\n } else {\n requestsToAwait.push(request.end);\n }\n }\n\n await Promise.all(requestsToAwait).catch(() => null);\n await this.activeConnection.close().catch(() => null);\n\n this.connectionState.next({\n type: 'state',\n state: 'idle',\n error: null,\n });\n }\n\n /**\n * Method to request the server.\n * Handles data transformation, batching of requests, and subscription lifecycle.\n *\n * @param op - The operation details including id, type, path, input and signal\n * @param transformer - Data transformer for serializing requests and deserializing responses\n * @param lastEventId - Optional ID of the last received event for subscriptions\n *\n * @returns An observable that emits operation results and handles cleanup\n */\n public request({\n op: { id, type, path, input, signal },\n transformer,\n lastEventId,\n }: {\n op: Pick<Operation, 'id' | 'type' | 'path' | 'input' | 'signal'>;\n transformer: CombinedDataTransformer;\n lastEventId?: string;\n }) {\n return observable<\n OperationResultEnvelope<unknown, TRPCClientError<AnyTRPCRouter>>,\n TRPCClientError<AnyTRPCRouter>\n >((observer) => {\n const abort = this.batchSend(\n {\n id,\n method: type,\n params: {\n input: transformer.input.serialize(input),\n path,\n lastEventId,\n },\n },\n {\n ...observer,\n next(event) {\n const transformed = transformResult(event, transformer.output);\n\n if (!transformed.ok) {\n observer.error(TRPCClientError.from(transformed.error));\n return;\n }\n\n observer.next({\n result: transformed.result,\n });\n },\n },\n );\n\n const onAbort = () => observer.complete();\n if (signal?.aborted) {\n onAbort();\n } else {\n signal?.addEventListener('abort', onAbort, { once: true });\n }\n\n return () => {\n abort();\n\n if (type === 'subscription' && this.activeConnection.isOpen()) {\n this.send({\n id,\n method: 'subscription.stop',\n });\n }\n\n signal?.removeEventListener('abort', onAbort);\n };\n });\n }\n\n public get connection() {\n return backwardCompatibility(this.activeConnection);\n }\n\n /**\n * Manages the reconnection process for the WebSocket using retry logic.\n * Ensures that only one reconnection attempt is active at a time by tracking the current\n * reconnection state in the `reconnecting` promise.\n */\n private reconnecting: Promise<void> | null = null;\n private reconnect(closedError: TRPCWebSocketClosedError) {\n this.connectionState.next({\n type: 'state',\n state: 'connecting',\n error: TRPCClientError.from(closedError),\n });\n if (this.reconnecting) return;\n\n const tryReconnect = async (attemptIndex: number) => {\n try {\n await sleep(this.reconnectRetryDelay(attemptIndex));\n if (this.allowReconnect) {\n await this.activeConnection.close();\n await this.activeConnection.open();\n\n if (this.requestManager.hasPendingRequests()) {\n this.send(\n this.requestManager\n .getPendingRequests()\n .map(({ message }) => message),\n );\n }\n }\n this.reconnecting = null;\n } catch {\n await tryReconnect(attemptIndex + 1);\n }\n };\n\n this.reconnecting = tryReconnect(0);\n }\n\n private setupWebSocketListeners(ws: WebSocket) {\n const handleCloseOrError = (cause: unknown) => {\n const reqs = this.requestManager.getPendingRequests();\n for (const { message, callbacks } of reqs) {\n if (message.method === 'subscription') continue;\n\n callbacks.error(\n TRPCClientError.from(\n cause ??\n new TRPCWebSocketClosedError({\n message: 'WebSocket closed',\n cause,\n }),\n ),\n );\n this.requestManager.delete(message.id);\n }\n };\n\n ws.addEventListener('open', () => {\n run(async () => {\n if (this.lazyMode) {\n this.inactivityTimeout.start();\n }\n\n this.callbacks.onOpen?.();\n\n this.connectionState.next({\n type: 'state',\n state: 'pending',\n error: null,\n });\n }).catch((error) => {\n ws.close(3000);\n handleCloseOrError(error);\n });\n });\n\n ws.addEventListener('message', ({ data }) => {\n this.inactivityTimeout.reset();\n\n // Handle PING/PONG as text regardless of encoder\n if (['PING', 'PONG'].includes(data)) return;\n\n const incomingMessage = this.encoder.decode(\n data,\n ) as TRPCClientIncomingMessage;\n if ('method' in incomingMessage) {\n this.handleIncomingRequest(incomingMessage);\n return;\n }\n\n this.handleResponseMessage(incomingMessage);\n });\n\n ws.addEventListener('close', (event) => {\n handleCloseOrError(event);\n this.callbacks.onClose?.(event);\n\n if (!this.lazyMode || this.requestManager.hasPendingSubscriptions()) {\n this.reconnect(\n new TRPCWebSocketClosedError({\n message: 'WebSocket closed',\n cause: event,\n }),\n );\n }\n });\n\n ws.addEventListener('error', (event) => {\n handleCloseOrError(event);\n this.callbacks.onError?.(event);\n\n this.reconnect(\n new TRPCWebSocketClosedError({\n message: 'WebSocket closed',\n cause: event,\n }),\n );\n });\n }\n\n private handleResponseMessage(message: TRPCResponseMessage) {\n const request = this.requestManager.getPendingRequest(message.id);\n if (!request) return;\n\n request.callbacks.next(message);\n\n let completed = true;\n if ('result' in message && request.message.method === 'subscription') {\n if (message.result.type === 'data') {\n request.message.params.lastEventId = message.result.id;\n }\n\n if (message.result.type !== 'stopped') {\n completed = false;\n }\n }\n\n if (completed) {\n request.callbacks.complete();\n this.requestManager.delete(message.id);\n }\n }\n\n private handleIncomingRequest(message: TRPCClientIncomingRequest) {\n if (message.method === 'reconnect') {\n this.reconnect(\n new TRPCWebSocketClosedError({\n message: 'Server requested reconnect',\n }),\n );\n }\n }\n\n /**\n * Sends a message or batch of messages directly to the server.\n */\n private send(\n messageOrMessages: TRPCClientOutgoingMessage | TRPCClientOutgoingMessage[],\n ) {\n if (!this.activeConnection.isOpen()) {\n throw new Error('Active connection is not open');\n }\n\n const messages =\n messageOrMessages instanceof Array\n ? messageOrMessages\n : [messageOrMessages];\n this.activeConnection.ws.send(\n this.encoder.encode(messages.length === 1 ? messages[0] : messages),\n );\n }\n\n /**\n * Groups requests for batch sending.\n *\n * @returns A function to abort the batched request.\n */\n private batchSend(message: TRPCClientOutgoingMessage, callbacks: TCallbacks) {\n this.inactivityTimeout.reset();\n\n run(async () => {\n if (!this.activeConnection.isOpen()) {\n await this.open();\n }\n await sleep(0);\n\n if (!this.requestManager.hasOutgoingRequests()) return;\n\n this.send(this.requestManager.flush().map(({ message }) => message));\n }).catch((err) => {\n this.requestManager.delete(message.id);\n callbacks.error(TRPCClientError.from(err));\n });\n\n return this.requestManager.register(message, callbacks);\n }\n}\n","import type { Encoder } from './wsClient/encoder';\nimport { jsonEncoder } from './wsClient/encoder';\nimport type { WebSocketClientOptions } from './wsClient/options';\nimport { WsClient } from './wsClient/wsClient';\n\nexport function createWSClient(opts: WebSocketClientOptions) {\n return new WsClient(opts);\n}\n\nexport type TRPCWebSocketClient = ReturnType<typeof createWSClient>;\n\nexport { jsonEncoder, type Encoder, type WebSocketClientOptions };\n","import { observable } from '@trpc/server/observable';\nimport type {\n AnyRouter,\n inferClientTypes,\n} from '@trpc/server/unstable-core-do-not-import';\nimport type { TransformerOptions } from '../../unstable-internals';\nimport { getTransformer } from '../../unstable-internals';\nimport type { TRPCLink } from '../types';\nimport type {\n Encoder,\n TRPCWebSocketClient,\n WebSocketClientOptions,\n} from './createWsClient';\nimport { createWSClient, jsonEncoder } from './createWsClient';\n\nexport type WebSocketLinkOptions<TRouter extends AnyRouter> = {\n client: TRPCWebSocketClient;\n} & TransformerOptions<inferClientTypes<TRouter>>;\n\nexport function wsLink<TRouter extends AnyRouter>(\n opts: WebSocketLinkOptions<TRouter>,\n): TRPCLink<TRouter> {\n const { client } = opts;\n const transformer = getTransformer(opts.transformer);\n return () => {\n return ({ op }) => {\n return observable((observer) => {\n const connStateSubscription =\n op.type === 'subscription'\n ? client.connectionState.subscribe({\n next(result) {\n observer.next({\n result,\n context: op.context,\n });\n },\n })\n : null;\n\n const requestSubscription = client\n .request({\n op,\n transformer,\n })\n .subscribe(observer);\n\n return () => {\n requestSubscription.unsubscribe();\n connStateSubscription?.unsubscribe();\n };\n });\n };\n };\n}\n\nexport {\n createWSClient,\n jsonEncoder,\n type Encoder,\n type TRPCWebSocketClient,\n type WebSocketClientOptions,\n};\n"],"mappings":"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"}