UNPKG

@trpc/client

Version:

The tRPC client library

1 lines • 77.1 kB
{"version":3,"file":"wsLink-l0dX0ah6.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/unstable-core-do-not-import';\n\nexport type { Encoder };\n\n/**\n * The default WebSocket encoder. Sends messages as JSON text frames.\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\n/**\n * Options for `createWSClient`.\n *\n * @see https://trpc.io/docs/client/links/wsLink#wslink--createwsclient-options\n */\nexport interface WebSocketClientOptions extends UrlOptionsWithConnectionParams {\n  /**\n   * The `WebSocket` class to use, for runtimes that don't have one.\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   * Encodes and decodes messages on the wire, for example as a binary format.\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   * @see https://trpc.io/docs/server/websockets#connectionParams\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 * Calls a user callback, including `connectionState` subscribers. A throw\n * would skip the rest of the client's bookkeeping (e.g. end the reconnect\n * loop, or, with event-emitter based WebSockets such as `ws`, skip the other\n * listeners for that event), so it is rethrown asynchronously instead.\n */\nexport function callUserCallback(callback: () => void) {\n  try {\n    callback();\n  } catch (error) {\n    setTimeout(() => {\n      throw error;\n    });\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  // oxlint-disable-next-line typescript/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   * The socket the request was sent on, or `null` while it's outgoing.\n   */\n  ws: WebSocket | null;\n  /**\n   * The socket a subscription is moving to. `ws` keeps delivering until this\n   * one confirms the subscription with `started`.\n   */\n  successor: WebSocket | null;\n  /**\n   * The event ids `ws` delivered while the subscription was moving, which\n   * its successor replays and are dropped from it.\n   */\n  overlapEventIds: Set<string> | null;\n  /**\n   * The sockets a subscription moved from, each until everything it sent\n   * before the stop has arrived. Their events without ids are still\n   * delivered, since the new socket may have started listening after they\n   * were sent.\n   */\n  movedFrom: Set<WebSocket>;\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   * Subscriptions sent again under a new id, by their earlier id, while\n   * sockets they moved from may still send events under that one.\n   */\n  private drainingRequests: 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      ws: null,\n      successor: null,\n      overlapEventIds: null,\n      movedFrom: new Set(),\n      callbacks: {\n        next: callbacks.next,\n        complete: () => {\n          callbacks.complete();\n          resolve();\n        },\n        error: (e) => {\n          try {\n            callbacks.error(e);\n          } finally {\n            resolve();\n          }\n        },\n      },\n    });\n\n    return () => {\n      this.delete(message);\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   * Matches the message itself rather than its id: a retry reuses the id, and\n   * registers its request before the failed request with that id is deleted.\n   */\n  public delete(message: TRPCClientOutgoingMessage) {\n    this.outgoingRequests = this.outgoingRequests.filter(\n      (request) => request.message !== message,\n    );\n    const id = String(message.id);\n    if (this.pendingRequests[id]?.message === message) {\n      delete this.pendingRequests[id];\n    }\n    for (const [drainingId, request] of Object.entries(this.drainingRequests)) {\n      if (request.message === message) {\n        delete this.drainingRequests[drainingId];\n      }\n    }\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   * @param ws - The socket the requests are sent on.\n   * @returns The list of requests that were transitioned to the pending state.\n   */\n  public flush(ws: WebSocket | null = null) {\n    const requests = this.outgoingRequests;\n    this.outgoingRequests = [];\n\n    for (const request of requests) {\n      request.ws = ws;\n      request.successor = null;\n      request.movedFrom = new Set();\n      this.pendingRequests[request.id] = request;\n    }\n    return requests;\n  }\n\n  /**\n   * Moves a pending request back to the outgoing queue, to be sent again\n   * with `id`. Its earlier id then matches only the sockets it's still\n   * draining; see `getDrainingRequest()`.\n   */\n  public requeue(\n    message: TRPCClientOutgoingMessage,\n    id: TRPCClientOutgoingMessage['id'],\n  ) {\n    const earlierId = String(message.id);\n    const request = this.pendingRequests[earlierId];\n    if (request?.message !== message) return;\n\n    delete this.pendingRequests[earlierId];\n    if (request.movedFrom.size) this.drainingRequests[earlierId] = request;\n    message.id = id;\n    this.outgoingRequests.push({\n      ...request,\n      id: String(id),\n      ws: null,\n      successor: null,\n      overlapEventIds: null,\n      movedFrom: new Set(),\n    });\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 a subscription sent again under a new id by its earlier id,\n   * while a socket it moved from may still send events under that one.\n   */\n  public getDrainingRequest(messageId: MessageIdLike) {\n    if (messageId === null) return null;\n\n    return this.drainingRequests[String(messageId)];\n  }\n\n  /**\n   * Retrieves the subscriptions sent again under a new id whose earlier id\n   * is still in use on a socket they moved from.\n   */\n  public getDrainingRequests() {\n    return Object.values(this.drainingRequests);\n  }\n\n  /**\n   * Forgets the earlier ids of subscriptions no longer draining any socket.\n   */\n  public deleteDrained() {\n    for (const [id, request] of Object.entries(this.drainingRequests)) {\n      if (!request.movedFrom.size) delete this.drainingRequests[id];\n    }\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 a message is still waiting to be sent.\n   */\n  public isOutgoing(message: TRPCClientOutgoingMessage) {\n    return this.outgoingRequests.some((request) => request.message === message);\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\nconst pongCallbacks = new WeakMap<WebSocket, ((() => void) | null)[]>();\n\n/**\n * Sends \"PING\" on `ws` and calls `onPong`, if given, once its \"PONG\"\n * arrives. The server answers in order, so by then everything it sent before\n * it read this \"PING\" has arrived too.\n */\nexport function sendPing(ws: WebSocket, onPong: (() => void) | null) {\n  let callbacks = pongCallbacks.get(ws);\n  if (!callbacks) {\n    const queue: ((() => void) | null)[] = [];\n    callbacks = queue;\n    pongCallbacks.set(ws, queue);\n    ws.addEventListener('message', ({ data }) => {\n      if (data === 'PONG') queue.shift()?.();\n    });\n  }\n  callbacks.push(onPong);\n  ws.send('PING');\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      sendPing(ws, null);\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 {\n  callUserCallback,\n  ResettableTimeout,\n  TRPCWebSocketClosedError,\n} from './utils';\nimport type { WebSocketConnectionOptions } from './wsConnection';\nimport { backwardCompatibility, sendPing, 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  /**\n   * The last id sent on the wire. Each request gets its own instead of its\n   * `op.id`: a retry reuses the failed attempt's `op.id`, and the server's\n   * late frames for that attempt must not reach the retry. A subscription\n   * sent again after its socket was lost gets a new one for the same reason.\n   */\n  private lastRequestId = 0;\n  private readonly connectionOptions: WebSocketConnectionOptions;\n  /**\n   * The connection new requests are sent on. A reconnect replaces it only\n   * once the new connection is fully open, `connectionParams` included, so\n   * until then requests keep going to the old one.\n   */\n  private 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.connectionOptions = {\n      WebSocketPonyfill: opts.WebSocket,\n      urlOptions: opts,\n      keepAlive: {\n        ...keepAliveDefaults,\n        ...opts.keepAlive,\n      },\n      encoder: this.encoder,\n    };\n    this.activeConnection = this.createConnection();\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  private createConnection() {\n    const connection = new WsConnection(this.connectionOptions);\n    connection.wsObservable.subscribe({\n      next: (ws) => {\n        if (!ws) return;\n        this.setupWebSocketListeners(ws);\n      },\n    });\n    return connection;\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.reconnecting) return this.reconnecting;\n    this.connectionAttemptIndex = 0;\n    if (this.connectionState.get().state === 'idle') {\n      callUserCallback(() =>\n        this.connectionState.next({\n          type: 'state',\n          state: 'connecting',\n          error: null,\n        }),\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      // A socket whose `connectionParams` failed is open, but not usable.\n      await this.activeConnection.close().catch(() => null);\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.replacementError = null;\n    this.retiredConnection = null;\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        callUserCallback(() => request.callbacks.complete());\n      } else if (request.state === 'outgoing') {\n        callUserCallback(() =>\n          request.callbacks.error(\n            TRPCClientError.from(\n              new TRPCWebSocketClosedError({\n                message: 'Closed before connection was established',\n              }),\n            ),\n          ),\n        );\n      } else {\n        requestsToAwait.push(request.end);\n      }\n    }\n\n    await Promise.all(requestsToAwait).catch(() => null);\n    this.nextConnection?.ws?.close();\n    await this.activeConnection.close().catch(() => null);\n\n    callUserCallback(() =>\n      this.connectionState.next({\n        type: 'state',\n        state: 'idle',\n        error: null,\n      }),\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 type, path, input and signal. Each subscription to the returned observable is sent with a new id, not `op.id`.\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: { 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 message: TRPCClientOutgoingMessage = {\n        id: ++this.lastRequestId,\n        method: type,\n        params: {\n          input: transformer.input.serialize(input),\n          path,\n          lastEventId,\n        },\n      };\n      const abort = this.batchSend(message, {\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      const onAbort = () => observer.complete();\n      if (signal?.aborted) {\n        onAbort();\n      } else {\n        signal?.addEventListener('abort', onAbort, { once: true });\n      }\n\n      return () => {\n        // A subscription sent again after its socket was lost has a new id.\n        const { id } = message;\n        const pending = this.requestManager.getPendingRequest(id);\n        // Under its earlier ids, it may still be draining other sockets.\n        const draining = this.requestManager\n          .getDrainingRequests()\n          .filter((request) => request.message === message);\n        abort();\n\n        if (type === 'subscription' && pending) {\n          this.stopSubscription(id, pending.ws);\n          this.stopSubscription(id, pending.successor);\n        }\n        this.closeIfDrained(pending?.ws);\n        for (const request of [pending, ...draining]) {\n          for (const earlier of request?.movedFrom ?? []) {\n            this.closeIfDrained(earlier);\n          }\n        }\n\n        signal?.removeEventListener('abort', onAbort);\n      };\n    });\n  }\n\n  /**\n   * The current WebSocket and its state, or `null` when there is no socket.\n   */\n  public get connection() {\n    return backwardCompatibility(\n      this.activeConnection.ws\n        ? this.activeConnection\n        : (this.nextConnection ?? this.activeConnection),\n    );\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  /**\n   * The connection a reconnect is opening to replace the active one.\n   */\n  private nextConnection: WsConnection | null = null;\n  /**\n   * The connection the active one replaced, while it's open. If the active\n   * one is lost first, this one serves every request again until the next.\n   */\n  private retiredConnection: WsConnection | null = null;\n  /**\n   * Why the latest attempt to replace a socket that's still open failed.\n   * It's shown once that socket closes, since until then it serves every\n   * request.\n   */\n  private replacementError: TRPCWebSocketClosedError | null = null;\n  /**\n   * The retry attempt that opened the current connection.\n   */\n  private connectionAttemptIndex = 0;\n  /**\n   * Set when the server rejects the current connection with an `id: null`\n   * error, e.g. because `createContext` threw. The server closes the socket\n   * right after, so the reconnect that follows carries on with the backoff\n   * instead of starting over at attempt 0. Cleared again when the socket\n   * receives a result, which proves the connection was accepted.\n   * @see https://github.com/trpc/trpc/issues/4774\n   */\n  private connectionRejected = false;\n  /** Subscriptions whose subscriber has seen `started`. */\n  private readonly started = new WeakSet<TRPCClientOutgoingMessage>();\n\n  /**\n   * Opens a new connection, retrying until one opens. While the current\n   * socket is open, it keeps serving every request, new ones included, until\n   * the new one is fully open, `connectionParams` included.\n   *\n   * @param closedError - Why the current socket was lost, or `null` when it's\n   * still open and serving requests, e.g. on a server-requested reconnect.\n   */\n  private reconnect(closedError: TRPCWebSocketClosedError | null) {\n    if (!this.allowReconnect) {\n      return;\n    }\n\n    if (closedError) {\n      const error = this.replacementError ?? closedError;\n      this.replacementError = null;\n      callUserCallback(() =>\n        this.connectionState.next({\n          type: 'state',\n          state: 'connecting',\n          error: TRPCClientError.from(error),\n        }),\n      );\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          const next = this.createConnection();\n          this.nextConnection = next;\n          try {\n            await next.open();\n            // `open()` resolves even if the socket closed while its\n            // `connectionParams` were pending.\n            if (!next.isOpen()) {\n              throw new Error('WebSocket closed before it was ready');\n            }\n          } catch (error) {\n            await next.close().catch(() => null);\n            throw error;\n          } finally {\n            this.nextConnection = null;\n          }\n\n          if (this.allowReconnect) {\n            // Read only now: losing the active socket while `next` opened may\n            // have restored the one it replaced.\n            const previous = this.activeConnection;\n            this.activeConnection = next;\n            this.retiredConnection = previous.isOpen() ? previous : null;\n            this.connectionAttemptIndex = attemptIndex;\n            this.replacementError = null;\n            this.handOff(previous.ws);\n          } else {\n            await next.close();\n          }\n        }\n        this.reconnecting = null;\n      } catch (error) {\n        const closedError = new TRPCWebSocketClosedError({\n          message: 'Initialization error',\n          cause: error,\n        });\n        if (this.allowReconnect && this.activeConnection.isOpen()) {\n          // The old socket keeps serving every request, so this is shown only\n          // once it closes.\n          this.replacementError = closedError;\n        } else if (this.allowReconnect) {\n          callUserCallback(() =>\n            this.connectionState.next({\n              type: 'state',\n              state: 'connecting',\n              error: TRPCClientError.from(closedError),\n            }),\n          );\n        }\n        await tryReconnect(attemptIndex + 1);\n      }\n    };\n\n    this.reconnecting = tryReconnect(\n      this.connectionRejected ? this.connectionAttemptIndex + 1 : 0,\n    );\n  }\n\n  /**\n   * Moves the pending subscriptions to the active socket, which has just\n   * opened: each is resubscribed with the id of the last event it delivered.\n   * A subscription whose old socket is still open keeps delivering from it\n   * until the new socket confirms it with `started`; see `confirmMove()`.\n   * Closes the previous socket once nothing is left on it.\n   */\n  private handOff(previous: WebSocket | null) {\n    if (\n      this.connectionState.get().state !== 'pending' &&\n      this.activeConnection.isOpen()\n    ) {\n      callUserCallback(() =>\n        this.connectionState.next({\n          type: 'state',\n          state: 'pending',\n          error: null,\n        }),\n      );\n    }\n\n    const ws = this.activeConnection.ws;\n    const subscriptions = this.requestManager\n      .getPendingRequests()\n      .filter(\n        (request) =>\n          request.message.method === 'subscription' && request.ws !== ws,\n      );\n    if (subscriptions.length) {\n      this.send(subscriptions.map(({ message }) => message));\n      for (const request of subscriptions) {\n        // The socket it was moving to hasn't started it, and won't be used.\n        this.stopSubscription(request.message.id, request.successor);\n        if (request.ws && request.ws.readyState === request.ws.OPEN) {\n          request.successor = ws;\n          request.overlapEventIds = new Set();\n        } else {\n          // Its socket was lost, so it starts again.\n          this.started.delete(request.message);\n          request.ws = ws;\n          request.successor = null;\n        }\n      }\n    }\n    this.closeIfDrained(previous);\n  }\n\n  /**\n   * Completes a subscription's move once its new socket has confirmed it:\n   * stops it on the old socket, whose later messages for it are dropped\n   * apart from events without ids, and closes that socket once everything it\n   * sent before the stop has arrived and nothing else is left on it.\n   */\n  private confirmMove(request: {\n    message: TRPCClientOutgoingMessage;\n    ws: WebSocket | null;\n    successor: WebSocket | null;\n    movedFrom: Set<WebSocket>;\n  }) {\n    const previous = request.ws;\n    request.ws = request.successor;\n    request.successor = null;\n    if (previous && this.stopSubscription(request.message.id, previous)) {\n      request.movedFrom.add(previous);\n      // `stopped` can wait on the procedure, but the \"PONG\" can't.\n      sendPing(previous, () => this.releaseMovedFrom(request, previous));\n    }\n    this.closeIfDrained(previous);\n  }\n\n  private releaseMovedFrom(\n    request: { movedFrom: Set<WebSocket> },\n    ws: WebSocket,\n  ) {\n    if (!request.movedFrom.delete(ws)) return;\n    this.requestManager.deleteDrained();\n    this.closeIfDrained(ws);\n  }\n\n  private hasRequestsOn(ws: WebSocket | null) {\n    return [\n      ...this.requestManager.getPendingRequests(),\n      ...this.requestManager.getDrainingRequests(),\n    ].some(\n      (request) =>\n        request.ws === ws || (ws !== null && request.movedFrom.has(ws)),\n    );\n  }\n\n  /**\n   * Closes `ws` once it's been replaced and nothing is left on it.\n   */\n  private closeIfDrained(ws: WebSocket | null | undefined) {\n    if (!ws || ws === this.activeConnection.ws) return;\n    if (this.hasRequestsOn(ws)) return;\n    ws.close();\n  }\n\n  /**\n   * Stops a subscription on the socket it was sent on, if that's still open.\n   *\n   * @returns Whether the stop was sent.\n   */\n  private stopSubscription(\n    id: TRPCClientOutgoingMessage['id'],\n    ws: WebSocket | null,\n  ) {\n    if (!ws || ws.readyState !== ws.OPEN) return false;\n    ws.send(this.encoder.encode({ id, method: 'subscription.stop' }));\n    return true;\n  }\n\n  /**\n   * Fails the pending queries and mutations sent on `ws`, and returns its\n   * pending subscriptions, which are kept to be sent again.\n   */\n  private failPendingRequests(ws: WebSocket, cause: unknown) {\n    const subscriptions = [];\n    for (const request of this.requestManager.getPendingRequests()) {\n      const { message, callbacks } = request;\n      if (request.ws !== ws) continue;\n      if (message.method === 'subscription') {\n        subscriptions.push(request);\n        continue;\n      }\n\n      callUserCallback(() =>\n        callbacks.error(\n          TRPCClientError.from(\n            cause ??\n              new TRPCWebSocketClosedError({\n                message: 'WebSocket closed',\n                cause,\n              }),\n          ),\n        ),\n      );\n      this.requestManager.delete(message);\n    }\n    return subscriptions;\n  }\n\n  private setupWebSocketListeners(ws: WebSocket) {\n    this.connectionRejected = false;\n\n    /**\n     * Fails the queries and mutations sent on `ws`. Its subscriptions are\n     * sent again on the active socket if that's open, or else by\n     * `reconnect()` once the next one opens.\n     */\n    const handleCloseOrError = (cause: unknown) => {\n      if (ws === this.retiredConnection?.ws) this.retiredConnection = null;\n      const retired = this.retiredConnection;\n      let restored = false;\n      if (ws === this.activeConnection.ws && retired?.isOpen()) {\n        restored = true;\n        // The socket it replaced serves every request again, until the next\n        // one opens.\n        this.activeConnection = retired;\n        this.retiredConnection = null;\n        this.replacementError = new TRPCWebSocketClosedError({\n          message: 'WebSocket closed',\n          cause,\n        });\n      }\n      for (const request of this.requestManager.getPendingRequests()) {\n        if (request.successor === ws) {\n          // Its old socket still has it.\n          request.successor = null;\n          request.overlapEventIds = null;\n        }\n        request.movedFrom.delete(ws);\n      }\n      for (const request of this.requestManager.getDrainingRequests()) {\n        request.movedFrom.delete(ws);\n      }\n      this.requestManager.deleteDrained();\n      const lost = this.failPendingRequests(ws, cause).filter((request) => {\n        if (!request.successor) return true;\n        // Its new socket has it already.\n        request.ws = request.successor;\n        request.successor = null;\n        return false;\n      });\n      if (\n        ws === this.activeConnection.ws ||\n        (this.reconnecting && !this.activeConnection.isOpen())\n      ) {\n        return;\n      }\n\n      for (const { message, callbacks } of lost) {\n        this.started.delete(message);\n        // The active socket may still have its old id, e.g. while stopping it.\n        this.requestManager.requeue(message, ++this.lastRequestId);\n        this.flushOutgoing(message, callbacks);\n      }\n      if (restored) this.reconnect(null);\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        // A socket opened to replace one that's still open changes nothing\n        // the subscribers can see until `handOff()` puts it in use.\n        if (\n          this.connectionState.get().state === 'pending' ||\n          (ws !== this.activeConnection.ws && this.activeConnection.isOpen())\n        ) {\n          return;\n        }\n        callUserCallback(() =>\n          this.connectionState.next({\n            type: 'state',\n            state: 'pending',\n            error: null,\n          }),\n        );\n      }).catch((error) => {\n        ws.close(3000);\n        handleCloseOrError(error);\n      });\n    });\n\n    const onMessage = ({ data }: MessageEvent) => {\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        if (ws === this.activeConnection.ws) {\n          this.handleIncomingRequest(incomingMessage);\n        }\n        return;\n      }\n\n      // A replaced socket still answers its own queries and mutations, but\n      // says nothing about the connection the client now uses.\n      const isRetired =\n        ws !== this.activeConnection.ws && ws !== this.nextConnection?.ws;\n      if (\n        !isRetired &&\n        incomingMessage.id === null &&\n        'error' in incomingMessage\n      ) {\n        this.connectionRejected = true;\n      } else if (!isRetired && 'result' in incomingMessage) {\n        // The server only sends results once `createContext` has succeeded,\n        // so an earlier `id: null` error on this socket was not a rejection,\n        // e.g. the server's reply to a malformed frame.\n        this.connectionRejected = false;\n      }\n\n      this.handleResponseMessage(incomingMessage, ws);\n    };\n    // With `ws`, a throw from a `message` listener stops the socket from\n    // reading any further frames while it stays open.\n    ws.addEventListener('message', (event) => {\n      callUserCallback(() => onMessage(event));\n    });\n\n    ws.addEventListener('close', (event) => {\n      handleCloseOrError(event);\n      callUserCallback(() => this.callbacks.onClose?.(event));\n\n      if (\n        ws === this.activeConnection.ws &&\n        (!this.lazyMode || this.requestManager.hasPendingSubscriptions())\n      ) {\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      callUserCallback(() => this.callbacks.onError?.(event));\n\n      if (ws !== this.activeConnection.ws) return;\n\n      this.reconnect(\n        new TRPCWebSocketClosedError({\n          message: 'WebSocket closed',\n          cause: event,\n        }),\n      );\n    });\n  }\n\n  private handleResponseMessage(message: TRPCResponseMessage, ws: WebSocket) {\n    const request =\n      this.requestManager.getPendingRequest(message.id) ??\n      this.requestManager.getDrainingRequest(message.id);\n    if (!request) return;\n    const result = 'result' in message ? message.result : null;\n    if (request.successor === ws) {\n      this.confirmMove(request);\n    } else if (request.ws !== ws) {\n      // A subscription moved to a new socket ignores the old one's frames,\n      // apart from events without ids until it's stopped there: those can't\n      // be replayed, and the new socket may have started listening after\n      // the old one sent them.\n      if (request.movedFrom.has(ws)) {\n        if (result?.type === 'stopped') {\n          this.releaseMovedFrom(request, ws);\n        } else if (result?.type === 'data' && result.id === undefined) {\n          callUserCallback(() => request.callbacks.next(message));\n        }\n      }\n      return;\n    }\n    // The subscriber doesn't see a move, even one its old socket didn't\n    // live to finish.\n    if (result?.type === 'started' && this.started.has(request.message)) {\n      return;\n    }\n\n    if (result?.type === 'data' && result.id !== undefined) {\n      const eventId = String(result.id);\n      if (request.successor) {\n        request.overlapEventIds?.add(eventId);\n      } else if (request.overlapEventIds?.delete(eventId)) {\n        // Already delivered by the old socket.\n        return;\n      }\n    }\n\n    callUserCallback(() => 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      } else if (message.result.type === 'started') {\n        this.started.add(request.message);\n      }\n\n      if (message.result.type !== 'stopped') {\n        completed = false;\n      }\n    }\n\n    if (completed) {\n      callUserCallback(() => request.callbacks.complete());\n      this.requestManager.delete(request.message);\n    }\n  }\n\n  private handleIncomingRequest(message: TRPCClientIncomingRequest) {\n    if (message.method === 'reconnect') {\n      this.reconnect(null);\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    this.flushOutgoing(message, callbacks);\n    return this.requestManager.register(message, callbacks);\n  }\n\n  /**\n   * Sends `message` with the other outgoing requests once the connection is\n   * open, batching those queued in the same tick.\n   */\n  private flushOutgoing(\n    message: TRPCClientOutgoingMessage,\n    callbacks: TCallbacks,\n  ) {\n    run(async () => {\n      // During a reconnect, requests go to the old socket while it's open.\n      do {\n        if (!this.activeConnection.isOpen()) {\n          await this.open();\n        }\n        await sleep(0);\n      } while (!this.activeConnection.isOpen() && this.reconnecting);\n\n      if (!this.requestManager.hasOutgoingRequests()) return;\n      // Another run already sent this request. Each queued request's own run\n      // sends it once the new socket opens.\n      if (\n        !this.activeConnection.isOpen() &&\n        !this.requestManager.isOutgoing(message)\n      ) {\n        return;\n      }\n\n      const requests = this.requestManager.flush(this.activeConnection.ws);\n      try {\n        this.send(requests.map((request) => request.message));\n      } catch (err) {\n        for (const request of requests) {\n          this.requestManager.delete(request.message);\n          callUserCallback(() =>\n            request.callbacks.error(TRPCClientError.from(err as Error)),\n          );\n        }\n      }\n    }).catch((err) => {\n      this.requestManager.delete(message);\n      callUserCallback(() => callbacks.error(TRPCClientError.from(err)));\n    });\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\n/**\n * Opens and manages a WebSocket connection to a tRPC server. Pass it to\n * `wsLink`.\n *\n * @see https://trpc.io/docs/client/links/wsLink#wslink--createwsclient-options\n */\nexport function createWSClient(opts: WebSocketClientOptions) {\n  return new WsClient(opts);\n}\n\n/**\n * The client returned by {@link createWSClient}.\n */\nexport type TRPCWebSocketClient = ReturnType<typeof createWSClient>;\n\nexport { jsonEncoder, type Encoder, type WebSocketClientOptions };\n","import type { Unsubscribable } from '@trpc/server/observable';\nimport { 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';\nimport { callUserCallback } from './wsClient/utils';\n\n/**\n * Options for {@link wsLink}.\n *\n * @see https://trpc.io/docs/client/links/wsLink#wslink--createwsclient-options\n */\nexport type WebSocketLinkOptions<TRouter extends AnyRouter> = {\n  /**\n   * The client made with `createWSClient`.\n   */\n  client: TRPCWebSocketClient;\n} & TransformerOptions<inferClientTypes<TRouter>>;\n\n/**\n * A terminating link that sends calls over a WebSocket.\n *\n * @see https://trpc.io/docs/client/links/wsLink\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        let connStateSubscription: Unsubscribable | null = null;\n        if (op.type === 'subscription') {\n          // The current state is sent during `subscribe`, and a throwing\n          // `onConnectionStateChange` must not stop the request below from\n          // being sent. The client guards the states it sends later.\n          let isSubscribing = true;\n          connStateSubscription = client.connectionState.subscribe({\n            next(result) {\n              const emit = () =>\n                observer.next({\n                  result,\n                  context: op.context,\n                });\n              if (isSubscribing) {\n                callUserCallback(emit);\n              } else {\n                emit();\n              }\n            },\n          });\n          isSubscribing = false;\n        }\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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"}