{"version":3,"file":"websocket.cjs","names":["AsyncQueue","reconnectDelayMs","resolveProtocolPath","#detachSocket","toWebSocketUrl","toAbsoluteUrl","#attachSocket","hasHeaders","toError","isRecord","#handleUnexpectedDisconnect","#scheduleReconnect","#runReconnectLoop","MaxWebSocketReconnectAttemptsError"],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n  Message,\n  Command,\n  CommandResponse,\n  ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport {\n  toAbsoluteUrl,\n  toWebSocketUrl,\n  isRecord,\n  hasHeaders,\n  toError,\n  resolveProtocolPath,\n} from \"./utils.js\";\nimport type {\n  HeaderValue,\n  ProtocolRequestHook,\n  PendingResponse,\n  ProtocolTransportPaths,\n  ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\nimport { MaxWebSocketReconnectAttemptsError } from \"../error.js\";\nimport {\n  DEFAULT_MAX_RECONNECT_ATTEMPTS,\n  reconnectDelayMs,\n  type ReconnectInfo,\n} from \"../../../utils/reconnect.js\";\n\nconst WEB_SOCKET_CONNECTING = 0;\nconst WEB_SOCKET_OPEN = 1;\nconst WEB_SOCKET_CLOSED = 3;\n\n/**\n * Reconnect tuning for {@link ProtocolWebSocketTransportAdapter}. A subset\n * of {@link ProtocolWebSocketTransportOptions}.\n */\nexport interface WebSocketReconnectOptions {\n  /**\n   * Maximum reconnection attempts after an unexpected disconnect.\n   * Defaults to {@link DEFAULT_MAX_RECONNECT_ATTEMPTS}.\n   */\n  maxReconnectAttempts?: number;\n\n  /** Invoked before each reconnect attempt with its scheduled delay. */\n  onReconnect?: (options: ReconnectInfo) => void;\n}\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a `threadId` at construction or later\n * via {@link setThreadId} — the socket connects to\n * `ws://.../threads/:thread_id/stream/events`.\n *\n * On unexpected disconnect the adapter reconnects with exponential\n * backoff (see {@link ProtocolWebSocketTransportOptions.maxReconnectAttempts}).\n * The server replays buffered events on the new socket; the SDK\n * deduplicates by `event_id`. {@link ProtocolWebSocketTransportOptions.onReconnected}\n * runs after each successful reconnect so `ThreadStream` can re-issue\n * `subscription.subscribe` commands.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n  threadId: string;\n\n  private readonly queue = new AsyncQueue<Message>();\n\n  private readonly apiUrl: string;\n\n  private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n  private readonly onRequest?: ProtocolRequestHook;\n\n  private readonly webSocketFactory: (url: string) => WebSocket;\n\n  private readonly paths?: Pick<ProtocolTransportPaths, \"stream\">;\n\n  private readonly maxReconnectAttempts: number;\n\n  private readonly onReconnect?: ProtocolWebSocketTransportOptions[\"onReconnect\"];\n\n  private readonly onConnected?: ProtocolWebSocketTransportOptions[\"onConnected\"];\n\n  private readonly reconnectDelayMs: (attempt: number) => number;\n\n  private onReconnected?: () => void | Promise<void>;\n\n  private readonly pending = new Map<number, PendingResponse>();\n\n  private socket: WebSocket | null = null;\n\n  private closed = false;\n\n  private intentionalClose = false;\n\n  private reconnectInFlight: Promise<void> | null = null;\n\n  constructor(options: ProtocolWebSocketTransportOptions) {\n    this.apiUrl = options.apiUrl;\n    this.threadId = options.threadId ?? \"\";\n    this.defaultHeaders = options.defaultHeaders;\n    this.onRequest = options.onRequest;\n    this.webSocketFactory =\n      options.webSocketFactory ?? ((url) => new WebSocket(url));\n    this.paths = options.paths;\n    this.maxReconnectAttempts =\n      options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;\n    this.onReconnect = options.onReconnect;\n    this.onConnected = options.onConnected;\n    this.onReconnected = options.onReconnected;\n    this.reconnectDelayMs = options.reconnectDelayMs ?? reconnectDelayMs;\n  }\n\n  /** {@inheritDoc TransportAdapter.setThreadId} */\n  setThreadId(threadId: string): void {\n    if (threadId === this.threadId) {\n      return;\n    }\n    if (\n      this.reconnectInFlight != null ||\n      (this.socket != null && this.socket.readyState !== WEB_SOCKET_CLOSED)\n    ) {\n      throw new Error(\n        \"Protocol WebSocket transport cannot be rebound to a different thread while the socket is open. Close the current stream and create a new WebSocket transport for the new thread.\"\n      );\n    }\n    this.threadId = threadId;\n  }\n\n  /**\n   * Socket URL derives from the currently-bound thread so a single adapter\n   * can follow {@link setThreadId} re-binds; the next {@link open} connects\n   * to the new thread. A fixed `paths.stream` string overrides the default.\n   */\n  private get streamUrl(): string {\n    return resolveProtocolPath(\n      this.paths?.stream,\n      this.threadId,\n      (id) => `/threads/${id}/stream/events`\n    );\n  }\n\n  /**\n   * Register a callback invoked after each successful reconnect. Used\n   * by {@link ThreadStream} to re-send active `subscription.subscribe`\n   * commands.\n   */\n  setOnReconnected(handler: () => void | Promise<void>): void {\n    this.onReconnected = handler;\n  }\n\n  async open(): Promise<void> {\n    const reconnecting = this.reconnectInFlight != null;\n    if (this.closed) {\n      throw new Error(\"Protocol WebSocket transport is closed.\");\n    }\n    if (this.socket?.readyState === WEB_SOCKET_OPEN) {\n      return;\n    }\n    if (this.socket != null) {\n      this.#detachSocket(this.socket);\n      this.socket = null;\n    }\n\n    this.assertBrowserSafeTransportConfig();\n\n    const wsUrl = toWebSocketUrl(\n      toAbsoluteUrl(this.apiUrl, this.streamUrl).toString()\n    );\n    const socket = this.webSocketFactory(wsUrl);\n    this.socket = socket;\n    this.intentionalClose = false;\n\n    this.#attachSocket(socket);\n\n    await new Promise<void>((resolve, reject) => {\n      const onOpen = () => {\n        cleanup();\n        resolve();\n      };\n      const onError = () => {\n        cleanup();\n        reject(new Error(\"Failed to open protocol WebSocket.\"));\n      };\n      const cleanup = () => {\n        socket.removeEventListener(\"open\", onOpen);\n        socket.removeEventListener(\"error\", onError);\n      };\n      socket.addEventListener(\"open\", onOpen, { once: true });\n      socket.addEventListener(\"error\", onError, { once: true });\n    });\n\n    if (!reconnecting) {\n      await this.onConnected?.({ kind: \"initial\", attempt: 0 });\n    }\n  }\n\n  async send(\n    command: Command\n  ): Promise<CommandResponse | ErrorResponse | void> {\n    return await this.sendCommand(command);\n  }\n\n  events(): AsyncIterable<Message> {\n    const queue = this.queue;\n    return {\n      [Symbol.asyncIterator]: () => ({\n        next: async () => await queue.shift(),\n        return: async () => {\n          queue.close();\n          return { done: true, value: undefined };\n        },\n      }),\n    };\n  }\n\n  async close(): Promise<void> {\n    if (this.closed) {\n      return;\n    }\n\n    this.closed = true;\n    this.intentionalClose = true;\n\n    for (const { reject } of this.pending.values()) {\n      reject(new Error(\"Protocol WebSocket connection closed.\"));\n    }\n    this.pending.clear();\n    this.queue.close();\n\n    const socket = this.socket;\n    this.socket = null;\n    if (!socket) {\n      return;\n    }\n\n    this.#detachSocket(socket);\n\n    await new Promise<void>((resolve) => {\n      if (socket.readyState === WEB_SOCKET_CLOSED) {\n        resolve();\n        return;\n      }\n\n      const onClose = () => {\n        socket.removeEventListener(\"close\", onClose);\n        resolve();\n      };\n\n      socket.addEventListener(\"close\", onClose, { once: true });\n      if (\n        socket.readyState === WEB_SOCKET_OPEN ||\n        socket.readyState === WEB_SOCKET_CONNECTING\n      ) {\n        socket.close();\n      } else {\n        resolve();\n      }\n    });\n  }\n\n  private assertBrowserSafeTransportConfig(): void {\n    if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n      throw new Error(\n        \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n      );\n    }\n  }\n\n  private async sendCommand(\n    command: Command\n  ): Promise<CommandResponse | ErrorResponse> {\n    // Wait for an in-flight reconnect only when the socket is not usable.\n    // After `open()` succeeds, `#runReconnectLoop` may still be awaiting\n    // `onReconnected` (e.g. ThreadStream resubscribe). Those callbacks call\n    // `sendCommand` and must not await the same `reconnectInFlight` promise.\n    let socket = this.socket;\n    if (\n      this.reconnectInFlight != null &&\n      (socket == null || socket.readyState !== WEB_SOCKET_OPEN)\n    ) {\n      await this.reconnectInFlight.catch(() => undefined);\n      socket = this.socket;\n    }\n\n    if (socket == null || socket.readyState !== WEB_SOCKET_OPEN) {\n      throw new Error(\"Protocol WebSocket is not open.\");\n    }\n\n    return await new Promise<CommandResponse | ErrorResponse>(\n      (resolve, reject) => {\n        this.pending.set(command.id, { resolve, reject });\n\n        try {\n          socket.send(JSON.stringify(command));\n        } catch (error) {\n          this.pending.delete(command.id);\n          reject(toError(error));\n        }\n      }\n    );\n  }\n\n  #attachSocket(socket: WebSocket): void {\n    socket.addEventListener(\"message\", this.handleMessage);\n    socket.addEventListener(\"close\", this.handleClose);\n    socket.addEventListener(\"error\", this.handleSocketError);\n  }\n\n  #detachSocket(socket: WebSocket): void {\n    socket.removeEventListener(\"message\", this.handleMessage);\n    socket.removeEventListener(\"close\", this.handleClose);\n    socket.removeEventListener(\"error\", this.handleSocketError);\n  }\n\n  private readonly handleMessage = (event: MessageEvent): void => {\n    let payload: unknown;\n    try {\n      payload = JSON.parse(String(event.data));\n    } catch {\n      return;\n    }\n\n    if (\n      isRecord(payload) &&\n      typeof payload.id === \"number\" &&\n      (payload.type === \"success\" || payload.type === \"error\")\n    ) {\n      const pending = this.pending.get(payload.id);\n      if (pending) {\n        this.pending.delete(payload.id);\n        pending.resolve(payload as CommandResponse | ErrorResponse);\n      }\n      return;\n    }\n\n    if (isRecord(payload) && payload.type === \"event\") {\n      this.queue.push(payload as Message);\n    }\n  };\n\n  private readonly handleClose = (): void => {\n    const socket = this.socket;\n    if (socket != null) {\n      this.#detachSocket(socket);\n    }\n    this.socket = null;\n\n    if (this.intentionalClose || this.closed) {\n      this.queue.close();\n      return;\n    }\n\n    this.#handleUnexpectedDisconnect(\n      new Error(\"Protocol WebSocket closed unexpectedly.\")\n    );\n  };\n\n  private readonly handleSocketError = (): void => {\n    if (this.closed || this.intentionalClose) {\n      return;\n    }\n\n    this.#handleUnexpectedDisconnect(\n      new Error(\"Protocol WebSocket encountered an error.\")\n    );\n  };\n\n  #handleUnexpectedDisconnect(cause: unknown): void {\n    const error = toError(cause);\n    for (const { reject } of this.pending.values()) {\n      reject(error);\n    }\n    this.pending.clear();\n\n    if (this.maxReconnectAttempts <= 0) {\n      this.queue.close(error);\n      return;\n    }\n\n    this.#scheduleReconnect(cause);\n  }\n\n  #scheduleReconnect(cause: unknown): void {\n    if (this.closed || this.intentionalClose) {\n      return;\n    }\n    if (this.reconnectInFlight != null) {\n      return;\n    }\n\n    this.reconnectInFlight = Promise.resolve()\n      .then(() => this.#runReconnectLoop(cause))\n      .finally(() => {\n        this.reconnectInFlight = null;\n      });\n  }\n\n  async #runReconnectLoop(initialCause: unknown): Promise<void> {\n    let lastError: unknown = initialCause;\n\n    for (let attempt = 1; attempt <= this.maxReconnectAttempts; attempt += 1) {\n      if (this.closed || this.intentionalClose) {\n        return;\n      }\n\n      const delay = this.reconnectDelayMs(attempt);\n      this.onReconnect?.({ attempt, cause: lastError, delayMs: delay });\n      if (delay > 0) {\n        await new Promise<void>((resolve) => {\n          setTimeout(resolve, delay);\n        });\n      }\n\n      if (this.closed || this.intentionalClose) {\n        return;\n      }\n\n      try {\n        await this.open();\n        if (this.onReconnected) {\n          await this.onReconnected();\n        }\n        await this.onConnected?.({ kind: \"reconnected\", attempt });\n        return;\n      } catch (error) {\n        lastError = error;\n      }\n    }\n\n    this.queue.close(\n      new MaxWebSocketReconnectAttemptsError(\n        this.maxReconnectAttempts,\n        lastError\n      )\n    );\n  }\n}\n"],"mappings":"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"}