@soniox/node 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["buildSegment","DEFAULT_KEEPALIVE_INTERVAL_MS","MIN_KEEPALIVE_INTERVAL_MS","DEFAULT_CONNECT_TIMEOUT_MS","error","buildSegment","DEFAULT_TIMEOUT_MS"],"sources":["../src/constants.ts","../../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/async/auth.ts","../src/async/files.ts","../src/async/models.ts","../src/async/stt.ts","../src/async/tts.ts","../src/async/webhooks.ts","../src/http/url.ts","../src/http/fetch-adapter.ts","../src/realtime/index.ts","../src/client.ts"],"sourcesContent":["// API base URLs\nexport const SONIOX_API_BASE_URL = 'https://api.soniox.com';\nexport const SONIOX_API_WS_URL = 'wss://stt-rt.soniox.com/transcribe-websocket';\nexport const SONIOX_TTS_API_BASE_URL = 'https://tts-rt.soniox.com';\nexport const SONIOX_TTS_WS_URL = 'wss://tts-rt.soniox.com/tts-websocket';\n\n// Temporary API key\nexport const SONIOX_TMP_API_KEY_USAGE_TYPE = 'transcribe_websocket';\nexport const SONIOX_TMP_API_KEY_DURATION_MIN = 1;\nexport const SONIOX_TMP_API_KEY_DURATION_MAX = 3600;\n\n// Webhook authentication environment variables\nexport const SONIOX_API_WEBHOOK_HEADER_ENV = 'SONIOX_API_WEBHOOK_HEADER';\nexport const SONIOX_API_WEBHOOK_SECRET_ENV = 'SONIOX_API_WEBHOOK_SECRET';\n","/**\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 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-v4' });\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-preview';\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","import type { HttpClient } from '../http/client.js';\nimport type { TemporaryApiKeyRequest, TemporaryApiKeyResponse } from '../types/public/index.js';\n\nexport class SonioxAuthAPI {\n constructor(private http: HttpClient) {}\n\n /**\n * Creates a temporary API key for client-side use.\n *\n * @param request - Request parameters for the temporary key\n * @param signal - Optional AbortSignal for cancellation\n * @returns The temporary API key response\n *\n * @example\n * ```typescript\n * const sttKey = await client.auth.createTemporaryKey({\n * usage_type: 'transcribe_websocket',\n * expires_in_seconds: 300,\n * });\n *\n * const ttsKey = await client.auth.createTemporaryKey({\n * usage_type: 'tts_rt',\n * expires_in_seconds: 300,\n * });\n * ```\n */\n async createTemporaryKey(request: TemporaryApiKeyRequest, signal?: AbortSignal): Promise<TemporaryApiKeyResponse> {\n if (\n !Number.isFinite(request.expires_in_seconds) ||\n request.expires_in_seconds < 1 ||\n request.expires_in_seconds > 3600\n ) {\n throw new Error('expires_in_seconds must be a finite number between 1 and 3600');\n }\n\n const response = await this.http.request<TemporaryApiKeyResponse>({\n method: 'POST',\n path: '/v1/auth/temporary-api-key',\n body: request,\n ...(signal && { signal }),\n });\n\n return response.data;\n }\n}\n","import type { HttpClient } from '../http/client.js';\nimport { isNotFoundError } from '../http/errors.js';\nimport type {\n FileIdentifier,\n ListFilesOptions,\n ListFilesResponse,\n DeleteAllFilesOptions,\n SonioxFileData,\n UploadFileInput,\n UploadFileOptions,\n} from '../types/public/index.js';\n\n/**\n * Uploaded file\n */\nexport class SonioxFile {\n readonly id: string;\n readonly filename: string;\n readonly size: number;\n readonly created_at: string;\n readonly client_reference_id: string | undefined;\n\n constructor(\n data: SonioxFileData,\n private readonly _http: HttpClient\n ) {\n this.id = data.id;\n this.filename = data.filename;\n this.size = data.size;\n this.created_at = data.created_at;\n\n if (data.client_reference_id) {\n if (data.client_reference_id.length > 256) {\n throw new Error('client_reference_id exceeds maximum length of 256 characters');\n }\n\n this.client_reference_id = data.client_reference_id;\n }\n }\n\n /**\n * Returns the raw data for this file.\n */\n toJSON(): SonioxFileData {\n return {\n id: this.id,\n filename: this.filename,\n size: this.size,\n created_at: this.created_at,\n client_reference_id: this.client_reference_id,\n };\n }\n\n /**\n * Permanently deletes this file.\n * This operation is idempotent - succeeds even if the file doesn't exist.\n *\n * @param signal - Optional AbortSignal for cancellation\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const file = await client.files.get('550e8400-e29b-41d4-a716-446655440000');\n * if (file) {\n * await file.delete();\n * }\n * ```\n */\n async delete(signal?: AbortSignal): Promise<void> {\n try {\n await this._http.request<null>({\n method: 'DELETE',\n path: `/v1/files/${this.id}`,\n ...(signal && { signal }),\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n}\n\n/**\n * Result set for file listing\n */\nexport class FileListResult implements AsyncIterable<SonioxFile> {\n /**\n * Files from the first page of results\n */\n readonly files: SonioxFile[];\n\n /**\n * Pagination cursor for the next page. Null if no more pages\n */\n readonly next_page_cursor: string | null;\n\n constructor(\n initialResponse: ListFilesResponse<SonioxFileData>,\n private readonly _http: HttpClient,\n private readonly _limit: number | undefined,\n private readonly _signal: AbortSignal | undefined = undefined\n ) {\n this.files = initialResponse.files.map((data) => new SonioxFile(data, _http));\n this.next_page_cursor = initialResponse.next_page_cursor;\n }\n\n /**\n * Returns the raw data for this list result.\n * Also used by JSON.stringify() to prevent serialization of internal HTTP client.\n */\n toJSON(): ListFilesResponse<SonioxFileData> {\n return {\n files: this.files.map((f) => f.toJSON()),\n next_page_cursor: this.next_page_cursor,\n };\n }\n\n /**\n * Returns true if there are more pages of results beyond the first page\n */\n isPaged(): boolean {\n return this.next_page_cursor !== null;\n }\n\n /**\n * Async iterator that automatically fetches all pages\n * Use with `for await...of` to iterate through all files\n */\n async *[Symbol.asyncIterator](): AsyncIterator<SonioxFile> {\n // Yield files from the first page\n for (const file of this.files) {\n yield file;\n }\n\n // Fetch and yield subsequent pages\n let cursor = this.next_page_cursor;\n while (cursor !== null) {\n const response = await this._http.request<ListFilesResponse<SonioxFileData>>({\n method: 'GET',\n path: '/v1/files',\n query: {\n limit: this._limit,\n cursor,\n },\n ...(this._signal && { signal: this._signal }),\n });\n\n for (const data of response.data.files) {\n yield new SonioxFile(data, this._http);\n }\n\n cursor = response.data.next_page_cursor;\n }\n }\n}\n\n/**\n * Helper to extract file ID from a FileIdentifier\n */\nfunction getFileId(file: FileIdentifier): string {\n return typeof file === 'string' ? file : file.id;\n}\n\n/**\n * Maximum file size allowed by the API (1 GB)\n */\nconst MAX_FILE_SIZE = 1073741824;\n\n/**\n * Default filename when none can be inferred\n */\nconst DEFAULT_FILENAME = 'file';\n\n/**\n * Checks if the input is an async-iterable Node.js readable stream\n */\nfunction isNodeReadableStream(input: unknown): input is NodeJS.ReadableStream {\n return (\n typeof input === 'object' &&\n input !== null &&\n 'pipe' in input &&\n typeof (input as NodeJS.ReadableStream).pipe === 'function' &&\n Symbol.asyncIterator in input\n );\n}\n\n/**\n * Checks if the input is a Web ReadableStream\n */\nfunction isWebReadableStream(input: unknown): input is ReadableStream<Uint8Array> {\n return (\n typeof input === 'object' &&\n input !== null &&\n 'getReader' in input &&\n typeof (input as ReadableStream).getReader === 'function'\n );\n}\n\n/**\n * Collects chunks from a Node.js readable stream into a Buffer\n * Aborts early if size exceeds MAX_FILE_SIZE to prevent OOM\n */\nasync function collectNodeStream(stream: NodeJS.ReadableStream): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let totalLength = 0;\n\n for await (const chunk of stream as AsyncIterable<Buffer | Uint8Array | string>) {\n if (typeof chunk === 'string') {\n throw new Error(\n 'Stream returned string chunks. Use a binary stream (e.g., fs.createReadStream without encoding option).'\n );\n }\n\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\n totalLength += buf.length;\n if (totalLength > MAX_FILE_SIZE) {\n throw new Error(`File size exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n chunks.push(buf);\n }\n\n return Buffer.concat(chunks);\n}\n\n/**\n * Collects chunks from a Web ReadableStream into a Uint8Array\n * Aborts early if size exceeds MAX_FILE_SIZE to prevent OOM\n */\nasync function collectWebStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n totalLength += value.length;\n if (totalLength > MAX_FILE_SIZE) {\n throw new Error(`File size exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n}\n\n/**\n * Resolves the file input to a Blob and filename\n */\nasync function resolveFileInput(\n input: UploadFileInput,\n filenameOverride?: string\n): Promise<{ blob: Blob; filename: string }> {\n // Blob (includes File which has a name property)\n if (input instanceof Blob) {\n if (input.size > MAX_FILE_SIZE) {\n throw new Error(`File size (${input.size} bytes) exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n const filename =\n filenameOverride ?? ('name' in input && typeof input.name === 'string' ? input.name : DEFAULT_FILENAME);\n\n return {\n blob: input,\n filename,\n };\n }\n\n // Buffer\n if (Buffer.isBuffer(input)) {\n if (input.length > MAX_FILE_SIZE) {\n throw new Error(`File size (${input.length} bytes) exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n return {\n blob: new Blob([new Uint8Array(input)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n // Uint8Array (but not Buffer)\n if (input instanceof Uint8Array) {\n if (input.length > MAX_FILE_SIZE) {\n throw new Error(`File size (${input.length} bytes) exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n return {\n blob: new Blob([new Uint8Array(input)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n // Web ReadableStream\n if (isWebReadableStream(input)) {\n const data = await collectWebStream(input);\n return {\n blob: new Blob([new Uint8Array(data)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n // Node.js ReadableStream\n if (isNodeReadableStream(input)) {\n const buffer = await collectNodeStream(input);\n return {\n blob: new Blob([new Uint8Array(buffer)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n throw new Error('Invalid file input. Expected Buffer, Uint8Array, Blob, or ReadableStream.');\n}\n\nexport class SonioxFilesAPI {\n constructor(private http: HttpClient) {}\n\n /**\n * Uploads a file to Soniox for transcription\n *\n * @param file - Buffer, Uint8Array, Blob, or ReadableStream\n * @param options - Upload options\n * @returns The uploaded file metadata\n * @throws {@link SonioxHttpError} On API errors\n * @throws `Error` On validation errors (file too large, invalid input)\n *\n * @example Upload from file path (Node.js)\n * ```typescript\n * import * as fs from 'node:fs';\n *\n * const buffer = await fs.promises.readFile('/path/to/audio.mp3');\n * const file = await client.files.upload(buffer, { filename: 'audio.mp3' });\n * ```\n *\n * @example Upload from file path (Bun)\n * ```typescript\n * const file = await client.files.upload(Bun.file('/path/to/audio.mp3'));\n * ```\n *\n * @example Upload with tracking ID\n * ```typescript\n * const file = await client.files.upload(buffer, {\n * filename: 'audio.mp3',\n * client_reference_id: 'order-12345',\n * });\n * ```\n *\n * @example Upload with cancellation\n * ```typescript\n * const controller = new AbortController();\n * setTimeout(() => controller.abort(), 30000);\n *\n * const file = await client.files.upload(buffer, {\n * filename: 'audio.mp3',\n * signal: controller.signal,\n * });\n * ```\n */\n async upload(file: UploadFileInput, options: UploadFileOptions = {}): Promise<SonioxFile> {\n const { filename, client_reference_id, signal, timeout_ms } = options;\n\n // Validate client_reference_id length\n if (client_reference_id !== undefined && client_reference_id.length > 256) {\n throw new Error(\n `client_reference_id exceeds maximum length of 256 characters (got ${client_reference_id.length})`\n );\n }\n\n // Resolve the file input to a Blob and filename\n const { blob, filename: resolvedFilename } = await resolveFileInput(file, filename);\n\n // Build the FormData\n const formData = new FormData();\n formData.append('file', blob, resolvedFilename);\n\n if (client_reference_id !== undefined) {\n formData.append('client_reference_id', client_reference_id);\n }\n\n // Build request options\n const requestOptions: Parameters<HttpClient['request']>[0] = {\n method: 'POST',\n path: '/v1/files',\n body: formData,\n };\n\n if (signal !== undefined) {\n requestOptions.signal = signal;\n }\n\n if (timeout_ms !== undefined) {\n requestOptions.timeoutMs = timeout_ms;\n }\n\n // Make the request\n const response = await this.http.request<SonioxFileData>(requestOptions);\n\n return new SonioxFile(response.data, this.http);\n }\n\n /**\n * Retrieves list of uploaded files\n *\n * The returned result is async iterable - use `for await...of`\n *\n * @param options - Optional pagination and cancellation parameters\n * @returns FileListResult\n * @throws {@link SonioxHttpError}\n *\n * @example\n * ```typescript\n * const result = await client.files.list();\n *\n * // Automatic paging - iterates through ALL files across all pages\n * for await (const file of result) {\n * console.log(file.filename, file.size);\n * }\n *\n * // Or access just the first page\n * for (const file of result.files) {\n * console.log(file.filename);\n * }\n *\n * // Check if there are more pages\n * if (result.isPaged()) {\n * console.log('More pages available');\n * }\n *\n * // Manual paging using cursor\n * const page1 = await client.files.list({ limit: 10 });\n * if (page1.next_page_cursor) {\n * const page2 = await client.files.list({ cursor: page1.next_page_cursor });\n * }\n *\n * // With cancellation\n * const controller = new AbortController();\n * const result = await client.files.list({ signal: controller.signal });\n * ```\n */\n async list(options: ListFilesOptions = {}): Promise<FileListResult> {\n const { limit, cursor, signal } = options;\n\n const response = await this.http.request<ListFilesResponse<SonioxFileData>>({\n method: 'GET',\n path: '/v1/files',\n query: {\n limit,\n cursor,\n },\n ...(signal && { signal }),\n });\n\n return new FileListResult(response.data, this.http, limit, signal);\n }\n\n /**\n * Retrieve metadata for an uploaded file.\n *\n * @param file - The UUID of the file or a SonioxFile instance\n * @param signal - Optional AbortSignal for cancellation\n * @returns The file instance, or null if not found\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const file = await client.files.get('550e8400-e29b-41d4-a716-446655440000');\n * if (file) {\n * console.log(file.filename, file.size);\n * }\n * ```\n */\n async get(file: FileIdentifier, signal?: AbortSignal): Promise<SonioxFile | null> {\n const file_id = getFileId(file);\n try {\n const response = await this.http.request<SonioxFileData>({\n method: 'GET',\n path: `/v1/files/${file_id}`,\n ...(signal && { signal }),\n });\n return new SonioxFile(response.data, this.http);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Permanently deletes a file.\n * This operation is idempotent - succeeds even if the file doesn't exist.\n *\n * @param file - The UUID of the file or a SonioxFile instance\n * @param signal - Optional AbortSignal for cancellation\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Delete by ID\n * await client.files.delete('550e8400-e29b-41d4-a716-446655440000');\n *\n * // Or delete a file instance\n * const file = await client.files.get('550e8400-e29b-41d4-a716-446655440000');\n * if (file) {\n * await client.files.delete(file);\n * }\n *\n * // Or just use the instance method\n * await file.delete();\n * ```\n */\n async delete(file: FileIdentifier, signal?: AbortSignal): Promise<void> {\n const file_id = getFileId(file);\n try {\n await this.http.request<null>({\n method: 'DELETE',\n path: `/v1/files/${file_id}`,\n ...(signal && { signal }),\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n\n /**\n * Permanently deletes all uploaded files.\n * Iterates through all pages of files and deletes each one.\n *\n * @param options - Optional signal and progress callback.\n * @returns The number of files deleted.\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` If the operation is aborted via signal.\n *\n * @example\n * ```typescript\n * // Delete all files\n * await client.files.delete_all();\n * console.log(`Deleted all files.`);\n\n * // With cancellation\n * const controller = new AbortController();\n * await client.files.delete_all({ signal: controller.signal });\n * ```\n */\n async delete_all(options: DeleteAllFilesOptions = {}): Promise<void> {\n const { signal } = options;\n const result = await this.list({ signal });\n\n for await (const file of result) {\n signal?.throwIfAborted();\n await this.delete(file, signal);\n }\n }\n}\n","import type { HttpClient } from '../http/client.js';\nimport type { SonioxModel } from '../types/public/index.js';\n\nexport class SonioxModelsAPI {\n constructor(private http: HttpClient) {}\n\n /**\n * List of available models and their attributes.\n * @see https://soniox.com/docs/stt/api-reference/models/get_models\n * @param signal - Optional AbortSignal for cancellation\n * @returns List of available models and their attributes.\n */\n async list(signal?: AbortSignal): Promise<SonioxModel[]> {\n const response = await this.http.request<{ models: SonioxModel[] }>({\n method: 'GET',\n path: '/v1/models',\n ...(signal && { signal }),\n });\n return response.data.models;\n }\n}\n","import type { HttpClient } from '../http/client.js';\nimport { isNotFoundError } from '../http/errors.js';\nimport type {\n CreateTranscriptionOptions,\n ISonioxTranscript,\n ISonioxTranscription,\n ListTranscriptionsOptions,\n ListTranscriptionsResponse,\n DeleteAllTranscriptionsOptions,\n SegmentTranscriptOptions,\n SonioxTranscriptionData,\n TranscribeFromFileIdOptions,\n TranscribeFromFileOptions,\n TranscribeFromUrlOptions,\n TranscribeOptions,\n TranscriptionIdentifier,\n TranscriptionStatus,\n TranscriptResponse,\n TranscriptSegment,\n TranscriptToken,\n TranscriptionContext,\n UploadFileInput,\n WaitOptions,\n} from '../types/public/index.js';\n\nimport type { SonioxFilesAPI } from './files.js';\nimport { segmentTokens } from './segments.js';\n\n/**\n * Minimum polling interval in ms\n */\nconst MIN_POLL_INTERVAL_MS = 1000;\n\n/**\n * Default polling interval in ms\n */\nconst DEFAULT_POLL_INTERVAL_MS = 1000;\n\n/**\n * Default timeout for waiting in ms (5 minutes)\n */\nconst DEFAULT_TIMEOUT_MS = 300000;\n\n/**\n * Helper to sleep for a given number of ms, interruptible by abort signal\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error('Transcription wait aborted'));\n return;\n }\n\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n\n function onAbort() {\n clearTimeout(timeoutId);\n reject(new Error('Transcription wait aborted'));\n }\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * Helper to extract transcription ID from a TranscriptionIdentifier\n */\nfunction getTranscriptionId(transcription: TranscriptionIdentifier): string {\n return typeof transcription === 'string' ? transcription : transcription.id;\n}\n\n/**\n * Creates an AbortSignal that fires when either the timeout expires or the provided signal aborts\n * Returns the signal and a cleanup function to clear the timeout\n */\nfunction createTimeoutSignal(\n timeout_ms: number | undefined,\n signal: AbortSignal | undefined\n): { signal: AbortSignal | undefined; cleanup: () => void } {\n // No timeout and no signal - return undefined\n if (timeout_ms === undefined && signal === undefined) {\n return { signal: undefined, cleanup: () => {} };\n }\n\n // Only user signal, no timeout\n if (timeout_ms === undefined) {\n return { signal, cleanup: () => {} };\n }\n\n if (!Number.isFinite(timeout_ms) || timeout_ms <= 0) {\n throw new Error('timeout_ms must be a finite positive number');\n }\n\n // Create timeout-based abort controller\n const timeoutController = new AbortController();\n const timeoutId = setTimeout(() => {\n timeoutController.abort(new Error(`Operation timed out after ${timeout_ms}ms`));\n }, timeout_ms);\n\n const cleanup = () => clearTimeout(timeoutId);\n\n // Only timeout, no user signal\n if (signal === undefined) {\n return { signal: timeoutController.signal, cleanup };\n }\n\n // Both timeout and user signal - combine them\n const combinedController = new AbortController();\n\n const onAbort = () => {\n cleanup();\n timeoutController.signal.removeEventListener('abort', onTimeout);\n combinedController.abort(signal.reason ?? new Error('Operation aborted'));\n };\n\n const onTimeout = () => {\n signal.removeEventListener('abort', onAbort);\n combinedController.abort(timeoutController.signal.reason);\n };\n\n if (signal.aborted) {\n cleanup();\n combinedController.abort(signal.reason ?? new Error('Operation aborted'));\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n timeoutController.signal.addEventListener('abort', onTimeout, { once: true });\n }\n\n return {\n signal: combinedController.signal,\n cleanup: () => {\n cleanup();\n signal.removeEventListener('abort', onAbort);\n timeoutController.signal.removeEventListener('abort', onTimeout);\n },\n };\n}\n\n/**\n * Groups contiguous tokens into segments based on specified grouping keys.\n * A new segment starts when any of the `group_by` fields changes\n *\n * @param tokens - Array of transcript tokens to segment\n * @param options - Segmentation options\n * @param options.group_by - Fields to group by (default: ['speaker', 'language'])\n * @returns Array of segments with combined text and timing\n *\n * @example\n * ```typescript\n * const transcript = await transcription.getTranscript();\n *\n * // Group by both speaker and language (default)\n * const segments = segmentTranscript(transcript.tokens);\n *\n * // Group by speaker only\n * const bySpeaker = segmentTranscript(transcript.tokens, { group_by: ['speaker'] });\n *\n * // Group by language only\n * const byLanguage = segmentTranscript(transcript.tokens, { group_by: ['language'] });\n *\n * for (const seg of segments) {\n * console.log(`[Speaker ${seg.speaker}] ${seg.text}`);\n * }\n * ```\n */\nexport function segmentTranscript(\n tokens: TranscriptToken[],\n options: SegmentTranscriptOptions = {}\n): TranscriptSegment[] {\n return segmentTokens(tokens, options, buildSegment);\n}\n\n/**\n * Helper to build a segment from a list of tokens\n */\nfunction buildSegment(\n tokens: TranscriptToken[],\n speaker: string | null | undefined,\n language: string | null | undefined\n): TranscriptSegment {\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 != null && { speaker }),\n ...(language != null && { language }),\n tokens,\n };\n}\n\n/**\n * A Transcript result containing the transcribed text and tokens.\n */\nexport class SonioxTranscript implements ISonioxTranscript {\n /**\n * Unique identifier of the transcription this transcript belongs to.\n */\n readonly id: string;\n\n /**\n * Complete transcribed text content.\n */\n readonly text: string;\n\n /**\n * List of detailed token information with timestamps and metadata.\n */\n readonly tokens: TranscriptToken[];\n\n constructor(data: TranscriptResponse) {\n this.id = data.id;\n this.text = data.text;\n this.tokens = data.tokens;\n }\n\n /**\n * Groups tokens into segments based on specified grouping keys.\n *\n * A new segment starts when any of the `group_by` fields changes.\n *\n * @param options - Segmentation options\n * @param options.group_by - Fields to group by (default: ['speaker', 'language'])\n * @returns Array of segments with combined text and timing\n *\n * @example\n * ```typescript\n * const transcript = await transcription.getTranscript();\n *\n * // Group by both speaker and language (default)\n * const segments = transcript.segments();\n *\n * // Group by speaker only\n * const bySpeaker = transcript.segments({ group_by: ['speaker'] });\n *\n * for (const s of segments) {\n * console.log(`[Speaker ${s.speaker}] ${s.text}`);\n * }\n * ```\n */\n segments(options?: SegmentTranscriptOptions): TranscriptSegment[] {\n return segmentTranscript(this.tokens, options);\n }\n}\n\n/**\n * A Transcription instance\n */\nexport class SonioxTranscription implements ISonioxTranscription {\n /**\n * Unique identifier of the transcription.\n */\n readonly id: string;\n\n /**\n * Current status of the transcription.\n */\n readonly status: TranscriptionStatus;\n\n /**\n * UTC timestamp when the transcription was created.\n */\n readonly created_at: string;\n\n /**\n * Speech-to-text model used.\n */\n readonly model: string;\n\n /**\n * URL of the audio file being transcribed.\n */\n readonly audio_url: string | null | undefined;\n\n /**\n * ID of the uploaded file being transcribed.\n */\n readonly file_id: string | null | undefined;\n\n /**\n * Name of the file being transcribed.\n */\n readonly filename: string;\n\n /**\n * Expected languages in the audio.\n */\n readonly language_hints: string[] | undefined;\n\n /**\n * When true, speakers are identified and separated in the transcription output.\n */\n readonly enable_speaker_diarization: boolean;\n\n /**\n * When true, language is detected for each part of the transcription.\n */\n readonly enable_language_identification: boolean;\n\n /**\n * Duration of the audio in milliseconds. Only available after processing begins.\n */\n readonly audio_duration_ms: number | null | undefined;\n\n /**\n * Error type if transcription failed.\n */\n readonly error_type: string | null | undefined;\n\n /**\n * Error message if transcription failed.\n */\n readonly error_message: string | null | undefined;\n\n /**\n * URL to receive webhook notifications.\n */\n readonly webhook_url: string | null | undefined;\n\n /**\n * Name of the authentication header sent with webhook notifications.\n */\n readonly webhook_auth_header_name: string | null | undefined;\n\n /**\n * Authentication header value (masked).\n */\n readonly webhook_auth_header_value: string | null | undefined;\n\n /**\n * HTTP status code received when webhook was delivered.\n */\n readonly webhook_status_code: number | null | undefined;\n\n /**\n * Optional tracking identifier.\n */\n readonly client_reference_id: string | null | undefined;\n\n /**\n * Additional context provided for the transcription.\n */\n readonly context: TranscriptionContext | null | undefined;\n\n /**\n * Pre-fetched transcript. Only available when using `transcribe()` with `wait: true`,\n * `fetch_transcript !== false`, and the transcription completed successfully\n */\n readonly transcript: SonioxTranscript | null | undefined;\n\n constructor(\n data: SonioxTranscriptionData,\n private readonly _http: HttpClient,\n transcript?: SonioxTranscript | null\n ) {\n this.id = data.id;\n this.status = data.status;\n this.created_at = data.created_at;\n this.model = data.model;\n this.audio_url = data.audio_url;\n this.file_id = data.file_id;\n this.filename = data.filename;\n this.language_hints = data.language_hints ?? undefined;\n this.enable_speaker_diarization = data.enable_speaker_diarization;\n this.enable_language_identification = data.enable_language_identification;\n this.audio_duration_ms = data.audio_duration_ms;\n this.error_type = data.error_type;\n this.error_message = data.error_message;\n this.webhook_url = data.webhook_url;\n this.webhook_auth_header_name = data.webhook_auth_header_name;\n this.webhook_auth_header_value = data.webhook_auth_header_value;\n this.webhook_status_code = data.webhook_status_code;\n this.client_reference_id = data.client_reference_id;\n this.context = data.context;\n this.transcript = transcript;\n }\n\n /**\n * Returns the raw data for this transcription.\n */\n toJSON(): SonioxTranscriptionData {\n return {\n id: this.id,\n status: this.status,\n created_at: this.created_at,\n model: this.model,\n audio_url: this.audio_url,\n file_id: this.file_id,\n filename: this.filename,\n language_hints: this.language_hints,\n enable_speaker_diarization: this.enable_speaker_diarization,\n enable_language_identification: this.enable_language_identification,\n audio_duration_ms: this.audio_duration_ms,\n error_type: this.error_type,\n error_message: this.error_message,\n webhook_url: this.webhook_url,\n webhook_auth_header_name: this.webhook_auth_header_name,\n webhook_auth_header_value: this.webhook_auth_header_value,\n webhook_status_code: this.webhook_status_code,\n client_reference_id: this.client_reference_id,\n context: this.context,\n };\n }\n\n /**\n * Permanently deletes this transcription.\n * This operation is idempotent - succeeds even if the transcription doesn't exist.\n *\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * await transcription.delete();\n * ```\n */\n async delete(): Promise<void> {\n try {\n await this._http.request<null>({\n method: 'DELETE',\n path: `/v1/transcriptions/${this.id}`,\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n\n /**\n * Permanently deletes this transcription and its associated file (if any).\n * This operation is idempotent - succeeds even if resources don't exist.\n *\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Clean up both transcription and uploaded file\n * const transcription = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * });\n * // ... use transcription ...\n * await transcription.destroy(); // Deletes both transcription and file\n * ```\n */\n async destroy(): Promise<void> {\n // Delete the transcription first\n await this.delete();\n\n // Delete the associated file if present\n if (this.file_id) {\n try {\n await this._http.request<null>({\n method: 'DELETE',\n path: `/v1/files/${this.file_id}`,\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n }\n\n /**\n * Retrieves the full transcript text and tokens for this transcription.\n * Only available for successfully completed transcriptions.\n *\n * Returns cached transcript if available (when using `transcribe()` with `wait: true`).\n * Use `force: true` to bypass the cache and fetch fresh data from the API.\n *\n * @param options - Optional settings\n * @param options.force - If true, bypasses cached transcript and fetches from API\n * @param options.signal - Optional AbortSignal for request cancellation\n * @returns The transcript with text and detailed tokens, or null if not found.\n * @throws {@link SonioxHttpError} On API errors (except 404).\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * if (transcription) {\n * const transcript = await transcription.getTranscript();\n * if (transcript) {\n * console.log(transcript.text);\n * }\n * }\n *\n * // Force re-fetch from API\n * const freshTranscript = await transcription.getTranscript({ force: true });\n * ```\n */\n async getTranscript(options?: { force?: boolean; signal?: AbortSignal }): Promise<SonioxTranscript | null> {\n const { force, signal } = options ?? {};\n\n // Return cached transcript if available (null means transcription failed, undefined means not fetched)\n if (!force && this.transcript !== undefined) {\n return this.transcript;\n }\n\n try {\n const response = await this._http.request<TranscriptResponse>({\n method: 'GET',\n path: `/v1/transcriptions/${this.id}/transcript`,\n ...(signal && { signal }),\n });\n return new SonioxTranscript(response.data);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Re-fetches this transcription to get the latest status.\n * @param signal - Optional AbortSignal for request cancellation.\n * @returns A new SonioxTranscription instance with updated data.\n * @throws {@link SonioxHttpError}\n *\n * @example\n * ```typescript\n * let transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * transcription = await transcription.refresh();\n * console.log(transcription.status);\n * ```\n */\n async refresh(signal?: AbortSignal): Promise<SonioxTranscription> {\n const response = await this._http.request<SonioxTranscriptionData>({\n method: 'GET',\n path: `/v1/transcriptions/${this.id}`,\n ...(signal && { signal }),\n });\n return new SonioxTranscription(response.data, this._http);\n }\n\n /**\n * Waits for the transcription to complete or fail.\n * Polls the API at the specified interval until the status is 'completed' or 'error'.\n *\n * @param options - Wait options including polling interval, timeout, and callbacks.\n * @returns The completed or errored transcription.\n * @throws `Error` If the wait times out or is aborted.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * });\n *\n * // Simple wait\n * const completed = await transcription.wait();\n *\n * // Wait with progress callback\n * const completed = await transcription.wait({\n * interval_ms: 2000,\n * on_status_change: (status) => console.log(`Status: ${status}`),\n * });\n * ```\n */\n async wait(options: WaitOptions = {}): Promise<SonioxTranscription> {\n const {\n interval_ms: requestedInterval = DEFAULT_POLL_INTERVAL_MS,\n timeout_ms = DEFAULT_TIMEOUT_MS,\n on_status_change,\n signal,\n } = options;\n\n // Enforce minimum polling interval\n const interval_ms = Math.max(requestedInterval, MIN_POLL_INTERVAL_MS);\n\n // If already completed or errored, return immediately\n if (this.status !== 'queued' && this.status !== 'processing') {\n return this;\n }\n\n // Check abort signal before any network calls\n if (signal?.aborted) {\n throw new Error('Transcription wait aborted');\n }\n\n const startTime = Date.now();\n\n // Helper to check timeout\n const checkTimeout = () => {\n if (Date.now() - startTime > timeout_ms) {\n throw new Error(`Transcription wait timed out after ${timeout_ms}ms`);\n }\n };\n\n // Check timeout before initial refresh\n checkTimeout();\n\n let lastStatus = this.status as TranscriptionStatus;\n let current = await this.refresh(signal);\n\n while (current.status === 'queued' || current.status === 'processing') {\n // Notify on status change\n if (current.status !== lastStatus) {\n on_status_change?.(current.status, current.toJSON());\n lastStatus = current.status;\n }\n\n // Check timeout before sleeping\n checkTimeout();\n\n // Wait for interval (interruptible by abort signal)\n await sleep(interval_ms, signal);\n\n // Check timeout after sleep, before making network call\n checkTimeout();\n\n // Refresh status (passes signal to HTTP request)\n current = await current.refresh(signal);\n }\n\n // Notify on final status change\n if (current.status !== lastStatus) {\n on_status_change?.(current.status, current.toJSON());\n }\n\n return current;\n }\n}\n\n/**\n * Result set for transcription listing.\n */\nexport class TranscriptionListResult implements AsyncIterable<SonioxTranscription> {\n /**\n * Transcriptions from the first page of results.\n */\n readonly transcriptions: SonioxTranscription[];\n\n /**\n * Pagination cursor for the next page. Null if no more pages.\n */\n readonly next_page_cursor: string | null;\n\n constructor(\n initialResponse: ListTranscriptionsResponse<SonioxTranscriptionData>,\n private readonly _http: HttpClient,\n private readonly _options: ListTranscriptionsOptions,\n private readonly _signal?: AbortSignal\n ) {\n this.transcriptions = initialResponse.transcriptions.map((data) => new SonioxTranscription(data, _http));\n this.next_page_cursor = initialResponse.next_page_cursor;\n }\n\n /**\n * Returns the raw data for this list result\n */\n toJSON(): ListTranscriptionsResponse<SonioxTranscriptionData> {\n return {\n transcriptions: this.transcriptions.map((t) => t.toJSON()),\n next_page_cursor: this.next_page_cursor,\n };\n }\n\n /**\n * Returns true if there are more pages of results beyond the first page.\n */\n isPaged(): boolean {\n return this.next_page_cursor !== null;\n }\n\n /**\n * Async iterator that automatically fetches all pages.\n * Use with `for await...of` to iterate through all transcriptions.\n */\n async *[Symbol.asyncIterator](): AsyncIterator<SonioxTranscription> {\n // Yield transcriptions from the first page\n for (const transcription of this.transcriptions) {\n yield transcription;\n }\n\n // Fetch and yield subsequent pages\n let cursor = this.next_page_cursor;\n while (cursor !== null) {\n const response = await this._http.request<ListTranscriptionsResponse<SonioxTranscriptionData>>({\n method: 'GET',\n path: '/v1/transcriptions',\n query: {\n limit: this._options.limit,\n cursor,\n },\n ...(this._signal && { signal: this._signal }),\n });\n\n for (const data of response.data.transcriptions) {\n yield new SonioxTranscription(data, this._http);\n }\n\n cursor = response.data.next_page_cursor;\n }\n }\n}\n\nexport class SonioxSttApi {\n constructor(\n private http: HttpClient,\n private filesApi: SonioxFilesAPI\n ) {}\n\n /**\n * Creates a new transcription from audio_url or file_id\n *\n * @param options - Transcription options including model and audio source.\n * @returns The created transcription.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * // Transcribe from URL\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * });\n *\n * // Transcribe from uploaded file\n * const file = await client.files.upload(buffer);\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * file_id: file.id,\n * });\n *\n * // With speaker diarization\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * enable_speaker_diarization: true,\n * });\n * ```\n */\n async create(options: CreateTranscriptionOptions, signal?: AbortSignal): Promise<SonioxTranscription> {\n // Validate client_reference_id length\n if (options.client_reference_id !== undefined && options.client_reference_id.length > 256) {\n throw new Error(\n `client_reference_id exceeds maximum length of 256 characters (got ${options.client_reference_id.length})`\n );\n }\n\n const response = await this.http.request<SonioxTranscriptionData>({\n method: 'POST',\n path: '/v1/transcriptions',\n body: options,\n ...(signal && { signal }),\n });\n\n return new SonioxTranscription(response.data, this.http);\n }\n\n /**\n * Retrieves list of transcriptions\n *\n * The returned result is async iterable - use `for await...of` to iterate through all pages\n *\n * @param options - Optional pagination and filter parameters.\n * @returns TranscriptionListResult with async iteration support.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * const result = await client.stt.list();\n *\n * // Automatic paging - iterates through ALL transcriptions across all pages\n * for await (const transcription of result) {\n * console.log(transcription.id, transcription.status);\n * }\n *\n * // Or access just the first page\n * for (const transcription of result.transcriptions) {\n * console.log(transcription.id);\n * }\n *\n * // Check if there are more pages\n * if (result.isPaged()) {\n * console.log('More pages available');\n * }\n * ```\n */\n async list(options: ListTranscriptionsOptions = {}, signal?: AbortSignal): Promise<TranscriptionListResult> {\n const response = await this.http.request<ListTranscriptionsResponse<SonioxTranscriptionData>>({\n method: 'GET',\n path: '/v1/transcriptions',\n query: {\n limit: options.limit,\n cursor: options.cursor,\n },\n ...(signal && { signal }),\n });\n\n return new TranscriptionListResult(response.data, this.http, options, signal);\n }\n\n /**\n * Retrieves a transcription by ID\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance.\n * @returns The transcription, or null if not found.\n * @throws {@link SonioxHttpError} On API errors (except 404).\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * if (transcription) {\n * console.log(transcription.status, transcription.model);\n * }\n * ```\n */\n async get(id: TranscriptionIdentifier, signal?: AbortSignal): Promise<SonioxTranscription | null> {\n const transcription_id = getTranscriptionId(id);\n try {\n const response = await this.http.request<SonioxTranscriptionData>({\n method: 'GET',\n path: `/v1/transcriptions/${transcription_id}`,\n ...(signal && { signal }),\n });\n return new SonioxTranscription(response.data, this.http);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Permanently deletes a transcription.\n * This operation is idempotent - succeeds even if the transcription doesn't exist.\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Delete by ID\n * await client.stt.delete('550e8400-e29b-41d4-a716-446655440000');\n *\n * // Or delete a transcription instance\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * if (transcription) {\n * await client.stt.delete(transcription);\n * }\n * ```\n */\n async delete(id: TranscriptionIdentifier, signal?: AbortSignal): Promise<void> {\n const transcription_id = getTranscriptionId(id);\n try {\n await this.http.request<null>({\n method: 'DELETE',\n path: `/v1/transcriptions/${transcription_id}`,\n ...(signal && { signal }),\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n\n /**\n * Permanently deletes a transcription and its associated file (if any).\n * This operation is idempotent - succeeds even if resources don't exist.\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Clean up both transcription and uploaded file\n * const transcription = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * });\n * // ... use transcription ...\n * await client.stt.destroy(transcription); // Deletes both\n *\n * // Or by ID\n * await client.stt.destroy('550e8400-e29b-41d4-a716-446655440000');\n * ```\n */\n async destroy(id: TranscriptionIdentifier): Promise<void> {\n // Get the full transcription to access file_id\n const transcription = await this.get(id);\n\n // If transcription doesn't exist, nothing to do\n if (!transcription) {\n return;\n }\n\n // Delete transcription first\n await this.delete(transcription);\n\n // Delete the associated file if present (ignore 404)\n if (transcription.file_id) {\n try {\n await this.filesApi.delete(transcription.file_id);\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n }\n\n /**\n * Retrieves the full transcript text and tokens for a completed transcription.\n * Only available for successfully completed transcriptions.\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance\n * @returns The transcript with text and detailed tokens, or null if not found\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const transcript = await client.stt.getTranscript('550e8400-e29b-41d4-a716-446655440000');\n * if (transcript) {\n * console.log(transcript.text);\n * for (const token of transcript.tokens) {\n * console.log(token.text, token.start_ms, token.end_ms, token.confidence);\n * }\n * }\n * ```\n */\n async getTranscript(id: TranscriptionIdentifier, signal?: AbortSignal): Promise<SonioxTranscript | null> {\n const transcription_id = getTranscriptionId(id);\n try {\n const response = await this.http.request<TranscriptResponse>({\n method: 'GET',\n path: `/v1/transcriptions/${transcription_id}/transcript`,\n ...(signal && { signal }),\n });\n return new SonioxTranscript(response.data);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Waits for a transcription to complete\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance.\n * @param options - Wait options including polling interval, timeout, and callbacks.\n * @returns The completed or errored transcription.\n * @throws `Error` If the wait times out or is aborted.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * const completed = await client.stt.wait('550e8400-e29b-41d4-a716-446655440000');\n *\n * // With progress callback\n * const completed = await client.stt.wait('id', {\n * interval_ms: 2000,\n * on_status_change: (status) => console.log(`Status: ${status}`),\n * });\n * ```\n */\n async wait(id: TranscriptionIdentifier, options?: WaitOptions): Promise<SonioxTranscription> {\n const transcription = await this.get(id, options?.signal);\n if (!transcription) {\n throw new Error(`Transcription not found: ${getTranscriptionId(id)}`);\n }\n return transcription.wait(options);\n }\n\n /**\n * Wrapper to transcribe from a URL.\n *\n * @param audio_url - Publicly accessible audio URL\n * @param options - Transcription options (excluding audio_url)\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n */\n async transcribeFromUrl(audio_url: string, options: TranscribeFromUrlOptions): Promise<SonioxTranscription> {\n return this.transcribe({ ...options, audio_url });\n }\n\n /**\n * Wrapper to transcribe from an uploaded file ID.\n *\n * @param file_id - ID of a previously uploaded file\n * @param options - Transcription options (excluding file_id)\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n */\n async transcribeFromFileId(file_id: string, options: TranscribeFromFileIdOptions): Promise<SonioxTranscription> {\n return this.transcribe({ ...options, file_id });\n }\n\n /**\n * Wrapper to transcribe from raw file data.\n *\n * @param file - Buffer, Uint8Array, Blob, or ReadableStream\n * @param options - Transcription options (excluding file)\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n */\n async transcribeFromFile(file: UploadFileInput, options: TranscribeFromFileOptions): Promise<SonioxTranscription> {\n return this.transcribe({ ...options, file });\n }\n\n /**\n * Unified transcribe method - supports direct file upload\n *\n * When `file` is provided, uploads it first then creates a transcription\n * When `wait: true`, waits for completion before returning\n * When `cleanup` is specified (requires `wait: true`), cleans up resources after completion or on error/timeout\n *\n * @param options - Transcribe options including model, audio source, and wait settings.\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` On validation errors or wait timeout.\n *\n * @example\n * ```typescript\n * // Transcribe from URL and wait for completion\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * wait: true,\n * });\n *\n * // Upload file and transcribe in one call\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer, // or Blob, ReadableStream\n * filename: 'meeting.mp3',\n * enable_speaker_diarization: true,\n * wait: true,\n * });\n *\n * // With wait progress callback\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * wait_options: {\n * interval_ms: 2000,\n * on_status_change: (status) => console.log(`Status: ${status}`),\n * },\n * });\n *\n * // Auto-cleanup uploaded file after transcription\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * cleanup: ['file'], // Deletes uploaded file, keeps transcription record\n * });\n *\n * // Auto-cleanup everything after transcription\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * cleanup: ['file', 'transcription'], // Deletes both file and transcription record\n * });\n * ```\n */\n async transcribe(options: TranscribeOptions): Promise<SonioxTranscription> {\n // Validate that exactly one audio source is provided\n const sourceCount = [\n options.file !== undefined,\n options.file_id !== undefined,\n options.audio_url !== undefined,\n ].filter(Boolean).length;\n\n if (sourceCount === 0) {\n throw new Error('One of file, file_id, or audio_url must be provided');\n }\n if (sourceCount > 1) {\n throw new Error('Only one of file, file_id, or audio_url can be provided');\n }\n\n // Validate audio_url format if provided\n if (options.audio_url !== undefined) {\n const urlPattern = /^https?:\\/\\/[^\\s]+$/;\n if (!urlPattern.test(options.audio_url)) {\n throw new Error('audio_url must be a valid HTTP or HTTPS URL');\n }\n }\n\n // Validate webhook auth header - both must be provided together or neither\n const hasHeaderName = options.webhook_auth_header_name !== undefined;\n const hasHeaderValue = options.webhook_auth_header_value !== undefined;\n if (hasHeaderName !== hasHeaderValue) {\n throw new Error('webhook_auth_header_name and webhook_auth_header_value must be provided together');\n }\n\n // Validate client_reference_id length\n if (options.client_reference_id !== undefined && options.client_reference_id.length > 256) {\n throw new Error(\n `client_reference_id exceeds maximum length of 256 characters (got ${options.client_reference_id.length})`\n );\n }\n\n // Validate cleanup - only allowed when wait=true\n if (options.cleanup?.length && !options.wait) {\n throw new Error('cleanup can only be used when wait=true');\n }\n\n // Create combined signal from timeout_ms and signal options\n const { signal: combinedSignal, cleanup: cleanupSignal } = createTimeoutSignal(options.timeout_ms, options.signal);\n\n // Track created resources for cleanup on error\n let fileIdToCleanup: string | undefined;\n let transcriptionId: string | undefined;\n\n // Helper to perform cleanup (ignores errors to avoid masking original error)\n const performCleanup = async (finalFileId?: string | null) => {\n if (!options.cleanup?.length) return;\n\n // Use the file_id from the completed transcription if available, otherwise use tracked id\n const fileId = finalFileId ?? fileIdToCleanup;\n\n // Delete file if requested and we have one\n if (options.cleanup.includes('file') && fileId) {\n try {\n await this.filesApi.delete(fileId);\n } catch {\n // Ignore cleanup errors\n }\n }\n\n // Delete transcription if requested and we have one\n if (options.cleanup.includes('transcription') && transcriptionId) {\n try {\n await this.delete(transcriptionId);\n } catch {\n // Ignore cleanup errors\n }\n }\n };\n\n try {\n let file_id = options.file_id;\n\n // If file data provided, upload first\n if (options.file) {\n const uploaded = await this.filesApi.upload(options.file, {\n filename: options.filename,\n client_reference_id: options.client_reference_id,\n signal: combinedSignal,\n });\n file_id = uploaded.id;\n fileIdToCleanup = uploaded.id;\n } else if (options.file_id) {\n // Track provided file_id for cleanup on error\n fileIdToCleanup = options.file_id;\n }\n\n // Process webhook_url with optional webhook_query\n let webhook_url = options.webhook_url;\n if (webhook_url && options.webhook_query) {\n const url = new URL(webhook_url);\n const params =\n options.webhook_query instanceof URLSearchParams\n ? options.webhook_query\n : typeof options.webhook_query === 'string'\n ? new URLSearchParams(options.webhook_query)\n : new URLSearchParams(options.webhook_query);\n\n params.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n webhook_url = url.toString();\n }\n\n // Build create options (exclude file-upload specific fields)\n const createOptions: CreateTranscriptionOptions = {\n model: options.model,\n audio_url: options.audio_url,\n file_id,\n language_hints: options.language_hints,\n language_hints_strict: options.language_hints_strict,\n enable_language_identification: options.enable_language_identification,\n enable_speaker_diarization: options.enable_speaker_diarization,\n context: options.context,\n translation: options.translation,\n webhook_url,\n webhook_auth_header_name: options.webhook_auth_header_name,\n webhook_auth_header_value: options.webhook_auth_header_value,\n client_reference_id: options.client_reference_id,\n };\n\n // Create transcription\n const transcription = await this.create(createOptions, combinedSignal);\n transcriptionId = transcription.id;\n\n // Track file_id from transcription response for cleanup on error\n // This handles cases where audio_url creates an associated file\n if (transcription.file_id) {\n fileIdToCleanup = transcription.file_id;\n }\n\n // Wait if requested (defaults to false)\n if (options.wait) {\n // Merge the combined signal with any existing wait_options.signal\n const waitOptions: WaitOptions = {\n ...options.wait_options,\n };\n if (combinedSignal && waitOptions.signal) {\n // Both signals exist — combine so either can cancel the wait\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n combinedSignal.addEventListener('abort', onAbort, { once: true });\n waitOptions.signal.addEventListener('abort', onAbort, { once: true });\n if (combinedSignal.aborted || waitOptions.signal.aborted) controller.abort();\n waitOptions.signal = controller.signal;\n } else if (combinedSignal) {\n waitOptions.signal = combinedSignal;\n }\n const completed = await transcription.wait(waitOptions);\n\n const shouldFetchTranscript = options.fetch_transcript !== false;\n\n // Fetch transcript if transcription completed successfully and opted in\n let transcript: SonioxTranscript | null | undefined;\n if (completed.status === 'completed') {\n if (shouldFetchTranscript) {\n transcript = await completed.getTranscript(combinedSignal ? { signal: combinedSignal } : undefined);\n }\n } else {\n transcript = null;\n }\n\n // Create a new transcription instance with the transcript attached\n const result = new SonioxTranscription(completed.toJSON(), this.http, transcript);\n\n // Perform cleanup after fetching transcript\n await performCleanup(completed.file_id);\n\n return result;\n }\n\n return transcription;\n } catch (error) {\n if (options.wait) {\n await performCleanup();\n }\n\n throw error;\n } finally {\n cleanupSignal();\n }\n }\n\n /**\n * Permanently deletes all transcriptions.\n * Iterates through all pages of transcriptions and deletes each one.\n *\n * @param options - Optional signal.\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` If the operation is aborted via signal.\n *\n * @example\n * ```typescript\n * // Delete all transcriptions\n * await client.stt.delete_all();\n * console.log(`Deleted all transcriptions.`);\n *\n * // With cancellation\n * const controller = new AbortController();\n * await client.stt.delete_all({ signal: controller.signal });\n * ```\n */\n async delete_all(options: DeleteAllTranscriptionsOptions = {}): Promise<void> {\n const { signal } = options;\n const result = await this.list({}, signal);\n\n for await (const transcription of result) {\n signal?.throwIfAborted();\n await this.delete(transcription, signal);\n }\n }\n\n /**\n * Permanently deletes all transcriptions and their associated files.\n * Iterates through all pages of transcriptions and calls {@link destroy}\n * on each one, removing both the transcription and its uploaded file.\n *\n * @param options - Optional signal and progress callback.\n * @returns The number of transcriptions destroyed.\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` If the operation is aborted via signal.\n *\n * @example\n * ```typescript\n * // Destroy all transcriptions and their files\n * await client.stt.destroy_all();\n * console.log(`Destroyed all transcriptions and their files.`);\n *\n * // With cancellation\n * const controller = new AbortController();\n * await client.stt.destroy_all({ signal: controller.signal });\n * ```\n */\n async destroy_all(options: DeleteAllTranscriptionsOptions = {}): Promise<void> {\n const { signal } = options;\n const result = await this.list({}, signal);\n\n for await (const transcription of result) {\n signal?.throwIfAborted();\n await this.destroy(transcription);\n }\n }\n}\n","import { writeFile } from 'node:fs/promises';\n\nimport { TtsRestClient } from '@soniox/core';\nimport type { GenerateSpeechOptions, TtsModel } from '@soniox/core';\n\nimport type { HttpClient } from '../http/client.js';\n\nexport type { GenerateSpeechOptions } from '@soniox/core';\n\n/**\n * REST API for Text-to-Speech generation and TTS model listing.\n *\n * Accessed via `client.tts` on {@link SonioxNodeClient}.\n *\n * Inherits browser-safe `generate()` and `generateStream()` from\n * `TtsRestClient` in `@soniox/core`, and adds Node-specific methods\n * `generateToFile()` and `listModels()`.\n */\nexport class SonioxTtsApi extends TtsRestClient {\n private readonly http: HttpClient;\n\n constructor(apiKey: string, ttsApiUrl: string, http: HttpClient) {\n super(apiKey, ttsApiUrl);\n this.http = http;\n }\n\n /**\n * Generate speech audio and write to a file or writable stream.\n *\n * @param output - File path (string) or a `WritableStream<Uint8Array>`\n * @param options - Generation options\n * @returns Number of bytes written\n *\n * @example Write to file\n * ```typescript\n * const bytes = await client.tts.generateToFile('output.wav', {\n * text: 'Hello world',\n * voice: 'Adrian',\n * language: 'en',\n * });\n * ```\n *\n * @example Write to a writable stream\n * ```typescript\n * const bytes = await client.tts.generateToFile(writableStream, {\n * text: 'Hello world',\n * voice: 'Adrian',\n * language: 'en',\n * });\n * ```\n */\n async generateToFile(output: string | WritableStream<Uint8Array>, options: GenerateSpeechOptions): Promise<number> {\n if (typeof output === 'string') {\n const audio = await this.generate(options);\n await writeFile(output, audio);\n return audio.byteLength;\n }\n\n let bytesWritten = 0;\n const writer = output.getWriter();\n try {\n for await (const chunk of this.generateStream(options)) {\n await writer.write(chunk);\n bytesWritten += chunk.byteLength;\n }\n } finally {\n writer.releaseLock();\n }\n return bytesWritten;\n }\n\n /**\n * List available TTS models and their voices.\n *\n * @example\n * ```typescript\n * const models = await client.tts.listModels();\n * for (const model of models) {\n * console.log(model.id, model.voices.map(v => v.id));\n * }\n * ```\n */\n async listModels(signal?: AbortSignal): Promise<TtsModel[]> {\n const response = await this.http.request<{ models: TtsModel[] }>({\n method: 'GET',\n path: '/v1/tts-models',\n ...(signal && { signal }),\n });\n return response.data.models;\n }\n}\n","import { SONIOX_API_WEBHOOK_HEADER_ENV, SONIOX_API_WEBHOOK_SECRET_ENV } from '../constants.js';\nimport type {\n ExpressLikeRequest,\n FastifyLikeRequest,\n HandleWebhookOptions,\n HonoLikeContext,\n NestJSLikeRequest,\n WebhookAuthConfig,\n WebhookEvent,\n WebhookEventStatus,\n WebhookHandlerResult,\n WebhookHandlerResultWithFetch,\n WebhookHeaders,\n} from '../types/public/webhooks.js';\n\nimport type { SonioxSttApi } from './stt.js';\n\nexport type {\n ExpressLikeRequest,\n FastifyLikeRequest,\n HandleWebhookOptions,\n HonoLikeContext,\n NestJSLikeRequest,\n WebhookAuthConfig,\n WebhookEvent,\n WebhookEventStatus,\n WebhookHandlerResult,\n WebhookHandlerResultWithFetch,\n WebhookHeaders,\n};\n\nconst VALID_STATUSES: WebhookEventStatus[] = ['completed', 'error'];\n\n/**\n * Get webhook authentication configuration from environment variables.\n *\n * Reads `SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET` environment variables.\n * Returns undefined if either variable is not set (both are required for authentication).\n *\n * @returns WebhookAuthConfig if both env vars are set, undefined otherwise\n *\n * @example\n * ```typescript\n * // Set environment variables:\n * // SONIOX_API_WEBHOOK_HEADER=X-Webhook-Secret\n * // SONIOX_API_WEBHOOK_SECRET=my-secret-token\n *\n * const auth = getWebhookAuthFromEnv();\n * // Returns: { name: 'X-Webhook-Secret', value: 'my-secret-token' }\n * ```\n */\nexport function getWebhookAuthFromEnv(): WebhookAuthConfig | undefined {\n const headerName = process.env[SONIOX_API_WEBHOOK_HEADER_ENV];\n const headerValue = process.env[SONIOX_API_WEBHOOK_SECRET_ENV];\n\n // Both header name and secret value must be set for auth to work\n if (headerName && headerValue) {\n return {\n name: headerName,\n value: headerValue,\n };\n }\n\n return undefined;\n}\n\n/**\n * Resolve webhook authentication configuration.\n *\n * If explicit auth is provided, it is used. Otherwise, attempts to read from\n * environment variables (`SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET`).\n *\n * @param auth - Explicit authentication configuration (takes precedence)\n * @returns Resolved WebhookAuthConfig or undefined if not configured\n */\nfunction resolveWebhookAuth(auth?: WebhookAuthConfig): WebhookAuthConfig | undefined {\n return auth ?? getWebhookAuthFromEnv();\n}\n\n/**\n * Type guard to check if a value is a valid WebhookEvent\n *\n * @param payload - Value to check\n * @returns True if payload is a valid WebhookEvent\n *\n * @example\n * ```typescript\n * if (isWebhookEvent(body)) {\n * console.log(body.id, body.status);\n * }\n * ```\n */\nexport function isWebhookEvent(payload: unknown): payload is WebhookEvent {\n if (typeof payload !== 'object' || payload === null) {\n return false;\n }\n\n const obj = payload as Record<string, unknown>;\n\n if (typeof obj.id !== 'string' || obj.id.length === 0) {\n return false;\n }\n\n if (!VALID_STATUSES.includes(obj.status as WebhookEventStatus)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Parse and validate a webhook event payload\n *\n * @param payload - Raw payload to parse (object or JSON string)\n * @returns Validated WebhookEvent\n * @throws `Error` if payload is invalid\n *\n * @example\n * ```typescript\n * try {\n * const event = parseWebhookEvent(req.body);\n * console.log(event.id, event.status);\n * } catch (error) {\n * console.error('Invalid webhook payload:', error.message);\n * }\n * ```\n */\nexport function parseWebhookEvent(payload: unknown): WebhookEvent {\n // Handle string input (parse as JSON)\n if (typeof payload === 'string') {\n try {\n payload = JSON.parse(payload);\n } catch {\n throw new Error('Invalid webhook payload: not valid JSON');\n }\n }\n\n if (typeof payload !== 'object' || payload === null) {\n throw new Error('Invalid webhook payload: expected an object');\n }\n\n const obj = payload as Record<string, unknown>;\n\n if (typeof obj.id !== 'string') {\n throw new Error('Invalid webhook payload: missing or invalid \"id\" field');\n }\n\n if (obj.id.length === 0) {\n throw new Error('Invalid webhook payload: \"id\" field cannot be empty');\n }\n\n if (!VALID_STATUSES.includes(obj.status as WebhookEventStatus)) {\n throw new Error(`Invalid webhook payload: \"status\" must be \"completed\" or \"error\", got \"${String(obj.status)}\"`);\n }\n\n return {\n id: obj.id,\n status: obj.status as WebhookEventStatus,\n };\n}\n\n/**\n * Get a header value from various header formats (case-insensitive)\n */\nfunction getHeaderValue(headers: WebhookHeaders, name: string): string | null {\n const lowerName = name.toLowerCase();\n\n // Fetch API Headers\n if (headers instanceof Headers) {\n return headers.get(lowerName);\n }\n\n // Object with get method (like Express headers or custom)\n if (typeof (headers as { get?(name: string): string | null }).get === 'function') {\n return (headers as { get(name: string): string | null }).get(lowerName);\n }\n\n // Plain object\n const record = headers as Record<string, string | string[] | undefined>;\n\n // Try exact match first\n if (lowerName in record) {\n const value = record[lowerName];\n return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);\n }\n\n // Case-insensitive search\n for (const key of Object.keys(record)) {\n if (key.toLowerCase() === lowerName) {\n const value = record[key];\n return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);\n }\n }\n\n return null;\n}\n\n/**\n * Verify webhook authentication header\n *\n * @param headers - Request headers\n * @param auth - Authentication configuration with expected header name and value\n * @returns True if authentication passes, false otherwise\n *\n * @example\n * ```typescript\n * const isValid = verifyWebhookAuth(req.headers, {\n * name: 'X-Webhook-Secret',\n * value: process.env.WEBHOOK_SECRET,\n * });\n *\n * if (!isValid) {\n * return res.status(401).send('Unauthorized');\n * }\n * ```\n */\nexport function verifyWebhookAuth(headers: WebhookHeaders, auth: WebhookAuthConfig): boolean {\n const headerValue = getHeaderValue(headers, auth.name);\n return headerValue === auth.value;\n}\n\n/**\n * Framework-agnostic webhook handler\n *\n * Validates the HTTP method, authentication (if configured), and parses the webhook payload.\n * Returns a result object that can be used to construct an HTTP response.\n *\n * Authentication is resolved in this order:\n * 1. Explicit `auth` option if provided\n * 2. Environment variables `SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET`\n * 3. No authentication if neither is configured\n *\n * @param options - Handler options including method, headers, body, and optional auth\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * // Option 1: Set environment variables (recommended)\n * // SONIOX_API_WEBHOOK_HEADER=X-Webhook-Secret\n * // SONIOX_API_WEBHOOK_SECRET=my-secret\n * const result = handleWebhook({\n * method: req.method,\n * headers: req.headers,\n * body: req.body,\n * });\n *\n * // Option 2: Explicit auth config (overrides env vars)\n * const result = handleWebhook({\n * method: req.method,\n * headers: req.headers,\n * body: req.body,\n * auth: {\n * name: 'X-Webhook-Secret',\n * value: process.env.WEBHOOK_SECRET,\n * },\n * });\n *\n * if (result.ok) {\n * console.log('Transcription completed:', result.event.id);\n * }\n *\n * res.status(result.status).json(result.ok ? { received: true } : { error: result.error });\n * ```\n */\nexport function handleWebhook(options: HandleWebhookOptions): WebhookHandlerResult {\n const { method, headers, body, auth } = options;\n\n // Validate HTTP method\n if (method.toUpperCase() !== 'POST') {\n return {\n ok: false,\n status: 405,\n error: 'Method not allowed',\n };\n }\n\n // Resolve authentication from explicit config or environment variables\n const resolvedAuth = resolveWebhookAuth(auth);\n\n // Verify authentication if configured\n if (resolvedAuth) {\n if (!verifyWebhookAuth(headers, resolvedAuth)) {\n return {\n ok: false,\n status: 401,\n error: 'Unauthorized',\n };\n }\n }\n\n // Parse and validate payload\n try {\n const event = parseWebhookEvent(body);\n return {\n ok: true,\n status: 200,\n event,\n };\n } catch (error) {\n return {\n ok: false,\n status: 400,\n error: error instanceof Error ? error.message : 'Invalid webhook payload',\n };\n }\n}\n\n/**\n * Handle a webhook from a Fetch API Request (Bun/Deno/Node 18+)\n *\n * @param request - Fetch API Request object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * // Bun.serve handler\n * Bun.serve({\n * async fetch(req) {\n * if (new URL(req.url).pathname === '/webhook') {\n * const result = await handleWebhookRequest(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return new Response(\n * JSON.stringify(result.ok ? { received: true } : { error: result.error }),\n * { status: result.status, headers: { 'Content-Type': 'application/json' } }\n * );\n * }\n * },\n * });\n * ```\n */\nexport async function handleWebhookRequest(request: Request, auth?: WebhookAuthConfig): Promise<WebhookHandlerResult> {\n if (request.method.toUpperCase() !== 'POST') {\n return {\n ok: false,\n status: 405,\n error: 'Method not allowed',\n };\n }\n\n let body: unknown;\n\n try {\n body = await request.json();\n } catch {\n return {\n ok: false,\n status: 400,\n error: 'Invalid webhook payload: not valid JSON',\n };\n }\n\n const options: HandleWebhookOptions = {\n method: request.method,\n headers: request.headers,\n body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from an Express-like request\n *\n * @param req - Express request object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { handleWebhookExpress } from '@soniox/node';\n *\n * const app = express();\n * app.use(express.json());\n *\n * app.post('/webhook', (req, res) => {\n * const result = handleWebhookExpress(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * res.status(result.status).json(\n * result.ok ? { received: true } : { error: result.error }\n * );\n * });\n * ```\n */\nexport function handleWebhookExpress(req: ExpressLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResult {\n const options: HandleWebhookOptions = {\n method: req.method,\n headers: req.headers,\n body: req.body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from a Fastify request\n *\n * @param req - Fastify request object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import Fastify from 'fastify';\n * import { handleWebhookFastify } from '@soniox/node';\n *\n * const fastify = Fastify();\n *\n * fastify.post('/webhook', async (req, reply) => {\n * const result = handleWebhookFastify(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return reply.status(result.status).send(\n * result.ok ? { received: true } : { error: result.error }\n * );\n * });\n * ```\n */\nexport function handleWebhookFastify(req: FastifyLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResult {\n const options: HandleWebhookOptions = {\n method: req.method,\n headers: req.headers,\n body: req.body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from a NestJS request\n *\n * Works with NestJS using either Express or Fastify adapter.\n *\n * @param req - NestJS request object (injected via @Req() decorator)\n * @param auth - Optional authentication configuration (overrides env vars)\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import { Controller, Post, Req, Res, HttpStatus } from '@nestjs/common';\n * import { Request, Response } from 'express';\n * import { handleWebhookNestJS } from '@soniox/node';\n *\n * @Controller('webhook')\n * export class WebhookController {\n * @Post()\n * handleWebhook(@Req() req: Request, @Res() res: Response) {\n * const result = handleWebhookNestJS(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return res.status(result.status).json(\n * result.ok ? { received: true } : { error: result.error }\n * );\n * }\n * }\n * ```\n */\nexport function handleWebhookNestJS(req: NestJSLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResult {\n const options: HandleWebhookOptions = {\n method: req.method,\n headers: req.headers,\n body: req.body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from a Hono context\n *\n * @param c - Hono context object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import { Hono } from 'hono';\n * import { handleWebhookHono } from '@soniox/node';\n *\n * const app = new Hono();\n *\n * app.post('/webhook', async (c) => {\n * const result = await handleWebhookHono(c);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return c.json(\n * result.ok ? { received: true } : { error: result.error },\n * result.status\n * );\n * });\n * ```\n */\nexport async function handleWebhookHono(c: HonoLikeContext, auth?: WebhookAuthConfig): Promise<WebhookHandlerResult> {\n let body: unknown;\n\n try {\n body = await c.req.json();\n } catch {\n return {\n ok: false,\n status: 400,\n error: 'Invalid webhook payload: not valid JSON',\n };\n }\n\n // Build headers object from Hono's header getter\n const headers: WebhookHeaders = {\n get(name: string): string | null {\n return c.req.header(name) ?? null;\n },\n };\n\n const options: HandleWebhookOptions = {\n method: c.req.method,\n headers,\n body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Webhook utilities API accessible via client.webhooks\n *\n * Provides methods for handling incoming Soniox webhook requests.\n * When used via the client, results include lazy fetch helpers for transcripts.\n */\nexport class SonioxWebhooksAPI {\n private stt: SonioxSttApi | undefined;\n\n /**\n * @internal\n */\n constructor(stt?: SonioxSttApi) {\n this.stt = stt;\n }\n\n /**\n * Enhance a webhook result with fetch helpers\n */\n private withFetchHelpers(result: WebhookHandlerResult): WebhookHandlerResultWithFetch {\n const stt = this.stt;\n const event = result.event;\n\n // If no stt API or no event, return result without fetch helpers\n if (!stt || !event) {\n return {\n ...result,\n fetchTranscript: undefined,\n fetchTranscription: undefined,\n };\n }\n\n const transcriptionId = event.id;\n\n return {\n ...result,\n fetchTranscript: event.status === 'completed' ? () => stt.getTranscript(transcriptionId) : undefined,\n fetchTranscription: () => stt.get(transcriptionId),\n };\n }\n\n /**\n * Get webhook authentication configuration from environment variables.\n *\n * Reads `SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET` environment variables.\n * Returns undefined if either variable is not set (both are required for authentication).\n */\n getAuthFromEnv(): WebhookAuthConfig | undefined {\n return getWebhookAuthFromEnv();\n }\n\n /**\n * Type guard to check if a value is a valid WebhookEvent\n */\n isEvent(payload: unknown): payload is WebhookEvent {\n return isWebhookEvent(payload);\n }\n\n /**\n * Parse and validate a webhook event payload\n */\n parseEvent(payload: unknown): WebhookEvent {\n return parseWebhookEvent(payload);\n }\n\n /**\n * Verify webhook authentication header\n */\n verifyAuth(headers: WebhookHeaders, auth: WebhookAuthConfig): boolean {\n return verifyWebhookAuth(headers, auth);\n }\n\n /**\n * Framework-agnostic webhook handler\n */\n handle(options: HandleWebhookOptions): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhook(options));\n }\n\n /**\n * Handle a webhook from a Fetch API Request\n */\n async handleRequest(request: Request, auth?: WebhookAuthConfig): Promise<WebhookHandlerResultWithFetch> {\n const result = await handleWebhookRequest(request, auth);\n return this.withFetchHelpers(result);\n }\n\n /**\n * Handle a webhook from an Express-like request\n *\n * @example\n * ```typescript\n * app.post('/webhook', async (req, res) => {\n * const result = soniox.webhooks.handleExpress(req);\n *\n * if (result.ok && result.event.status === 'completed') {\n * const transcript = await result.fetchTranscript();\n * console.log(transcript?.text);\n * }\n *\n * res.status(result.status).json({ received: true });\n * });\n * ```\n */\n handleExpress(req: ExpressLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhookExpress(req, auth));\n }\n\n /**\n * Handle a webhook from a Fastify request\n */\n handleFastify(req: FastifyLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhookFastify(req, auth));\n }\n\n /**\n * Handle a webhook from a NestJS request\n */\n handleNestJS(req: NestJSLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhookNestJS(req, auth));\n }\n\n /**\n * Handle a webhook from a Hono context\n */\n async handleHono(c: HonoLikeContext, auth?: WebhookAuthConfig): Promise<WebhookHandlerResultWithFetch> {\n const result = await handleWebhookHono(c, auth);\n return this.withFetchHelpers(result);\n }\n}\n","/**\n * URL utilities for the HTTP client.\n */\n\nimport type { QueryParams } from '../types/public/http.js';\n\n/**\n * Builds a complete URL from base URL, path, and query parameters\n */\nexport function buildUrl(baseUrl: string | undefined, path: string, query?: QueryParams): string {\n // Join base URL and path\n let url = joinUrl(baseUrl ?? '', path);\n\n // Append query string if provided\n if (query) {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n params.append(key, String(value));\n }\n }\n const queryString = params.toString();\n if (queryString) {\n url += (url.includes('?') ? '&' : '?') + queryString;\n }\n }\n\n return url;\n}\n\n/**\n * Joins a base URL with a path\n */\nfunction joinUrl(baseUrl: string, path: string): string {\n if (!baseUrl) return path;\n if (!path) return baseUrl;\n if (/^https?:\\/\\//i.test(path)) return path;\n\n const base = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n return base + suffix;\n}\n\n/**\n * Normalizes fetch Headers to a plain object with lowercase keys\n */\nexport function normalizeHeaders(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\n/**\n * Merges header objects\n */\nexport function mergeHeaders(...headerObjects: (Record<string, string> | undefined)[]): Record<string, string> {\n const result: Record<string, string> = {};\n for (const headers of headerObjects) {\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n result[key.toLowerCase()] = value;\n }\n }\n }\n return result;\n}\n","/**\n * Default fetch-based HTTP client implementation.\n *\n * @module\n */\n\nimport type {\n HttpClient,\n HttpClientOptions,\n HttpObservabilityHooks,\n HttpRequest,\n HttpRequestBody,\n HttpRequestMeta,\n HttpResponse,\n HttpResponseMeta,\n HttpResponseType,\n} from './client.js';\nimport {\n createAbortError,\n createHttpError,\n createNetworkError,\n createParseError,\n createTimeoutError,\n isAbortError,\n SonioxHttpError,\n} from './errors.js';\nimport { buildUrl, mergeHeaders, normalizeHeaders } from './url.js';\n\n/** Default timeout in milliseconds (30 seconds) TODO: Move to constants? */\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Determines the Content-Type header based on the body type.\n */\nfunction getContentTypeForBody(body: HttpRequestBody | undefined): string | undefined {\n if (body === null || body === undefined) {\n return undefined;\n }\n if (typeof body === 'string') {\n return 'text/plain; charset=utf-8';\n }\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n // Let the browser/runtime set the boundary automatically\n return undefined;\n }\n if (body instanceof ArrayBuffer || body instanceof Uint8Array) {\n return 'application/octet-stream';\n }\n // Object/Record - JSON\n return 'application/json';\n}\n\n/**\n * Prepares the request body for fetch.\n */\nfunction prepareBody(body: HttpRequestBody | undefined): string | ArrayBuffer | Uint8Array | FormData | undefined {\n if (body === null || body === undefined) {\n return undefined;\n }\n if (typeof body === 'string') {\n return body;\n }\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n return body;\n }\n if (body instanceof ArrayBuffer) {\n return body;\n }\n if (body instanceof Uint8Array) {\n return body;\n }\n // Object - serialize to JSON\n return JSON.stringify(body);\n}\n\n/**\n * Parses the response based on the expected type.\n */\nasync function parseResponse<T>(\n response: Response,\n responseType: HttpResponseType,\n url: string,\n method: HttpRequest['method']\n): Promise<T> {\n // Handle 204 No Content and empty responses\n const contentLength = response.headers.get('content-length');\n if (response.status === 204 || contentLength === '0') {\n switch (responseType) {\n case 'arrayBuffer':\n return new ArrayBuffer(0) as T;\n case 'text':\n return '' as T;\n case 'json':\n default:\n return null as T;\n }\n }\n\n switch (responseType) {\n case 'text':\n return (await response.text()) as T;\n\n case 'arrayBuffer':\n return (await response.arrayBuffer()) as T;\n\n case 'json':\n default: {\n // Always read as text first so we have the body available for error reporting\n const text = await response.text();\n if (!text) {\n return null as T;\n }\n try {\n return JSON.parse(text) as T;\n } catch (error) {\n throw createParseError(url, method, text, error);\n }\n }\n }\n}\n\n/**\n * Default fetch-based HTTP client.\n *\n * @example Basic usage\n * ```typescript\n * const client = new FetchHttpClient({\n * base_url: 'https://api.example.com/v1',\n * default_headers: {\n * 'Authorization': 'Bearer token',\n * },\n * });\n *\n * const response = await client.request<{ users: User[] }>({\n * method: 'GET',\n * path: '/users',\n * query: { active: true },\n * });\n * ```\n *\n * @example With custom fetch (e.g., for testing)\n * ```typescript\n * const mockFetch = vi.fn().mockResolvedValue(new Response('{}'));\n * const client = new FetchHttpClient({\n * base_url: 'https://api.example.com',\n * fetch: mockFetch,\n * });\n * ```\n *\n * @example Sending different body types\n * ```typescript\n * // JSON body (default for objects)\n * await client.request({\n * method: 'POST',\n * path: '/data',\n * body: { name: 'test', value: 123 },\n * });\n *\n * // Text body\n * await client.request({\n * method: 'POST',\n * path: '/text',\n * body: 'plain text content',\n * headers: { 'Content-Type': 'text/plain' },\n * });\n *\n * // Binary body\n * await client.request({\n * method: 'POST',\n * path: '/upload',\n * body: new Uint8Array([1, 2, 3]),\n * });\n *\n * // FormData (for file uploads)\n * const formData = new FormData();\n * formData.append('file', fileBlob, 'document.pdf');\n * await client.request({\n * method: 'POST',\n * path: '/files',\n * body: formData,\n * });\n * ```\n */\nexport class FetchHttpClient implements HttpClient {\n private readonly baseUrl: string | undefined;\n private readonly defaultHeaders: Record<string, string>;\n private readonly defaultTimeoutMs: number;\n private readonly hooks: HttpObservabilityHooks;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: HttpClientOptions = {}) {\n this.baseUrl = options.base_url;\n this.defaultHeaders = options.default_headers ?? {};\n this.defaultTimeoutMs = options.default_timeout_ms ?? DEFAULT_TIMEOUT_MS;\n this.hooks = options.hooks ?? {};\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n\n if (!this.fetchImpl) {\n throw new Error('fetch is not available. Please provide a fetch implementation via options.fetch');\n }\n }\n\n /**\n * Performs an HTTP request.\n *\n * @param request - Request configuration\n * @returns Promise resolving to the response\n * @throws {@link SonioxHttpError}\n */\n async request<T>(request: HttpRequest): Promise<HttpResponse<T>> {\n const startTime = Date.now();\n const url = buildUrl(this.baseUrl, request.path, request.query);\n const method = request.method;\n const responseType: HttpResponseType = request.responseType ?? 'json';\n\n // Prepare headers\n const contentTypeHeader = getContentTypeForBody(request.body);\n const isFormData = typeof FormData !== 'undefined' && request.body instanceof FormData;\n\n const defaultHeadersWithoutContentType = isFormData\n ? Object.fromEntries(Object.entries(this.defaultHeaders).filter(([key]) => key.toLowerCase() !== 'content-type'))\n : this.defaultHeaders;\n\n const headers = mergeHeaders(\n defaultHeadersWithoutContentType,\n contentTypeHeader ? { 'Content-Type': contentTypeHeader } : undefined,\n request.headers\n );\n\n // Create metadata for hooks\n const requestMeta: HttpRequestMeta = {\n startTime,\n url,\n method,\n headers,\n };\n\n // Call onRequest hook\n this.hooks.onRequest?.(request, requestMeta);\n\n // Setup timeout\n const timeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;\n const timeoutController = new AbortController();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timeoutId = setTimeout(() => {\n timeoutController.abort(new Error('Request timeout'));\n }, timeoutMs);\n }\n\n // Combine timeout signal with user-provided signal\n const combined = request.signal ? combineAbortSignals(timeoutController.signal, request.signal) : null;\n const signal = combined ? combined.signal : timeoutController.signal;\n\n try {\n // Perform the fetch\n const preparedBody = prepareBody(request.body);\n const response = await this.fetchImpl(url, {\n method,\n headers,\n signal,\n ...(preparedBody !== undefined && { body: preparedBody }),\n } as RequestInit);\n\n // Clear timeout\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Normalize response headers\n const responseHeaders = normalizeHeaders(response.headers);\n\n // Check for HTTP errors (non-2xx status)\n if (!response.ok) {\n const bodyText = await response.text().catch(() => '');\n throw createHttpError(url, method, response.status, responseHeaders, bodyText);\n }\n\n // Parse response\n const data = await parseResponse<T>(response, responseType, url, method);\n\n const result: HttpResponse<T> = {\n status: response.status,\n headers: responseHeaders,\n data,\n };\n\n // Call onResponse hook\n const responseMeta: HttpResponseMeta = {\n ...requestMeta,\n durationMs: Date.now() - startTime,\n status: response.status,\n };\n this.hooks.onResponse?.(result, responseMeta);\n\n return result;\n } catch (error) {\n // Clear timeout on error\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Calculate duration for error hook\n const errorMeta: HttpResponseMeta = {\n ...requestMeta,\n durationMs: Date.now() - startTime,\n };\n\n // Normalize error\n const normalizedError = this.normalizeError(error, url, method, timeoutMs, timeoutController);\n\n // Call onError hook\n this.hooks.onError?.(normalizedError, errorMeta);\n\n throw normalizedError;\n } finally {\n combined?.cleanup();\n }\n }\n\n /**\n * Normalizes various error types into SonioxHttpError.\n */\n private normalizeError(\n error: unknown,\n url: string,\n method: HttpRequest['method'],\n timeoutMs: number,\n timeoutController: AbortController\n ): SonioxHttpError {\n // Already a SonioxHttpError\n if (error instanceof SonioxHttpError) {\n return error;\n }\n\n // Check if this was our timeout\n if (timeoutController.signal.aborted && isAbortError(error)) {\n return createTimeoutError(url, method, timeoutMs);\n }\n\n // User-initiated abort\n if (isAbortError(error)) {\n return createAbortError(url, method, error);\n }\n\n // Network error (TypeError from fetch usually indicates network issues)\n if (error instanceof TypeError) {\n return createNetworkError(url, method, error);\n }\n\n // Generic error - treat as network error\n return createNetworkError(url, method, error);\n }\n}\n\n/**\n * Combines multiple AbortSignals into one.\n * The resulting signal will abort if any of the input signals abort.\n * Returns the combined signal and a cleanup function to remove listeners.\n */\nfunction combineAbortSignals(...signals: AbortSignal[]): { signal: AbortSignal; cleanup: () => void } {\n const controller = new AbortController();\n const handlers: Array<{ signal: AbortSignal; handler: () => void }> = [];\n\n const cleanup = () => {\n for (const { signal, handler } of handlers) {\n signal.removeEventListener('abort', handler);\n }\n handlers.length = 0;\n };\n\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return { signal: controller.signal, cleanup };\n }\n\n const handler = () => {\n controller.abort(signal.reason);\n };\n handlers.push({ signal, handler });\n signal.addEventListener('abort', handler, { once: true });\n }\n\n return { signal: controller.signal, cleanup };\n}\n","import { RealtimeSttSession, RealtimeTtsConnection } from '@soniox/core';\nimport type { RealtimeTtsStream, TtsStreamInput } from '@soniox/core';\n\nimport type { RealtimeClientOptions, SttSessionConfig, SttSessionOptions } from '../types/public/realtime.js';\n\n/**\n * Callable TTS factory with `.multiStream()` for multi-stream connections.\n */\nexport interface TtsFactory {\n /**\n * Create a single-stream TTS connection.\n * Opens a WebSocket, starts one stream, and returns a ready-to-use stream\n * that owns its connection (closing the stream closes the connection).\n *\n * @example\n * ```typescript\n * const stream = await client.realtime.tts({\n * model: 'tts-rt-v1-preview',\n * voice: 'Adrian',\n * language: 'en',\n * audio_format: 'wav',\n * });\n * stream.sendText(\"Hello world\");\n * stream.finish();\n * for await (const chunk of stream) { process(chunk); }\n * ```\n */\n (input?: TtsStreamInput): Promise<RealtimeTtsStream>;\n\n /**\n * Create a multi-stream TTS connection.\n * Opens a single WebSocket that can host up to 5 concurrent streams.\n *\n * @example\n * ```typescript\n * const conn = await client.realtime.tts.multiStream();\n * const s1 = await conn.stream({\n * model: 'tts-rt-v1-preview',\n * voice: 'Adrian',\n * language: 'en',\n * audio_format: 'wav',\n * });\n * // Use any voice returned by client.tts.listModels()\n * const s2 = await conn.stream({\n * model: 'tts-rt-v1-preview',\n * voice: someOtherVoice,\n * language: 'en',\n * audio_format: 'wav',\n * });\n * ```\n */\n multiStream(): Promise<RealtimeTtsConnection>;\n}\n\n/**\n * Real-time API factory for creating STT sessions and TTS connections.\n *\n * @example STT\n * ```typescript\n * const session = client.realtime.stt({ model: 'stt-rt-v4' });\n * await session.connect();\n * ```\n *\n * @example TTS (single stream)\n * ```typescript\n * const stream = await client.realtime.tts({\n * model: 'tts-rt-v1-preview',\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) { ... }\n * ```\n *\n * @example TTS (multi-stream)\n * ```typescript\n * const conn = await client.realtime.tts.multiStream();\n * const stream = await conn.stream({\n * model: 'tts-rt-v1-preview',\n * voice: 'Adrian',\n * language: 'en',\n * audio_format: 'wav',\n * });\n * ```\n */\nexport class SonioxRealtimeApi {\n private readonly options: RealtimeClientOptions;\n\n readonly tts: TtsFactory;\n\n constructor(options: RealtimeClientOptions) {\n this.options = options;\n\n const ttsCall = (input?: TtsStreamInput): Promise<RealtimeTtsStream> => {\n return this.createSingleTtsStream(input ?? {});\n };\n ttsCall.multiStream = (): Promise<RealtimeTtsConnection> => {\n return this.createTtsConnection();\n };\n this.tts = ttsCall;\n }\n\n /**\n * Create a new Speech-to-Text session.\n *\n * `config` is shallow-merged on top of `stt_defaults` from the client\n * options; caller-provided fields override the defaults.\n */\n stt(config: SttSessionConfig, options?: SttSessionOptions): RealtimeSttSession {\n const mergedOptions: SttSessionOptions = {\n ...this.options.default_session_options,\n ...options,\n };\n const mergedConfig: SttSessionConfig = {\n ...this.options.stt_defaults,\n ...config,\n };\n return new RealtimeSttSession(this.options.api_key, this.options.ws_base_url, mergedConfig, mergedOptions);\n }\n\n private async createSingleTtsStream(input: TtsStreamInput): Promise<RealtimeTtsStream> {\n const connection = new RealtimeTtsConnection(\n this.options.api_key,\n this.options.tts_ws_url,\n this.options.tts_defaults ?? {},\n this.options.tts_connection_options\n );\n return connection._openStream(input, true);\n }\n\n private async createTtsConnection(): Promise<RealtimeTtsConnection> {\n const connection = new RealtimeTtsConnection(\n this.options.api_key,\n this.options.tts_ws_url,\n this.options.tts_defaults ?? {},\n this.options.tts_connection_options\n );\n await connection.connect();\n return connection;\n }\n}\n\n// Re-export STT session class\nexport { RealtimeSttSession } from '@soniox/core';\n\n// Re-export TTS classes\nexport { RealtimeTtsConnection, RealtimeTtsStream } from '@soniox/core';\n\n// Re-export helpers\nexport { segmentRealtimeTokens } from '@soniox/core';\nexport { RealtimeSegmentBuffer } from '@soniox/core';\nexport { RealtimeUtteranceBuffer } from '@soniox/core';\n\n// Re-export errors\nexport {\n RealtimeError,\n AuthError,\n BadRequestError,\n QuotaError,\n ConnectionError,\n NetworkError,\n AbortError,\n StateError,\n} from '@soniox/core';\n","import { resolveConnectionConfig } from '@soniox/core';\n\nimport { SonioxAuthAPI } from './async/auth.js';\nimport { SonioxFilesAPI } from './async/files.js';\nimport { SonioxModelsAPI } from './async/models.js';\nimport { SonioxSttApi } from './async/stt.js';\nimport { SonioxTtsApi } from './async/tts.js';\nimport { SonioxWebhooksAPI } from './async/webhooks.js';\nimport { FetchHttpClient } from './http/fetch-adapter.js';\nimport { SonioxRealtimeApi } from './realtime/index.js';\nimport type { SonioxNodeClientOptions } from './types/public/index.js';\n\n/**\n * Soniox Node Client\n *\n * @example\n * ```typescript\n * import { SonioxNodeClient } from '@soniox/node';\n *\n * // Default (US) region\n * const client = new SonioxNodeClient({ api_key: 'your-api-key' });\n *\n * // EU region\n * const client = new SonioxNodeClient({ api_key: 'your-api-key', region: 'eu' });\n *\n * // REST TTS\n * const audio = await client.tts.generate({\n * text: 'Hello',\n * voice: 'Adrian',\n * language: 'en',\n * });\n *\n * // WebSocket TTS\n * const stream = await client.realtime.tts({\n * model: 'tts-rt-v1-preview',\n * voice: 'Adrian',\n * language: 'en',\n * audio_format: 'wav',\n * });\n * ```\n */\nexport class SonioxNodeClient {\n readonly files: SonioxFilesAPI;\n readonly stt: SonioxSttApi;\n readonly tts: SonioxTtsApi;\n readonly models: SonioxModelsAPI;\n readonly webhooks: SonioxWebhooksAPI;\n readonly auth: SonioxAuthAPI;\n readonly realtime: SonioxRealtimeApi;\n\n constructor(options: SonioxNodeClientOptions = {}) {\n const apiKey = options.api_key ?? process.env['SONIOX_API_KEY'];\n if (!apiKey) {\n throw new Error(\n 'Missing API key. Provide it via options.api_key or set the SONIOX_API_KEY environment variable.'\n );\n }\n\n const regionDefaults = resolveConnectionConfig({\n api_key: apiKey,\n region: options.region ?? process.env['SONIOX_REGION'],\n base_domain: options.base_domain ?? process.env['SONIOX_BASE_DOMAIN'],\n stt_defaults: options.stt_defaults,\n tts_defaults: options.tts_defaults,\n });\n\n const baseURL = options.base_url ?? process.env['SONIOX_API_BASE_URL'] ?? regionDefaults.api_domain;\n const http =\n options.http_client ??\n new FetchHttpClient({\n base_url: baseURL,\n default_headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n this.files = new SonioxFilesAPI(http);\n this.stt = new SonioxSttApi(http, this.files);\n this.models = new SonioxModelsAPI(http);\n this.webhooks = new SonioxWebhooksAPI(this.stt);\n this.auth = new SonioxAuthAPI(http);\n\n const ttsApiUrl = options.tts_api_url ?? process.env['SONIOX_TTS_API_URL'] ?? regionDefaults.tts_api_url;\n this.tts = new SonioxTtsApi(apiKey, ttsApiUrl, http);\n\n const wsBaseUrl = options.realtime?.ws_base_url ?? process.env['SONIOX_WS_URL'] ?? regionDefaults.stt_ws_url;\n const ttsWsUrl = options.realtime?.tts_ws_url ?? process.env['SONIOX_TTS_WS_URL'] ?? regionDefaults.tts_ws_url;\n\n this.realtime = new SonioxRealtimeApi({\n api_key: apiKey,\n ws_base_url: wsBaseUrl,\n tts_ws_url: ttsWsUrl,\n stt_defaults: regionDefaults.stt_defaults,\n tts_defaults: regionDefaults.tts_defaults,\n tts_connection_options: options.realtime?.tts_connection_options,\n default_session_options: options.realtime?.default_session_options,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["buildSegment","DEFAULT_KEEPALIVE_INTERVAL_MS","MIN_KEEPALIVE_INTERVAL_MS","DEFAULT_CONNECT_TIMEOUT_MS","error","buildSegment","DEFAULT_TIMEOUT_MS"],"sources":["../src/constants.ts","../../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/async/auth.ts","../src/async/files.ts","../src/async/models.ts","../src/async/stt.ts","../src/async/tts.ts","../src/async/webhooks.ts","../src/http/url.ts","../src/http/fetch-adapter.ts","../src/realtime/index.ts","../src/client.ts"],"sourcesContent":["// API base URLs\nexport const SONIOX_API_BASE_URL = 'https://api.soniox.com';\nexport const SONIOX_API_WS_URL = 'wss://stt-rt.soniox.com/transcribe-websocket';\nexport const SONIOX_TTS_API_BASE_URL = 'https://tts-rt.soniox.com';\nexport const SONIOX_TTS_WS_URL = 'wss://tts-rt.soniox.com/tts-websocket';\n\n// Temporary API key\nexport const SONIOX_TMP_API_KEY_USAGE_TYPE = 'transcribe_websocket';\nexport const SONIOX_TMP_API_KEY_DURATION_MIN = 1;\nexport const SONIOX_TMP_API_KEY_DURATION_MAX = 3600;\n\n// Webhook authentication environment variables\nexport const SONIOX_API_WEBHOOK_HEADER_ENV = 'SONIOX_API_WEBHOOK_HEADER';\nexport const SONIOX_API_WEBHOOK_SECRET_ENV = 'SONIOX_API_WEBHOOK_SECRET';\n","/**\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 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-v4' });\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","import type { HttpClient } from '../http/client.js';\nimport type { TemporaryApiKeyRequest, TemporaryApiKeyResponse } from '../types/public/index.js';\n\nexport class SonioxAuthAPI {\n constructor(private http: HttpClient) {}\n\n /**\n * Creates a temporary API key for client-side use.\n *\n * @param request - Request parameters for the temporary key\n * @param signal - Optional AbortSignal for cancellation\n * @returns The temporary API key response\n *\n * @example\n * ```typescript\n * const sttKey = await client.auth.createTemporaryKey({\n * usage_type: 'transcribe_websocket',\n * expires_in_seconds: 300,\n * });\n *\n * const ttsKey = await client.auth.createTemporaryKey({\n * usage_type: 'tts_rt',\n * expires_in_seconds: 300,\n * single_use: true,\n * max_session_duration_seconds: 600,\n * });\n * ```\n */\n async createTemporaryKey(request: TemporaryApiKeyRequest, signal?: AbortSignal): Promise<TemporaryApiKeyResponse> {\n if (\n !Number.isFinite(request.expires_in_seconds) ||\n request.expires_in_seconds < 1 ||\n request.expires_in_seconds > 3600\n ) {\n throw new Error('expires_in_seconds must be a finite number between 1 and 3600');\n }\n\n if (\n request.max_session_duration_seconds !== undefined &&\n (!Number.isFinite(request.max_session_duration_seconds) ||\n request.max_session_duration_seconds < 1 ||\n request.max_session_duration_seconds > 18000)\n ) {\n throw new Error('max_session_duration_seconds must be a finite number between 1 and 18000');\n }\n\n const response = await this.http.request<TemporaryApiKeyResponse>({\n method: 'POST',\n path: '/v1/auth/temporary-api-key',\n body: request,\n ...(signal && { signal }),\n });\n\n return response.data;\n }\n}\n","import type { HttpClient } from '../http/client.js';\nimport { isNotFoundError } from '../http/errors.js';\nimport type {\n FileIdentifier,\n ListFilesOptions,\n ListFilesResponse,\n DeleteAllFilesOptions,\n SonioxFileData,\n UploadFileInput,\n UploadFileOptions,\n} from '../types/public/index.js';\n\n/**\n * Uploaded file\n */\nexport class SonioxFile {\n readonly id: string;\n readonly filename: string;\n readonly size: number;\n readonly created_at: string;\n readonly client_reference_id: string | undefined;\n\n constructor(\n data: SonioxFileData,\n private readonly _http: HttpClient\n ) {\n this.id = data.id;\n this.filename = data.filename;\n this.size = data.size;\n this.created_at = data.created_at;\n\n if (data.client_reference_id) {\n if (data.client_reference_id.length > 256) {\n throw new Error('client_reference_id exceeds maximum length of 256 characters');\n }\n\n this.client_reference_id = data.client_reference_id;\n }\n }\n\n /**\n * Returns the raw data for this file.\n */\n toJSON(): SonioxFileData {\n return {\n id: this.id,\n filename: this.filename,\n size: this.size,\n created_at: this.created_at,\n client_reference_id: this.client_reference_id,\n };\n }\n\n /**\n * Permanently deletes this file.\n * This operation is idempotent - succeeds even if the file doesn't exist.\n *\n * @param signal - Optional AbortSignal for cancellation\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const file = await client.files.get('550e8400-e29b-41d4-a716-446655440000');\n * if (file) {\n * await file.delete();\n * }\n * ```\n */\n async delete(signal?: AbortSignal): Promise<void> {\n try {\n await this._http.request<null>({\n method: 'DELETE',\n path: `/v1/files/${this.id}`,\n ...(signal && { signal }),\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n}\n\n/**\n * Result set for file listing\n */\nexport class FileListResult implements AsyncIterable<SonioxFile> {\n /**\n * Files from the first page of results\n */\n readonly files: SonioxFile[];\n\n /**\n * Pagination cursor for the next page. Null if no more pages\n */\n readonly next_page_cursor: string | null;\n\n constructor(\n initialResponse: ListFilesResponse<SonioxFileData>,\n private readonly _http: HttpClient,\n private readonly _limit: number | undefined,\n private readonly _signal: AbortSignal | undefined = undefined\n ) {\n this.files = initialResponse.files.map((data) => new SonioxFile(data, _http));\n this.next_page_cursor = initialResponse.next_page_cursor;\n }\n\n /**\n * Returns the raw data for this list result.\n * Also used by JSON.stringify() to prevent serialization of internal HTTP client.\n */\n toJSON(): ListFilesResponse<SonioxFileData> {\n return {\n files: this.files.map((f) => f.toJSON()),\n next_page_cursor: this.next_page_cursor,\n };\n }\n\n /**\n * Returns true if there are more pages of results beyond the first page\n */\n isPaged(): boolean {\n return this.next_page_cursor !== null;\n }\n\n /**\n * Async iterator that automatically fetches all pages\n * Use with `for await...of` to iterate through all files\n */\n async *[Symbol.asyncIterator](): AsyncIterator<SonioxFile> {\n // Yield files from the first page\n for (const file of this.files) {\n yield file;\n }\n\n // Fetch and yield subsequent pages\n let cursor = this.next_page_cursor;\n while (cursor !== null) {\n const response = await this._http.request<ListFilesResponse<SonioxFileData>>({\n method: 'GET',\n path: '/v1/files',\n query: {\n limit: this._limit,\n cursor,\n },\n ...(this._signal && { signal: this._signal }),\n });\n\n for (const data of response.data.files) {\n yield new SonioxFile(data, this._http);\n }\n\n cursor = response.data.next_page_cursor;\n }\n }\n}\n\n/**\n * Helper to extract file ID from a FileIdentifier\n */\nfunction getFileId(file: FileIdentifier): string {\n return typeof file === 'string' ? file : file.id;\n}\n\n/**\n * Maximum file size allowed by the API (1 GB)\n */\nconst MAX_FILE_SIZE = 1073741824;\n\n/**\n * Default filename when none can be inferred\n */\nconst DEFAULT_FILENAME = 'file';\n\n/**\n * Checks if the input is an async-iterable Node.js readable stream\n */\nfunction isNodeReadableStream(input: unknown): input is NodeJS.ReadableStream {\n return (\n typeof input === 'object' &&\n input !== null &&\n 'pipe' in input &&\n typeof (input as NodeJS.ReadableStream).pipe === 'function' &&\n Symbol.asyncIterator in input\n );\n}\n\n/**\n * Checks if the input is a Web ReadableStream\n */\nfunction isWebReadableStream(input: unknown): input is ReadableStream<Uint8Array> {\n return (\n typeof input === 'object' &&\n input !== null &&\n 'getReader' in input &&\n typeof (input as ReadableStream).getReader === 'function'\n );\n}\n\n/**\n * Collects chunks from a Node.js readable stream into a Buffer\n * Aborts early if size exceeds MAX_FILE_SIZE to prevent OOM\n */\nasync function collectNodeStream(stream: NodeJS.ReadableStream): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let totalLength = 0;\n\n for await (const chunk of stream as AsyncIterable<Buffer | Uint8Array | string>) {\n if (typeof chunk === 'string') {\n throw new Error(\n 'Stream returned string chunks. Use a binary stream (e.g., fs.createReadStream without encoding option).'\n );\n }\n\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\n totalLength += buf.length;\n if (totalLength > MAX_FILE_SIZE) {\n throw new Error(`File size exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n chunks.push(buf);\n }\n\n return Buffer.concat(chunks);\n}\n\n/**\n * Collects chunks from a Web ReadableStream into a Uint8Array\n * Aborts early if size exceeds MAX_FILE_SIZE to prevent OOM\n */\nasync function collectWebStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n totalLength += value.length;\n if (totalLength > MAX_FILE_SIZE) {\n throw new Error(`File size exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n}\n\n/**\n * Resolves the file input to a Blob and filename\n */\nasync function resolveFileInput(\n input: UploadFileInput,\n filenameOverride?: string\n): Promise<{ blob: Blob; filename: string }> {\n // Blob (includes File which has a name property)\n if (input instanceof Blob) {\n if (input.size > MAX_FILE_SIZE) {\n throw new Error(`File size (${input.size} bytes) exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n const filename =\n filenameOverride ?? ('name' in input && typeof input.name === 'string' ? input.name : DEFAULT_FILENAME);\n\n return {\n blob: input,\n filename,\n };\n }\n\n // Buffer\n if (Buffer.isBuffer(input)) {\n if (input.length > MAX_FILE_SIZE) {\n throw new Error(`File size (${input.length} bytes) exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n return {\n blob: new Blob([new Uint8Array(input)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n // Uint8Array (but not Buffer)\n if (input instanceof Uint8Array) {\n if (input.length > MAX_FILE_SIZE) {\n throw new Error(`File size (${input.length} bytes) exceeds maximum allowed size (${MAX_FILE_SIZE} bytes)`);\n }\n\n return {\n blob: new Blob([new Uint8Array(input)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n // Web ReadableStream\n if (isWebReadableStream(input)) {\n const data = await collectWebStream(input);\n return {\n blob: new Blob([new Uint8Array(data)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n // Node.js ReadableStream\n if (isNodeReadableStream(input)) {\n const buffer = await collectNodeStream(input);\n return {\n blob: new Blob([new Uint8Array(buffer)]),\n filename: filenameOverride ?? DEFAULT_FILENAME,\n };\n }\n\n throw new Error('Invalid file input. Expected Buffer, Uint8Array, Blob, or ReadableStream.');\n}\n\nexport class SonioxFilesAPI {\n constructor(private http: HttpClient) {}\n\n /**\n * Uploads a file to Soniox for transcription\n *\n * @param file - Buffer, Uint8Array, Blob, or ReadableStream\n * @param options - Upload options\n * @returns The uploaded file metadata\n * @throws {@link SonioxHttpError} On API errors\n * @throws `Error` On validation errors (file too large, invalid input)\n *\n * @example Upload from file path (Node.js)\n * ```typescript\n * import * as fs from 'node:fs';\n *\n * const buffer = await fs.promises.readFile('/path/to/audio.mp3');\n * const file = await client.files.upload(buffer, { filename: 'audio.mp3' });\n * ```\n *\n * @example Upload from file path (Bun)\n * ```typescript\n * const file = await client.files.upload(Bun.file('/path/to/audio.mp3'));\n * ```\n *\n * @example Upload with tracking ID\n * ```typescript\n * const file = await client.files.upload(buffer, {\n * filename: 'audio.mp3',\n * client_reference_id: 'order-12345',\n * });\n * ```\n *\n * @example Upload with cancellation\n * ```typescript\n * const controller = new AbortController();\n * setTimeout(() => controller.abort(), 30000);\n *\n * const file = await client.files.upload(buffer, {\n * filename: 'audio.mp3',\n * signal: controller.signal,\n * });\n * ```\n */\n async upload(file: UploadFileInput, options: UploadFileOptions = {}): Promise<SonioxFile> {\n const { filename, client_reference_id, signal, timeout_ms } = options;\n\n // Validate client_reference_id length\n if (client_reference_id !== undefined && client_reference_id.length > 256) {\n throw new Error(\n `client_reference_id exceeds maximum length of 256 characters (got ${client_reference_id.length})`\n );\n }\n\n // Resolve the file input to a Blob and filename\n const { blob, filename: resolvedFilename } = await resolveFileInput(file, filename);\n\n // Build the FormData\n const formData = new FormData();\n formData.append('file', blob, resolvedFilename);\n\n if (client_reference_id !== undefined) {\n formData.append('client_reference_id', client_reference_id);\n }\n\n // Build request options\n const requestOptions: Parameters<HttpClient['request']>[0] = {\n method: 'POST',\n path: '/v1/files',\n body: formData,\n };\n\n if (signal !== undefined) {\n requestOptions.signal = signal;\n }\n\n if (timeout_ms !== undefined) {\n requestOptions.timeoutMs = timeout_ms;\n }\n\n // Make the request\n const response = await this.http.request<SonioxFileData>(requestOptions);\n\n return new SonioxFile(response.data, this.http);\n }\n\n /**\n * Retrieves list of uploaded files\n *\n * The returned result is async iterable - use `for await...of`\n *\n * @param options - Optional pagination and cancellation parameters\n * @returns FileListResult\n * @throws {@link SonioxHttpError}\n *\n * @example\n * ```typescript\n * const result = await client.files.list();\n *\n * // Automatic paging - iterates through ALL files across all pages\n * for await (const file of result) {\n * console.log(file.filename, file.size);\n * }\n *\n * // Or access just the first page\n * for (const file of result.files) {\n * console.log(file.filename);\n * }\n *\n * // Check if there are more pages\n * if (result.isPaged()) {\n * console.log('More pages available');\n * }\n *\n * // Manual paging using cursor\n * const page1 = await client.files.list({ limit: 10 });\n * if (page1.next_page_cursor) {\n * const page2 = await client.files.list({ cursor: page1.next_page_cursor });\n * }\n *\n * // With cancellation\n * const controller = new AbortController();\n * const result = await client.files.list({ signal: controller.signal });\n * ```\n */\n async list(options: ListFilesOptions = {}): Promise<FileListResult> {\n const { limit, cursor, signal } = options;\n\n const response = await this.http.request<ListFilesResponse<SonioxFileData>>({\n method: 'GET',\n path: '/v1/files',\n query: {\n limit,\n cursor,\n },\n ...(signal && { signal }),\n });\n\n return new FileListResult(response.data, this.http, limit, signal);\n }\n\n /**\n * Retrieve metadata for an uploaded file.\n *\n * @param file - The UUID of the file or a SonioxFile instance\n * @param signal - Optional AbortSignal for cancellation\n * @returns The file instance, or null if not found\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const file = await client.files.get('550e8400-e29b-41d4-a716-446655440000');\n * if (file) {\n * console.log(file.filename, file.size);\n * }\n * ```\n */\n async get(file: FileIdentifier, signal?: AbortSignal): Promise<SonioxFile | null> {\n const file_id = getFileId(file);\n try {\n const response = await this.http.request<SonioxFileData>({\n method: 'GET',\n path: `/v1/files/${file_id}`,\n ...(signal && { signal }),\n });\n return new SonioxFile(response.data, this.http);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Permanently deletes a file.\n * This operation is idempotent - succeeds even if the file doesn't exist.\n *\n * @param file - The UUID of the file or a SonioxFile instance\n * @param signal - Optional AbortSignal for cancellation\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Delete by ID\n * await client.files.delete('550e8400-e29b-41d4-a716-446655440000');\n *\n * // Or delete a file instance\n * const file = await client.files.get('550e8400-e29b-41d4-a716-446655440000');\n * if (file) {\n * await client.files.delete(file);\n * }\n *\n * // Or just use the instance method\n * await file.delete();\n * ```\n */\n async delete(file: FileIdentifier, signal?: AbortSignal): Promise<void> {\n const file_id = getFileId(file);\n try {\n await this.http.request<null>({\n method: 'DELETE',\n path: `/v1/files/${file_id}`,\n ...(signal && { signal }),\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n\n /**\n * Permanently deletes all uploaded files.\n * Iterates through all pages of files and deletes each one.\n *\n * @param options - Optional signal and progress callback.\n * @returns The number of files deleted.\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` If the operation is aborted via signal.\n *\n * @example\n * ```typescript\n * // Delete all files\n * await client.files.delete_all();\n * console.log(`Deleted all files.`);\n\n * // With cancellation\n * const controller = new AbortController();\n * await client.files.delete_all({ signal: controller.signal });\n * ```\n */\n async delete_all(options: DeleteAllFilesOptions = {}): Promise<void> {\n const { signal } = options;\n const result = await this.list({ signal });\n\n for await (const file of result) {\n signal?.throwIfAborted();\n await this.delete(file, signal);\n }\n }\n}\n","import type { HttpClient } from '../http/client.js';\nimport type { SonioxModel } from '../types/public/index.js';\n\nexport class SonioxModelsAPI {\n constructor(private http: HttpClient) {}\n\n /**\n * List of available models and their attributes.\n * @see https://soniox.com/docs/stt/api-reference/models/get_models\n * @param signal - Optional AbortSignal for cancellation\n * @returns List of available models and their attributes.\n */\n async list(signal?: AbortSignal): Promise<SonioxModel[]> {\n const response = await this.http.request<{ models: SonioxModel[] }>({\n method: 'GET',\n path: '/v1/models',\n ...(signal && { signal }),\n });\n return response.data.models;\n }\n}\n","import type { HttpClient } from '../http/client.js';\nimport { isNotFoundError } from '../http/errors.js';\nimport type {\n CreateTranscriptionOptions,\n ISonioxTranscript,\n ISonioxTranscription,\n ListTranscriptionsOptions,\n ListTranscriptionsResponse,\n DeleteAllTranscriptionsOptions,\n SegmentTranscriptOptions,\n SonioxTranscriptionData,\n TranscribeFromFileIdOptions,\n TranscribeFromFileOptions,\n TranscribeFromUrlOptions,\n TranscribeOptions,\n TranscriptionIdentifier,\n TranscriptionStatus,\n TranscriptResponse,\n TranscriptSegment,\n TranscriptToken,\n TranscriptionContext,\n UploadFileInput,\n WaitOptions,\n} from '../types/public/index.js';\n\nimport type { SonioxFilesAPI } from './files.js';\nimport { segmentTokens } from './segments.js';\n\n/**\n * Minimum polling interval in ms\n */\nconst MIN_POLL_INTERVAL_MS = 1000;\n\n/**\n * Default polling interval in ms\n */\nconst DEFAULT_POLL_INTERVAL_MS = 1000;\n\n/**\n * Default timeout for waiting in ms (5 minutes)\n */\nconst DEFAULT_TIMEOUT_MS = 300000;\n\n/**\n * Helper to sleep for a given number of ms, interruptible by abort signal\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error('Transcription wait aborted'));\n return;\n }\n\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n\n function onAbort() {\n clearTimeout(timeoutId);\n reject(new Error('Transcription wait aborted'));\n }\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * Helper to extract transcription ID from a TranscriptionIdentifier\n */\nfunction getTranscriptionId(transcription: TranscriptionIdentifier): string {\n return typeof transcription === 'string' ? transcription : transcription.id;\n}\n\n/**\n * Creates an AbortSignal that fires when either the timeout expires or the provided signal aborts\n * Returns the signal and a cleanup function to clear the timeout\n */\nfunction createTimeoutSignal(\n timeout_ms: number | undefined,\n signal: AbortSignal | undefined\n): { signal: AbortSignal | undefined; cleanup: () => void } {\n // No timeout and no signal - return undefined\n if (timeout_ms === undefined && signal === undefined) {\n return { signal: undefined, cleanup: () => {} };\n }\n\n // Only user signal, no timeout\n if (timeout_ms === undefined) {\n return { signal, cleanup: () => {} };\n }\n\n if (!Number.isFinite(timeout_ms) || timeout_ms <= 0) {\n throw new Error('timeout_ms must be a finite positive number');\n }\n\n // Create timeout-based abort controller\n const timeoutController = new AbortController();\n const timeoutId = setTimeout(() => {\n timeoutController.abort(new Error(`Operation timed out after ${timeout_ms}ms`));\n }, timeout_ms);\n\n const cleanup = () => clearTimeout(timeoutId);\n\n // Only timeout, no user signal\n if (signal === undefined) {\n return { signal: timeoutController.signal, cleanup };\n }\n\n // Both timeout and user signal - combine them\n const combinedController = new AbortController();\n\n const onAbort = () => {\n cleanup();\n timeoutController.signal.removeEventListener('abort', onTimeout);\n combinedController.abort(signal.reason ?? new Error('Operation aborted'));\n };\n\n const onTimeout = () => {\n signal.removeEventListener('abort', onAbort);\n combinedController.abort(timeoutController.signal.reason);\n };\n\n if (signal.aborted) {\n cleanup();\n combinedController.abort(signal.reason ?? new Error('Operation aborted'));\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n timeoutController.signal.addEventListener('abort', onTimeout, { once: true });\n }\n\n return {\n signal: combinedController.signal,\n cleanup: () => {\n cleanup();\n signal.removeEventListener('abort', onAbort);\n timeoutController.signal.removeEventListener('abort', onTimeout);\n },\n };\n}\n\n/**\n * Groups contiguous tokens into segments based on specified grouping keys.\n * A new segment starts when any of the `group_by` fields changes\n *\n * @param tokens - Array of transcript tokens to segment\n * @param options - Segmentation options\n * @param options.group_by - Fields to group by (default: ['speaker', 'language'])\n * @returns Array of segments with combined text and timing\n *\n * @example\n * ```typescript\n * const transcript = await transcription.getTranscript();\n *\n * // Group by both speaker and language (default)\n * const segments = segmentTranscript(transcript.tokens);\n *\n * // Group by speaker only\n * const bySpeaker = segmentTranscript(transcript.tokens, { group_by: ['speaker'] });\n *\n * // Group by language only\n * const byLanguage = segmentTranscript(transcript.tokens, { group_by: ['language'] });\n *\n * for (const seg of segments) {\n * console.log(`[Speaker ${seg.speaker}] ${seg.text}`);\n * }\n * ```\n */\nexport function segmentTranscript(\n tokens: TranscriptToken[],\n options: SegmentTranscriptOptions = {}\n): TranscriptSegment[] {\n return segmentTokens(tokens, options, buildSegment);\n}\n\n/**\n * Helper to build a segment from a list of tokens\n */\nfunction buildSegment(\n tokens: TranscriptToken[],\n speaker: string | null | undefined,\n language: string | null | undefined\n): TranscriptSegment {\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 != null && { speaker }),\n ...(language != null && { language }),\n tokens,\n };\n}\n\n/**\n * A Transcript result containing the transcribed text and tokens.\n */\nexport class SonioxTranscript implements ISonioxTranscript {\n /**\n * Unique identifier of the transcription this transcript belongs to.\n */\n readonly id: string;\n\n /**\n * Complete transcribed text content.\n */\n readonly text: string;\n\n /**\n * List of detailed token information with timestamps and metadata.\n */\n readonly tokens: TranscriptToken[];\n\n constructor(data: TranscriptResponse) {\n this.id = data.id;\n this.text = data.text;\n this.tokens = data.tokens;\n }\n\n /**\n * Groups tokens into segments based on specified grouping keys.\n *\n * A new segment starts when any of the `group_by` fields changes.\n *\n * @param options - Segmentation options\n * @param options.group_by - Fields to group by (default: ['speaker', 'language'])\n * @returns Array of segments with combined text and timing\n *\n * @example\n * ```typescript\n * const transcript = await transcription.getTranscript();\n *\n * // Group by both speaker and language (default)\n * const segments = transcript.segments();\n *\n * // Group by speaker only\n * const bySpeaker = transcript.segments({ group_by: ['speaker'] });\n *\n * for (const s of segments) {\n * console.log(`[Speaker ${s.speaker}] ${s.text}`);\n * }\n * ```\n */\n segments(options?: SegmentTranscriptOptions): TranscriptSegment[] {\n return segmentTranscript(this.tokens, options);\n }\n}\n\n/**\n * A Transcription instance\n */\nexport class SonioxTranscription implements ISonioxTranscription {\n /**\n * Unique identifier of the transcription.\n */\n readonly id: string;\n\n /**\n * Current status of the transcription.\n */\n readonly status: TranscriptionStatus;\n\n /**\n * UTC timestamp when the transcription was created.\n */\n readonly created_at: string;\n\n /**\n * Speech-to-text model used.\n */\n readonly model: string;\n\n /**\n * URL of the audio file being transcribed.\n */\n readonly audio_url: string | null | undefined;\n\n /**\n * ID of the uploaded file being transcribed.\n */\n readonly file_id: string | null | undefined;\n\n /**\n * Name of the file being transcribed.\n */\n readonly filename: string;\n\n /**\n * Expected languages in the audio.\n */\n readonly language_hints: string[] | undefined;\n\n /**\n * When true, speakers are identified and separated in the transcription output.\n */\n readonly enable_speaker_diarization: boolean;\n\n /**\n * When true, language is detected for each part of the transcription.\n */\n readonly enable_language_identification: boolean;\n\n /**\n * Duration of the audio in milliseconds. Only available after processing begins.\n */\n readonly audio_duration_ms: number | null | undefined;\n\n /**\n * Error type if transcription failed.\n */\n readonly error_type: string | null | undefined;\n\n /**\n * Error message if transcription failed.\n */\n readonly error_message: string | null | undefined;\n\n /**\n * URL to receive webhook notifications.\n */\n readonly webhook_url: string | null | undefined;\n\n /**\n * Name of the authentication header sent with webhook notifications.\n */\n readonly webhook_auth_header_name: string | null | undefined;\n\n /**\n * Authentication header value (masked).\n */\n readonly webhook_auth_header_value: string | null | undefined;\n\n /**\n * HTTP status code received when webhook was delivered.\n */\n readonly webhook_status_code: number | null | undefined;\n\n /**\n * Optional tracking identifier.\n */\n readonly client_reference_id: string | null | undefined;\n\n /**\n * Additional context provided for the transcription.\n */\n readonly context: TranscriptionContext | null | undefined;\n\n /**\n * Pre-fetched transcript. Only available when using `transcribe()` with `wait: true`,\n * `fetch_transcript !== false`, and the transcription completed successfully\n */\n readonly transcript: SonioxTranscript | null | undefined;\n\n constructor(\n data: SonioxTranscriptionData,\n private readonly _http: HttpClient,\n transcript?: SonioxTranscript | null\n ) {\n this.id = data.id;\n this.status = data.status;\n this.created_at = data.created_at;\n this.model = data.model;\n this.audio_url = data.audio_url;\n this.file_id = data.file_id;\n this.filename = data.filename;\n this.language_hints = data.language_hints ?? undefined;\n this.enable_speaker_diarization = data.enable_speaker_diarization;\n this.enable_language_identification = data.enable_language_identification;\n this.audio_duration_ms = data.audio_duration_ms;\n this.error_type = data.error_type;\n this.error_message = data.error_message;\n this.webhook_url = data.webhook_url;\n this.webhook_auth_header_name = data.webhook_auth_header_name;\n this.webhook_auth_header_value = data.webhook_auth_header_value;\n this.webhook_status_code = data.webhook_status_code;\n this.client_reference_id = data.client_reference_id;\n this.context = data.context;\n this.transcript = transcript;\n }\n\n /**\n * Returns the raw data for this transcription.\n */\n toJSON(): SonioxTranscriptionData {\n return {\n id: this.id,\n status: this.status,\n created_at: this.created_at,\n model: this.model,\n audio_url: this.audio_url,\n file_id: this.file_id,\n filename: this.filename,\n language_hints: this.language_hints,\n enable_speaker_diarization: this.enable_speaker_diarization,\n enable_language_identification: this.enable_language_identification,\n audio_duration_ms: this.audio_duration_ms,\n error_type: this.error_type,\n error_message: this.error_message,\n webhook_url: this.webhook_url,\n webhook_auth_header_name: this.webhook_auth_header_name,\n webhook_auth_header_value: this.webhook_auth_header_value,\n webhook_status_code: this.webhook_status_code,\n client_reference_id: this.client_reference_id,\n context: this.context,\n };\n }\n\n /**\n * Permanently deletes this transcription.\n * This operation is idempotent - succeeds even if the transcription doesn't exist.\n *\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * await transcription.delete();\n * ```\n */\n async delete(): Promise<void> {\n try {\n await this._http.request<null>({\n method: 'DELETE',\n path: `/v1/transcriptions/${this.id}`,\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n\n /**\n * Permanently deletes this transcription and its associated file (if any).\n * This operation is idempotent - succeeds even if resources don't exist.\n *\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Clean up both transcription and uploaded file\n * const transcription = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * });\n * // ... use transcription ...\n * await transcription.destroy(); // Deletes both transcription and file\n * ```\n */\n async destroy(): Promise<void> {\n // Delete the transcription first\n await this.delete();\n\n // Delete the associated file if present\n if (this.file_id) {\n try {\n await this._http.request<null>({\n method: 'DELETE',\n path: `/v1/files/${this.file_id}`,\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n }\n\n /**\n * Retrieves the full transcript text and tokens for this transcription.\n * Only available for successfully completed transcriptions.\n *\n * Returns cached transcript if available (when using `transcribe()` with `wait: true`).\n * Use `force: true` to bypass the cache and fetch fresh data from the API.\n *\n * @param options - Optional settings\n * @param options.force - If true, bypasses cached transcript and fetches from API\n * @param options.signal - Optional AbortSignal for request cancellation\n * @returns The transcript with text and detailed tokens, or null if not found.\n * @throws {@link SonioxHttpError} On API errors (except 404).\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * if (transcription) {\n * const transcript = await transcription.getTranscript();\n * if (transcript) {\n * console.log(transcript.text);\n * }\n * }\n *\n * // Force re-fetch from API\n * const freshTranscript = await transcription.getTranscript({ force: true });\n * ```\n */\n async getTranscript(options?: { force?: boolean; signal?: AbortSignal }): Promise<SonioxTranscript | null> {\n const { force, signal } = options ?? {};\n\n // Return cached transcript if available (null means transcription failed, undefined means not fetched)\n if (!force && this.transcript !== undefined) {\n return this.transcript;\n }\n\n try {\n const response = await this._http.request<TranscriptResponse>({\n method: 'GET',\n path: `/v1/transcriptions/${this.id}/transcript`,\n ...(signal && { signal }),\n });\n return new SonioxTranscript(response.data);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Re-fetches this transcription to get the latest status.\n * @param signal - Optional AbortSignal for request cancellation.\n * @returns A new SonioxTranscription instance with updated data.\n * @throws {@link SonioxHttpError}\n *\n * @example\n * ```typescript\n * let transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * transcription = await transcription.refresh();\n * console.log(transcription.status);\n * ```\n */\n async refresh(signal?: AbortSignal): Promise<SonioxTranscription> {\n const response = await this._http.request<SonioxTranscriptionData>({\n method: 'GET',\n path: `/v1/transcriptions/${this.id}`,\n ...(signal && { signal }),\n });\n return new SonioxTranscription(response.data, this._http);\n }\n\n /**\n * Waits for the transcription to complete or fail.\n * Polls the API at the specified interval until the status is 'completed' or 'error'.\n *\n * @param options - Wait options including polling interval, timeout, and callbacks.\n * @returns The completed or errored transcription.\n * @throws `Error` If the wait times out or is aborted.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * });\n *\n * // Simple wait\n * const completed = await transcription.wait();\n *\n * // Wait with progress callback\n * const completed = await transcription.wait({\n * interval_ms: 2000,\n * on_status_change: (status) => console.log(`Status: ${status}`),\n * });\n * ```\n */\n async wait(options: WaitOptions = {}): Promise<SonioxTranscription> {\n const {\n interval_ms: requestedInterval = DEFAULT_POLL_INTERVAL_MS,\n timeout_ms = DEFAULT_TIMEOUT_MS,\n on_status_change,\n signal,\n } = options;\n\n // Enforce minimum polling interval\n const interval_ms = Math.max(requestedInterval, MIN_POLL_INTERVAL_MS);\n\n // If already completed or errored, return immediately\n if (this.status !== 'queued' && this.status !== 'processing') {\n return this;\n }\n\n // Check abort signal before any network calls\n if (signal?.aborted) {\n throw new Error('Transcription wait aborted');\n }\n\n const startTime = Date.now();\n\n // Helper to check timeout\n const checkTimeout = () => {\n if (Date.now() - startTime > timeout_ms) {\n throw new Error(`Transcription wait timed out after ${timeout_ms}ms`);\n }\n };\n\n // Check timeout before initial refresh\n checkTimeout();\n\n let lastStatus = this.status as TranscriptionStatus;\n let current = await this.refresh(signal);\n\n while (current.status === 'queued' || current.status === 'processing') {\n // Notify on status change\n if (current.status !== lastStatus) {\n on_status_change?.(current.status, current.toJSON());\n lastStatus = current.status;\n }\n\n // Check timeout before sleeping\n checkTimeout();\n\n // Wait for interval (interruptible by abort signal)\n await sleep(interval_ms, signal);\n\n // Check timeout after sleep, before making network call\n checkTimeout();\n\n // Refresh status (passes signal to HTTP request)\n current = await current.refresh(signal);\n }\n\n // Notify on final status change\n if (current.status !== lastStatus) {\n on_status_change?.(current.status, current.toJSON());\n }\n\n return current;\n }\n}\n\n/**\n * Result set for transcription listing.\n */\nexport class TranscriptionListResult implements AsyncIterable<SonioxTranscription> {\n /**\n * Transcriptions from the first page of results.\n */\n readonly transcriptions: SonioxTranscription[];\n\n /**\n * Pagination cursor for the next page. Null if no more pages.\n */\n readonly next_page_cursor: string | null;\n\n constructor(\n initialResponse: ListTranscriptionsResponse<SonioxTranscriptionData>,\n private readonly _http: HttpClient,\n private readonly _options: ListTranscriptionsOptions,\n private readonly _signal?: AbortSignal\n ) {\n this.transcriptions = initialResponse.transcriptions.map((data) => new SonioxTranscription(data, _http));\n this.next_page_cursor = initialResponse.next_page_cursor;\n }\n\n /**\n * Returns the raw data for this list result\n */\n toJSON(): ListTranscriptionsResponse<SonioxTranscriptionData> {\n return {\n transcriptions: this.transcriptions.map((t) => t.toJSON()),\n next_page_cursor: this.next_page_cursor,\n };\n }\n\n /**\n * Returns true if there are more pages of results beyond the first page.\n */\n isPaged(): boolean {\n return this.next_page_cursor !== null;\n }\n\n /**\n * Async iterator that automatically fetches all pages.\n * Use with `for await...of` to iterate through all transcriptions.\n */\n async *[Symbol.asyncIterator](): AsyncIterator<SonioxTranscription> {\n // Yield transcriptions from the first page\n for (const transcription of this.transcriptions) {\n yield transcription;\n }\n\n // Fetch and yield subsequent pages\n let cursor = this.next_page_cursor;\n while (cursor !== null) {\n const response = await this._http.request<ListTranscriptionsResponse<SonioxTranscriptionData>>({\n method: 'GET',\n path: '/v1/transcriptions',\n query: {\n limit: this._options.limit,\n cursor,\n },\n ...(this._signal && { signal: this._signal }),\n });\n\n for (const data of response.data.transcriptions) {\n yield new SonioxTranscription(data, this._http);\n }\n\n cursor = response.data.next_page_cursor;\n }\n }\n}\n\nexport class SonioxSttApi {\n constructor(\n private http: HttpClient,\n private filesApi: SonioxFilesAPI\n ) {}\n\n /**\n * Creates a new transcription from audio_url or file_id\n *\n * @param options - Transcription options including model and audio source.\n * @returns The created transcription.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * // Transcribe from URL\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * });\n *\n * // Transcribe from uploaded file\n * const file = await client.files.upload(buffer);\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * file_id: file.id,\n * });\n *\n * // With speaker diarization\n * const transcription = await client.stt.create({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * enable_speaker_diarization: true,\n * });\n * ```\n */\n async create(options: CreateTranscriptionOptions, signal?: AbortSignal): Promise<SonioxTranscription> {\n // Validate client_reference_id length\n if (options.client_reference_id !== undefined && options.client_reference_id.length > 256) {\n throw new Error(\n `client_reference_id exceeds maximum length of 256 characters (got ${options.client_reference_id.length})`\n );\n }\n\n const response = await this.http.request<SonioxTranscriptionData>({\n method: 'POST',\n path: '/v1/transcriptions',\n body: options,\n ...(signal && { signal }),\n });\n\n return new SonioxTranscription(response.data, this.http);\n }\n\n /**\n * Retrieves list of transcriptions\n *\n * The returned result is async iterable - use `for await...of` to iterate through all pages\n *\n * @param options - Optional pagination and filter parameters.\n * @returns TranscriptionListResult with async iteration support.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * const result = await client.stt.list();\n *\n * // Automatic paging - iterates through ALL transcriptions across all pages\n * for await (const transcription of result) {\n * console.log(transcription.id, transcription.status);\n * }\n *\n * // Or access just the first page\n * for (const transcription of result.transcriptions) {\n * console.log(transcription.id);\n * }\n *\n * // Check if there are more pages\n * if (result.isPaged()) {\n * console.log('More pages available');\n * }\n * ```\n */\n async list(options: ListTranscriptionsOptions = {}, signal?: AbortSignal): Promise<TranscriptionListResult> {\n const response = await this.http.request<ListTranscriptionsResponse<SonioxTranscriptionData>>({\n method: 'GET',\n path: '/v1/transcriptions',\n query: {\n limit: options.limit,\n cursor: options.cursor,\n },\n ...(signal && { signal }),\n });\n\n return new TranscriptionListResult(response.data, this.http, options, signal);\n }\n\n /**\n * Retrieves a transcription by ID\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance.\n * @returns The transcription, or null if not found.\n * @throws {@link SonioxHttpError} On API errors (except 404).\n *\n * @example\n * ```typescript\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * if (transcription) {\n * console.log(transcription.status, transcription.model);\n * }\n * ```\n */\n async get(id: TranscriptionIdentifier, signal?: AbortSignal): Promise<SonioxTranscription | null> {\n const transcription_id = getTranscriptionId(id);\n try {\n const response = await this.http.request<SonioxTranscriptionData>({\n method: 'GET',\n path: `/v1/transcriptions/${transcription_id}`,\n ...(signal && { signal }),\n });\n return new SonioxTranscription(response.data, this.http);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Permanently deletes a transcription.\n * This operation is idempotent - succeeds even if the transcription doesn't exist.\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Delete by ID\n * await client.stt.delete('550e8400-e29b-41d4-a716-446655440000');\n *\n * // Or delete a transcription instance\n * const transcription = await client.stt.get('550e8400-e29b-41d4-a716-446655440000');\n * if (transcription) {\n * await client.stt.delete(transcription);\n * }\n * ```\n */\n async delete(id: TranscriptionIdentifier, signal?: AbortSignal): Promise<void> {\n const transcription_id = getTranscriptionId(id);\n try {\n await this.http.request<null>({\n method: 'DELETE',\n path: `/v1/transcriptions/${transcription_id}`,\n ...(signal && { signal }),\n });\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n\n /**\n * Permanently deletes a transcription and its associated file (if any).\n * This operation is idempotent - succeeds even if resources don't exist.\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * // Clean up both transcription and uploaded file\n * const transcription = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * });\n * // ... use transcription ...\n * await client.stt.destroy(transcription); // Deletes both\n *\n * // Or by ID\n * await client.stt.destroy('550e8400-e29b-41d4-a716-446655440000');\n * ```\n */\n async destroy(id: TranscriptionIdentifier): Promise<void> {\n // Get the full transcription to access file_id\n const transcription = await this.get(id);\n\n // If transcription doesn't exist, nothing to do\n if (!transcription) {\n return;\n }\n\n // Delete transcription first\n await this.delete(transcription);\n\n // Delete the associated file if present (ignore 404)\n if (transcription.file_id) {\n try {\n await this.filesApi.delete(transcription.file_id);\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n }\n }\n\n /**\n * Retrieves the full transcript text and tokens for a completed transcription.\n * Only available for successfully completed transcriptions.\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance\n * @returns The transcript with text and detailed tokens, or null if not found\n * @throws {@link SonioxHttpError} On API errors (except 404)\n *\n * @example\n * ```typescript\n * const transcript = await client.stt.getTranscript('550e8400-e29b-41d4-a716-446655440000');\n * if (transcript) {\n * console.log(transcript.text);\n * for (const token of transcript.tokens) {\n * console.log(token.text, token.start_ms, token.end_ms, token.confidence);\n * }\n * }\n * ```\n */\n async getTranscript(id: TranscriptionIdentifier, signal?: AbortSignal): Promise<SonioxTranscript | null> {\n const transcription_id = getTranscriptionId(id);\n try {\n const response = await this.http.request<TranscriptResponse>({\n method: 'GET',\n path: `/v1/transcriptions/${transcription_id}/transcript`,\n ...(signal && { signal }),\n });\n return new SonioxTranscript(response.data);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Waits for a transcription to complete\n *\n * @param id - The UUID of the transcription or a SonioxTranscription instance.\n * @param options - Wait options including polling interval, timeout, and callbacks.\n * @returns The completed or errored transcription.\n * @throws `Error` If the wait times out or is aborted.\n * @throws {@link SonioxHttpError} On API errors.\n *\n * @example\n * ```typescript\n * const completed = await client.stt.wait('550e8400-e29b-41d4-a716-446655440000');\n *\n * // With progress callback\n * const completed = await client.stt.wait('id', {\n * interval_ms: 2000,\n * on_status_change: (status) => console.log(`Status: ${status}`),\n * });\n * ```\n */\n async wait(id: TranscriptionIdentifier, options?: WaitOptions): Promise<SonioxTranscription> {\n const transcription = await this.get(id, options?.signal);\n if (!transcription) {\n throw new Error(`Transcription not found: ${getTranscriptionId(id)}`);\n }\n return transcription.wait(options);\n }\n\n /**\n * Wrapper to transcribe from a URL.\n *\n * @param audio_url - Publicly accessible audio URL\n * @param options - Transcription options (excluding audio_url)\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n */\n async transcribeFromUrl(audio_url: string, options: TranscribeFromUrlOptions): Promise<SonioxTranscription> {\n return this.transcribe({ ...options, audio_url });\n }\n\n /**\n * Wrapper to transcribe from an uploaded file ID.\n *\n * @param file_id - ID of a previously uploaded file\n * @param options - Transcription options (excluding file_id)\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n */\n async transcribeFromFileId(file_id: string, options: TranscribeFromFileIdOptions): Promise<SonioxTranscription> {\n return this.transcribe({ ...options, file_id });\n }\n\n /**\n * Wrapper to transcribe from raw file data.\n *\n * @param file - Buffer, Uint8Array, Blob, or ReadableStream\n * @param options - Transcription options (excluding file)\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n */\n async transcribeFromFile(file: UploadFileInput, options: TranscribeFromFileOptions): Promise<SonioxTranscription> {\n return this.transcribe({ ...options, file });\n }\n\n /**\n * Unified transcribe method - supports direct file upload\n *\n * When `file` is provided, uploads it first then creates a transcription\n * When `wait: true`, waits for completion before returning\n * When `cleanup` is specified (requires `wait: true`), cleans up resources after completion or on error/timeout\n *\n * @param options - Transcribe options including model, audio source, and wait settings.\n * @returns The transcription (completed if wait=true, otherwise in queued/processing state).\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` On validation errors or wait timeout.\n *\n * @example\n * ```typescript\n * // Transcribe from URL and wait for completion\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',\n * wait: true,\n * });\n *\n * // Upload file and transcribe in one call\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer, // or Blob, ReadableStream\n * filename: 'meeting.mp3',\n * enable_speaker_diarization: true,\n * wait: true,\n * });\n *\n * // With wait progress callback\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * wait_options: {\n * interval_ms: 2000,\n * on_status_change: (status) => console.log(`Status: ${status}`),\n * },\n * });\n *\n * // Auto-cleanup uploaded file after transcription\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * cleanup: ['file'], // Deletes uploaded file, keeps transcription record\n * });\n *\n * // Auto-cleanup everything after transcription\n * const result = await client.stt.transcribe({\n * model: 'stt-async-v4',\n * file: buffer,\n * wait: true,\n * cleanup: ['file', 'transcription'], // Deletes both file and transcription record\n * });\n * ```\n */\n async transcribe(options: TranscribeOptions): Promise<SonioxTranscription> {\n // Validate that exactly one audio source is provided\n const sourceCount = [\n options.file !== undefined,\n options.file_id !== undefined,\n options.audio_url !== undefined,\n ].filter(Boolean).length;\n\n if (sourceCount === 0) {\n throw new Error('One of file, file_id, or audio_url must be provided');\n }\n if (sourceCount > 1) {\n throw new Error('Only one of file, file_id, or audio_url can be provided');\n }\n\n // Validate audio_url format if provided\n if (options.audio_url !== undefined) {\n const urlPattern = /^https?:\\/\\/[^\\s]+$/;\n if (!urlPattern.test(options.audio_url)) {\n throw new Error('audio_url must be a valid HTTP or HTTPS URL');\n }\n }\n\n // Validate webhook auth header - both must be provided together or neither\n const hasHeaderName = options.webhook_auth_header_name !== undefined;\n const hasHeaderValue = options.webhook_auth_header_value !== undefined;\n if (hasHeaderName !== hasHeaderValue) {\n throw new Error('webhook_auth_header_name and webhook_auth_header_value must be provided together');\n }\n\n // Validate client_reference_id length\n if (options.client_reference_id !== undefined && options.client_reference_id.length > 256) {\n throw new Error(\n `client_reference_id exceeds maximum length of 256 characters (got ${options.client_reference_id.length})`\n );\n }\n\n // Validate cleanup - only allowed when wait=true\n if (options.cleanup?.length && !options.wait) {\n throw new Error('cleanup can only be used when wait=true');\n }\n\n // Create combined signal from timeout_ms and signal options\n const { signal: combinedSignal, cleanup: cleanupSignal } = createTimeoutSignal(options.timeout_ms, options.signal);\n\n // Track created resources for cleanup on error\n let fileIdToCleanup: string | undefined;\n let transcriptionId: string | undefined;\n\n // Helper to perform cleanup (ignores errors to avoid masking original error)\n const performCleanup = async (finalFileId?: string | null) => {\n if (!options.cleanup?.length) return;\n\n // Use the file_id from the completed transcription if available, otherwise use tracked id\n const fileId = finalFileId ?? fileIdToCleanup;\n\n // Delete file if requested and we have one\n if (options.cleanup.includes('file') && fileId) {\n try {\n await this.filesApi.delete(fileId);\n } catch {\n // Ignore cleanup errors\n }\n }\n\n // Delete transcription if requested and we have one\n if (options.cleanup.includes('transcription') && transcriptionId) {\n try {\n await this.delete(transcriptionId);\n } catch {\n // Ignore cleanup errors\n }\n }\n };\n\n try {\n let file_id = options.file_id;\n\n // If file data provided, upload first\n if (options.file) {\n const uploaded = await this.filesApi.upload(options.file, {\n filename: options.filename,\n client_reference_id: options.client_reference_id,\n signal: combinedSignal,\n });\n file_id = uploaded.id;\n fileIdToCleanup = uploaded.id;\n } else if (options.file_id) {\n // Track provided file_id for cleanup on error\n fileIdToCleanup = options.file_id;\n }\n\n // Process webhook_url with optional webhook_query\n let webhook_url = options.webhook_url;\n if (webhook_url && options.webhook_query) {\n const url = new URL(webhook_url);\n const params =\n options.webhook_query instanceof URLSearchParams\n ? options.webhook_query\n : typeof options.webhook_query === 'string'\n ? new URLSearchParams(options.webhook_query)\n : new URLSearchParams(options.webhook_query);\n\n params.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n webhook_url = url.toString();\n }\n\n // Build create options (exclude file-upload specific fields)\n const createOptions: CreateTranscriptionOptions = {\n model: options.model,\n audio_url: options.audio_url,\n file_id,\n language_hints: options.language_hints,\n language_hints_strict: options.language_hints_strict,\n enable_language_identification: options.enable_language_identification,\n enable_speaker_diarization: options.enable_speaker_diarization,\n context: options.context,\n translation: options.translation,\n webhook_url,\n webhook_auth_header_name: options.webhook_auth_header_name,\n webhook_auth_header_value: options.webhook_auth_header_value,\n client_reference_id: options.client_reference_id,\n };\n\n // Create transcription\n const transcription = await this.create(createOptions, combinedSignal);\n transcriptionId = transcription.id;\n\n // Track file_id from transcription response for cleanup on error\n // This handles cases where audio_url creates an associated file\n if (transcription.file_id) {\n fileIdToCleanup = transcription.file_id;\n }\n\n // Wait if requested (defaults to false)\n if (options.wait) {\n // Merge the combined signal with any existing wait_options.signal\n const waitOptions: WaitOptions = {\n ...options.wait_options,\n };\n if (combinedSignal && waitOptions.signal) {\n // Both signals exist — combine so either can cancel the wait\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n combinedSignal.addEventListener('abort', onAbort, { once: true });\n waitOptions.signal.addEventListener('abort', onAbort, { once: true });\n if (combinedSignal.aborted || waitOptions.signal.aborted) controller.abort();\n waitOptions.signal = controller.signal;\n } else if (combinedSignal) {\n waitOptions.signal = combinedSignal;\n }\n const completed = await transcription.wait(waitOptions);\n\n const shouldFetchTranscript = options.fetch_transcript !== false;\n\n // Fetch transcript if transcription completed successfully and opted in\n let transcript: SonioxTranscript | null | undefined;\n if (completed.status === 'completed') {\n if (shouldFetchTranscript) {\n transcript = await completed.getTranscript(combinedSignal ? { signal: combinedSignal } : undefined);\n }\n } else {\n transcript = null;\n }\n\n // Create a new transcription instance with the transcript attached\n const result = new SonioxTranscription(completed.toJSON(), this.http, transcript);\n\n // Perform cleanup after fetching transcript\n await performCleanup(completed.file_id);\n\n return result;\n }\n\n return transcription;\n } catch (error) {\n if (options.wait) {\n await performCleanup();\n }\n\n throw error;\n } finally {\n cleanupSignal();\n }\n }\n\n /**\n * Permanently deletes all transcriptions.\n * Iterates through all pages of transcriptions and deletes each one.\n *\n * @param options - Optional signal.\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` If the operation is aborted via signal.\n *\n * @example\n * ```typescript\n * // Delete all transcriptions\n * await client.stt.delete_all();\n * console.log(`Deleted all transcriptions.`);\n *\n * // With cancellation\n * const controller = new AbortController();\n * await client.stt.delete_all({ signal: controller.signal });\n * ```\n */\n async delete_all(options: DeleteAllTranscriptionsOptions = {}): Promise<void> {\n const { signal } = options;\n const result = await this.list({}, signal);\n\n for await (const transcription of result) {\n signal?.throwIfAborted();\n await this.delete(transcription, signal);\n }\n }\n\n /**\n * Permanently deletes all transcriptions and their associated files.\n * Iterates through all pages of transcriptions and calls {@link destroy}\n * on each one, removing both the transcription and its uploaded file.\n *\n * @param options - Optional signal and progress callback.\n * @returns The number of transcriptions destroyed.\n * @throws {@link SonioxHttpError} On API errors.\n * @throws `Error` If the operation is aborted via signal.\n *\n * @example\n * ```typescript\n * // Destroy all transcriptions and their files\n * await client.stt.destroy_all();\n * console.log(`Destroyed all transcriptions and their files.`);\n *\n * // With cancellation\n * const controller = new AbortController();\n * await client.stt.destroy_all({ signal: controller.signal });\n * ```\n */\n async destroy_all(options: DeleteAllTranscriptionsOptions = {}): Promise<void> {\n const { signal } = options;\n const result = await this.list({}, signal);\n\n for await (const transcription of result) {\n signal?.throwIfAborted();\n await this.destroy(transcription);\n }\n }\n}\n","import { writeFile } from 'node:fs/promises';\n\nimport { TtsRestClient } from '@soniox/core';\nimport type { GenerateSpeechOptions, TtsModel } from '@soniox/core';\n\nimport type { HttpClient } from '../http/client.js';\n\nexport type { GenerateSpeechOptions } from '@soniox/core';\n\n/**\n * REST API for Text-to-Speech generation and TTS model listing.\n *\n * Accessed via `client.tts` on {@link SonioxNodeClient}.\n *\n * Inherits browser-safe `generate()` and `generateStream()` from\n * `TtsRestClient` in `@soniox/core`, and adds Node-specific methods\n * `generateToFile()` and `listModels()`.\n */\nexport class SonioxTtsApi extends TtsRestClient {\n private readonly http: HttpClient;\n\n constructor(apiKey: string, ttsApiUrl: string, http: HttpClient) {\n super(apiKey, ttsApiUrl);\n this.http = http;\n }\n\n /**\n * Generate speech audio and write to a file or writable stream.\n *\n * @param output - File path (string) or a `WritableStream<Uint8Array>`\n * @param options - Generation options\n * @returns Number of bytes written\n *\n * @example Write to file\n * ```typescript\n * const bytes = await client.tts.generateToFile('output.wav', {\n * text: 'Hello world',\n * voice: 'Adrian',\n * language: 'en',\n * });\n * ```\n *\n * @example Write to a writable stream\n * ```typescript\n * const bytes = await client.tts.generateToFile(writableStream, {\n * text: 'Hello world',\n * voice: 'Adrian',\n * language: 'en',\n * });\n * ```\n */\n async generateToFile(output: string | WritableStream<Uint8Array>, options: GenerateSpeechOptions): Promise<number> {\n if (typeof output === 'string') {\n const audio = await this.generate(options);\n await writeFile(output, audio);\n return audio.byteLength;\n }\n\n let bytesWritten = 0;\n const writer = output.getWriter();\n try {\n for await (const chunk of this.generateStream(options)) {\n await writer.write(chunk);\n bytesWritten += chunk.byteLength;\n }\n } finally {\n writer.releaseLock();\n }\n return bytesWritten;\n }\n\n /**\n * List available TTS models and their voices.\n *\n * @example\n * ```typescript\n * const models = await client.tts.listModels();\n * for (const model of models) {\n * console.log(model.id, model.voices.map(v => v.id));\n * }\n * ```\n */\n async listModels(signal?: AbortSignal): Promise<TtsModel[]> {\n const response = await this.http.request<{ models: TtsModel[] }>({\n method: 'GET',\n path: '/v1/tts-models',\n ...(signal && { signal }),\n });\n return response.data.models;\n }\n}\n","import { SONIOX_API_WEBHOOK_HEADER_ENV, SONIOX_API_WEBHOOK_SECRET_ENV } from '../constants.js';\nimport type {\n ExpressLikeRequest,\n FastifyLikeRequest,\n HandleWebhookOptions,\n HonoLikeContext,\n NestJSLikeRequest,\n WebhookAuthConfig,\n WebhookEvent,\n WebhookEventStatus,\n WebhookHandlerResult,\n WebhookHandlerResultWithFetch,\n WebhookHeaders,\n} from '../types/public/webhooks.js';\n\nimport type { SonioxSttApi } from './stt.js';\n\nexport type {\n ExpressLikeRequest,\n FastifyLikeRequest,\n HandleWebhookOptions,\n HonoLikeContext,\n NestJSLikeRequest,\n WebhookAuthConfig,\n WebhookEvent,\n WebhookEventStatus,\n WebhookHandlerResult,\n WebhookHandlerResultWithFetch,\n WebhookHeaders,\n};\n\nconst VALID_STATUSES: WebhookEventStatus[] = ['completed', 'error'];\n\n/**\n * Get webhook authentication configuration from environment variables.\n *\n * Reads `SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET` environment variables.\n * Returns undefined if either variable is not set (both are required for authentication).\n *\n * @returns WebhookAuthConfig if both env vars are set, undefined otherwise\n *\n * @example\n * ```typescript\n * // Set environment variables:\n * // SONIOX_API_WEBHOOK_HEADER=X-Webhook-Secret\n * // SONIOX_API_WEBHOOK_SECRET=my-secret-token\n *\n * const auth = getWebhookAuthFromEnv();\n * // Returns: { name: 'X-Webhook-Secret', value: 'my-secret-token' }\n * ```\n */\nexport function getWebhookAuthFromEnv(): WebhookAuthConfig | undefined {\n const headerName = process.env[SONIOX_API_WEBHOOK_HEADER_ENV];\n const headerValue = process.env[SONIOX_API_WEBHOOK_SECRET_ENV];\n\n // Both header name and secret value must be set for auth to work\n if (headerName && headerValue) {\n return {\n name: headerName,\n value: headerValue,\n };\n }\n\n return undefined;\n}\n\n/**\n * Resolve webhook authentication configuration.\n *\n * If explicit auth is provided, it is used. Otherwise, attempts to read from\n * environment variables (`SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET`).\n *\n * @param auth - Explicit authentication configuration (takes precedence)\n * @returns Resolved WebhookAuthConfig or undefined if not configured\n */\nfunction resolveWebhookAuth(auth?: WebhookAuthConfig): WebhookAuthConfig | undefined {\n return auth ?? getWebhookAuthFromEnv();\n}\n\n/**\n * Type guard to check if a value is a valid WebhookEvent\n *\n * @param payload - Value to check\n * @returns True if payload is a valid WebhookEvent\n *\n * @example\n * ```typescript\n * if (isWebhookEvent(body)) {\n * console.log(body.id, body.status);\n * }\n * ```\n */\nexport function isWebhookEvent(payload: unknown): payload is WebhookEvent {\n if (typeof payload !== 'object' || payload === null) {\n return false;\n }\n\n const obj = payload as Record<string, unknown>;\n\n if (typeof obj.id !== 'string' || obj.id.length === 0) {\n return false;\n }\n\n if (!VALID_STATUSES.includes(obj.status as WebhookEventStatus)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Parse and validate a webhook event payload\n *\n * @param payload - Raw payload to parse (object or JSON string)\n * @returns Validated WebhookEvent\n * @throws `Error` if payload is invalid\n *\n * @example\n * ```typescript\n * try {\n * const event = parseWebhookEvent(req.body);\n * console.log(event.id, event.status);\n * } catch (error) {\n * console.error('Invalid webhook payload:', error.message);\n * }\n * ```\n */\nexport function parseWebhookEvent(payload: unknown): WebhookEvent {\n // Handle string input (parse as JSON)\n if (typeof payload === 'string') {\n try {\n payload = JSON.parse(payload);\n } catch {\n throw new Error('Invalid webhook payload: not valid JSON');\n }\n }\n\n if (typeof payload !== 'object' || payload === null) {\n throw new Error('Invalid webhook payload: expected an object');\n }\n\n const obj = payload as Record<string, unknown>;\n\n if (typeof obj.id !== 'string') {\n throw new Error('Invalid webhook payload: missing or invalid \"id\" field');\n }\n\n if (obj.id.length === 0) {\n throw new Error('Invalid webhook payload: \"id\" field cannot be empty');\n }\n\n if (!VALID_STATUSES.includes(obj.status as WebhookEventStatus)) {\n throw new Error(`Invalid webhook payload: \"status\" must be \"completed\" or \"error\", got \"${String(obj.status)}\"`);\n }\n\n return {\n id: obj.id,\n status: obj.status as WebhookEventStatus,\n };\n}\n\n/**\n * Get a header value from various header formats (case-insensitive)\n */\nfunction getHeaderValue(headers: WebhookHeaders, name: string): string | null {\n const lowerName = name.toLowerCase();\n\n // Fetch API Headers\n if (headers instanceof Headers) {\n return headers.get(lowerName);\n }\n\n // Object with get method (like Express headers or custom)\n if (typeof (headers as { get?(name: string): string | null }).get === 'function') {\n return (headers as { get(name: string): string | null }).get(lowerName);\n }\n\n // Plain object\n const record = headers as Record<string, string | string[] | undefined>;\n\n // Try exact match first\n if (lowerName in record) {\n const value = record[lowerName];\n return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);\n }\n\n // Case-insensitive search\n for (const key of Object.keys(record)) {\n if (key.toLowerCase() === lowerName) {\n const value = record[key];\n return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);\n }\n }\n\n return null;\n}\n\n/**\n * Verify webhook authentication header\n *\n * @param headers - Request headers\n * @param auth - Authentication configuration with expected header name and value\n * @returns True if authentication passes, false otherwise\n *\n * @example\n * ```typescript\n * const isValid = verifyWebhookAuth(req.headers, {\n * name: 'X-Webhook-Secret',\n * value: process.env.WEBHOOK_SECRET,\n * });\n *\n * if (!isValid) {\n * return res.status(401).send('Unauthorized');\n * }\n * ```\n */\nexport function verifyWebhookAuth(headers: WebhookHeaders, auth: WebhookAuthConfig): boolean {\n const headerValue = getHeaderValue(headers, auth.name);\n return headerValue === auth.value;\n}\n\n/**\n * Framework-agnostic webhook handler\n *\n * Validates the HTTP method, authentication (if configured), and parses the webhook payload.\n * Returns a result object that can be used to construct an HTTP response.\n *\n * Authentication is resolved in this order:\n * 1. Explicit `auth` option if provided\n * 2. Environment variables `SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET`\n * 3. No authentication if neither is configured\n *\n * @param options - Handler options including method, headers, body, and optional auth\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * // Option 1: Set environment variables (recommended)\n * // SONIOX_API_WEBHOOK_HEADER=X-Webhook-Secret\n * // SONIOX_API_WEBHOOK_SECRET=my-secret\n * const result = handleWebhook({\n * method: req.method,\n * headers: req.headers,\n * body: req.body,\n * });\n *\n * // Option 2: Explicit auth config (overrides env vars)\n * const result = handleWebhook({\n * method: req.method,\n * headers: req.headers,\n * body: req.body,\n * auth: {\n * name: 'X-Webhook-Secret',\n * value: process.env.WEBHOOK_SECRET,\n * },\n * });\n *\n * if (result.ok) {\n * console.log('Transcription completed:', result.event.id);\n * }\n *\n * res.status(result.status).json(result.ok ? { received: true } : { error: result.error });\n * ```\n */\nexport function handleWebhook(options: HandleWebhookOptions): WebhookHandlerResult {\n const { method, headers, body, auth } = options;\n\n // Validate HTTP method\n if (method.toUpperCase() !== 'POST') {\n return {\n ok: false,\n status: 405,\n error: 'Method not allowed',\n };\n }\n\n // Resolve authentication from explicit config or environment variables\n const resolvedAuth = resolveWebhookAuth(auth);\n\n // Verify authentication if configured\n if (resolvedAuth) {\n if (!verifyWebhookAuth(headers, resolvedAuth)) {\n return {\n ok: false,\n status: 401,\n error: 'Unauthorized',\n };\n }\n }\n\n // Parse and validate payload\n try {\n const event = parseWebhookEvent(body);\n return {\n ok: true,\n status: 200,\n event,\n };\n } catch (error) {\n return {\n ok: false,\n status: 400,\n error: error instanceof Error ? error.message : 'Invalid webhook payload',\n };\n }\n}\n\n/**\n * Handle a webhook from a Fetch API Request (Bun/Deno/Node 18+)\n *\n * @param request - Fetch API Request object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * // Bun.serve handler\n * Bun.serve({\n * async fetch(req) {\n * if (new URL(req.url).pathname === '/webhook') {\n * const result = await handleWebhookRequest(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return new Response(\n * JSON.stringify(result.ok ? { received: true } : { error: result.error }),\n * { status: result.status, headers: { 'Content-Type': 'application/json' } }\n * );\n * }\n * },\n * });\n * ```\n */\nexport async function handleWebhookRequest(request: Request, auth?: WebhookAuthConfig): Promise<WebhookHandlerResult> {\n if (request.method.toUpperCase() !== 'POST') {\n return {\n ok: false,\n status: 405,\n error: 'Method not allowed',\n };\n }\n\n let body: unknown;\n\n try {\n body = await request.json();\n } catch {\n return {\n ok: false,\n status: 400,\n error: 'Invalid webhook payload: not valid JSON',\n };\n }\n\n const options: HandleWebhookOptions = {\n method: request.method,\n headers: request.headers,\n body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from an Express-like request\n *\n * @param req - Express request object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { handleWebhookExpress } from '@soniox/node';\n *\n * const app = express();\n * app.use(express.json());\n *\n * app.post('/webhook', (req, res) => {\n * const result = handleWebhookExpress(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * res.status(result.status).json(\n * result.ok ? { received: true } : { error: result.error }\n * );\n * });\n * ```\n */\nexport function handleWebhookExpress(req: ExpressLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResult {\n const options: HandleWebhookOptions = {\n method: req.method,\n headers: req.headers,\n body: req.body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from a Fastify request\n *\n * @param req - Fastify request object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import Fastify from 'fastify';\n * import { handleWebhookFastify } from '@soniox/node';\n *\n * const fastify = Fastify();\n *\n * fastify.post('/webhook', async (req, reply) => {\n * const result = handleWebhookFastify(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return reply.status(result.status).send(\n * result.ok ? { received: true } : { error: result.error }\n * );\n * });\n * ```\n */\nexport function handleWebhookFastify(req: FastifyLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResult {\n const options: HandleWebhookOptions = {\n method: req.method,\n headers: req.headers,\n body: req.body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from a NestJS request\n *\n * Works with NestJS using either Express or Fastify adapter.\n *\n * @param req - NestJS request object (injected via @Req() decorator)\n * @param auth - Optional authentication configuration (overrides env vars)\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import { Controller, Post, Req, Res, HttpStatus } from '@nestjs/common';\n * import { Request, Response } from 'express';\n * import { handleWebhookNestJS } from '@soniox/node';\n *\n * @Controller('webhook')\n * export class WebhookController {\n * @Post()\n * handleWebhook(@Req() req: Request, @Res() res: Response) {\n * const result = handleWebhookNestJS(req);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return res.status(result.status).json(\n * result.ok ? { received: true } : { error: result.error }\n * );\n * }\n * }\n * ```\n */\nexport function handleWebhookNestJS(req: NestJSLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResult {\n const options: HandleWebhookOptions = {\n method: req.method,\n headers: req.headers,\n body: req.body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Handle a webhook from a Hono context\n *\n * @param c - Hono context object\n * @param auth - Optional authentication configuration\n * @returns Result with ok status, HTTP status code, and either event or error\n *\n * @example\n * ```typescript\n * import { Hono } from 'hono';\n * import { handleWebhookHono } from '@soniox/node';\n *\n * const app = new Hono();\n *\n * app.post('/webhook', async (c) => {\n * const result = await handleWebhookHono(c);\n *\n * if (result.ok) {\n * console.log('Received webhook:', result.event.id);\n * }\n *\n * return c.json(\n * result.ok ? { received: true } : { error: result.error },\n * result.status\n * );\n * });\n * ```\n */\nexport async function handleWebhookHono(c: HonoLikeContext, auth?: WebhookAuthConfig): Promise<WebhookHandlerResult> {\n let body: unknown;\n\n try {\n body = await c.req.json();\n } catch {\n return {\n ok: false,\n status: 400,\n error: 'Invalid webhook payload: not valid JSON',\n };\n }\n\n // Build headers object from Hono's header getter\n const headers: WebhookHeaders = {\n get(name: string): string | null {\n return c.req.header(name) ?? null;\n },\n };\n\n const options: HandleWebhookOptions = {\n method: c.req.method,\n headers,\n body,\n };\n if (auth) {\n options.auth = auth;\n }\n return handleWebhook(options);\n}\n\n/**\n * Webhook utilities API accessible via client.webhooks\n *\n * Provides methods for handling incoming Soniox webhook requests.\n * When used via the client, results include lazy fetch helpers for transcripts.\n */\nexport class SonioxWebhooksAPI {\n private stt: SonioxSttApi | undefined;\n\n /**\n * @internal\n */\n constructor(stt?: SonioxSttApi) {\n this.stt = stt;\n }\n\n /**\n * Enhance a webhook result with fetch helpers\n */\n private withFetchHelpers(result: WebhookHandlerResult): WebhookHandlerResultWithFetch {\n const stt = this.stt;\n const event = result.event;\n\n // If no stt API or no event, return result without fetch helpers\n if (!stt || !event) {\n return {\n ...result,\n fetchTranscript: undefined,\n fetchTranscription: undefined,\n };\n }\n\n const transcriptionId = event.id;\n\n return {\n ...result,\n fetchTranscript: event.status === 'completed' ? () => stt.getTranscript(transcriptionId) : undefined,\n fetchTranscription: () => stt.get(transcriptionId),\n };\n }\n\n /**\n * Get webhook authentication configuration from environment variables.\n *\n * Reads `SONIOX_API_WEBHOOK_HEADER` and `SONIOX_API_WEBHOOK_SECRET` environment variables.\n * Returns undefined if either variable is not set (both are required for authentication).\n */\n getAuthFromEnv(): WebhookAuthConfig | undefined {\n return getWebhookAuthFromEnv();\n }\n\n /**\n * Type guard to check if a value is a valid WebhookEvent\n */\n isEvent(payload: unknown): payload is WebhookEvent {\n return isWebhookEvent(payload);\n }\n\n /**\n * Parse and validate a webhook event payload\n */\n parseEvent(payload: unknown): WebhookEvent {\n return parseWebhookEvent(payload);\n }\n\n /**\n * Verify webhook authentication header\n */\n verifyAuth(headers: WebhookHeaders, auth: WebhookAuthConfig): boolean {\n return verifyWebhookAuth(headers, auth);\n }\n\n /**\n * Framework-agnostic webhook handler\n */\n handle(options: HandleWebhookOptions): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhook(options));\n }\n\n /**\n * Handle a webhook from a Fetch API Request\n */\n async handleRequest(request: Request, auth?: WebhookAuthConfig): Promise<WebhookHandlerResultWithFetch> {\n const result = await handleWebhookRequest(request, auth);\n return this.withFetchHelpers(result);\n }\n\n /**\n * Handle a webhook from an Express-like request\n *\n * @example\n * ```typescript\n * app.post('/webhook', async (req, res) => {\n * const result = soniox.webhooks.handleExpress(req);\n *\n * if (result.ok && result.event.status === 'completed') {\n * const transcript = await result.fetchTranscript();\n * console.log(transcript?.text);\n * }\n *\n * res.status(result.status).json({ received: true });\n * });\n * ```\n */\n handleExpress(req: ExpressLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhookExpress(req, auth));\n }\n\n /**\n * Handle a webhook from a Fastify request\n */\n handleFastify(req: FastifyLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhookFastify(req, auth));\n }\n\n /**\n * Handle a webhook from a NestJS request\n */\n handleNestJS(req: NestJSLikeRequest, auth?: WebhookAuthConfig): WebhookHandlerResultWithFetch {\n return this.withFetchHelpers(handleWebhookNestJS(req, auth));\n }\n\n /**\n * Handle a webhook from a Hono context\n */\n async handleHono(c: HonoLikeContext, auth?: WebhookAuthConfig): Promise<WebhookHandlerResultWithFetch> {\n const result = await handleWebhookHono(c, auth);\n return this.withFetchHelpers(result);\n }\n}\n","/**\n * URL utilities for the HTTP client.\n */\n\nimport type { QueryParams } from '../types/public/http.js';\n\n/**\n * Builds a complete URL from base URL, path, and query parameters\n */\nexport function buildUrl(baseUrl: string | undefined, path: string, query?: QueryParams): string {\n // Join base URL and path\n let url = joinUrl(baseUrl ?? '', path);\n\n // Append query string if provided\n if (query) {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n params.append(key, String(value));\n }\n }\n const queryString = params.toString();\n if (queryString) {\n url += (url.includes('?') ? '&' : '?') + queryString;\n }\n }\n\n return url;\n}\n\n/**\n * Joins a base URL with a path\n */\nfunction joinUrl(baseUrl: string, path: string): string {\n if (!baseUrl) return path;\n if (!path) return baseUrl;\n if (/^https?:\\/\\//i.test(path)) return path;\n\n const base = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n return base + suffix;\n}\n\n/**\n * Normalizes fetch Headers to a plain object with lowercase keys\n */\nexport function normalizeHeaders(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\n/**\n * Merges header objects\n */\nexport function mergeHeaders(...headerObjects: (Record<string, string> | undefined)[]): Record<string, string> {\n const result: Record<string, string> = {};\n for (const headers of headerObjects) {\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n result[key.toLowerCase()] = value;\n }\n }\n }\n return result;\n}\n","/**\n * Default fetch-based HTTP client implementation.\n *\n * @module\n */\n\nimport type {\n HttpClient,\n HttpClientOptions,\n HttpObservabilityHooks,\n HttpRequest,\n HttpRequestBody,\n HttpRequestMeta,\n HttpResponse,\n HttpResponseMeta,\n HttpResponseType,\n} from './client.js';\nimport {\n createAbortError,\n createHttpError,\n createNetworkError,\n createParseError,\n createTimeoutError,\n isAbortError,\n SonioxHttpError,\n} from './errors.js';\nimport { buildUrl, mergeHeaders, normalizeHeaders } from './url.js';\n\n/** Default timeout in milliseconds (30 seconds) TODO: Move to constants? */\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Determines the Content-Type header based on the body type.\n */\nfunction getContentTypeForBody(body: HttpRequestBody | undefined): string | undefined {\n if (body === null || body === undefined) {\n return undefined;\n }\n if (typeof body === 'string') {\n return 'text/plain; charset=utf-8';\n }\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n // Let the browser/runtime set the boundary automatically\n return undefined;\n }\n if (body instanceof ArrayBuffer || body instanceof Uint8Array) {\n return 'application/octet-stream';\n }\n // Object/Record - JSON\n return 'application/json';\n}\n\n/**\n * Prepares the request body for fetch.\n */\nfunction prepareBody(body: HttpRequestBody | undefined): string | ArrayBuffer | Uint8Array | FormData | undefined {\n if (body === null || body === undefined) {\n return undefined;\n }\n if (typeof body === 'string') {\n return body;\n }\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n return body;\n }\n if (body instanceof ArrayBuffer) {\n return body;\n }\n if (body instanceof Uint8Array) {\n return body;\n }\n // Object - serialize to JSON\n return JSON.stringify(body);\n}\n\n/**\n * Parses the response based on the expected type.\n */\nasync function parseResponse<T>(\n response: Response,\n responseType: HttpResponseType,\n url: string,\n method: HttpRequest['method']\n): Promise<T> {\n // Handle 204 No Content and empty responses\n const contentLength = response.headers.get('content-length');\n if (response.status === 204 || contentLength === '0') {\n switch (responseType) {\n case 'arrayBuffer':\n return new ArrayBuffer(0) as T;\n case 'text':\n return '' as T;\n case 'json':\n default:\n return null as T;\n }\n }\n\n switch (responseType) {\n case 'text':\n return (await response.text()) as T;\n\n case 'arrayBuffer':\n return (await response.arrayBuffer()) as T;\n\n case 'json':\n default: {\n // Always read as text first so we have the body available for error reporting\n const text = await response.text();\n if (!text) {\n return null as T;\n }\n try {\n return JSON.parse(text) as T;\n } catch (error) {\n throw createParseError(url, method, text, error);\n }\n }\n }\n}\n\n/**\n * Default fetch-based HTTP client.\n *\n * @example Basic usage\n * ```typescript\n * const client = new FetchHttpClient({\n * base_url: 'https://api.example.com/v1',\n * default_headers: {\n * 'Authorization': 'Bearer token',\n * },\n * });\n *\n * const response = await client.request<{ users: User[] }>({\n * method: 'GET',\n * path: '/users',\n * query: { active: true },\n * });\n * ```\n *\n * @example With custom fetch (e.g., for testing)\n * ```typescript\n * const mockFetch = vi.fn().mockResolvedValue(new Response('{}'));\n * const client = new FetchHttpClient({\n * base_url: 'https://api.example.com',\n * fetch: mockFetch,\n * });\n * ```\n *\n * @example Sending different body types\n * ```typescript\n * // JSON body (default for objects)\n * await client.request({\n * method: 'POST',\n * path: '/data',\n * body: { name: 'test', value: 123 },\n * });\n *\n * // Text body\n * await client.request({\n * method: 'POST',\n * path: '/text',\n * body: 'plain text content',\n * headers: { 'Content-Type': 'text/plain' },\n * });\n *\n * // Binary body\n * await client.request({\n * method: 'POST',\n * path: '/upload',\n * body: new Uint8Array([1, 2, 3]),\n * });\n *\n * // FormData (for file uploads)\n * const formData = new FormData();\n * formData.append('file', fileBlob, 'document.pdf');\n * await client.request({\n * method: 'POST',\n * path: '/files',\n * body: formData,\n * });\n * ```\n */\nexport class FetchHttpClient implements HttpClient {\n private readonly baseUrl: string | undefined;\n private readonly defaultHeaders: Record<string, string>;\n private readonly defaultTimeoutMs: number;\n private readonly hooks: HttpObservabilityHooks;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: HttpClientOptions = {}) {\n this.baseUrl = options.base_url;\n this.defaultHeaders = options.default_headers ?? {};\n this.defaultTimeoutMs = options.default_timeout_ms ?? DEFAULT_TIMEOUT_MS;\n this.hooks = options.hooks ?? {};\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n\n if (!this.fetchImpl) {\n throw new Error('fetch is not available. Please provide a fetch implementation via options.fetch');\n }\n }\n\n /**\n * Performs an HTTP request.\n *\n * @param request - Request configuration\n * @returns Promise resolving to the response\n * @throws {@link SonioxHttpError}\n */\n async request<T>(request: HttpRequest): Promise<HttpResponse<T>> {\n const startTime = Date.now();\n const url = buildUrl(this.baseUrl, request.path, request.query);\n const method = request.method;\n const responseType: HttpResponseType = request.responseType ?? 'json';\n\n // Prepare headers\n const contentTypeHeader = getContentTypeForBody(request.body);\n const isFormData = typeof FormData !== 'undefined' && request.body instanceof FormData;\n\n const defaultHeadersWithoutContentType = isFormData\n ? Object.fromEntries(Object.entries(this.defaultHeaders).filter(([key]) => key.toLowerCase() !== 'content-type'))\n : this.defaultHeaders;\n\n const headers = mergeHeaders(\n defaultHeadersWithoutContentType,\n contentTypeHeader ? { 'Content-Type': contentTypeHeader } : undefined,\n request.headers\n );\n\n // Create metadata for hooks\n const requestMeta: HttpRequestMeta = {\n startTime,\n url,\n method,\n headers,\n };\n\n // Call onRequest hook\n this.hooks.onRequest?.(request, requestMeta);\n\n // Setup timeout\n const timeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;\n const timeoutController = new AbortController();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs > 0) {\n timeoutId = setTimeout(() => {\n timeoutController.abort(new Error('Request timeout'));\n }, timeoutMs);\n }\n\n // Combine timeout signal with user-provided signal\n const combined = request.signal ? combineAbortSignals(timeoutController.signal, request.signal) : null;\n const signal = combined ? combined.signal : timeoutController.signal;\n\n try {\n // Perform the fetch\n const preparedBody = prepareBody(request.body);\n const response = await this.fetchImpl(url, {\n method,\n headers,\n signal,\n ...(preparedBody !== undefined && { body: preparedBody }),\n } as RequestInit);\n\n // Clear timeout\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Normalize response headers\n const responseHeaders = normalizeHeaders(response.headers);\n\n // Check for HTTP errors (non-2xx status)\n if (!response.ok) {\n const bodyText = await response.text().catch(() => '');\n throw createHttpError(url, method, response.status, responseHeaders, bodyText);\n }\n\n // Parse response\n const data = await parseResponse<T>(response, responseType, url, method);\n\n const result: HttpResponse<T> = {\n status: response.status,\n headers: responseHeaders,\n data,\n };\n\n // Call onResponse hook\n const responseMeta: HttpResponseMeta = {\n ...requestMeta,\n durationMs: Date.now() - startTime,\n status: response.status,\n };\n this.hooks.onResponse?.(result, responseMeta);\n\n return result;\n } catch (error) {\n // Clear timeout on error\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Calculate duration for error hook\n const errorMeta: HttpResponseMeta = {\n ...requestMeta,\n durationMs: Date.now() - startTime,\n };\n\n // Normalize error\n const normalizedError = this.normalizeError(error, url, method, timeoutMs, timeoutController);\n\n // Call onError hook\n this.hooks.onError?.(normalizedError, errorMeta);\n\n throw normalizedError;\n } finally {\n combined?.cleanup();\n }\n }\n\n /**\n * Normalizes various error types into SonioxHttpError.\n */\n private normalizeError(\n error: unknown,\n url: string,\n method: HttpRequest['method'],\n timeoutMs: number,\n timeoutController: AbortController\n ): SonioxHttpError {\n // Already a SonioxHttpError\n if (error instanceof SonioxHttpError) {\n return error;\n }\n\n // Check if this was our timeout\n if (timeoutController.signal.aborted && isAbortError(error)) {\n return createTimeoutError(url, method, timeoutMs);\n }\n\n // User-initiated abort\n if (isAbortError(error)) {\n return createAbortError(url, method, error);\n }\n\n // Network error (TypeError from fetch usually indicates network issues)\n if (error instanceof TypeError) {\n return createNetworkError(url, method, error);\n }\n\n // Generic error - treat as network error\n return createNetworkError(url, method, error);\n }\n}\n\n/**\n * Combines multiple AbortSignals into one.\n * The resulting signal will abort if any of the input signals abort.\n * Returns the combined signal and a cleanup function to remove listeners.\n */\nfunction combineAbortSignals(...signals: AbortSignal[]): { signal: AbortSignal; cleanup: () => void } {\n const controller = new AbortController();\n const handlers: Array<{ signal: AbortSignal; handler: () => void }> = [];\n\n const cleanup = () => {\n for (const { signal, handler } of handlers) {\n signal.removeEventListener('abort', handler);\n }\n handlers.length = 0;\n };\n\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return { signal: controller.signal, cleanup };\n }\n\n const handler = () => {\n controller.abort(signal.reason);\n };\n handlers.push({ signal, handler });\n signal.addEventListener('abort', handler, { once: true });\n }\n\n return { signal: controller.signal, cleanup };\n}\n","import { RealtimeSttSession, RealtimeTtsConnection } from '@soniox/core';\nimport type { RealtimeTtsStream, TtsStreamInput } from '@soniox/core';\n\nimport type { RealtimeClientOptions, SttSessionConfig, SttSessionOptions } from '../types/public/realtime.js';\n\n/**\n * Callable TTS factory with `.multiStream()` for multi-stream connections.\n */\nexport interface TtsFactory {\n /**\n * Create a single-stream TTS connection.\n * Opens a WebSocket, starts one stream, and returns a ready-to-use stream\n * that owns its connection (closing the stream closes the connection).\n *\n * @example\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 world\");\n * stream.finish();\n * for await (const chunk of stream) { process(chunk); }\n * ```\n */\n (input?: TtsStreamInput): Promise<RealtimeTtsStream>;\n\n /**\n * Create a multi-stream TTS connection.\n * Opens a single WebSocket that can host up to 5 concurrent streams.\n *\n * @example\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 * // Use any voice returned by client.tts.listModels()\n * const s2 = await conn.stream({\n * model: 'tts-rt-v1',\n * voice: someOtherVoice,\n * language: 'en',\n * audio_format: 'wav',\n * });\n * ```\n */\n multiStream(): Promise<RealtimeTtsConnection>;\n}\n\n/**\n * Real-time API factory for creating STT sessions and TTS connections.\n *\n * @example STT\n * ```typescript\n * const session = client.realtime.stt({ model: 'stt-rt-v4' });\n * await session.connect();\n * ```\n *\n * @example TTS (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) { ... }\n * ```\n *\n * @example TTS (multi-stream)\n * ```typescript\n * const conn = await client.realtime.tts.multiStream();\n * const stream = await conn.stream({\n * model: 'tts-rt-v1',\n * voice: 'Adrian',\n * language: 'en',\n * audio_format: 'wav',\n * });\n * ```\n */\nexport class SonioxRealtimeApi {\n private readonly options: RealtimeClientOptions;\n\n readonly tts: TtsFactory;\n\n constructor(options: RealtimeClientOptions) {\n this.options = options;\n\n const ttsCall = (input?: TtsStreamInput): Promise<RealtimeTtsStream> => {\n return this.createSingleTtsStream(input ?? {});\n };\n ttsCall.multiStream = (): Promise<RealtimeTtsConnection> => {\n return this.createTtsConnection();\n };\n this.tts = ttsCall;\n }\n\n /**\n * Create a new Speech-to-Text session.\n *\n * `config` is shallow-merged on top of `stt_defaults` from the client\n * options; caller-provided fields override the defaults.\n */\n stt(config: SttSessionConfig, options?: SttSessionOptions): RealtimeSttSession {\n const mergedOptions: SttSessionOptions = {\n ...this.options.default_session_options,\n ...options,\n };\n const mergedConfig: SttSessionConfig = {\n ...this.options.stt_defaults,\n ...config,\n };\n return new RealtimeSttSession(this.options.api_key, this.options.ws_base_url, mergedConfig, mergedOptions);\n }\n\n private async createSingleTtsStream(input: TtsStreamInput): Promise<RealtimeTtsStream> {\n const connection = new RealtimeTtsConnection(\n this.options.api_key,\n this.options.tts_ws_url,\n this.options.tts_defaults ?? {},\n this.options.tts_connection_options\n );\n return connection._openStream(input, true);\n }\n\n private async createTtsConnection(): Promise<RealtimeTtsConnection> {\n const connection = new RealtimeTtsConnection(\n this.options.api_key,\n this.options.tts_ws_url,\n this.options.tts_defaults ?? {},\n this.options.tts_connection_options\n );\n await connection.connect();\n return connection;\n }\n}\n\n// Re-export STT session class\nexport { RealtimeSttSession } from '@soniox/core';\n\n// Re-export TTS classes\nexport { RealtimeTtsConnection, RealtimeTtsStream } from '@soniox/core';\n\n// Re-export helpers\nexport { segmentRealtimeTokens } from '@soniox/core';\nexport { RealtimeSegmentBuffer } from '@soniox/core';\nexport { RealtimeUtteranceBuffer } from '@soniox/core';\n\n// Re-export errors\nexport {\n RealtimeError,\n AuthError,\n BadRequestError,\n QuotaError,\n ConnectionError,\n NetworkError,\n AbortError,\n StateError,\n} from '@soniox/core';\n","import { resolveConnectionConfig } from '@soniox/core';\n\nimport { SonioxAuthAPI } from './async/auth.js';\nimport { SonioxFilesAPI } from './async/files.js';\nimport { SonioxModelsAPI } from './async/models.js';\nimport { SonioxSttApi } from './async/stt.js';\nimport { SonioxTtsApi } from './async/tts.js';\nimport { SonioxWebhooksAPI } from './async/webhooks.js';\nimport { FetchHttpClient } from './http/fetch-adapter.js';\nimport { SonioxRealtimeApi } from './realtime/index.js';\nimport type { SonioxNodeClientOptions } from './types/public/index.js';\n\n/**\n * Soniox Node Client\n *\n * @example\n * ```typescript\n * import { SonioxNodeClient } from '@soniox/node';\n *\n * // Default (US) region\n * const client = new SonioxNodeClient({ api_key: 'your-api-key' });\n *\n * // EU region\n * const client = new SonioxNodeClient({ api_key: 'your-api-key', region: 'eu' });\n *\n * // REST TTS\n * const audio = await client.tts.generate({\n * text: 'Hello',\n * voice: 'Adrian',\n * language: 'en',\n * });\n *\n * // WebSocket TTS\n * const stream = await client.realtime.tts({\n * model: 'tts-rt-v1',\n * voice: 'Adrian',\n * language: 'en',\n * audio_format: 'wav',\n * });\n * ```\n */\nexport class SonioxNodeClient {\n readonly files: SonioxFilesAPI;\n readonly stt: SonioxSttApi;\n readonly tts: SonioxTtsApi;\n readonly models: SonioxModelsAPI;\n readonly webhooks: SonioxWebhooksAPI;\n readonly auth: SonioxAuthAPI;\n readonly realtime: SonioxRealtimeApi;\n\n constructor(options: SonioxNodeClientOptions = {}) {\n const apiKey = options.api_key ?? process.env['SONIOX_API_KEY'];\n if (!apiKey) {\n throw new Error(\n 'Missing API key. Provide it via options.api_key or set the SONIOX_API_KEY environment variable.'\n );\n }\n\n const regionDefaults = resolveConnectionConfig({\n api_key: apiKey,\n region: options.region ?? process.env['SONIOX_REGION'],\n base_domain: options.base_domain ?? process.env['SONIOX_BASE_DOMAIN'],\n stt_defaults: options.stt_defaults,\n tts_defaults: options.tts_defaults,\n });\n\n const baseURL = options.base_url ?? process.env['SONIOX_API_BASE_URL'] ?? regionDefaults.api_domain;\n const http =\n options.http_client ??\n new FetchHttpClient({\n base_url: baseURL,\n default_headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n this.files = new SonioxFilesAPI(http);\n this.stt = new SonioxSttApi(http, this.files);\n this.models = new SonioxModelsAPI(http);\n this.webhooks = new SonioxWebhooksAPI(this.stt);\n this.auth = new SonioxAuthAPI(http);\n\n const ttsApiUrl = options.tts_api_url ?? process.env['SONIOX_TTS_API_URL'] ?? regionDefaults.tts_api_url;\n this.tts = new SonioxTtsApi(apiKey, ttsApiUrl, http);\n\n const wsBaseUrl = options.realtime?.ws_base_url ?? process.env['SONIOX_WS_URL'] ?? regionDefaults.stt_ws_url;\n const ttsWsUrl = options.realtime?.tts_ws_url ?? process.env['SONIOX_TTS_WS_URL'] ?? regionDefaults.tts_ws_url;\n\n this.realtime = new SonioxRealtimeApi({\n api_key: apiKey,\n ws_base_url: wsBaseUrl,\n tts_ws_url: ttsWsUrl,\n stt_defaults: regionDefaults.stt_defaults,\n tts_defaults: regionDefaults.tts_defaults,\n tts_connection_options: options.realtime?.tts_connection_options,\n default_session_options: options.realtime?.default_session_options,\n });\n }\n}\n"],"mappings":"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"}