import { deepMergeObjects, getEnv } from './helpers.js' import type { InternalGladiaClientOptions } from './internal_types.js' import type { Headers } from './network/types.js' import type { GladiaClientOptions } from './types.js' import { LiveV2Client } from './v2/live/index.js' import { PreRecordedV2Client } from './v2/prerecorded/index.js' import { SDK_VERSION } from './version.js' function normalizeGladiaHeaders(headers: Headers | [string, string][]): Headers { const entries = Array.isArray(headers) ? headers : Object.entries(headers) return Object.fromEntries( entries.map(([key, value]) => { const lcKey = key.toLowerCase() return [lcKey.startsWith('x-gladia-') ? lcKey : key, value] }) ) } function assertValidOptions(options: InternalGladiaClientOptions) { let url: URL try { url = new URL(options.apiUrl) } catch { throw new Error(`Invalid url: "${options.apiUrl}".`) } if (!options?.apiKey && url.hostname.endsWith('.gladia.io')) { throw new Error(`You have to set your "apiKey" or define a proxy "apiUrl".`) } if (!['https:', 'http:', 'wss:', 'ws:'].includes(url.protocol)) { throw new Error( `Only HTTP and WebSocket protocols are supported for apiUrl (received: ${url.protocol}).` ) } } const defaultHttpDelay = (attemptCount: number) => Math.min(0.3 * 2 ** (attemptCount - 1) * 1_000, 10_000) const defaultWsDelay = (attemptCount: number) => Math.min(0.3 * 2 ** (attemptCount - 1) * 1_000, 2_000) const gladiaVersion = `SdkJavascript/${SDK_VERSION}` const defaultOptions: InternalGladiaClientOptions = { apiKey: getEnv('GLADIA_API_KEY'), apiUrl: getEnv('GLADIA_API_URL', 'https://api.gladia.io'), region: getEnv['region']>('GLADIA_REGION'), httpHeaders: { 'x-gladia-version': gladiaVersion, }, httpRetry: { maxAttempts: 2, statusCodes: [408, 413, 429, [500, 599]], delay: defaultHttpDelay, }, httpTimeout: 10_000, wsRetry: { maxAttemptsPerConnection: 5, closeCodes: [ [1002, 4399], [4500, 9999], ], delay: defaultWsDelay, maxConnections: 0, }, wsTimeout: 10_000, prerecordedTimeouts: { transcribe: 7_200_000, poll: 7_200_000, createAndPoll: 7_200_000, uploadFile: 300_000, getFile: 300_000, create: 30_000, delete: 30_000, get: 10_000, }, } /** * Entrypoint for Gladia SDK. */ export class GladiaClient { private options: InternalGladiaClientOptions constructor(options?: GladiaClientOptions) { if (options?.httpHeaders) { options.httpHeaders = normalizeGladiaHeaders(options.httpHeaders) } this.options = deepMergeObjects(defaultOptions, options) } preRecordedV2(options?: GladiaClientOptions): PreRecordedV2Client { if (options?.httpHeaders) { options.httpHeaders = normalizeGladiaHeaders(options.httpHeaders) } const mergedOptions = deepMergeObjects(this.options, options) if (mergedOptions.apiKey) { mergedOptions.httpHeaders = deepMergeObjects(mergedOptions.httpHeaders, { 'x-gladia-key': mergedOptions.apiKey, }) } if (mergedOptions.httpHeaders['x-gladia-version'] !== gladiaVersion) { mergedOptions.httpHeaders['x-gladia-version'] = `${mergedOptions.httpHeaders['x-gladia-version'].trim()} ${gladiaVersion}`.trim() } assertValidOptions(mergedOptions) return new PreRecordedV2Client(mergedOptions) } preRecorded: (options?: GladiaClientOptions) => PreRecordedV2Client = this.preRecordedV2 liveV2(options?: GladiaClientOptions): LiveV2Client { if (options?.httpHeaders) { options.httpHeaders = normalizeGladiaHeaders(options.httpHeaders) } const mergedOptions = deepMergeObjects(this.options, options) if (mergedOptions.apiKey) { mergedOptions.httpHeaders = deepMergeObjects(mergedOptions.httpHeaders, { 'x-gladia-key': mergedOptions.apiKey, }) } if (mergedOptions.httpHeaders['x-gladia-version'] !== gladiaVersion) { mergedOptions.httpHeaders['x-gladia-version'] = `${mergedOptions.httpHeaders['x-gladia-version'].trim()} ${gladiaVersion}`.trim() } assertValidOptions(mergedOptions) return new LiveV2Client(mergedOptions) } live: (options?: GladiaClientOptions) => LiveV2Client = this.liveV2 }