{"version":3,"file":"index.cjs","names":["buildSegment","DEFAULT_KEEPALIVE_INTERVAL_MS","MIN_KEEPALIVE_INTERVAL_MS","DEFAULT_CONNECT_TIMEOUT_MS","error","track","resolver"],"sources":["../../core/src/errors.ts","../../core/src/http-errors.ts","../../core/src/connection.ts","../../core/src/segments.ts","../../core/src/realtime/async-queue.ts","../../core/src/realtime/emitter.ts","../../core/src/realtime/errors.ts","../../core/src/realtime/stt.ts","../../core/src/realtime/tts.ts","../../core/src/realtime/segments.ts","../../core/src/realtime/segment-buffer.ts","../../core/src/realtime/utterance-buffer.ts","../../core/src/tts-rest.ts","../src/audio/errors.ts","../src/audio/microphone.ts","../src/auth.ts","../src/recording.ts","../src/client.ts","../src/permissions/browser.ts"],"sourcesContent":["/**\n * Base error class for all Soniox SDK errors.\n *\n * All SDK errors extend this class for error handling across both REST (HTTP) and WebSocket (Real-time) APIs.\n *\n * @example\n * ```typescript\n * try {\n *   await client.transcribe(file);\n *   await session.connect();\n * } catch (error) {\n *   if (error instanceof SonioxError) {\n *     console.log(error.code);       // 'auth_error', 'network_error', etc.\n *     console.log(error.statusCode); // 401, 500, etc. (when applicable)\n *     console.log(error.toJSON());   // Consistent serialization\n *   }\n * }\n * ```\n */\n\nimport type { SonioxErrorCode } from './types/errors.js';\n\nexport class SonioxError extends Error {\n  /**\n   * Error code describing the type of error.\n   * Typed as `string` at the base level to allow subclasses (e.g. HTTP errors)\n   * to use their own error code unions.\n   */\n  readonly code: SonioxErrorCode | (string & {});\n\n  /**\n   * HTTP status code when applicable (e.g., 401 for auth errors, 500 for server errors).\n   */\n  readonly statusCode: number | undefined;\n\n  /**\n   * The underlying error that caused this error, if any.\n   */\n  readonly cause: unknown;\n\n  constructor(\n    message: string,\n    code: SonioxErrorCode | (string & {}) = 'soniox_error',\n    statusCode?: number,\n    cause?: unknown\n  ) {\n    super(message);\n    this.name = 'SonioxError';\n    this.code = code;\n    this.statusCode = statusCode;\n    this.cause = cause;\n\n    if (Error.captureStackTrace) {\n      Error.captureStackTrace(this, this.constructor);\n    }\n\n    Object.setPrototypeOf(this, new.target.prototype);\n  }\n\n  /**\n   * Creates a human-readable string representation\n   */\n  override toString(): string {\n    const parts = [`${this.name} [${this.code}]: ${this.message}`];\n    if (this.statusCode !== undefined) {\n      parts.push(`  Status: ${this.statusCode}`);\n    }\n    return parts.join('\\n');\n  }\n\n  /**\n   * Converts to a plain object for logging/serialization\n   */\n  toJSON(): Record<string, unknown> {\n    return {\n      name: this.name,\n      code: this.code,\n      message: this.message,\n      ...(this.statusCode !== undefined && { statusCode: this.statusCode }),\n    };\n  }\n}\n","/**\n * HTTP error handling for the Soniox SDK.\n *\n * Lives in `@soniox/core` so it can be shared by the browser-safe\n * `TtsRestClient` and the Node `HttpClient`. `@soniox/node` re-exports\n * these symbols for backwards compatibility.\n */\n\nimport { SonioxError } from './errors.js';\nimport type { HttpErrorCode, HttpErrorDetails, HttpMethod } from './types/errors.js';\n\n/** Maximum body text length to include in error details (4KB) */\nconst MAX_BODY_TEXT_LENGTH = 4096;\n\n/**\n * HTTP error class for all HTTP-related failures (REST API).\n *\n * Thrown when HTTP requests fail due to network issues, timeouts,\n * server errors, or response parsing failures.\n */\nexport class SonioxHttpError extends SonioxError {\n  /** Categorized HTTP error code */\n  declare readonly code: HttpErrorCode;\n  /** Request URL */\n  readonly url: string;\n  /** HTTP method */\n  readonly method: HttpMethod;\n  /** Response headers (only for http_error) */\n  readonly headers: Record<string, string> | undefined;\n  /** Response body text, capped at 4KB (only for http_error/parse_error) */\n  readonly bodyText: string | undefined;\n\n  constructor(details: HttpErrorDetails) {\n    super(details.message, details.code, details.statusCode, details.cause);\n    this.name = 'SonioxHttpError';\n    this.url = details.url;\n    this.method = details.method;\n    this.headers = details.headers;\n    this.bodyText = details.bodyText;\n  }\n\n  /**\n   * Creates a human-readable string representation\n   */\n  override toString(): string {\n    const parts = [`SonioxHttpError [${this.code}]: ${this.message}`];\n    parts.push(`  Method: ${this.method}`);\n    parts.push(`  URL: ${this.url}`);\n    if (this.statusCode !== undefined) {\n      parts.push(`  Status: ${this.statusCode}`);\n    }\n    return parts.join('\\n');\n  }\n\n  /**\n   * Converts to a plain object for logging/serialization\n   */\n  override toJSON(): Record<string, unknown> {\n    return {\n      name: this.name,\n      code: this.code,\n      message: this.message,\n      url: this.url,\n      method: this.method,\n      ...(this.statusCode !== undefined && { statusCode: this.statusCode }),\n      ...(this.headers !== undefined && { headers: this.headers }),\n      ...(this.bodyText !== undefined && { bodyText: this.bodyText }),\n    };\n  }\n}\n\n/**\n * Creates a network error\n */\nexport function createNetworkError(url: string, method: HttpMethod, cause: unknown): SonioxHttpError {\n  const message = cause instanceof Error ? cause.message : 'Network request failed';\n  return new SonioxHttpError({\n    code: 'network_error',\n    message: `Network error: ${message}`,\n    url,\n    method,\n    cause,\n  });\n}\n\n/**\n * Creates a timeout error\n */\nexport function createTimeoutError(url: string, method: HttpMethod, timeoutMs: number): SonioxHttpError {\n  return new SonioxHttpError({\n    code: 'timeout',\n    message: `Request timed out after ${timeoutMs}ms`,\n    url,\n    method,\n  });\n}\n\n/**\n * Creates an abort error\n */\nexport function createAbortError(url: string, method: HttpMethod, cause?: unknown): SonioxHttpError {\n  return new SonioxHttpError({\n    code: 'aborted',\n    message: 'Request was aborted',\n    url,\n    method,\n    cause,\n  });\n}\n\n/**\n * Creates an HTTP error (non-2xx status)\n */\nexport function createHttpError(\n  url: string,\n  method: HttpMethod,\n  statusCode: number,\n  headers: Record<string, string>,\n  bodyText: string\n): SonioxHttpError {\n  const cappedBody = truncateBodyText(bodyText);\n  return new SonioxHttpError({\n    code: 'http_error',\n    message: `HTTP ${statusCode}`,\n    url,\n    method,\n    statusCode,\n    headers,\n    bodyText: cappedBody,\n  });\n}\n\n/**\n * Creates a parse error (invalid JSON, etc.)\n */\nexport function createParseError(url: string, method: HttpMethod, bodyText: string, cause: unknown): SonioxHttpError {\n  const message = cause instanceof Error ? cause.message : 'Failed to parse response';\n  const cappedBody = truncateBodyText(bodyText);\n  return new SonioxHttpError({\n    code: 'parse_error',\n    message: `Parse error: ${message}`,\n    url,\n    method,\n    bodyText: cappedBody,\n    cause,\n  });\n}\n\n/**\n * Truncates body text to the maximum allowed length\n */\nfunction truncateBodyText(text: string): string {\n  if (text.length <= MAX_BODY_TEXT_LENGTH) {\n    return text;\n  }\n  return text.slice(0, MAX_BODY_TEXT_LENGTH) + '... [truncated]';\n}\n\n/**\n * Type guard to check if an error is an AbortError\n */\nexport function isAbortError(error: unknown): boolean {\n  if (error instanceof Error) {\n    return error.name === 'AbortError' || error.name === 'TimeoutError';\n  }\n  return false;\n}\n\n/**\n * Type guard to check if an error is any SonioxError (base class).\n * This catches all SDK errors including HTTP and real-time errors.\n */\nexport function isSonioxError(error: unknown): error is SonioxError {\n  return error instanceof SonioxError;\n}\n\n/**\n * Type guard to check if an error is a SonioxHttpError\n */\nexport function isSonioxHttpError(error: unknown): error is SonioxHttpError {\n  return error instanceof SonioxHttpError;\n}\n\n/**\n * Checks if an error is a 404 Not Found error\n */\nexport function isNotFoundError(error: unknown): boolean {\n  return isSonioxHttpError(error) && error.statusCode === 404;\n}\n","/**\n * Connection configuration and region resolution.\n *\n * Provides types and utilities for resolving Soniox API endpoints\n * based on region and explicit overrides.\n */\n\nimport type { SttSessionConfig } from './types/realtime.js';\nimport type { TtsStreamConfig } from './types/tts.js';\n\n/**\n * Context passed to the config resolver function by the SDK.\n *\n * `usage` indicates what the resolved config will be used for, so the\n * server can generate a temporary API key with the correct scope.\n * `params` is a freeform bag for any custom data the developer wants\n * to forward to their backend.\n */\nexport type ConfigContext = {\n  /** What the config will be used for. Set by the SDK internally. */\n  usage?: 'transcribe_websocket' | 'tts_rt' | undefined;\n  /** Freeform data the developer can forward to their backend. */\n  params?: Record<string, unknown> | undefined;\n};\n\n/**\n * Soniox deployment region.\n *\n * Defined regions:\n * - `'eu'` — European Union (`*.eu.soniox.com`)\n * - `'jp'` — Japan (`*.jp.soniox.com`)\n * - `undefined` — Default (United States). The US region has no subdomain.\n *\n * A region name (other than `'us'`) is shorthand for setting `base_domain`\n * to `{region}.soniox.com`. The string `'us'` is accepted and normalized to\n * the default (United States) base domain; there is no `us.soniox.com` host.\n *\n * The type stays open (`string & {}`) for forward compatibility with regions\n * added after this SDK version was published, but passing an unknown region\n * simply prepends it as a subdomain and may not resolve.\n *\n * @see https://soniox.com/docs/stt/data-residency\n */\nexport type SonioxRegion = 'eu' | 'jp' | (string & {});\n\n/**\n * Connection configuration for Soniox APIs.\n *\n * Can be provided as a plain object (sync) or returned from an async function\n * to support fetching configuration from a server at runtime.\n */\nexport type SonioxConnectionConfig = {\n  /** API key for authentication. */\n  api_key: string;\n\n  /**\n   * Deployment region. Determines which regional endpoints are used.\n   * Leave `undefined` for the default (US) region.\n   *\n   * Shorthand for `base_domain: '{region}.soniox.com'`.\n   * `base_domain` takes precedence when both are provided.\n   *\n   * @see https://soniox.com/docs/stt/data-residency\n   */\n  region?: SonioxRegion | undefined;\n\n  /**\n   * Base domain for all Soniox service URLs.\n   *\n   * A single override that derives all four service endpoints:\n   * - `api_domain`  → `https://api.{base_domain}`\n   * - `stt_ws_url`  → `wss://stt-rt.{base_domain}/transcribe-websocket`\n   * - `tts_api_url` → `https://tts-rt.{base_domain}`\n   * - `tts_ws_url`  → `wss://tts-rt.{base_domain}/tts-websocket`\n   *\n   * Takes precedence over `region`. Individual URL fields (`api_domain`,\n   * `stt_ws_url`, etc.) still take final precedence over this value.\n   *\n   * @example 'eu.soniox.com'\n   */\n  base_domain?: string | undefined;\n\n  /**\n   * REST API domain override (e.g. `'https://api.eu.soniox.com'`).\n   * When set, takes precedence over the region-derived domain.\n   */\n  api_domain?: string | undefined;\n\n  /**\n   * STT WebSocket URL override (e.g. `'wss://stt-rt.eu.soniox.com/transcribe-websocket'`).\n   * When set, takes precedence over the region-derived URL.\n   */\n  stt_ws_url?: string | undefined;\n\n  /**\n   * TTS REST API URL override (e.g. `'https://tts-rt.eu.soniox.com'`).\n   * When set, takes precedence over the region-derived URL.\n   */\n  tts_api_url?: string | undefined;\n\n  /**\n   * TTS WebSocket URL override (e.g. `'wss://tts-rt.eu.soniox.com/tts-websocket'`).\n   * When set, takes precedence over the region-derived URL.\n   */\n  tts_ws_url?: string | undefined;\n\n  /**\n   * Server-provided STT session defaults (model, language hints, context, etc.).\n   *\n   * Available to the `session_config` function passed to `client.realtime.record()`,\n   * allowing server-driven defaults. Not applied automatically — the caller must\n   * explicitly spread them.\n   */\n  stt_defaults?: Partial<SttSessionConfig> | undefined;\n\n  /**\n   * Server-provided TTS stream defaults (model, voice, language, audio_format, etc.).\n   *\n   * Automatically merged as the base layer when opening TTS streams.\n   * Caller-provided fields override these defaults.\n   */\n  tts_defaults?: Partial<TtsStreamConfig> | undefined;\n\n  /**\n   * @deprecated Use `stt_defaults` instead. Kept as an alias for backward\n   * compatibility; the resolver treats it as equivalent to `stt_defaults`\n   * when that field is absent. Planned for removal in the next major version.\n   */\n  session_defaults?: Partial<SttSessionConfig> | undefined;\n};\n\n/**\n * Fully resolved connection configuration with all URLs determined.\n */\nexport type ResolvedConnectionConfig = {\n  api_key: string;\n  api_domain: string;\n  stt_ws_url: string;\n  tts_api_url: string;\n  tts_ws_url: string;\n  /** Server-provided STT session defaults (empty object when not provided). */\n  stt_defaults: Partial<SttSessionConfig>;\n  /** Server-provided TTS stream defaults (empty object when not provided). */\n  tts_defaults: Partial<TtsStreamConfig>;\n\n  /**\n   * @deprecated Use `stt_defaults` instead. Kept in the resolver output as\n   * an alias for backward compatibility; planned for removal in the next\n   * major version.\n   */\n  session_defaults: Partial<SttSessionConfig>;\n};\n\n/** Root domain used for the default (US) deployment. */\nconst DEFAULT_BASE_DOMAIN = 'soniox.com';\n\n/**\n * Derives the four Soniox service URLs from a base domain.\n * All Soniox deployments follow the same subdomain pattern:\n *   api.{base}  /  stt-rt.{base}  /  tts-rt.{base}\n */\nfunction urlsFromBase(base: string) {\n  return {\n    api_domain: `https://api.${base}`,\n    stt_ws_url: `wss://stt-rt.${base}/transcribe-websocket`,\n    tts_api_url: `https://tts-rt.${base}`,\n    tts_ws_url: `wss://tts-rt.${base}/tts-websocket`,\n  };\n}\n\n/**\n * Resolve a {@link SonioxConnectionConfig} into fully qualified URLs.\n *\n * Resolution priority (highest → lowest) for each URL:\n * 1. Explicit field (`api_domain`, `stt_ws_url`, `tts_api_url`, `tts_ws_url`)\n * 2. Derived from `base_domain`\n * 3. Derived from `region` → `{region}.soniox.com`\n * 4. Default US base domain (`soniox.com`)\n */\nexport function resolveConnectionConfig(config: SonioxConnectionConfig): ResolvedConnectionConfig {\n  const { region, base_domain, api_domain, stt_ws_url, tts_api_url, tts_ws_url } = config;\n\n  const normalizedRegion = region !== undefined && region.toLowerCase() !== 'us' ? region : undefined;\n  const effectiveBase =\n    base_domain ?? (normalizedRegion !== undefined ? `${normalizedRegion}.soniox.com` : DEFAULT_BASE_DOMAIN);\n  const derived = urlsFromBase(effectiveBase);\n\n  const sttDefaults = config.stt_defaults ?? config.session_defaults ?? {};\n\n  return {\n    api_key: config.api_key,\n    api_domain: api_domain ?? derived.api_domain,\n    stt_ws_url: stt_ws_url ?? derived.stt_ws_url,\n    tts_api_url: tts_api_url ?? derived.tts_api_url,\n    tts_ws_url: tts_ws_url ?? derived.tts_ws_url,\n    stt_defaults: sttDefaults,\n    tts_defaults: config.tts_defaults ?? {},\n    session_defaults: sttDefaults,\n  };\n}\n","import type { SegmentGroupKey } from './types/transcriptions.js';\n\ntype SegmentTokensOptions = {\n  group_by?: SegmentGroupKey[] | undefined;\n};\n\ntype SegmentableToken = {\n  speaker?: string | null | undefined;\n  language?: string | null | undefined;\n};\n\nconst DEFAULT_GROUP_BY: SegmentGroupKey[] = ['speaker', 'language'];\n\nexport function segmentTokens<TToken extends SegmentableToken, TSegment>(\n  tokens: TToken[],\n  options: SegmentTokensOptions | undefined,\n  buildSegment: (tokens: TToken[], speaker: string | null | undefined, language: string | null | undefined) => TSegment\n): TSegment[] {\n  if (tokens.length === 0) {\n    return [];\n  }\n\n  const groupBy = options?.group_by ?? DEFAULT_GROUP_BY;\n  const groupBySpeaker = groupBy.includes('speaker');\n  const groupByLanguage = groupBy.includes('language');\n\n  const segments: TSegment[] = [];\n  let currentTokens: TToken[] = [];\n  let currentSpeaker: string | null | undefined;\n  let currentLanguage: string | null | undefined;\n\n  for (const token of tokens) {\n    const speakerChanged = groupBySpeaker && token.speaker !== currentSpeaker;\n    const languageChanged = groupByLanguage && token.language !== currentLanguage;\n\n    if (currentTokens.length > 0 && (speakerChanged || languageChanged)) {\n      segments.push(buildSegment(currentTokens, currentSpeaker, currentLanguage));\n      currentTokens = [];\n    }\n\n    currentTokens.push(token);\n    currentSpeaker = token.speaker;\n    currentLanguage = token.language;\n  }\n\n  if (currentTokens.length > 0) {\n    segments.push(buildSegment(currentTokens, currentSpeaker, currentLanguage));\n  }\n\n  return segments;\n}\n","/**\n * Generic async event queue that supports iteration with proper error propagation.\n *\n * This utility enables `for await...of` consumption of events while properly\n * surfacing errors to consumers instead of silently ending iteration.\n */\nexport class AsyncEventQueue<T> implements AsyncIterable<T> {\n  private queue: T[] = [];\n  private waiters: Array<{\n    resolve: (result: IteratorResult<T>) => void;\n    reject: (error: Error) => void;\n  }> = [];\n  private done = false;\n  private error: Error | null = null;\n\n  /**\n   * Push an event to the queue.\n   * If there are waiting consumers, delivers immediately.\n   */\n  push(event: T): void {\n    if (this.done) {\n      return;\n    }\n\n    const waiter = this.waiters.shift();\n    if (waiter) {\n      waiter.resolve({ value: event, done: false });\n    } else {\n      this.queue.push(event);\n    }\n  }\n\n  /**\n   * End the queue normally.\n   * Waiting consumers will receive `{ done: true }`.\n   */\n  end(): void {\n    if (this.done) return;\n\n    this.done = true;\n    this.flushWaiters();\n  }\n\n  /**\n   * End the queue with an error.\n   * Waiting consumers will have their promises rejected.\n   * Future `next()` calls will also reject with this error.\n   * Any queued events are discarded.\n   */\n  abort(error: Error): void {\n    if (this.done) return;\n\n    this.done = true;\n    this.error = error;\n    this.queue = [];\n    this.flushWaiters();\n  }\n\n  /**\n   * Whether the queue has ended (normally or with error).\n   */\n  get isDone(): boolean {\n    return this.done;\n  }\n\n  /**\n   * Drop buffered events without ending the queue.\n   *\n   * Intended for owners that know their consumer has gone away (e.g. an\n   * async-iterator consumer broke out of its `for await` loop). The queue\n   * remains active and accepts future pushes. Callers must ensure no other\n   * iterator is concurrently consuming this queue, since this also drops\n   * events those consumers would have observed.\n   */\n  clear(): void {\n    this.queue = [];\n  }\n\n  /**\n   * Async iterator implementation.\n   *\n   * The returned iterator implements `return()` so consumers that exit\n   * `for await` early (via `break`, `throw`, or an outer `return`) cleanly\n   * release the iteration without further work. The queue itself is left\n   * in place — call {@link clear} or {@link end}/{@link abort} if buffered\n   * events should also be dropped.\n   */\n  [Symbol.asyncIterator](): AsyncIterator<T> {\n    return {\n      next: () => this.next(),\n      return: (value?: T) => Promise.resolve({ value: value as T, done: true }),\n    };\n  }\n\n  /**\n   * Get the next event from the queue.\n   */\n  private next(): Promise<IteratorResult<T>> {\n    const error = this.error;\n    if (error) {\n      return Promise.reject(error);\n    }\n\n    // If there are queued events, return immediately\n    const event = this.queue.shift();\n    if (event !== undefined) {\n      return Promise.resolve({ value: event, done: false });\n    }\n\n    // If done, return done or reject with error\n    if (this.done) {\n      return Promise.resolve({ value: undefined as never, done: true });\n    }\n\n    // Wait for next event\n    return new Promise((resolve, reject) => {\n      this.waiters.push({ resolve, reject });\n    });\n  }\n\n  /**\n   * Flush all waiting consumers when queue ends.\n   */\n  private flushWaiters(): void {\n    for (const { resolve, reject } of this.waiters) {\n      if (this.error) {\n        reject(this.error);\n      } else {\n        resolve({ value: undefined as never, done: true });\n      }\n    }\n    this.waiters = [];\n  }\n}\n","/**\n * A minimal, runtime-agnostic typed event emitter.\n * Does not depend on Node.js EventEmitter.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class TypedEmitter<Events extends Record<string, (...args: any[]) => void>> {\n  private listeners = new Map<keyof Events, Set<Events[keyof Events]>>();\n  private readonly errorEvent = 'error' as keyof Events;\n\n  /**\n   * Register an event handler.\n   */\n  on<E extends keyof Events>(event: E, handler: Events[E]): this {\n    let handlers = this.listeners.get(event);\n    if (!handlers) {\n      handlers = new Set();\n      this.listeners.set(event, handlers);\n    }\n    handlers.add(handler);\n    return this;\n  }\n\n  /**\n   * Register a one-time event handler.\n   */\n  once<E extends keyof Events>(event: E, handler: Events[E]): this {\n    const wrapper = ((...args: Parameters<Events[E]>) => {\n      this.off(event, wrapper);\n      (handler as (...args: Parameters<Events[E]>) => void)(...args);\n    }) as Events[E];\n    return this.on(event, wrapper);\n  }\n\n  /**\n   * Remove an event handler.\n   */\n  off<E extends keyof Events>(event: E, handler: Events[E]): this {\n    const handlers = this.listeners.get(event);\n    if (handlers) {\n      handlers.delete(handler);\n      if (handlers.size === 0) {\n        this.listeners.delete(event);\n      }\n    }\n    return this;\n  }\n\n  /**\n   * Emit an event to all registered handlers.\n   * Handler errors do not prevent other handlers from running.\n   * Errors are reported to an `error` event if present, otherwise rethrown async.\n   */\n  emit<E extends keyof Events>(event: E, ...args: Parameters<Events[E]>): void {\n    const handlers = this.listeners.get(event);\n    if (handlers) {\n      for (const handler of [...handlers]) {\n        try {\n          (handler as (...args: Parameters<Events[E]>) => void)(...args);\n        } catch (error) {\n          if (event === this.errorEvent) {\n            this.scheduleThrow(this.normalizeError(error));\n          } else {\n            this.reportListenerError(error);\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Remove all event handlers.\n   */\n  removeAllListeners(event?: keyof Events): void {\n    if (event !== undefined) {\n      this.listeners.delete(event);\n    } else {\n      this.listeners.clear();\n    }\n  }\n\n  private reportListenerError(error: unknown): void {\n    const normalizedError = this.normalizeError(error);\n    const handlers = this.listeners.get(this.errorEvent);\n    if (!handlers || handlers.size === 0) {\n      this.scheduleThrow(normalizedError);\n      return;\n    }\n\n    for (const handler of [...handlers]) {\n      try {\n        (handler as (error: Error) => void)(normalizedError);\n      } catch (handlerError) {\n        this.scheduleThrow(this.normalizeError(handlerError));\n      }\n    }\n  }\n\n  private normalizeError(error: unknown): Error {\n    if (error instanceof Error) {\n      return error;\n    }\n    return new Error(String(error));\n  }\n\n  private scheduleThrow(error: Error): void {\n    setTimeout(() => {\n      throw error;\n    }, 0);\n  }\n}\n","/**\n * Real-time (WebSocket) API error classes for the Soniox SDK\n * All real-time errors extend SonioxError\n */\n\nimport { SonioxError } from '../errors.js';\nimport type { RealtimeErrorCode } from '../types/errors.js';\n\n/**\n * Base error class for all real-time (WebSocket) SDK errors\n */\nexport class RealtimeError extends SonioxError {\n  /** Real-time error code */\n  declare readonly code: RealtimeErrorCode;\n\n  /**\n   * Original response payload for debugging.\n   * Contains the raw WebSocket message that caused the error.\n   */\n  readonly raw: unknown;\n\n  constructor(message: string, code: RealtimeErrorCode = 'realtime_error', statusCode?: number, raw?: unknown) {\n    super(message, code, statusCode);\n    this.name = 'RealtimeError';\n    this.raw = raw;\n  }\n\n  /**\n   * Creates a human-readable string representation\n   */\n  override toString(): string {\n    const parts = [`${this.name} [${this.code}]: ${this.message}`];\n    if (this.statusCode !== undefined) {\n      parts.push(`  Status: ${this.statusCode}`);\n    }\n    return parts.join('\\n');\n  }\n\n  /**\n   * Converts to a plain object for logging/serialization\n   */\n  override toJSON(): Record<string, unknown> {\n    return {\n      name: this.name,\n      code: this.code,\n      message: this.message,\n      ...(this.statusCode !== undefined && { statusCode: this.statusCode }),\n      ...(this.raw !== undefined && { raw: this.raw }),\n    };\n  }\n}\n\n/**\n * Authentication error (401).\n * Thrown when the API key is invalid or expired.\n */\nexport class AuthError extends RealtimeError {\n  constructor(message: string, statusCode?: number, raw?: unknown) {\n    super(message, 'auth_error', statusCode, raw);\n    this.name = 'AuthError';\n  }\n}\n\n/**\n * Bad request error (400).\n * Thrown for invalid configuration or parameters.\n */\nexport class BadRequestError extends RealtimeError {\n  constructor(message: string, statusCode?: number, raw?: unknown) {\n    super(message, 'bad_request', statusCode, raw);\n    this.name = 'BadRequestError';\n  }\n}\n\n/**\n * Quota error (402, 429).\n * Thrown when rate limits are exceeded or quota is exhausted.\n */\nexport class QuotaError extends RealtimeError {\n  constructor(message: string, statusCode?: number, raw?: unknown) {\n    super(message, 'quota_exceeded', statusCode, raw);\n    this.name = 'QuotaError';\n  }\n}\n\n/**\n * Connection error.\n * Thrown for WebSocket connection failures and transport errors.\n */\nexport class ConnectionError extends RealtimeError {\n  constructor(message: string, raw?: unknown) {\n    super(message, 'connection_error', undefined, raw);\n    this.name = 'ConnectionError';\n  }\n}\n\n/**\n * Network error.\n * Thrown for server-side network issues (408, 500, 503).\n */\nexport class NetworkError extends RealtimeError {\n  constructor(message: string, statusCode?: number, raw?: unknown) {\n    super(message, 'network_error', statusCode, raw);\n    this.name = 'NetworkError';\n  }\n}\n\n/**\n * Abort error.\n * Thrown when an operation is cancelled via AbortSignal.\n */\nexport class AbortError extends RealtimeError {\n  constructor(message = 'Operation aborted') {\n    super(message, 'aborted');\n    this.name = 'AbortError';\n  }\n}\n\n/**\n * State error.\n * Thrown when an operation is attempted in an invalid state.\n */\nexport class StateError extends RealtimeError {\n  constructor(message: string) {\n    super(message, 'state_error');\n    this.name = 'StateError';\n  }\n}\n\n/**\n * Whether an error is safe to retry via automatic reconnection.\n *\n * Retriable: {@link ConnectionError}, {@link NetworkError} (transient transport/server issues).\n * Non-retriable: {@link AuthError}, {@link BadRequestError}, {@link QuotaError},\n * {@link AbortError}, {@link StateError} (permanent or user-initiated).\n */\nexport function isRetriableError(error: unknown): boolean {\n  return error instanceof ConnectionError || error instanceof NetworkError;\n}\n\n/**\n * Map a Soniox error response to a typed error class.\n *\n * @param response - Error response from the WebSocket\n * @returns Appropriate error subclass\n */\nexport function mapErrorResponse(response: { error_code?: number; error_message?: string }): RealtimeError {\n  const { error_code, error_message } = response;\n  const message = error_message ?? 'Unknown error';\n\n  switch (error_code) {\n    case 401:\n      return new AuthError(message, error_code, response);\n\n    case 400:\n      return new BadRequestError(message, error_code, response);\n\n    case 402:\n    case 429:\n      return new QuotaError(message, error_code, response);\n\n    case 408:\n    case 500:\n    case 503:\n      return new NetworkError(message, error_code, response);\n\n    default:\n      return new RealtimeError(message, 'realtime_error', error_code, response);\n  }\n}\n","import type {\n  AudioData,\n  RealtimeEvent,\n  RealtimeResult,\n  RealtimeToken,\n  SendStreamOptions,\n  StateChangeReason,\n  SttSessionConfig,\n  SttSessionEvents,\n  SttSessionOptions,\n  SttSessionState,\n} from '../types/realtime.js';\n\nimport { AsyncEventQueue } from './async-queue.js';\nimport { TypedEmitter } from './emitter.js';\nimport { AbortError, ConnectionError, StateError, mapErrorResponse } from './errors.js';\n\n// Default keepalive interval\nconst DEFAULT_KEEPALIVE_INTERVAL_MS = 5000;\n\n// Minimum allowed keepalive interval to prevent network flooding\nconst MIN_KEEPALIVE_INTERVAL_MS = 1000;\n\n// Default timeout for WebSocket connection establishment\nconst DEFAULT_CONNECT_TIMEOUT_MS = 20000;\n\n/**\n * Convert audio data to Uint8Array\n * Handles Uint8Array and ArrayBuffer\n */\nfunction toUint8Array(data: AudioData): Uint8Array {\n  if (data instanceof ArrayBuffer) {\n    return new Uint8Array(data);\n  }\n\n  return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n}\n\n/**\n * Build the configuration message to send after WebSocket connection\n */\nfunction buildConfigMessage(config: SttSessionConfig, apiKey: string): Record<string, unknown> {\n  return {\n    api_key: apiKey,\n    model: config.model,\n    audio_format: config.audio_format ?? 'auto',\n    sample_rate: config.sample_rate,\n    num_channels: config.num_channels,\n    language_hints: config.language_hints,\n    language_hints_strict: config.language_hints_strict,\n    enable_speaker_diarization: config.enable_speaker_diarization,\n    enable_language_identification: config.enable_language_identification,\n    enable_endpoint_detection: config.enable_endpoint_detection,\n    client_reference_id: config.client_reference_id,\n    max_endpoint_delay_ms: config.max_endpoint_delay_ms,\n    endpoint_sensitivity: config.endpoint_sensitivity,\n    context: config.context,\n    translation: config.translation,\n  };\n}\n\n/**\n * Parse a result message from the WebSocket\n */\nfunction parseResultMessage(data: string): RealtimeResult & { raw: unknown } {\n  const raw = JSON.parse(data) as Record<string, unknown>;\n\n  // Check for error response\n  if ('error_code' in raw || 'error_message' in raw) {\n    throw mapErrorResponse(raw as { error_code?: number; error_message?: string });\n  }\n\n  // Parse tokens\n  const rawTokens = (raw.tokens as Array<Record<string, unknown>>) ?? [];\n  const tokens: RealtimeToken[] = rawTokens.map((t) => ({\n    text: typeof t.text === 'string' ? t.text : '',\n    start_ms: typeof t.start_ms === 'number' ? t.start_ms : undefined,\n    end_ms: typeof t.end_ms === 'number' ? t.end_ms : undefined,\n    confidence: typeof t.confidence === 'number' ? t.confidence : 0,\n    is_final: Boolean(t.is_final),\n    speaker: typeof t.speaker === 'string' ? t.speaker : undefined,\n    language: typeof t.language === 'string' ? t.language : undefined,\n    translation_status:\n      t.translation_status === 'none' || t.translation_status === 'original' || t.translation_status === 'translation'\n        ? t.translation_status\n        : undefined,\n    source_language: typeof t.source_language === 'string' ? t.source_language : undefined,\n  }));\n\n  return {\n    tokens,\n    final_audio_proc_ms: typeof raw.final_audio_proc_ms === 'number' ? raw.final_audio_proc_ms : 0,\n    total_audio_proc_ms: typeof raw.total_audio_proc_ms === 'number' ? raw.total_audio_proc_ms : 0,\n    finished: raw.finished === true,\n    raw,\n  };\n}\n\n/**\n * Check if a token is a special control token\n */\nfunction isSpecialToken(text: string): boolean {\n  return text === '<end>' || text === '<fin>';\n}\n\n/**\n * Filter out special control tokens from tokens array\n */\nfunction filterSpecialTokens(tokens: RealtimeToken[]): RealtimeToken[] {\n  return tokens.filter((t) => !isSpecialToken(t.text));\n}\n\n/**\n * Real-time Speech-to-Text session\n *\n * Provides WebSocket-based streaming transcription with support for:\n * - Event-based and async iterator consumption\n * - Pause/resume with automatic keepalive while paused\n * - AbortSignal cancellation\n *\n * @example\n * ```typescript\n * const session = new RealtimeSttSession(apiKey, wsUrl, { model: 'stt-rt-v5' });\n *\n * session.on('result', (result) => {\n *   console.log(result.tokens.map(t => t.text).join(''));\n * });\n *\n * await session.connect();\n * session.sendAudio(audioChunk);\n * await session.finish();\n * ```\n */\nexport class RealtimeSttSession implements AsyncIterable<RealtimeEvent> {\n  private readonly emitter = new TypedEmitter<SttSessionEvents>();\n  private readonly eventQueue = new AsyncEventQueue<RealtimeEvent>();\n  private iteratorAttached = false;\n\n  private readonly apiKey: string;\n  private readonly wsBaseUrl: string;\n  private readonly config: SttSessionConfig;\n  private readonly keepaliveIntervalMs: number;\n  private readonly connectTimeoutMs: number;\n  private readonly signal: AbortSignal | undefined;\n\n  private ws: WebSocket | null = null;\n  private _state: SttSessionState = 'idle';\n  private _paused = false;\n  private _pauseWarned = false;\n  private keepaliveInterval: ReturnType<typeof setInterval> | null = null;\n\n  // Finish promise handling\n  private finishResolver: (() => void) | null = null;\n  private finishRejecter: ((error: Error) => void) | null = null;\n\n  // Abort handler reference for cleanup\n  private abortHandler: (() => void) | null = null;\n\n  constructor(apiKey: string, wsBaseUrl: string, config: SttSessionConfig, options?: SttSessionOptions) {\n    this.apiKey = apiKey;\n    this.wsBaseUrl = wsBaseUrl;\n    this.config = config;\n    const keepaliveMs = options?.keepalive_interval_ms ?? DEFAULT_KEEPALIVE_INTERVAL_MS;\n    this.keepaliveIntervalMs =\n      Number.isFinite(keepaliveMs) && keepaliveMs > 0\n        ? Math.max(keepaliveMs, MIN_KEEPALIVE_INTERVAL_MS)\n        : DEFAULT_KEEPALIVE_INTERVAL_MS;\n    const connectMs = options?.connect_timeout_ms ?? DEFAULT_CONNECT_TIMEOUT_MS;\n    this.connectTimeoutMs = Number.isFinite(connectMs) && connectMs > 0 ? connectMs : DEFAULT_CONNECT_TIMEOUT_MS;\n    this.signal = options?.signal;\n\n    // Set up abort signal handler (store reference for cleanup)\n    if (this.signal) {\n      this.abortHandler = () => this.handleAbort();\n      this.signal.addEventListener('abort', this.abortHandler);\n    }\n  }\n\n  /**\n   * Current session state.\n   */\n  get state(): SttSessionState {\n    return this._state;\n  }\n\n  /**\n   * Whether the session is currently paused.\n   */\n  get paused(): boolean {\n    return this._paused;\n  }\n\n  /**\n   * Connect to the Soniox WebSocket API.\n   *\n   * @throws {@link AbortError} If aborted\n   * @throws {@link ConnectionError} If connection fails\n   * @throws {@link StateError} If already connected\n   */\n  async connect(): Promise<void> {\n    if (this._state !== 'idle') {\n      throw new StateError(`Cannot connect: session is in \"${this._state}\" state`);\n    }\n\n    this.checkAborted();\n    this.setState('connecting', 'user_action');\n\n    let connectTimer: ReturnType<typeof setTimeout> | undefined;\n    try {\n      await Promise.race([\n        this.createWebSocket().then((v) => {\n          clearTimeout(connectTimer);\n          return v;\n        }),\n        new Promise<never>((_resolve, reject) => {\n          connectTimer = setTimeout(() => {\n            if (this.ws) {\n              this.ws.close();\n            }\n            reject(new ConnectionError('Connection timed out'));\n          }, this.connectTimeoutMs);\n        }),\n      ]);\n      this.setState('connected', 'connected');\n      this.emitter.emit('connected');\n      this.updateKeepalive();\n    } catch (error) {\n      clearTimeout(connectTimer);\n      if (!this.isTerminalState(this._state)) {\n        const err = error instanceof Error ? error : new ConnectionError('Connection failed', error);\n        this.cleanup('error', err, 'error');\n      }\n      throw error;\n    }\n  }\n\n  /**\n   * Send audio data to the server\n   *\n   * @param data - Audio data as Uint8Array or ArrayBuffer\n   * @throws {@link AbortError} If aborted\n   * @throws {@link StateError} If not connected\n   */\n  sendAudio(data: AudioData): void {\n    this.checkAborted();\n\n    if (this._state !== 'connected') {\n      throw new StateError(`Cannot send audio: session is in \"${this._state}\" state`);\n    }\n\n    // If paused, just drop the audio silently\n    if (this._paused) {\n      return;\n    }\n\n    const chunk = toUint8Array(data);\n    this.sendMessage(chunk, true);\n  }\n\n  /**\n   * Stream audio data from an async iterable source.\n   *\n   * @param stream - Async iterable yielding audio chunks\n   * @param options - Optional pacing and auto-finish settings\n   * @throws {@link AbortError} If aborted during streaming\n   * @throws {@link StateError} If not connected\n   */\n  async sendStream(stream: AsyncIterable<AudioData>, options?: SendStreamOptions): Promise<void> {\n    for await (const chunk of stream) {\n      this.sendAudio(chunk);\n      if (options?.pace_ms) {\n        await new Promise((resolve) => setTimeout(resolve, options.pace_ms));\n      }\n    }\n    if (options?.finish) {\n      await this.finish();\n    }\n  }\n\n  /**\n   * Pause audio transmission and starts automatic keepalive messages\n   */\n  pause(): void {\n    if (this._paused) return;\n\n    this._paused = true;\n    this.finalize();\n\n    if (!this._pauseWarned && typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {\n      this._pauseWarned = true;\n      // eslint-disable-next-line no-console\n      console.warn('[Soniox] You are billed for the full stream duration even when session is paused.');\n    }\n\n    this.updateKeepalive();\n  }\n\n  /**\n   * Resume audio transmission\n   */\n  resume(): void {\n    if (!this._paused) return;\n\n    this._paused = false;\n    this.updateKeepalive();\n  }\n\n  /**\n   * Requests the server to finalize current transcription\n   */\n  finalize(options?: { trailing_silence_ms?: number }): void {\n    if (this._state !== 'connected' && this._state !== 'finishing') {\n      return;\n    }\n\n    const message: Record<string, unknown> = { type: 'finalize' };\n    if (options?.trailing_silence_ms !== undefined) {\n      message.trailing_silence_ms = options.trailing_silence_ms;\n    }\n    this.sendMessage(JSON.stringify(message), false);\n  }\n\n  /**\n   * Send a keepalive message\n   */\n  keepAlive(): void {\n    if (this._state !== 'connected' && this._state !== 'finishing') {\n      return;\n    }\n\n    this.sendMessage(JSON.stringify({ type: 'keepalive' }), false);\n  }\n\n  /**\n   * Gracefully finish the session\n   */\n  async finish(): Promise<void> {\n    this.checkAborted();\n\n    if (this._state !== 'connected') {\n      throw new StateError(`Cannot finish: session is in \"${this._state}\" state`);\n    }\n\n    // Stop pause mode\n    if (this._paused) {\n      this.resume();\n    }\n\n    this.setState('finishing', 'user_action');\n    this.updateKeepalive();\n\n    // Wait for finished response\n    const finishPromise = new Promise<void>((resolve, reject) => {\n      this.finishResolver = resolve;\n      this.finishRejecter = reject;\n    });\n\n    // Send empty string to signal end of audio\n    this.sendMessage('', false);\n\n    return finishPromise;\n  }\n\n  /**\n   * Close (cancel) the session immediately without waiting\n   */\n  close(): void {\n    if (this.isTerminalState(this._state)) {\n      return;\n    }\n\n    this.emitter.emit('disconnected', 'client_closed');\n    this.settleFinish(new StateError('Session canceled'));\n    this.cleanup('canceled', undefined, 'user_action');\n  }\n\n  /**\n   * Register an event handler\n   */\n  on<E extends keyof SttSessionEvents>(event: E, handler: SttSessionEvents[E]): this {\n    this.emitter.on(event, handler);\n    return this;\n  }\n\n  /**\n   * Register a one-time event handler\n   */\n  once<E extends keyof SttSessionEvents>(event: E, handler: SttSessionEvents[E]): this {\n    this.emitter.once(event, handler);\n    return this;\n  }\n\n  /**\n   * Remove an event handler\n   */\n  off<E extends keyof SttSessionEvents>(event: E, handler: SttSessionEvents[E]): this {\n    this.emitter.off(event, handler);\n    return this;\n  }\n\n  /**\n   * Async iterator for consuming events.\n   *\n   * The returned iterator's `return()` resets the internal iterator-attach\n   * flag and drops any buffered events, so consumers that exit `for await`\n   * early (via `break` etc.) stop accruing memory while the session keeps\n   * running.\n   */\n  [Symbol.asyncIterator](): AsyncIterator<RealtimeEvent> {\n    this.iteratorAttached = true;\n    const inner = this.eventQueue[Symbol.asyncIterator]();\n    return {\n      next: () => inner.next(),\n      return: (value?: RealtimeEvent) => {\n        this.iteratorAttached = false;\n        this.eventQueue.clear();\n        return inner.return?.(value) ?? Promise.resolve({ value: value as RealtimeEvent, done: true });\n      },\n    };\n  }\n\n  /**\n   * @internal Debug-only: forcefully close the underlying WebSocket to\n   * simulate an unexpected network disconnection.\n   */\n  __debugForceDisconnect(): void {\n    this.ws?.close(4999, 'debug: simulated disconnect');\n  }\n\n  /**\n   * Push an event to the async iterator queue only when a consumer has\n   * attached via `[Symbol.asyncIterator]()`. Listener-only consumers\n   * (the documented `.on()` pattern) never drain the queue, so pushing\n   * unconditionally would leak buffered events on long-running sessions.\n   */\n  private enqueueIfIterating(event: RealtimeEvent): void {\n    if (this.iteratorAttached) {\n      this.eventQueue.push(event);\n    }\n  }\n\n  private async createWebSocket(): Promise<void> {\n    return new Promise<void>((resolve, reject) => {\n      try {\n        const ws = new WebSocket(this.wsBaseUrl);\n        this.ws = ws;\n        ws.binaryType = 'arraybuffer';\n\n        const cleanup = () => {\n          ws.removeEventListener('open', onOpen);\n          ws.removeEventListener('error', onError);\n        };\n\n        const onOpen = () => {\n          cleanup();\n\n          // Send config message\n          const configMessage = buildConfigMessage(this.config, this.apiKey);\n          ws.send(JSON.stringify(configMessage));\n\n          // Set up message handlers\n          ws.addEventListener('message', this.handleMessage.bind(this));\n          ws.addEventListener('close', this.handleClose.bind(this));\n          ws.addEventListener('error', this.handleError.bind(this));\n\n          resolve();\n        };\n\n        const onError = (event: Event) => {\n          cleanup();\n          reject(new ConnectionError('WebSocket connection failed', event));\n        };\n\n        ws.addEventListener('open', onOpen);\n        ws.addEventListener('error', onError);\n\n        // Handle abort during connection\n        if (this.signal) {\n          this.signal.addEventListener(\n            'abort',\n            () => {\n              cleanup();\n              ws.close();\n              reject(new AbortError());\n            },\n            { once: true }\n          );\n        }\n      } catch (error) {\n        reject(new ConnectionError('Failed to create WebSocket', error));\n      }\n    });\n  }\n\n  private handleMessage(event: MessageEvent): void {\n    // Only handle string messages\n    if (typeof event.data !== 'string') {\n      return;\n    }\n\n    const data = event.data;\n\n    try {\n      const result = parseResultMessage(data);\n\n      // Check for special tokens\n      const hasEndpoint = result.tokens.some((t) => t.text === '<end>');\n      const hasFinalized = result.tokens.some((t) => t.text === '<fin>');\n\n      // Filter special tokens for user-facing events\n      const userTokens = filterSpecialTokens(result.tokens);\n\n      // Emit individual tokens\n      for (const token of userTokens) {\n        this.emitter.emit('token', token);\n      }\n\n      // Emit result with filtered tokens\n      const filteredResult: RealtimeResult = {\n        ...result,\n        tokens: userTokens,\n      };\n      this.emitter.emit('result', filteredResult);\n      this.enqueueIfIterating({ kind: 'result', data: filteredResult });\n\n      if (hasEndpoint) {\n        this.emitter.emit('endpoint');\n        this.enqueueIfIterating({ kind: 'endpoint' });\n      }\n\n      if (hasFinalized) {\n        this.emitter.emit('finalized');\n        this.enqueueIfIterating({ kind: 'finalized' });\n      }\n\n      // Check for finished\n      if (result.finished) {\n        this.emitter.emit('finished');\n        this.enqueueIfIterating({ kind: 'finished' });\n        this.settleFinish();\n        this.cleanup('finished', undefined, 'finished');\n      }\n    } catch (error) {\n      const err = error as Error;\n      this.emitter.emit('error', err);\n      this.settleFinish(err);\n      this.cleanup('error', err, 'error');\n    }\n  }\n\n  private handleClose(event: CloseEvent): void {\n    if (this.isTerminalState(this._state)) {\n      return;\n    }\n\n    this.emitter.emit('disconnected', event.reason || undefined);\n\n    if (this._state === 'finishing') {\n      const error = new ConnectionError('WebSocket closed before finished response', event);\n      this.emitter.emit('error', error);\n      this.settleFinish(error);\n      this.cleanup('error', error, 'connection_lost');\n      return;\n    }\n\n    // Unexpected close while connected — surface as error so consumers can react\n    const error = new ConnectionError('WebSocket closed unexpectedly', event);\n    this.emitter.emit('error', error);\n    this.cleanup('closed', error, 'connection_lost');\n  }\n\n  private handleError(event: Event): void {\n    const error = new ConnectionError('WebSocket error', event);\n    this.emitter.emit('error', error);\n    this.settleFinish(error);\n    this.cleanup('error', error, 'error');\n  }\n\n  private handleAbort(): void {\n    const error = new AbortError();\n    this.emitter.emit('error', error);\n    this.settleFinish(error);\n    this.cleanup('canceled', error, 'user_action');\n  }\n\n  private setState(newState: SttSessionState, reason?: StateChangeReason): void {\n    if (this._state === newState) {\n      return;\n    }\n    const oldState = this._state;\n    this._state = newState;\n    const update: { old_state: SttSessionState; new_state: SttSessionState; reason?: StateChangeReason } = {\n      old_state: oldState,\n      new_state: newState,\n    };\n    if (reason !== undefined) {\n      update.reason = reason;\n    }\n    this.emitter.emit('state_change', update);\n  }\n\n  private cleanup(\n    finalState: 'closed' | 'error' | 'finished' | 'canceled',\n    error?: Error,\n    reason?: StateChangeReason\n  ): void {\n    this.setState(finalState, reason);\n    this.stopKeepalive();\n\n    if (this.signal && this.abortHandler) {\n      this.signal.removeEventListener('abort', this.abortHandler);\n      this.abortHandler = null;\n    }\n\n    if (this.ws) {\n      this.ws.close();\n      this.ws = null;\n    }\n\n    // End the event queue\n    if (error) {\n      this.eventQueue.abort(error);\n    } else {\n      this.eventQueue.end();\n    }\n\n    this.emitter.removeAllListeners();\n  }\n\n  private isTerminalState(state: SttSessionState): boolean {\n    return state === 'closed' || state === 'error' || state === 'finished' || state === 'canceled';\n  }\n\n  private checkAborted(): void {\n    if (this.signal?.aborted) {\n      throw new AbortError();\n    }\n  }\n\n  private settleFinish(error?: Error): void {\n    if (!this.finishResolver && !this.finishRejecter) {\n      return;\n    }\n\n    const resolve = this.finishResolver;\n    const reject = this.finishRejecter;\n    this.finishResolver = null;\n    this.finishRejecter = null;\n\n    if (error) {\n      reject?.(error);\n    } else {\n      resolve?.();\n    }\n  }\n\n  private sendMessage(data: string | Uint8Array, shouldThrow: boolean): void {\n    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n      const error = new ConnectionError('WebSocket is not open');\n      this.emitter.emit('error', error);\n      this.settleFinish(error);\n      this.cleanup('error', error, 'error');\n      if (shouldThrow) {\n        throw error;\n      }\n      return;\n    }\n\n    try {\n      this.ws.send(data);\n    } catch (err) {\n      const error = new ConnectionError('WebSocket send failed', err);\n      this.emitter.emit('error', error);\n      this.settleFinish(error);\n      this.cleanup('error', error, 'error');\n      if (shouldThrow) {\n        throw error;\n      }\n    }\n  }\n\n  private startKeepalive(): void {\n    if (this.keepaliveInterval) return;\n\n    this.keepaliveInterval = setInterval(() => {\n      this.keepAlive();\n    }, this.keepaliveIntervalMs);\n  }\n\n  private stopKeepalive(): void {\n    if (this.keepaliveInterval) {\n      clearInterval(this.keepaliveInterval);\n      this.keepaliveInterval = null;\n    }\n  }\n\n  private updateKeepalive(): void {\n    const isActiveState = this._state === 'connected' || this._state === 'finishing';\n\n    if (isActiveState && this._paused) {\n      this.startKeepalive();\n    } else {\n      this.stopKeepalive();\n    }\n  }\n}\n","import type {\n  TtsConnectionEvents,\n  TtsConnectionOptions,\n  TtsEvent,\n  TtsStreamConfig,\n  TtsStreamEvents,\n  TtsStreamInput,\n  TtsStreamState,\n} from '../types/tts.js';\n\nimport { AsyncEventQueue } from './async-queue.js';\nimport { TypedEmitter } from './emitter.js';\nimport { ConnectionError, StateError, mapErrorResponse } from './errors.js';\n\nconst MAX_STREAMS_PER_CONNECTION = 5;\nconst DEFAULT_KEEPALIVE_INTERVAL_MS = 5000;\nconst MIN_KEEPALIVE_INTERVAL_MS = 1000;\nconst DEFAULT_CONNECT_TIMEOUT_MS = 20000;\n\nfunction generateStreamId(): string {\n  return globalThis.crypto.randomUUID();\n}\n\nfunction decodeBase64ToUint8Array(base64: string): Uint8Array {\n  const binaryString = atob(base64);\n  const bytes = new Uint8Array(binaryString.length);\n  for (let i = 0; i < binaryString.length; i++) {\n    bytes[i] = binaryString.charCodeAt(i);\n  }\n  return bytes;\n}\n\n/**\n * Merge a partial TTS stream input with defaults, validate required fields,\n * and return a fully resolved config ready for the WebSocket.\n */\nfunction resolveStreamConfig(input: TtsStreamInput, defaults: Partial<TtsStreamConfig>): TtsStreamConfig {\n  const merged = { ...defaults, ...input };\n  const model = merged.model;\n  const language = merged.language;\n  const voice = merged.voice;\n  const audio_format = merged.audio_format;\n\n  const missing: string[] = [];\n  if (!model) missing.push('model');\n  if (!language) missing.push('language');\n  if (!voice) missing.push('voice');\n  if (!audio_format) missing.push('audio_format');\n\n  if (missing.length > 0) {\n    throw new Error(\n      `Missing required TTS stream fields: ${missing.join(', ')}. ` +\n        'Provide them directly or via tts_defaults in your connection config.'\n    );\n  }\n\n  return {\n    model: model!,\n    language: language!,\n    voice: voice!,\n    audio_format: audio_format!,\n    ...(merged.sample_rate !== undefined && { sample_rate: merged.sample_rate }),\n    ...(merged.bitrate !== undefined && { bitrate: merged.bitrate }),\n    stream_id: merged.stream_id ?? generateStreamId(),\n  };\n}\n\n// =============================================================================\n// RealtimeTtsStream\n// =============================================================================\n\n/**\n * Handle for one TTS stream on a WebSocket connection.\n *\n * Emits typed events and supports async iteration over decoded audio chunks.\n *\n * @example Event-based\n * ```typescript\n * stream.on('audio', (chunk) => process(chunk));\n * stream.on('terminated', () => console.log('done'));\n * stream.sendText(\"Hello world\");\n * stream.finish();\n * ```\n *\n * @example Async iteration\n * ```typescript\n * stream.sendText(\"Hello world\");\n * stream.finish();\n * for await (const chunk of stream) {\n *   process(chunk);\n * }\n * ```\n */\nexport class RealtimeTtsStream extends TypedEmitter<TtsStreamEvents> implements AsyncIterable<Uint8Array> {\n  readonly streamId: string;\n\n  private _state: TtsStreamState = 'active';\n  private readonly audioQueue = new AsyncEventQueue<Uint8Array>();\n  private iteratorAttached = false;\n  private readonly connection: RealtimeTtsConnection;\n  private readonly ownsConnection: boolean;\n\n  /** @internal */\n  constructor(streamId: string, connection: RealtimeTtsConnection, ownsConnection: boolean) {\n    super();\n    this.streamId = streamId;\n    this.connection = connection;\n    this.ownsConnection = ownsConnection;\n  }\n\n  /** Current stream lifecycle state. */\n  get state(): TtsStreamState {\n    return this._state;\n  }\n\n  /**\n   * Send one text chunk to the TTS stream.\n   *\n   * @param text - Text to synthesize\n   * @param options.end - If true, signals this is the final text chunk\n   */\n  sendText(text: string, options?: { end?: boolean }): void {\n    if (this._state !== 'active') {\n      throw new StateError(`Cannot send text in state '${this._state}'`);\n    }\n    const payload = {\n      text,\n      text_end: options?.end ?? false,\n      stream_id: this.streamId,\n    };\n    this.connection._sendJson(payload);\n    if (options?.end) {\n      this._state = 'finishing';\n    }\n  }\n\n  /**\n   * Pipe an async iterable of text chunks into the stream.\n   * Automatically calls {@link finish} when the iterable completes.\n   *\n   * Designed for concurrent use: call `sendStream()` and consume audio\n   * via `for await` or events simultaneously.\n   *\n   * @example LLM token piping\n   * ```typescript\n   * stream.sendStream(llmTokenStream);\n   * for await (const audio of stream) { forward(audio); }\n   * ```\n   */\n  async sendStream(source: AsyncIterable<string>): Promise<void> {\n    for await (const chunk of source) {\n      if (this._state !== 'active') break;\n      this.sendText(chunk);\n    }\n    if (this._state === 'active') {\n      this.finish();\n    }\n  }\n\n  /**\n   * Signal that no more text will be sent for this stream.\n   * The server will finish generating audio and send `terminated`.\n   */\n  finish(): void {\n    if (this._state !== 'active') {\n      throw new StateError(`Cannot finish in state '${this._state}'`);\n    }\n    this.sendText('', { end: true });\n  }\n\n  /**\n   * Cancel this stream. The server will stop generating and send `terminated`.\n   */\n  cancel(): void {\n    if (this._state === 'ended' || this._state === 'error') return;\n    const payload = {\n      stream_id: this.streamId,\n      cancel: true,\n    };\n    try {\n      this.connection._sendJson(payload);\n    } catch {\n      // Connection may already be closed\n    }\n  }\n\n  /**\n   * Close this stream. For single-stream usage (created via `tts(input)`),\n   * also closes the underlying WebSocket connection.\n   */\n  close(): void {\n    this._endStream();\n    if (this.ownsConnection) {\n      this.connection.close();\n    }\n  }\n\n  /**\n   * Async iterator that yields decoded audio chunks.\n   *\n   * The returned iterator's `return()` resets the internal iterator-attach\n   * flag and drops any buffered audio, so consumers that exit `for await`\n   * early (via `break` etc.) stop accruing memory while the stream keeps\n   * receiving server audio.\n   */\n  [Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n    this.iteratorAttached = true;\n    const inner = this.audioQueue[Symbol.asyncIterator]();\n    return {\n      next: () => inner.next(),\n      return: (value?: Uint8Array) => {\n        this.iteratorAttached = false;\n        this.audioQueue.clear();\n        return inner.return?.(value) ?? Promise.resolve({ value: value as Uint8Array, done: true });\n      },\n    };\n  }\n\n  /**\n   * Push an audio chunk to the async iterator queue only when a consumer\n   * has attached via `[Symbol.asyncIterator]()`. Listener-only consumers\n   * (the documented `.on('audio', ...)` pattern) never drain the queue,\n   * so pushing unconditionally would leak buffered chunks.\n   */\n  private enqueueIfIterating(chunk: Uint8Array): void {\n    if (this.iteratorAttached) {\n      this.audioQueue.push(chunk);\n    }\n  }\n\n  /** @internal Dispatch a server event to this stream. */\n  _handleEvent(event: TtsEvent): void {\n    if (event.error_code !== undefined) {\n      const errPayload: { error_code: number; error_message?: string } = {\n        error_code: event.error_code,\n      };\n      if (event.error_message !== undefined) {\n        errPayload.error_message = event.error_message;\n      }\n      const error = mapErrorResponse(errPayload);\n      this._state = 'error';\n      this.emit('error', error);\n      this.audioQueue.abort(error);\n      this.connection._deactivateStream(this.streamId);\n      return;\n    }\n\n    if (event.audio !== undefined) {\n      const chunk = decodeBase64ToUint8Array(event.audio);\n      this.emit('audio', chunk);\n      this.enqueueIfIterating(chunk);\n    }\n\n    if (event.audio_end) {\n      this.emit('audioEnd');\n    }\n\n    if (event.terminated) {\n      this._endStream();\n    }\n  }\n\n  /** @internal Force-end this stream (connection closing). */\n  _forceEnd(): void {\n    if (this._state === 'ended' || this._state === 'error') return;\n    this._state = 'ended';\n    this.audioQueue.end();\n  }\n\n  private _endStream(): void {\n    if (this._state === 'ended') return;\n    this._state = 'ended';\n    this.emit('terminated');\n    this.audioQueue.end();\n    this.connection._deactivateStream(this.streamId);\n  }\n}\n\n// =============================================================================\n// RealtimeTtsConnection\n// =============================================================================\n\n/**\n * WebSocket connection for real-time Text-to-Speech.\n *\n * Supports up to 5 concurrent streams multiplexed by `stream_id`.\n * The connection automatically sends keepalive messages while open.\n *\n * @example Multi-stream\n * ```typescript\n * const conn = new RealtimeTtsConnection(apiKey, wsUrl, ttsDefaults);\n * await conn.connect();\n *\n * const s1 = conn.stream({ model, voice, language, audio_format });\n * s1.sendText(\"Hello\");\n * s1.finish();\n * for await (const chunk of s1) { ... }\n *\n * conn.close();\n * ```\n */\nexport class RealtimeTtsConnection extends TypedEmitter<TtsConnectionEvents> {\n  private readonly apiKey: string;\n  private readonly wsUrl: string;\n  private readonly ttsDefaults: Partial<TtsStreamConfig>;\n  private readonly keepaliveIntervalMs: number;\n  private readonly connectTimeoutMs: number;\n\n  private ws: WebSocket | null = null;\n  private connected = false;\n  private connecting = false;\n  private keepaliveTimer: ReturnType<typeof setInterval> | null = null;\n  private readonly activeStreams = new Map<string, RealtimeTtsStream>();\n\n  constructor(\n    apiKey: string,\n    wsUrl: string,\n    ttsDefaults: Partial<TtsStreamConfig> = {},\n    options?: TtsConnectionOptions\n  ) {\n    super();\n    this.apiKey = apiKey;\n    this.wsUrl = wsUrl;\n    this.ttsDefaults = ttsDefaults;\n\n    const keepaliveMs = options?.keepalive_interval_ms ?? DEFAULT_KEEPALIVE_INTERVAL_MS;\n    this.keepaliveIntervalMs =\n      Number.isFinite(keepaliveMs) && keepaliveMs > 0\n        ? Math.max(keepaliveMs, MIN_KEEPALIVE_INTERVAL_MS)\n        : DEFAULT_KEEPALIVE_INTERVAL_MS;\n\n    const connectMs = options?.connect_timeout_ms ?? DEFAULT_CONNECT_TIMEOUT_MS;\n    this.connectTimeoutMs = Number.isFinite(connectMs) && connectMs > 0 ? connectMs : DEFAULT_CONNECT_TIMEOUT_MS;\n  }\n\n  /** Whether the WebSocket is connected. */\n  get isConnected(): boolean {\n    return this.connected;\n  }\n\n  /**\n   * Open the WebSocket connection and start keepalive.\n   * Called automatically by {@link stream} if not yet connected.\n   */\n  async connect(): Promise<void> {\n    if (this.connected) return;\n    if (this.connecting) {\n      throw new StateError('Connection is already being established');\n    }\n\n    this.connecting = true;\n\n    try {\n      await this.createWebSocket();\n      this.connected = true;\n      this.startKeepalive();\n    } finally {\n      this.connecting = false;\n    }\n  }\n\n  /**\n   * Open a new TTS stream on this connection.\n   * Auto-connects if the WebSocket is not yet open.\n   *\n   * @param input - Stream configuration (merged with tts_defaults)\n   * @returns A ready-to-use stream handle\n   */\n  async stream(input: TtsStreamInput = {}): Promise<RealtimeTtsStream> {\n    return this._openStream(input, false);\n  }\n\n  /** @internal Open a stream, optionally marking it as connection-owning. */\n  async _openStream(input: TtsStreamInput, ownsConnection: boolean): Promise<RealtimeTtsStream> {\n    if (!this.connected) {\n      await this.connect();\n    }\n\n    if (this.activeStreams.size >= MAX_STREAMS_PER_CONNECTION) {\n      throw new StateError(`Maximum concurrent streams (${MAX_STREAMS_PER_CONNECTION}) reached`);\n    }\n\n    const config = resolveStreamConfig(input, this.ttsDefaults);\n\n    if (this.activeStreams.has(config.stream_id)) {\n      throw new StateError(`Stream '${config.stream_id}' is already active on this connection`);\n    }\n\n    const stream = new RealtimeTtsStream(config.stream_id, this, ownsConnection);\n    this.activeStreams.set(config.stream_id, stream);\n\n    const configPayload = {\n      api_key: this.apiKey,\n      ...config,\n    };\n    this._sendJson(configPayload);\n\n    return stream;\n  }\n\n  /**\n   * Close the WebSocket connection and terminate all active streams.\n   */\n  close(): void {\n    this.stopKeepalive();\n\n    for (const stream of this.activeStreams.values()) {\n      stream._forceEnd();\n    }\n    this.activeStreams.clear();\n\n    if (this.ws) {\n      try {\n        this.ws.close();\n      } catch {\n        // Ignore close errors\n      }\n      this.ws = null;\n    }\n\n    this.connected = false;\n    this.emit('close');\n  }\n\n  /** @internal Send a JSON payload on the WebSocket. */\n  _sendJson(payload: Record<string, unknown>): void {\n    if (!this.ws || !this.connected) {\n      throw new StateError('TTS connection is not open');\n    }\n    this.ws.send(JSON.stringify(payload));\n  }\n\n  /** @internal Remove a stream from the active set. */\n  _deactivateStream(streamId: string): void {\n    this.activeStreams.delete(streamId);\n  }\n\n  private async createWebSocket(): Promise<void> {\n    return new Promise<void>((resolve, reject) => {\n      const timer = setTimeout(() => {\n        try {\n          ws.close();\n        } catch {\n          // Ignore\n        }\n        reject(new ConnectionError('TTS WebSocket connection timed out'));\n      }, this.connectTimeoutMs);\n\n      let ws: WebSocket;\n      try {\n        ws = new WebSocket(this.wsUrl);\n        ws.binaryType = 'arraybuffer';\n      } catch (err) {\n        clearTimeout(timer);\n        reject(\n          new ConnectionError(`Failed to create TTS WebSocket: ${err instanceof Error ? err.message : String(err)}`)\n        );\n        return;\n      }\n\n      const onOpen = () => {\n        clearTimeout(timer);\n        ws.removeEventListener('error', onError);\n        this.ws = ws;\n\n        ws.addEventListener('message', (event: MessageEvent) => {\n          this.handleMessage(event);\n        });\n        ws.addEventListener('close', () => {\n          if (this.connected) {\n            this.connected = false;\n            this.stopKeepalive();\n            for (const stream of this.activeStreams.values()) {\n              stream._forceEnd();\n            }\n            this.activeStreams.clear();\n            this.emit('close');\n          }\n        });\n\n        resolve();\n      };\n\n      const onError = () => {\n        clearTimeout(timer);\n        ws.removeEventListener('open', onOpen);\n        reject(new ConnectionError('TTS WebSocket connection failed'));\n      };\n\n      ws.addEventListener('open', onOpen);\n      ws.addEventListener('error', onError);\n    });\n  }\n\n  private handleMessage(event: MessageEvent): void {\n    if (typeof event.data !== 'string') return;\n\n    let parsed: TtsEvent;\n    try {\n      parsed = JSON.parse(event.data) as TtsEvent;\n    } catch {\n      return;\n    }\n\n    const streamId = parsed.stream_id;\n\n    if (streamId !== undefined) {\n      const stream = this.activeStreams.get(streamId);\n      if (stream) {\n        stream._handleEvent(parsed);\n      }\n      return;\n    }\n\n    if (parsed.error_code !== undefined) {\n      const errPayload: { error_code: number; error_message?: string } = {\n        error_code: parsed.error_code,\n      };\n      if (parsed.error_message !== undefined) {\n        errPayload.error_message = parsed.error_message;\n      }\n      const error = mapErrorResponse(errPayload);\n      this.emit('error', error);\n    }\n  }\n\n  private startKeepalive(): void {\n    if (this.keepaliveTimer) return;\n    this.keepaliveTimer = setInterval(() => {\n      if (this.connected && this.ws) {\n        try {\n          this.ws.send(JSON.stringify({ keep_alive: true }));\n        } catch {\n          // Ignore send errors during keepalive\n        }\n      }\n    }, this.keepaliveIntervalMs);\n  }\n\n  private stopKeepalive(): void {\n    if (this.keepaliveTimer) {\n      clearInterval(this.keepaliveTimer);\n      this.keepaliveTimer = null;\n    }\n  }\n}\n","import { segmentTokens } from '../segments.js';\nimport type { RealtimeSegment, RealtimeSegmentOptions, RealtimeToken } from '../types/realtime.js';\n\n/**\n * Groups real-time tokens into segments based on specified grouping keys.\n *\n * A new segment starts when any of the `group_by` fields changes.\n * Tokens are concatenated as-is.\n *\n * @param tokens - Array of real-time tokens to segment\n * @param options - Segmentation options\n * @param options.group_by - Fields to group by (default: ['speaker', 'language'])\n * @param options.final_only - When true, only finalized tokens are included\n * @returns Array of segments with combined text and timing (if available)\n */\nexport function segmentRealtimeTokens(\n  tokens: RealtimeToken[],\n  options: RealtimeSegmentOptions = {}\n): RealtimeSegment[] {\n  const filteredTokens = options.final_only ? tokens.filter((token) => token.is_final) : tokens;\n  return segmentTokens(filteredTokens, options, buildSegment);\n}\n\nfunction buildSegment(\n  tokens: RealtimeToken[],\n  speaker: string | null | undefined,\n  language: string | null | undefined\n): RealtimeSegment {\n  const firstToken = tokens[0];\n  const lastToken = tokens[tokens.length - 1];\n\n  if (!firstToken || !lastToken) {\n    throw new Error('Cannot build segment from an empty token array');\n  }\n\n  const text = tokens.map((t) => t.text).join('');\n\n  return {\n    text,\n    start_ms: firstToken.start_ms,\n    end_ms: lastToken.end_ms,\n    ...(!!speaker && { speaker }),\n    ...(!!language && { language }),\n    tokens,\n  };\n}\n","import type {\n  RealtimeResult,\n  RealtimeSegment,\n  RealtimeSegmentBufferOptions,\n  RealtimeToken,\n} from '../types/realtime.js';\n\nimport { segmentRealtimeTokens } from './segments.js';\n\nconst DEFAULT_MAX_TOKENS = 2000;\n\n/**\n * Rolling buffer for turning real-time results into stable segments.\n */\nexport class RealtimeSegmentBuffer {\n  private tokens: RealtimeToken[] = [];\n  private readonly groupBy: RealtimeSegmentBufferOptions['group_by'];\n  private readonly finalOnly: boolean;\n  private readonly maxTokens: number | undefined;\n  private readonly maxMs: number | undefined;\n\n  constructor(options: RealtimeSegmentBufferOptions = {}) {\n    validatePositive('max_tokens', options.max_tokens);\n    validatePositive('max_ms', options.max_ms);\n\n    this.groupBy = options.group_by;\n    this.finalOnly = options.final_only ?? true;\n    this.maxTokens = options.max_tokens ?? DEFAULT_MAX_TOKENS;\n    this.maxMs = options.max_ms;\n  }\n\n  /**\n   * Number of tokens currently buffered.\n   */\n  get size(): number {\n    return this.tokens.length;\n  }\n\n  /**\n   * Add a real-time result and return stable segments.\n   */\n  add(result: RealtimeResult): RealtimeSegment[] {\n    const incoming = this.finalOnly ? result.tokens.filter((token) => token.is_final) : result.tokens;\n\n    if (incoming.length > 0) {\n      this.tokens.push(...incoming);\n    }\n\n    const stableSegments = this.flushStable(result.final_audio_proc_ms);\n    this.trim();\n    return stableSegments;\n  }\n\n  /**\n   * Clear all buffered tokens.\n   */\n  reset(): void {\n    this.tokens = [];\n  }\n\n  /**\n   * Flush all buffered tokens into segments and clear the buffer.\n   *\n   * Includes tokens that are not yet stable by final_audio_proc_ms.\n   */\n  flushAll(): RealtimeSegment[] {\n    if (this.tokens.length === 0) {\n      return [];\n    }\n\n    const segments = segmentRealtimeTokens(this.tokens, { group_by: this.groupBy });\n    this.tokens = [];\n    return segments;\n  }\n\n  private flushStable(finalAudioProcMs: number): RealtimeSegment[] {\n    if (!Number.isFinite(finalAudioProcMs) || finalAudioProcMs <= 0) {\n      return [];\n    }\n\n    const segments = segmentRealtimeTokens(this.tokens, { group_by: this.groupBy });\n    const stableSegments: RealtimeSegment[] = [];\n    let dropCount = 0;\n\n    // Only flush segments that are followed by another segment (i.e. a\n    // speaker/language boundary occurred).  The last segment is kept in the\n    // buffer because more tokens for the same group may arrive in subsequent\n    // results.  Use flushAll() to drain remaining tokens (e.g. on endpoint).\n    for (let i = 0; i < segments.length - 1; i++) {\n      const segment = segments[i]!;\n      const lastToken = segment.tokens[segment.tokens.length - 1];\n      const endMs = lastToken?.end_ms;\n      if (endMs === undefined || endMs > finalAudioProcMs) {\n        break;\n      }\n      stableSegments.push(segment);\n      dropCount += segment.tokens.length;\n    }\n\n    if (dropCount > 0) {\n      this.tokens = this.tokens.slice(dropCount);\n    }\n\n    return stableSegments;\n  }\n\n  private trim(): void {\n    if (this.maxTokens !== undefined && this.tokens.length > this.maxTokens) {\n      this.tokens = this.tokens.slice(this.tokens.length - this.maxTokens);\n    }\n\n    if (this.maxMs === undefined) {\n      return;\n    }\n\n    const latestEndMs = findLatestEndMs(this.tokens);\n    if (latestEndMs === undefined) {\n      return;\n    }\n\n    const cutoff = latestEndMs - this.maxMs;\n    if (cutoff <= 0) {\n      return;\n    }\n\n    let dropIndex = 0;\n    while (dropIndex < this.tokens.length) {\n      const token = this.tokens[dropIndex];\n      if (token?.end_ms === undefined || token.end_ms >= cutoff) {\n        break;\n      }\n      dropIndex += 1;\n    }\n\n    if (dropIndex > 0) {\n      this.tokens = this.tokens.slice(dropIndex);\n    }\n  }\n}\n\nfunction findLatestEndMs(tokens: RealtimeToken[]): number | undefined {\n  for (let i = tokens.length - 1; i >= 0; i -= 1) {\n    const endMs = tokens[i]?.end_ms;\n    if (typeof endMs === 'number') {\n      return endMs;\n    }\n  }\n  return undefined;\n}\n\nfunction validatePositive(name: string, value: number | undefined): void {\n  if (value === undefined) {\n    return;\n  }\n  if (!Number.isFinite(value) || value <= 0) {\n    throw new Error(`${name} must be a finite positive number`);\n  }\n}\n","import type {\n  RealtimeResult,\n  RealtimeSegment,\n  RealtimeUtterance,\n  RealtimeUtteranceBufferOptions,\n} from '../types/realtime.js';\n\nimport { RealtimeSegmentBuffer } from './segment-buffer.js';\n\n/**\n * Collects real-time results into utterances for endpoint-driven workflows.\n */\nexport class RealtimeUtteranceBuffer {\n  private readonly segmentBuffer: RealtimeSegmentBuffer;\n  private pendingSegments: RealtimeSegment[] = [];\n  private lastFinalAudioProcMs: number | undefined;\n  private lastTotalAudioProcMs: number | undefined;\n\n  constructor(options: RealtimeUtteranceBufferOptions = {}) {\n    this.segmentBuffer = new RealtimeSegmentBuffer(options);\n  }\n\n  /**\n   * Add a real-time result and collect stable segments.\n   */\n  addResult(result: RealtimeResult): RealtimeSegment[] {\n    this.lastFinalAudioProcMs = result.final_audio_proc_ms;\n    this.lastTotalAudioProcMs = result.total_audio_proc_ms;\n\n    const stableSegments = this.segmentBuffer.add(result);\n    if (stableSegments.length > 0) {\n      this.pendingSegments.push(...stableSegments);\n    }\n\n    return stableSegments;\n  }\n\n  /**\n   * Mark an endpoint and flush the current utterance.\n   */\n  markEndpoint(): RealtimeUtterance | undefined {\n    const trailingSegments = this.segmentBuffer.flushAll();\n    const segments = [...this.pendingSegments, ...trailingSegments];\n    this.pendingSegments = [];\n\n    if (segments.length === 0) {\n      return undefined;\n    }\n\n    return buildUtterance(segments, this.lastFinalAudioProcMs, this.lastTotalAudioProcMs);\n  }\n\n  /**\n   * Clear buffered segments and tokens.\n   */\n  reset(): void {\n    this.pendingSegments = [];\n    this.segmentBuffer.reset();\n  }\n}\n\nfunction buildUtterance(\n  segments: RealtimeSegment[],\n  finalAudioProcMs: number | undefined,\n  totalAudioProcMs: number | undefined\n): RealtimeUtterance {\n  const tokens = segments.flatMap((segment) => segment.tokens);\n  const text = segments.map((segment) => segment.text).join('');\n  const start_ms = segments[0]?.start_ms;\n  const end_ms = segments[segments.length - 1]?.end_ms;\n\n  const speaker = getCommonValue(segments.map((segment) => segment.speaker));\n  const language = getCommonValue(segments.map((segment) => segment.language));\n\n  return {\n    text,\n    segments,\n    tokens,\n    start_ms,\n    end_ms,\n    speaker,\n    language,\n    final_audio_proc_ms: finalAudioProcMs,\n    total_audio_proc_ms: totalAudioProcMs,\n  };\n}\n\nfunction getCommonValue<T>(values: Array<T | undefined>): T | undefined {\n  let common: T | undefined;\n  for (const value of values) {\n    if (value === undefined) {\n      return undefined;\n    }\n    if (common === undefined) {\n      common = value;\n      continue;\n    }\n    if (value !== common) {\n      return undefined;\n    }\n  }\n  return common;\n}\n","/**\n * Browser-safe REST TTS client.\n *\n * Uses only `globalThis.fetch` — no Node-specific dependencies.\n * Shared by both `@soniox/node` and `@soniox/client`.\n */\n\nimport { createAbortError, createHttpError, createNetworkError } from './http-errors.js';\nimport type { GenerateSpeechOptions } from './types/tts.js';\n\nconst DEFAULT_MODEL = 'tts-rt-v1';\nconst DEFAULT_LANGUAGE = 'en';\nconst DEFAULT_AUDIO_FORMAT = 'wav';\n\ntype TtsRestPayload = {\n  model: string;\n  language: string;\n  voice: string;\n  audio_format: string;\n  text: string;\n  sample_rate?: number;\n  bitrate?: number;\n};\n\nfunction buildPayload(options: GenerateSpeechOptions): TtsRestPayload {\n  const payload: TtsRestPayload = {\n    model: options.model ?? DEFAULT_MODEL,\n    language: options.language ?? DEFAULT_LANGUAGE,\n    voice: options.voice,\n    audio_format: options.audio_format ?? DEFAULT_AUDIO_FORMAT,\n    text: options.text,\n  };\n  if (options.sample_rate !== undefined) {\n    payload.sample_rate = options.sample_rate;\n  }\n  if (options.bitrate !== undefined) {\n    payload.bitrate = options.bitrate;\n  }\n  return payload;\n}\n\n/**\n * Normalizes fetch Headers to a plain object with lowercase keys.\n * Duplicated here (rather than imported from `@soniox/node`) to keep\n * this module browser-safe.\n */\nfunction headersToObject(headers: Headers): Record<string, string> {\n  const result: Record<string, string> = {};\n  headers.forEach((value, key) => {\n    result[key.toLowerCase()] = value;\n  });\n  return result;\n}\n\nfunction isAbortLikeError(error: unknown): boolean {\n  if (error instanceof Error) {\n    return error.name === 'AbortError' || error.name === 'TimeoutError';\n  }\n  return false;\n}\n\n/**\n * Browser-safe REST client for TTS generation.\n *\n * Provides `generate()` (buffered) and `generateStream()` (streaming)\n * using only `globalThis.fetch`. HTTP failures are surfaced as\n * {@link SonioxHttpError}, matching the rest of the Soniox SDK.\n *\n * Authentication uses the `Authorization: Bearer <api_key>` header.\n *\n * @example\n * ```typescript\n * const client = new TtsRestClient(apiKey, 'https://tts-rt.soniox.com');\n * const audio = await client.generate({ text: 'Hello', voice: 'Adrian' });\n * ```\n */\nexport class TtsRestClient {\n  private readonly apiKey: string;\n  private readonly ttsApiUrl: string;\n\n  constructor(apiKey: string, ttsApiUrl: string) {\n    this.apiKey = apiKey;\n    this.ttsApiUrl = ttsApiUrl;\n  }\n\n  /**\n   * Generate speech audio from text. Returns the full audio as a `Uint8Array`.\n   *\n   * @throws {@link SonioxHttpError} on non-2xx responses, network failures,\n   * or aborted requests.\n   */\n  async generate(options: GenerateSpeechOptions): Promise<Uint8Array> {\n    const url = `${this.ttsApiUrl}/tts`;\n    const response = await this.sendRequest(url, options);\n    const buffer = await response.arrayBuffer();\n    return new Uint8Array(buffer);\n  }\n\n  /**\n   * Generate speech audio from text as a streaming async iterable.\n   *\n   * Yields `Uint8Array` chunks as they arrive from the server response body.\n   * Lower time-to-first-audio than {@link generate}.\n   *\n   * **Known limitation:** Mid-stream server errors (reported via HTTP trailers)\n   * cannot be detected through the `fetch` API. The iterator may end early\n   * without an explicit error. Use WebSocket TTS for reliable error detection.\n   *\n   * @throws {@link SonioxHttpError} on non-2xx responses, network failures,\n   * or aborted requests (before the stream starts).\n   */\n  async *generateStream(options: GenerateSpeechOptions): AsyncIterable<Uint8Array> {\n    const url = `${this.ttsApiUrl}/tts`;\n    const response = await this.sendRequest(url, options);\n\n    if (!response.body) {\n      throw createHttpError(\n        url,\n        'POST',\n        response.status,\n        headersToObject(response.headers),\n        'Response has no body stream'\n      );\n    }\n\n    const reader = response.body.getReader();\n    try {\n      while (true) {\n        const { done, value } = await reader.read();\n        if (done) break;\n        yield value;\n      }\n    } finally {\n      reader.releaseLock();\n    }\n  }\n\n  /**\n   * Internal request helper. Performs the fetch, maps network/abort failures\n   * to {@link SonioxHttpError}, and throws on non-2xx responses.\n   */\n  private async sendRequest(url: string, options: GenerateSpeechOptions): Promise<Response> {\n    const payload = buildPayload(options);\n\n    let response: Response;\n    try {\n      response = await globalThis.fetch(url, {\n        method: 'POST',\n        headers: {\n          Authorization: `Bearer ${this.apiKey}`,\n          'Content-Type': 'application/json',\n        },\n        body: JSON.stringify(payload),\n        ...(options.signal && { signal: options.signal }),\n      });\n    } catch (cause) {\n      if (isAbortLikeError(cause)) {\n        throw createAbortError(url, 'POST', cause);\n      }\n      throw createNetworkError(url, 'POST', cause);\n    }\n\n    if (!response.ok) {\n      const bodyText = await response.text().catch(() => '');\n      throw createHttpError(url, 'POST', response.status, headersToObject(response.headers), bodyText);\n    }\n\n    return response;\n  }\n}\n","/**\n * Audio-specific error classes for the client SDK\n * These errors are thrown by AudioSource implementations when capture cannot begin\n */\n\nimport { SonioxError } from '@soniox/core';\n\n/**\n * Error codes for audio-related errors\n */\nexport type AudioErrorCode = 'permission_denied' | 'device_not_found' | 'audio_unavailable';\n\n/**\n * Thrown when microphone access is denied by the user or blocked by the browser.\n *\n * Maps to `getUserMedia` `NotAllowedError` DOMException.\n */\nexport class AudioPermissionError extends SonioxError {\n  constructor(message = 'Microphone access denied', cause?: unknown) {\n    super(message, 'permission_denied', undefined, cause);\n    this.name = 'AudioPermissionError';\n  }\n}\n\n/**\n * Thrown when no audio input device is found\n *\n * Maps to `getUserMedia` `NotFoundError` DOMException.\n */\nexport class AudioDeviceError extends SonioxError {\n  constructor(message = 'No audio input device found', cause?: unknown) {\n    super(message, 'device_not_found', undefined, cause);\n    this.name = 'AudioDeviceError';\n  }\n}\n\n/**\n * Thrown when audio capture is not supported in the current environment\n *\n * For example, when `getUserMedia` or `MediaRecorder` is not available.\n */\nexport class AudioUnavailableError extends SonioxError {\n  constructor(message = 'Audio capture is not supported in this environment', cause?: unknown) {\n    super(message, 'audio_unavailable', undefined, cause);\n    this.name = 'AudioUnavailableError';\n  }\n}\n","/**\n * Browser microphone audio source using getUserMedia + MediaRecorder\n */\n\nimport { AudioDeviceError, AudioPermissionError, AudioUnavailableError } from './errors.js';\nimport type { AudioSource, AudioSourceHandlers } from './types.js';\n\nconst DEFAULT_TIMESLICE_MS = 60;\n\nconst DEFAULT_AUDIO_CONSTRAINTS: MediaTrackConstraints = {\n  echoCancellation: false,\n  noiseSuppression: false,\n  autoGainControl: false,\n  channelCount: 1,\n  sampleRate: 16000,\n};\n\n/**\n * Options for MicrophoneSource\n */\nexport type MicrophoneSourceOptions = {\n  /**\n   * MediaTrackConstraints for the audio track.\n   * @default { echoCancellation: false, noiseSuppression: false, autoGainControl: false, channelCount: 1, sampleRate: 16000 }\n   */\n  constraints?: MediaTrackConstraints | undefined;\n\n  /**\n   * MediaRecorder options.\n   * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder\n   */\n  recorderOptions?: MediaRecorderOptions | undefined;\n\n  /**\n   * Time interval in milliseconds between audio data chunks.\n   * @default 60\n   */\n  timesliceMs?: number | undefined;\n};\n\n/**\n * Browser microphone audio source\n *\n * Uses `navigator.mediaDevices.getUserMedia` to capture audio from the microphone\n * and `MediaRecorder` to encode it into chunks.\n *\n * @example\n * ```typescript\n * const source = new MicrophoneSource();\n * await source.start({\n *   onData: (chunk) => session.sendAudio(chunk),\n *   onError: (err) => console.error(err),\n * });\n * // Later:\n * source.stop();\n * ```\n */\nexport class MicrophoneSource implements AudioSource {\n  private readonly constraints: MediaTrackConstraints;\n  private readonly recorderOptions: MediaRecorderOptions;\n  private readonly timesliceMs: number;\n\n  private mediaRecorder: MediaRecorder | null = null;\n  private stream: MediaStream | null = null;\n\n  // Bound event handlers for cleanup\n  private boundOnData: ((event: BlobEvent) => void) | null = null;\n  private boundOnError: ((event: Event) => void) | null = null;\n  private boundOnMute: (() => void) | null = null;\n  private boundOnUnmute: (() => void) | null = null;\n\n  // Guards against concurrent start() calls (see below).\n  private startGeneration = 0;\n\n  constructor(options: MicrophoneSourceOptions = {}) {\n    this.constraints = options.constraints ?? DEFAULT_AUDIO_CONSTRAINTS;\n    this.recorderOptions = options.recorderOptions ?? {};\n    this.timesliceMs = options.timesliceMs ?? DEFAULT_TIMESLICE_MS;\n  }\n\n  /**\n   * Request microphone access and start recording\n   *\n   * @throws AudioUnavailableError if getUserMedia or MediaRecorder is not supported\n   * @throws AudioPermissionError if microphone access is denied\n   * @throws AudioDeviceError if no microphone is found\n   */\n  async start(handlers: AudioSourceHandlers): Promise<void> {\n    // Stop any previous capture to prevent resource leaks on double-start.\n    this.stop();\n\n    // Increment generation so a stale concurrent start() can detect it was\n    // superseded while awaiting getUserMedia.\n    const generation = ++this.startGeneration;\n\n    // Check for browser support\n    if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n      throw new AudioUnavailableError('navigator.mediaDevices.getUserMedia is not available');\n    }\n\n    if (typeof MediaRecorder === 'undefined') {\n      throw new AudioUnavailableError('MediaRecorder is not available');\n    }\n\n    // Request microphone access\n    let stream: MediaStream;\n    try {\n      stream = await navigator.mediaDevices.getUserMedia({ audio: this.constraints });\n    } catch (err) {\n      if (err instanceof DOMException) {\n        if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {\n          throw new AudioPermissionError('Microphone access denied by user', err);\n        }\n        if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {\n          throw new AudioDeviceError('No microphone found', err);\n        }\n        if (err.name === 'NotReadableError' || err.name === 'TrackStartError') {\n          throw new AudioDeviceError('Microphone is already in use or not readable', err);\n        }\n      }\n      throw new AudioUnavailableError(err instanceof Error ? err.message : 'Failed to access microphone', err);\n    }\n\n    // A newer start() or stop() was called while we were awaiting getUserMedia.\n    // Release the stream we just acquired to avoid orphaning it.\n    if (generation !== this.startGeneration) {\n      stream.getTracks().forEach((track) => track.stop());\n      return;\n    }\n\n    this.stream = stream;\n\n    // Attach mute/unmute listeners to the audio track\n    const track = stream.getAudioTracks()[0];\n    if (track) {\n      const { onMuted, onUnmuted } = handlers;\n      this.boundOnMute = onMuted\n        ? () => {\n            onMuted();\n          }\n        : null;\n      this.boundOnUnmute = onUnmuted\n        ? () => {\n            onUnmuted();\n          }\n        : null;\n      if (this.boundOnMute) track.addEventListener('mute', this.boundOnMute);\n      if (this.boundOnUnmute) track.addEventListener('unmute', this.boundOnUnmute);\n    }\n\n    // Create MediaRecorder\n    try {\n      const recorder = new MediaRecorder(stream, this.recorderOptions);\n      this.mediaRecorder = recorder;\n\n      this.boundOnData = (event: BlobEvent) => {\n        if (event.data.size > 0) {\n          void event.data.arrayBuffer().then(\n            (buffer) => handlers.onData(buffer),\n            (err: unknown) => handlers.onError(err instanceof Error ? err : new Error(String(err)))\n          );\n        }\n      };\n\n      this.boundOnError = (event: Event) => {\n        const errorEvent = event as ErrorEvent;\n        const error = new Error(errorEvent.message || 'MediaRecorder error');\n        handlers.onError(error);\n      };\n\n      recorder.addEventListener('dataavailable', this.boundOnData);\n      recorder.addEventListener('error', this.boundOnError);\n\n      // Start recording\n      recorder.start(this.timesliceMs);\n    } catch (err) {\n      stream.getTracks().forEach((track) => track.stop());\n      this.stream = null;\n      this.mediaRecorder = null;\n      this.boundOnMute = null;\n      this.boundOnUnmute = null;\n      throw new AudioUnavailableError(err instanceof Error ? err.message : 'Failed to start MediaRecorder', err);\n    }\n  }\n\n  /**\n   * Stop recording and release all resources\n   */\n  stop(): void {\n    if (this.mediaRecorder) {\n      if (this.mediaRecorder.state !== 'inactive') {\n        const recorder = this.mediaRecorder;\n        const onData = this.boundOnData;\n        const onError = this.boundOnError;\n        recorder.addEventListener(\n          'stop',\n          () => {\n            if (onData) recorder.removeEventListener('dataavailable', onData);\n            if (onError) recorder.removeEventListener('error', onError);\n          },\n          { once: true }\n        );\n        recorder.stop();\n      } else {\n        // Already inactive — remove listeners directly.\n        if (this.boundOnData) {\n          this.mediaRecorder.removeEventListener('dataavailable', this.boundOnData);\n        }\n        if (this.boundOnError) {\n          this.mediaRecorder.removeEventListener('error', this.boundOnError);\n        }\n      }\n\n      this.boundOnData = null;\n      this.boundOnError = null;\n      this.mediaRecorder = null;\n    }\n\n    // Remove mute/unmute listeners and stop all tracks\n    if (this.stream) {\n      if (this.boundOnMute || this.boundOnUnmute) {\n        const track = this.stream.getAudioTracks()[0];\n        if (track) {\n          if (this.boundOnMute) track.removeEventListener('mute', this.boundOnMute);\n          if (this.boundOnUnmute) track.removeEventListener('unmute', this.boundOnUnmute);\n        }\n        this.boundOnMute = null;\n        this.boundOnUnmute = null;\n      }\n      this.stream.getTracks().forEach((track) => track.stop());\n      this.stream = null;\n    }\n  }\n\n  /**\n   * Pause audio capture\n   */\n  pause(): void {\n    if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {\n      this.mediaRecorder.pause();\n    }\n  }\n\n  /**\n   * Resume audio capture\n   */\n  resume(): void {\n    if (this.mediaRecorder && this.mediaRecorder.state === 'paused') {\n      this.mediaRecorder.resume();\n    }\n  }\n\n  /**\n   * Reinitialize the MediaRecorder on the existing stream so the next\n   * chunks contain a fresh container header (required after reconnecting\n   * to a new server session).\n   */\n  restart(): void {\n    if (!this.stream || !this.mediaRecorder) return;\n\n    // Detach listeners from the old recorder and stop it.\n    const oldRecorder = this.mediaRecorder;\n    if (this.boundOnData) oldRecorder.removeEventListener('dataavailable', this.boundOnData);\n    if (this.boundOnError) oldRecorder.removeEventListener('error', this.boundOnError);\n    if (oldRecorder.state !== 'inactive') {\n      oldRecorder.stop();\n    }\n\n    // Create a new recorder on the same stream.\n    const recorder = new MediaRecorder(this.stream, this.recorderOptions);\n    this.mediaRecorder = recorder;\n    if (this.boundOnData) recorder.addEventListener('dataavailable', this.boundOnData);\n    if (this.boundOnError) recorder.addEventListener('error', this.boundOnError);\n    recorder.start(this.timesliceMs);\n  }\n}\n","/**\n * API key configuration and resolution for client-side usage\n * Every recording session fetches a fresh key\n */\n\n/**\n * API key configuration.\n *\n * - `string` - A pre-fetched temporary API key (e.g., injected from SSR)\n * - `() => Promise<string>` - An async function that fetches a fresh temporary key\n *   from your backend. Called once per recording session.\n *\n * @deprecated Use {@link SonioxConnectionConfig} with `SonioxClientOptions.config` instead.\n *\n * @example\n * ```typescript\n * // Static key (for demos or SSR-injected keys)\n * const client = new SonioxClient({ api_key: 'temp:...' });\n *\n * // Async function (recommended for production)\n * const client = new SonioxClient({\n *   api_key: async () => {\n *     const res = await fetch('/api/get-temporary-key', { method: 'POST' });\n *     const { api_key } = await res.json();\n *     return api_key;\n *   },\n * });\n * ```\n *\n * Note: If you use Node.js, you can use the `SonioxNodeClient` to fetch a temporary API key via `client.auth.createTemporaryKey()`.\n */\nexport type ApiKeyConfig = string | (() => Promise<string>);\n\n/**\n * Resolves an ApiKeyConfig to a plain API key string.\n * @param config - The API key configuration\n * @returns The resolved API key string\n * @throws If the function rejects or returns a non-string value\n * @deprecated Use {@link SonioxConnectionConfig} with `SonioxClientOptions.config` instead.\n */\nexport async function resolveApiKey(config: ApiKeyConfig): Promise<string> {\n  if (typeof config === 'function') {\n    const key = await config();\n    if (typeof key !== 'string' || key.length === 0) {\n      throw new Error('api_key function must return a non-empty string');\n    }\n    return key;\n  }\n\n  if (typeof config !== 'string' || config.length === 0) {\n    throw new Error('api_key must be a non-empty string');\n  }\n\n  return config;\n}\n","/**\n * Recording - high-level orchestrator for real-time speech-to-text.\n *\n * Returned by `client.realtime.record()`. Manages the lifecycle of an AudioSource\n * and a RealtimeSttSession, including audio buffering during key fetch/connection.\n */\n\nimport { TypedEmitter, RealtimeSttSession, isRetriableError, ConnectionError } from '@soniox/core';\nimport type {\n  ConfigContext,\n  RealtimeResult,\n  RealtimeToken,\n  ResolvedConnectionConfig,\n  StateChangeReason,\n  SttSessionConfig,\n  SttSessionOptions,\n} from '@soniox/core';\n\nimport type { AudioSource } from './audio/types.js';\n\nconst TERMINAL_STATES: readonly RecordingState[] = ['stopped', 'canceled', 'error'];\n\nconst DEFAULT_RECONNECT_BASE_DELAY_MS = 1000;\nconst DEFAULT_MAX_RECONNECT_ATTEMPTS = 3;\n\n/**\n * Unified recording lifecycle states.\n */\nexport type RecordingState =\n  | 'idle'\n  | 'starting'\n  | 'connecting'\n  | 'recording'\n  | 'paused'\n  | 'reconnecting'\n  | 'stopping'\n  | 'stopped'\n  | 'error'\n  | 'canceled';\n\n/**\n * Reconnection configuration for automatic WebSocket recovery.\n */\nexport type ReconnectOptions = {\n  /**\n   * Enable automatic reconnection on retriable errors.\n   * @default false\n   */\n  auto_reconnect?: boolean | undefined;\n\n  /**\n   * Maximum number of consecutive reconnection attempts before giving up.\n   * @default 3\n   */\n  max_reconnect_attempts?: number | undefined;\n\n  /**\n   * Base delay in milliseconds for exponential backoff (1x, 2x, 4x, ...).\n   * @default 1000\n   */\n  reconnect_base_delay_ms?: number | undefined;\n\n  /**\n   * When true, clear accumulated transcript state (finalText, segments,\n   * utterances) on reconnect. Window-tracking state is always reset.\n   * @default false\n   */\n  reset_transcript_on_reconnect?: boolean | undefined;\n};\n\n/**\n * Payload for the `reconnecting` event.\n */\nexport type ReconnectingEvent = {\n  /** Current attempt number (1-based). */\n  attempt: number;\n  /** Maximum attempts configured. */\n  max_attempts: number;\n  /** Backoff delay before reconnect (ms). */\n  delay_ms: number;\n  /** Call to cancel this reconnection attempt. */\n  preventDefault: () => void;\n};\n\n/**\n * Events emitted by a Recording instance\n */\nexport type RecordingEvents = {\n  /** Parsed result received from the server. */\n  result: (result: RealtimeResult) => void;\n\n  /** Individual token received. */\n  token: (token: RealtimeToken) => void;\n\n  /** Error occurred during recording. */\n  error: (error: Error) => void;\n\n  /** Endpoint detected (speaker finished talking). */\n  endpoint: () => void;\n\n  /** Finalization complete. */\n  finalized: () => void;\n\n  /** Recording finished (server acknowledged end of stream). */\n  finished: () => void;\n\n  /** WebSocket connected and ready. */\n  connected: () => void;\n\n  /** Recording state transition. */\n  state_change: (update: { old_state: RecordingState; new_state: RecordingState; reason?: StateChangeReason }) => void;\n\n  /** About to attempt a reconnection. Call `preventDefault()` to cancel. */\n  reconnecting: (event: ReconnectingEvent) => void;\n\n  /** Successfully reconnected after a drop. */\n  reconnected: (event: { attempt: number }) => void;\n\n  /**\n   * New STT session started (initial or after reconnect).\n   * Consumers should reset any session-local tracking state (e.g. token\n   * window comparisons). The `reset_transcript` flag indicates whether\n   * accumulated transcript state should also be cleared.\n   */\n  session_restart: (event: { reset_transcript: boolean }) => void;\n\n  /** Audio source was muted externally (e.g. OS-level or hardware mute). */\n  source_muted: () => void;\n\n  /** Audio source was unmuted after an external mute. */\n  source_unmuted: () => void;\n};\n\n/**\n * Options for creating a recording\n */\nexport type RecordOptions = SttSessionConfig &\n  ReconnectOptions & {\n    /**\n     * Audio source to use. Defaults to MicrophoneSource if not provided.\n     */\n    source?: AudioSource | undefined;\n\n    /**\n     * AbortSignal for cancellation\n     */\n    signal?: AbortSignal | undefined;\n\n    /**\n     * Maximum number of audio chunks to buffer while waiting for key/connection\n     * @default 1000\n     */\n    buffer_queue_size?: number | undefined;\n\n    /**\n     * SDK-level session options (signal, etc.)\n     */\n    session_options?: SttSessionOptions | undefined;\n\n    /**\n     * Function that receives the resolved connection config (including\n     * `stt_defaults` from the server) and returns the final session config.\n     *\n     * When provided, its return value is used as the session config,\n     * and any flat session config fields on this object are ignored.\n     *\n     * @example\n     * ```typescript\n     * client.realtime.record({\n     *   session_config: (resolved) => ({\n     *     ...resolved.stt_defaults,\n     *     enable_endpoint_detection: true,\n     *   }),\n     * });\n     * ```\n     */\n    session_config?: ((resolved: ResolvedConnectionConfig) => SttSessionConfig) | undefined;\n  };\n\nconst DEFAULT_BUFFER_QUEUE_SIZE = 1000;\n\n/**\n * High-level recording orchestrator\n *\n * Manages the lifecycle of audio capture and real-time transcription:\n * 1. Starts audio source immediately (buffers chunks)\n * 2. Resolves connection config (API key + URLs, sync or async)\n * 3. Connects to the Soniox WebSocket API\n * 4. Drains buffered audio, then pipes live audio to the session\n *\n * @example\n * ```typescript\n * const recording = client.realtime.record({ model: 'stt-rt-v5' });\n * recording.on('result', (r) => console.log(r.tokens));\n * recording.on('error', (e) => console.error(e));\n *\n * // Later:\n * await recording.stop();\n * ```\n */\n/** @internal */\nexport type SttConfigInput = SttSessionConfig | ((resolved: ResolvedConnectionConfig) => SttSessionConfig);\n\nexport class Recording {\n  private readonly emitter = new TypedEmitter<RecordingEvents>();\n  private readonly configResolver: (context?: ConfigContext) => Promise<ResolvedConnectionConfig>;\n  private readonly sttConfigInput: SttConfigInput;\n  private readonly sessionOptions: SttSessionOptions | undefined;\n  private readonly source: AudioSource;\n  private readonly maxBufferSize: number;\n  private readonly signal: AbortSignal | undefined;\n\n  // Reconnect config\n  private readonly autoReconnect: boolean;\n  private readonly maxReconnectAttempts: number;\n  private readonly reconnectBaseDelay: number;\n  private readonly resetTranscriptOnReconnect: boolean;\n\n  private session: RealtimeSttSession | null = null;\n  private audioBuffer: ArrayBuffer[] = [];\n  private _state: RecordingState = 'idle';\n  private isBuffering = true;\n  private _isSourceMuted = false;\n  private _reconnectAttempt = 0;\n\n  // Stop promise handling\n  private stopResolver: (() => void) | null = null;\n  private stopRejecter: ((error: Error) => void) | null = null;\n\n  /** @internal */\n  constructor(\n    configResolver: (context?: ConfigContext) => Promise<ResolvedConnectionConfig>,\n    sttConfigInput: SttConfigInput,\n    source: AudioSource,\n    options?: {\n      buffer_queue_size?: number;\n      session_options?: SttSessionOptions;\n      signal?: AbortSignal;\n      auto_reconnect?: boolean;\n      max_reconnect_attempts?: number;\n      reconnect_base_delay_ms?: number;\n      reset_transcript_on_reconnect?: boolean;\n    }\n  ) {\n    this.configResolver = configResolver;\n    this.sttConfigInput = sttConfigInput;\n    this.source = source;\n    this.maxBufferSize = options?.buffer_queue_size ?? DEFAULT_BUFFER_QUEUE_SIZE;\n    this.sessionOptions = options?.session_options;\n    this.signal = options?.signal;\n    this.autoReconnect = options?.auto_reconnect ?? false;\n    this.maxReconnectAttempts = options?.max_reconnect_attempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;\n    this.reconnectBaseDelay = options?.reconnect_base_delay_ms ?? DEFAULT_RECONNECT_BASE_DELAY_MS;\n    this.resetTranscriptOnReconnect = options?.reset_transcript_on_reconnect ?? false;\n\n    // Start the async lifecycle on the next microtask so callers can attach listeners first\n    queueMicrotask(() => {\n      void this.run();\n    });\n  }\n\n  /**\n   * Current recording state\n   */\n  get state(): RecordingState {\n    return this._state;\n  }\n\n  /**\n   * Register an event handler\n   */\n  on<E extends keyof RecordingEvents>(event: E, handler: RecordingEvents[E]): this {\n    this.emitter.on(event, handler);\n    return this;\n  }\n\n  /**\n   * Register a one-time event handler\n   */\n  once<E extends keyof RecordingEvents>(event: E, handler: RecordingEvents[E]): this {\n    this.emitter.once(event, handler);\n    return this;\n  }\n\n  /**\n   * Remove an event handler\n   */\n  off<E extends keyof RecordingEvents>(event: E, handler: RecordingEvents[E]): this {\n    this.emitter.off(event, handler);\n    return this;\n  }\n\n  /**\n   * Gracefully stop recording\n   *\n   * Stops the audio source and waits for the server to process all\n   * buffered audio and return final results.\n   *\n   * @returns Promise that resolves when the server acknowledges completion\n   */\n  async stop(): Promise<void> {\n    if (this._state === 'stopped' || this._state === 'canceled' || this._state === 'error') {\n      return;\n    }\n\n    if (this._state === 'stopping') {\n      // Already stopping, return existing promise\n      return new Promise<void>((resolve, reject) => {\n        this.once('finished', resolve);\n        this.once('error', reject);\n      });\n    }\n\n    this.setState('stopping', 'user_action');\n    this.source.stop();\n\n    if (this.session && this.session.state === 'connected') {\n      const finishPromise = new Promise<void>((resolve, reject) => {\n        this.stopResolver = resolve;\n        this.stopRejecter = reject;\n      });\n\n      try {\n        await this.session.finish();\n      } catch (err) {\n        const error = err instanceof Error ? err : new Error(String(err));\n        this.settleStop(error);\n        this.cleanup('error', 'error');\n        return;\n      }\n\n      return finishPromise;\n    }\n\n    // Session not yet created or still mid-connect — run() or\n    // shouldAbortReconnect() will detect 'stopping' and settle.\n    const sessionPending = !this.session || this.session.state === 'idle' || this.session.state === 'connecting';\n    if (sessionPending) {\n      return new Promise<void>((resolve, reject) => {\n        this.stopResolver = resolve;\n        this.stopRejecter = reject;\n      });\n    }\n\n    // Session is in a terminal state (closed/error/finished/canceled) —\n    // nothing will settle a deferred promise, so resolve immediately.\n    this.session?.close();\n    this.settleStop();\n    this.cleanup('stopped', 'user_action');\n  }\n\n  /**\n   * Immediately cancel recording without waiting for final results\n   */\n  cancel(): void {\n    if (this._state === 'stopped' || this._state === 'canceled' || this._state === 'error') {\n      return;\n    }\n\n    this.source.stop();\n    this.session?.close();\n    this.cleanup('canceled', 'user_action');\n  }\n\n  /**\n   * Request the server to finalize current non-final tokens.\n   */\n  finalize(options?: { trailing_silence_ms?: number }): void {\n    this.session?.finalize(options);\n  }\n\n  /**\n   * Pause recording.\n   *\n   * Pauses the audio source (stops microphone capture) and pauses the\n   * session (activates automatic keepalive to prevent server disconnect).\n   */\n  pause(): void {\n    if (this._state !== 'recording') return;\n    this.source.pause?.();\n    this.session?.pause();\n    this.setState('paused', 'user_action');\n  }\n\n  /**\n   * Resume recording after pause.\n   *\n   * Resumes the audio source and session. Audio capture and transmission\n   * continue from where they left off. If audio was buffered during a\n   * reconnect while paused, the buffer is drained now.\n   */\n  resume(): void {\n    if (this._state !== 'paused') return;\n    this.source.resume?.();\n    if (!this._isSourceMuted) {\n      this.session?.resume();\n    }\n    this.setState('recording', 'user_action');\n\n    // Drain any audio buffered during a reconnect that completed while paused.\n    if (this.isBuffering && this.session) {\n      this.isBuffering = false;\n      for (const chunk of this.audioBuffer) {\n        if (this.isTerminalState()) break;\n        this.session.sendAudio(chunk);\n      }\n      this.audioBuffer = [];\n    }\n  }\n\n  /**\n   * @internal Debug-only: simulate an unexpected network disconnection.\n   * Tears down the current session and feeds a retriable error into the\n   * error handler so the reconnection logic kicks in exactly as it would\n   * during a real connection drop.\n   */\n  __debugForceDisconnect(): void {\n    if (this._state !== 'recording' && this._state !== 'paused') return;\n    this.session?.close();\n    this.session = null;\n    this.handleError(new ConnectionError('Debug: simulated disconnect'));\n  }\n\n  /**\n   * Force a reconnection — tears down the current session and audio\n   * encoder, then establishes a new session via the standard reconnect\n   * flow (backoff, config re-resolution, buffer drain).\n   *\n   * Use this to recover from stale connections after platform lifecycle\n   * events such as laptop sleep/wake (web `visibilitychange`) or app\n   * backgrounding (React Native `AppState`).\n   *\n   * Requires `auto_reconnect` to be enabled. No-op when the recording\n   * is not in `recording` or `paused` state.\n   */\n  reconnect(): void {\n    if (this._state !== 'recording' && this._state !== 'paused') return;\n    if (!this.autoReconnect) return;\n    this.session?.close();\n    this.session = null;\n    this.handleError(new ConnectionError('Reconnect requested'));\n  }\n\n  private async run(): Promise<void> {\n    // Check abort before starting\n    if (this.signal?.aborted) {\n      this.handleAbort();\n      return;\n    }\n\n    const onAbort = () => this.handleAbort();\n    this.signal?.addEventListener('abort', onAbort, { once: true });\n\n    this.setState('starting', 'user_action');\n\n    try {\n      // Start audio source (begins buffering)\n      await this.source.start({\n        onData: (chunk) => this.handleAudioData(chunk),\n        onError: (err) => this.handleError(err),\n        onMuted: () => this.handleSourceMuted(),\n        onUnmuted: () => this.handleSourceUnmuted(),\n      });\n    } catch (error) {\n      this.signal?.removeEventListener('abort', onAbort);\n      this.handleError(error instanceof Error ? error : new Error(String(error)));\n      return;\n    }\n\n    // Check if aborted/stopped during source startup\n    if (this.isTerminalState()) {\n      this.signal?.removeEventListener('abort', onAbort);\n      return;\n    }\n\n    // Resolve connection config (API key + URLs)\n    let resolvedConfig: ResolvedConnectionConfig;\n    try {\n      resolvedConfig = await this.configResolver();\n    } catch (error) {\n      this.signal?.removeEventListener('abort', onAbort);\n      this.source.stop();\n      this.handleError(error instanceof Error ? error : new Error(String(error)));\n      return;\n    }\n\n    // Remove startup abort listener\n    this.signal?.removeEventListener('abort', onAbort);\n\n    // Check if cancelled/stopped during config resolution\n    if (this.isTerminalState()) {\n      this.source.stop();\n      return;\n    }\n\n    // Resolve session config (may depend on server-provided stt_defaults)\n    const sttConfig =\n      typeof this.sttConfigInput === 'function' ? this.sttConfigInput(resolvedConfig) : this.sttConfigInput;\n\n    // Create session and connect\n    this.setState('connecting', 'user_action');\n\n    const sessionOptions: SttSessionOptions = {\n      ...this.sessionOptions,\n    };\n    if (this.signal !== undefined) {\n      sessionOptions.signal = this.signal;\n    }\n\n    const session = new RealtimeSttSession(\n      resolvedConfig.api_key,\n      resolvedConfig.stt_ws_url,\n      sttConfig,\n      sessionOptions\n    );\n    this.session = session;\n\n    // Wire up session events\n    this.wireSessionEvents(session);\n\n    try {\n      await session.connect();\n    } catch (error) {\n      this.source.stop();\n      this.handleError(error instanceof Error ? error : new Error(String(error)));\n      return;\n    }\n\n    // Check if cancelled/errored during connect - close the session\n    if (this.isTerminalState()) {\n      session.close();\n      return;\n    }\n\n    const stoppingEarly = this._state === 'stopping';\n\n    // Drain buffered audio and switch to live mode\n    if (!stoppingEarly) {\n      this.setState('recording', 'connected');\n      this.emitter.emit('connected');\n    }\n\n    this.isBuffering = false;\n    for (const chunk of this.audioBuffer) {\n      if (this._state !== 'recording' && this._state !== 'stopping') {\n        break;\n      }\n      session.sendAudio(chunk);\n    }\n    this.audioBuffer = [];\n\n    // If stop() was called before connection was established, finish the\n    // session now so the server processes all buffered audio\n    if (stoppingEarly) {\n      try {\n        await session.finish();\n      } catch (err) {\n        const error = err instanceof Error ? err : new Error(String(err));\n        this.settleStop(error);\n        this.cleanup('error', 'error');\n      }\n      // The 'finished' event handler will settle the stop promise and clean up.\n    }\n  }\n\n  private handleAudioData(chunk: ArrayBuffer): void {\n    if (this.isBuffering) {\n      if (this.audioBuffer.length >= this.maxBufferSize) {\n        this.audioBuffer.shift();\n      }\n      this.audioBuffer.push(chunk);\n      return;\n    }\n\n    if (this.session && (this._state === 'recording' || this._state === 'stopping')) {\n      try {\n        this.session.sendAudio(chunk);\n      } catch {\n        // Errors are handled via session event proxying\n      }\n    }\n  }\n\n  private handleSourceMuted(): void {\n    if (!this.isMuteTrackingState()) return;\n    this._isSourceMuted = true;\n    // Only toggle session keepalive when in recording state.\n    // When paused/reconnecting/connecting, session is already paused or absent.\n    if (this._state === 'recording') {\n      this.session?.pause();\n    }\n    this.emitter.emit('source_muted');\n  }\n\n  private handleSourceUnmuted(): void {\n    if (!this.isMuteTrackingState()) return;\n    this._isSourceMuted = false;\n    // Only resume session when in recording state.\n    // When paused, user pause takes precedence — don't resume.\n    if (this._state === 'recording') {\n      this.session?.resume();\n    }\n    this.emitter.emit('source_unmuted');\n  }\n\n  private isMuteTrackingState(): boolean {\n    const s = this._state;\n    return s === 'recording' || s === 'paused' || s === 'reconnecting' || s === 'connecting';\n  }\n\n  private wireSessionEvents(session: RealtimeSttSession): void {\n    session.on('result', (result) => this.emitter.emit('result', result));\n    session.on('token', (token) => this.emitter.emit('token', token));\n    session.on('endpoint', () => this.emitter.emit('endpoint'));\n    session.on('finalized', () => this.emitter.emit('finalized'));\n\n    session.on('finished', () => {\n      this.source.stop();\n      this.emitter.emit('finished');\n      this.settleStop();\n      this.cleanup('stopped', 'finished');\n    });\n\n    session.on('error', (error) => {\n      this.handleError(error);\n    });\n  }\n\n  private handleAbort(): void {\n    if (this.isTerminalState()) {\n      return;\n    }\n\n    this.source.stop();\n    this.session?.close();\n    const error = new Error('Recording aborted');\n    this.emitter.emit('error', error);\n    this.settleStop(error);\n    this.cleanup('canceled', 'user_action');\n  }\n\n  private handleError(error: Error): void {\n    if (this._state === 'error' || this._state === 'stopped' || this._state === 'canceled') {\n      return;\n    }\n\n    if (this.shouldReconnect(error)) {\n      void this.attemptReconnect(error);\n      return;\n    }\n\n    this.source.stop();\n    this.session?.close();\n    this.emitter.emit('error', error);\n    this.settleStop(error);\n    this.cleanup('error', 'error');\n  }\n\n  private shouldReconnect(error: Error): boolean {\n    if (!this.autoReconnect) return false;\n    if (this._state === 'stopping') return false;\n    if (this._state === 'reconnecting') return false;\n    if (this._reconnectAttempt >= this.maxReconnectAttempts) return false;\n    return isRetriableError(error);\n  }\n\n  private async attemptReconnect(triggerError: Error): Promise<void> {\n    // Capture pause state before we change recording state.\n    // Mute state is read later (at restoration time) so hardware\n    // mute/unmute that occurs during backoff is not lost.\n    const wasPaused = this._state === 'paused';\n\n    // Tear down old session (but NOT the audio source).\n    this.session?.close();\n    this.session = null;\n\n    // Switch to buffering mode — audio source keeps running.\n    this.isBuffering = true;\n    this.audioBuffer = [];\n\n    // Reinitialize the audio encoder immediately so audio captured\n    // during the reconnect window carries fresh container headers and\n    // can be drained to the new session. Skip when paused — the\n    // source isn't producing data, and we'll restart after connect.\n    if (!wasPaused) {\n      this.source.restart?.();\n    }\n\n    this._reconnectAttempt++;\n    this.setState('reconnecting', 'connection_lost');\n\n    const delay = this.reconnectBaseDelay * Math.pow(2, this._reconnectAttempt - 1);\n\n    // Emit reconnecting event with preventDefault support.\n    let prevented = false;\n    this.emitter.emit('reconnecting', {\n      attempt: this._reconnectAttempt,\n      max_attempts: this.maxReconnectAttempts,\n      delay_ms: delay,\n      preventDefault: () => {\n        prevented = true;\n      },\n    });\n\n    if (prevented) {\n      this.source.stop();\n      this.emitter.emit('error', triggerError);\n      this.settleStop(triggerError);\n      this.cleanup('error', 'error');\n      return;\n    }\n\n    // Wait backoff delay.\n    await new Promise((r) => setTimeout(r, delay));\n\n    if (this.shouldAbortReconnect()) return;\n\n    // Re-resolve config (fresh API key).\n    let resolvedConfig: ResolvedConnectionConfig;\n    try {\n      resolvedConfig = await this.configResolver();\n    } catch (err) {\n      this.handleError(err instanceof Error ? err : new Error(String(err)));\n      return;\n    }\n\n    if (this.shouldAbortReconnect()) return;\n\n    const sttConfig =\n      typeof this.sttConfigInput === 'function' ? this.sttConfigInput(resolvedConfig) : this.sttConfigInput;\n\n    this.setState('connecting', 'reconnecting');\n\n    const sessionOptions: SttSessionOptions = {\n      ...this.sessionOptions,\n    };\n    if (this.signal !== undefined) {\n      sessionOptions.signal = this.signal;\n    }\n\n    const session = new RealtimeSttSession(\n      resolvedConfig.api_key,\n      resolvedConfig.stt_ws_url,\n      sttConfig,\n      sessionOptions\n    );\n    this.session = session;\n    this.wireSessionEvents(session);\n\n    try {\n      await session.connect();\n    } catch (err) {\n      this.handleError(err instanceof Error ? err : new Error(String(err)));\n      return;\n    }\n\n    if (this.shouldAbortReconnect()) return;\n\n    // Signal store to reset window-tracking state before new results arrive.\n    this.emitter.emit('session_restart', { reset_transcript: this.resetTranscriptOnReconnect });\n\n    // Read mute state at restoration time so hardware changes during\n    // backoff / connect are not lost.\n    const currentlyMuted = this._isSourceMuted;\n\n    // Restore pause/mute state on the new session.\n    if (wasPaused || currentlyMuted) {\n      session.pause();\n    }\n\n    if (wasPaused) {\n      // Restart encoder now (was skipped above since source was paused)\n      // and immediately re-pause so resume() can drain later.\n      this.source.restart?.();\n      this.source.pause?.();\n      this.setState('paused', 'reconnected');\n    } else {\n      this.setState('recording', 'reconnected');\n      // Drain audio buffered during the reconnect window. The encoder\n      // was restarted early so these chunks carry fresh container headers.\n      this.isBuffering = false;\n      for (const chunk of this.audioBuffer) {\n        if (this.isTerminalState()) break;\n        session.sendAudio(chunk);\n      }\n      this.audioBuffer = [];\n    }\n\n    this.emitter.emit('connected');\n    this.emitter.emit('reconnected', { attempt: this._reconnectAttempt });\n    this._reconnectAttempt = 0;\n  }\n\n  /**\n   * Check whether an in-flight reconnect should be aborted.\n   * Handles both terminal states and a pending stop() request.\n   */\n  private shouldAbortReconnect(): boolean {\n    if (this.isTerminalState()) return true;\n    if (this._state === 'stopping') {\n      this.settleStop();\n      this.cleanup('stopped', 'user_action');\n      return true;\n    }\n    return false;\n  }\n\n  private cleanup(finalState: RecordingState, reason?: StateChangeReason): void {\n    this.setState(finalState, reason);\n    this.audioBuffer = [];\n    this.isBuffering = false;\n    this._isSourceMuted = false;\n    this._reconnectAttempt = 0;\n  }\n\n  private setState(newState: RecordingState, reason?: StateChangeReason): void {\n    if (this._state === newState) {\n      return;\n    }\n    const oldState = this._state;\n    this._state = newState;\n    const update: { old_state: RecordingState; new_state: RecordingState; reason?: StateChangeReason } = {\n      old_state: oldState,\n      new_state: newState,\n    };\n    if (reason !== undefined) {\n      update.reason = reason;\n    }\n    this.emitter.emit('state_change', update);\n  }\n\n  private isTerminalState(): boolean {\n    return TERMINAL_STATES.includes(this._state);\n  }\n\n  private settleStop(error?: Error): void {\n    const resolve = this.stopResolver;\n    const reject = this.stopRejecter;\n    this.stopResolver = null;\n    this.stopRejecter = null;\n\n    if (error) {\n      reject?.(error);\n    } else {\n      resolve?.();\n    }\n  }\n}\n","/**\n * SonioxClient - main entry point for the @soniox/client SDK\n *\n * Provides high-level `record()` for audio capture + transcription,\n * and low-level `stt()` for direct WebSocket session access\n */\n\nimport {\n  RealtimeSttSession,\n  RealtimeTtsConnection,\n  SonioxError,\n  TtsRestClient,\n  resolveConnectionConfig,\n} from '@soniox/core';\nimport type {\n  ConfigContext,\n  RealtimeTtsStream,\n  SonioxConnectionConfig,\n  ResolvedConnectionConfig,\n  GenerateSpeechOptions,\n  SttSessionConfig,\n  SttSessionOptions,\n  TtsStreamInput,\n} from '@soniox/core';\n\nimport { MicrophoneSource } from './audio/microphone.js';\nimport type { ApiKeyConfig } from './auth.js';\nimport { resolveApiKey } from './auth.js';\nimport type { PermissionResolver } from './permissions/types.js';\nimport type { RecordOptions, SttConfigInput } from './recording.js';\nimport { Recording } from './recording.js';\n\nconst SONIOX_WS_URL = 'wss://stt-rt.soniox.com/transcribe-websocket';\n\n/**\n * Options for creating a SonioxClient instance.\n */\nexport type SonioxClientOptions = {\n  /**\n   * Connection configuration — sync object or async function.\n   *\n   * When provided as a function, it is called once per recording session,\n   * allowing you to fetch a fresh temporary API key and connection settings\n   * from your backend at runtime.\n   *\n   * @example\n   * ```typescript\n   * // Sync config with region\n   * const client = new SonioxClient({\n   *   config: { api_key: tempKey, region: 'eu' },\n   * });\n   *\n   * // Async config (recommended for production)\n   * const client = new SonioxClient({\n   *   config: async () => {\n   *     const res = await fetch('/api/soniox-config', { method: 'POST' });\n   *     return await res.json(); // { api_key, region, ... }\n   *   },\n   * });\n   * ```\n   */\n  config?: SonioxConnectionConfig | ((context?: ConfigContext) => Promise<SonioxConnectionConfig>) | undefined;\n\n  /**\n   * API key configuration.\n   *\n   * - `string` - A pre-fetched temporary API key (e.g., injected from SSR)\n   * - `() => Promise<string>` - Async function that fetches a fresh key from your backend\n   *\n   * @deprecated Use `config` instead.\n   */\n  api_key?: ApiKeyConfig | undefined;\n\n  /**\n   * WebSocket URL for real-time connections.\n   * @default 'wss://stt-rt.soniox.com/transcribe-websocket'\n   * @deprecated Use `config.stt_ws_url` or `config.region` instead.\n   */\n  ws_base_url?: string | undefined;\n\n  /**\n   * Optional permission resolver for pre-flight microphone permission checks.\n   * Not set by default (SSR-safe, RN-safe).\n   *\n   * @example\n   * ```typescript\n   * import { BrowserPermissionResolver } from '@soniox/client';\n   * const client = new SonioxClient({\n   *   config: { api_key: tempKey },\n   *   permissions: new BrowserPermissionResolver(),\n   * });\n   * ```\n   */\n  permissions?: PermissionResolver | undefined;\n\n  /**\n   * Default maximum number of audio chunks to buffer while waiting for key/connection.\n   * Can be overridden per-recording.\n   * @default 1000\n   */\n  buffer_queue_size?: number | undefined;\n\n  /**\n   * Default session options applied to all sessions.\n   * Can be overridden per-recording.\n   */\n  default_session_options?: SttSessionOptions | undefined;\n};\n\n/**\n * Options for creating a low-level STT session.\n */\nexport type SttOptions = {\n  /**\n   * Resolved API key string (temporary key).\n   */\n  api_key: string;\n\n  /**\n   * Session options (signal, etc.).\n   */\n  session_options?: SttSessionOptions | undefined;\n};\n\n/**\n * Main entry point for the Soniox client SDK.\n *\n * @example\n * ```typescript\n * // Recommended: async config with region\n * const client = new SonioxClient({\n *   config: async () => {\n *     const res = await fetch('/api/soniox-config', { method: 'POST' });\n *     return await res.json(); // { api_key, region }\n *   },\n * });\n *\n * // High-level: record from microphone\n * const recording = client.realtime.record({ model: 'stt-rt-v5' });\n * recording.on('result', (r) => console.log(r.tokens));\n * await recording.stop();\n *\n * // Low-level: direct session access\n * const session = client.realtime.stt({ model: 'stt-rt-v5' }, { api_key: key });\n * await session.connect();\n * ```\n */\nexport class SonioxClient {\n  /** @internal */\n  readonly _configResolver: (context?: ConfigContext) => Promise<ResolvedConnectionConfig>;\n  /**\n   * STT WebSocket URL resolved at construction time from the client's `config`,\n   * when that config is a plain object (not an async function). Used by the\n   * synchronous low-level `client.realtime.stt()` factory so it honors the\n   * configured region. Remains `undefined` for async-config clients — those\n   * must supply `ws_base_url` or use `client.realtime.record()`.\n   *\n   * @internal\n   */\n  private readonly preResolvedSttWsUrl: string | undefined;\n  private readonly permissionResolver: PermissionResolver | undefined;\n  private readonly defaultBufferQueueSize: number;\n  private readonly defaultSessionOptions: SttSessionOptions | undefined;\n  private readonly wsBaseUrl: string | undefined;\n\n  /**\n   * Real-time API namespace\n   */\n  readonly realtime: {\n    /**\n     * Start a high-level recording session.\n     *\n     * Returns synchronously so callers can attach event listeners before\n     * any async work (key fetch, mic access, connection) begins.\n     *\n     * @param options - Session config + recording options\n     * @returns Recording instance\n     */\n    record: (options: RecordOptions) => Recording;\n\n    /**\n     * Create a low-level STT session.\n     *\n     * The WebSocket URL is derived from the client's `config` (respecting\n     * `region` / `base_domain` / `stt_ws_url`) when `config` is a plain\n     * object, or from `ws_base_url` on the legacy path. If `config` was\n     * passed as an async function, call `client.realtime.record()` instead,\n     * or pass `ws_base_url` explicitly to `SonioxClient`.\n     *\n     * @param config - Session configuration (sent to server)\n     * @param options - API key and session options\n     * @returns RealtimeSttSession instance\n     * @throws {@link SonioxError} if the WebSocket URL cannot be resolved\n     *   synchronously (async-config client without `ws_base_url`).\n     */\n    stt: (config: SttSessionConfig, options: SttOptions) => RealtimeSttSession;\n\n    /**\n     * TTS factory — callable for single-stream, `.multiStream()` for multi-stream.\n     *\n     * Uses the client's config resolver to obtain credentials and TTS WebSocket URL.\n     *\n     * @example Single stream\n     * ```typescript\n     * const stream = await client.realtime.tts({\n     *   model: 'tts-rt-v1',\n     *   voice: 'Adrian',\n     *   language: 'en',\n     *   audio_format: 'wav',\n     * });\n     * stream.sendText(\"Hello\");\n     * stream.finish();\n     * for await (const chunk of stream) { process(chunk); }\n     * ```\n     *\n     * @example Multi-stream\n     * ```typescript\n     * const conn = await client.realtime.tts.multiStream();\n     * const s1 = await conn.stream({\n     *   model: 'tts-rt-v1',\n     *   voice: 'Adrian',\n     *   language: 'en',\n     *   audio_format: 'wav',\n     * });\n     * ```\n     */\n    tts: ClientTtsFactory;\n  };\n\n  /**\n   * REST TTS API namespace.\n   *\n   * @example\n   * ```typescript\n   * const audio = await client.tts.generate({\n   *   text: 'Hello',\n   *   voice: 'Adrian',\n   *   language: 'en',\n   * });\n   * ```\n   */\n  readonly tts: {\n    /**\n     * Generate speech audio from text. Returns the full audio as a `Uint8Array`.\n     */\n    generate(options: GenerateSpeechOptions): Promise<Uint8Array>;\n    /**\n     * Generate speech audio as a streaming async iterable.\n     * Yields `Uint8Array` chunks as they arrive.\n     */\n    generateStream(options: GenerateSpeechOptions): AsyncIterable<Uint8Array>;\n  };\n\n  constructor(options: SonioxClientOptions) {\n    if (options.config !== undefined && options.api_key !== undefined) {\n      throw new Error('Cannot specify both `config` and `api_key`. Use `config` for new code.');\n    }\n    if (options.config === undefined && options.api_key === undefined) {\n      throw new Error('Either `config` or `api_key` must be provided.');\n    }\n\n    const { resolver, preResolvedSttWsUrl } = buildConfigResolver(options);\n    this._configResolver = resolver;\n    this.preResolvedSttWsUrl = preResolvedSttWsUrl;\n    this.wsBaseUrl = options.ws_base_url;\n    this.permissionResolver = options.permissions;\n    this.defaultBufferQueueSize = options.buffer_queue_size ?? 1000;\n    this.defaultSessionOptions = options.default_session_options;\n\n    const ttsCall = (input?: TtsStreamInput): Promise<RealtimeTtsStream> => this.createSingleTtsStream(input ?? {});\n    ttsCall.multiStream = (): Promise<RealtimeTtsConnection> => this.createTtsConnection();\n\n    this.tts = {\n      generate: (opts: GenerateSpeechOptions) => this.ttsRestGenerate(opts),\n      generateStream: (opts: GenerateSpeechOptions) => this.ttsRestGenerateStream(opts),\n    };\n\n    this.realtime = {\n      record: (recordOptions: RecordOptions) => this.createRecording(recordOptions),\n      stt: (config: SttSessionConfig, sttOptions: SttOptions) => this.createSession(config, sttOptions),\n      tts: ttsCall,\n    };\n  }\n\n  /**\n   * Permission resolver, if configured.\n   * Returns `undefined` if no resolver was provided (SSR-safe).\n   *\n   * @example\n   * ```typescript\n   * const mic = await client.permissions?.check('microphone');\n   * if (mic?.status === 'denied') {\n   *   showSettingsMessage();\n   * }\n   * ```\n   */\n  get permissions(): PermissionResolver | undefined {\n    return this.permissionResolver;\n  }\n\n  private createRecording(options: RecordOptions): Recording {\n    // Extract recording-specific options from the combined config\n    const {\n      source,\n      signal,\n      buffer_queue_size,\n      session_options,\n      session_config,\n      auto_reconnect,\n      max_reconnect_attempts,\n      reconnect_base_delay_ms,\n      reset_transcript_on_reconnect,\n      ...sttConfig\n    } = options;\n\n    const audioSource = source ?? new MicrophoneSource();\n\n    // When session_config function is provided, use it; otherwise use flat fields\n    const sttConfigInput: SttConfigInput = session_config ?? sttConfig;\n\n    return new Recording(this._configResolver, sttConfigInput, audioSource, {\n      buffer_queue_size: buffer_queue_size ?? this.defaultBufferQueueSize,\n      session_options: {\n        ...this.defaultSessionOptions,\n        ...session_options,\n        ...(signal !== undefined ? { signal } : {}),\n      },\n      ...(signal !== undefined ? { signal } : {}),\n      ...(auto_reconnect !== undefined ? { auto_reconnect } : {}),\n      ...(max_reconnect_attempts !== undefined ? { max_reconnect_attempts } : {}),\n      ...(reconnect_base_delay_ms !== undefined ? { reconnect_base_delay_ms } : {}),\n      ...(reset_transcript_on_reconnect !== undefined ? { reset_transcript_on_reconnect } : {}),\n    });\n  }\n\n  private createSession(config: SttSessionConfig, options: SttOptions): RealtimeSttSession {\n    const mergedSessionOptions: SttSessionOptions = {\n      ...this.defaultSessionOptions,\n      ...options.session_options,\n    };\n\n    const wsUrl = this.wsBaseUrl ?? this.preResolvedSttWsUrl;\n    if (wsUrl === undefined) {\n      throw new SonioxError(\n        'Cannot resolve STT WebSocket URL synchronously because `config` was provided as an async function. ' +\n          'Either pass `ws_base_url` to `SonioxClient`, use a sync `config` object, ' +\n          'or call `client.realtime.record()` which supports async config.',\n        'state_error'\n      );\n    }\n    return new RealtimeSttSession(options.api_key, wsUrl, config, mergedSessionOptions);\n  }\n\n  private async createSingleTtsStream(input: TtsStreamInput): Promise<RealtimeTtsStream> {\n    const resolved = await this._configResolver({ usage: 'tts_rt' });\n    const connection = new RealtimeTtsConnection(resolved.api_key, resolved.tts_ws_url, resolved.tts_defaults);\n    return connection._openStream(input, true);\n  }\n\n  private async createTtsConnection(): Promise<RealtimeTtsConnection> {\n    const resolved = await this._configResolver({ usage: 'tts_rt' });\n    const connection = new RealtimeTtsConnection(resolved.api_key, resolved.tts_ws_url, resolved.tts_defaults);\n    await connection.connect();\n    return connection;\n  }\n\n  private async ttsRestGenerate(options: GenerateSpeechOptions): Promise<Uint8Array> {\n    const resolved = await this._configResolver({ usage: 'tts_rt' });\n    const rest = new TtsRestClient(resolved.api_key, resolved.tts_api_url);\n    return rest.generate(options);\n  }\n\n  private async *ttsRestGenerateStream(options: GenerateSpeechOptions): AsyncIterable<Uint8Array> {\n    const resolved = await this._configResolver({ usage: 'tts_rt' });\n    const rest = new TtsRestClient(resolved.api_key, resolved.tts_api_url);\n    yield* rest.generateStream(options);\n  }\n}\n\n/**\n * Callable TTS factory with `.multiStream()` for multi-stream connections.\n */\nexport interface ClientTtsFactory {\n  (input?: TtsStreamInput): Promise<RealtimeTtsStream>;\n  multiStream(): Promise<RealtimeTtsConnection>;\n}\n\ntype ConfigResolver = (context?: ConfigContext) => Promise<ResolvedConnectionConfig>;\n\ntype BuiltConfigResolver = {\n  resolver: ConfigResolver;\n  /**\n   * STT WebSocket URL available synchronously (when `config` is a plain object\n   * or on the legacy `api_key` path). `undefined` when the user passed an\n   * async `config` function — the URL is only known after the function runs.\n   */\n  preResolvedSttWsUrl: string | undefined;\n};\n\nfunction buildConfigResolver(options: SonioxClientOptions): BuiltConfigResolver {\n  if (options.config !== undefined) {\n    const configInput = options.config;\n    const resolver: ConfigResolver = async (context) => {\n      const raw = typeof configInput === 'function' ? await configInput(context) : configInput;\n      return resolveConnectionConfig(raw);\n    };\n\n    // If the config is a plain object we can pre-resolve the URLs for the\n    // synchronous low-level `client.realtime.stt()` factory.\n    const preResolvedSttWsUrl =\n      typeof configInput === 'function' ? undefined : resolveConnectionConfig(configInput).stt_ws_url;\n\n    return { resolver, preResolvedSttWsUrl };\n  }\n\n  // Legacy path: api_key (+ optional ws_base_url)\n  const apiKeyConfig = options.api_key!;\n  const wsBaseUrl = options.ws_base_url ?? SONIOX_WS_URL;\n  const resolver: ConfigResolver = async () => {\n    const apiKey = await resolveApiKey(apiKeyConfig);\n    return {\n      api_key: apiKey,\n      api_domain: 'https://api.soniox.com',\n      stt_ws_url: wsBaseUrl,\n      tts_api_url: 'https://tts-rt.soniox.com',\n      tts_ws_url: 'wss://tts-rt.soniox.com/tts-websocket',\n      stt_defaults: {},\n      tts_defaults: {},\n      session_defaults: {},\n    };\n  };\n  return { resolver, preResolvedSttWsUrl: wsBaseUrl };\n}\n","/**\n * Browser implementation of PermissionResolver.\n *\n * Uses `navigator.permissions.query` (with Safari fallback) for checking\n * and `getUserMedia` for requesting microphone permission.\n */\n\nimport type { PermissionResolver, PermissionResult, PermissionType } from './types.js';\n\n/**\n * Browser permission resolver for checking and requesting microphone access.\n *\n * @example\n * ```typescript\n * const resolver = new BrowserPermissionResolver();\n * const mic = await resolver.check('microphone');\n * if (mic.status === 'prompt') {\n *   const result = await resolver.request('microphone');\n *   if (result.status === 'denied') {\n *     showDeniedMessage();\n *   }\n * }\n * ```\n */\nexport class BrowserPermissionResolver implements PermissionResolver {\n  /**\n   * Check current microphone permission status without prompting the user.\n   */\n  async check(permission: PermissionType): Promise<PermissionResult> {\n    if (permission === 'microphone') {\n      return this.checkMicrophone();\n    }\n    return { status: 'unavailable', can_request: false };\n  }\n\n  /**\n   * Request microphone permission from the user.\n   * This may show a browser permission prompt.\n   */\n  async request(permission: PermissionType): Promise<PermissionResult> {\n    if (permission === 'microphone') {\n      return this.requestMicrophone();\n    }\n    return { status: 'unavailable', can_request: false };\n  }\n\n  private async checkMicrophone(): Promise<PermissionResult> {\n    // Try the Permissions API first\n    if (typeof navigator !== 'undefined' && navigator.permissions?.query) {\n      try {\n        const result = await navigator.permissions.query({\n          name: 'microphone' as PermissionName,\n        });\n        return {\n          status: result.state === 'prompt' ? 'prompt' : result.state,\n          can_request: result.state !== 'denied',\n        };\n      } catch {\n        // Safari and some browsers don't support querying 'microphone'\n        // Fall through to capability check\n      }\n    }\n\n    // Fallback: check if getUserMedia is available at all\n    if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n      return { status: 'unavailable', can_request: false };\n    }\n\n    // Can't determine status without prompting\n    return { status: 'prompt', can_request: true };\n  }\n\n  private async requestMicrophone(): Promise<PermissionResult> {\n    if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n      return { status: 'unavailable', can_request: false };\n    }\n\n    try {\n      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n      stream.getTracks().forEach((track) => track.stop());\n      return { status: 'granted', can_request: true };\n    } catch (err) {\n      if (err instanceof DOMException) {\n        if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {\n          return { status: 'denied', can_request: false };\n        }\n        if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {\n          return { status: 'unavailable', can_request: false };\n        }\n      }\n      return { status: 'denied', can_request: false };\n    }\n  }\n}\n"],"mappings":"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"}