{"version":3,"file":"audioUtils-BLwBEdcX.cjs","sources":["../src/providers/audioUtils.ts"],"sourcesContent":["import type { SpeechError } from './types.js'\n\n/**\n * Plays an audio blob using an HTMLAudioElement (Decision D-3 — blob URL approach).\n * `fetch()` → buffer full response → `URL.createObjectURL(blob)` → `<audio>.play()`\n *\n * @param blob    Audio data (MP3, Opus, AAC, etc.)\n * @param signal  Optional AbortSignal — resolves immediately on abort\n * @returns Promise that resolves when playback ends or rejects on error/abort\n */\nexport function playBlobAudio(blob: Blob, signal?: AbortSignal): Promise<void> {\n  return new Promise<void>((resolve, reject) => {\n    const url = URL.createObjectURL(blob)\n    const audio = new Audio(url)\n\n    const cleanup = () => {\n      URL.revokeObjectURL(url)\n      audio.onended = null\n      audio.onerror = null\n    }\n\n    if (signal?.aborted) {\n      cleanup()\n      resolve()\n      return\n    }\n\n    const onAbort = () => {\n      audio.pause()\n      cleanup()\n      resolve()\n    }\n\n    audio.onended = () => {\n      signal?.removeEventListener('abort', onAbort)\n      cleanup()\n      resolve()\n    }\n\n    audio.onerror = () => {\n      signal?.removeEventListener('abort', onAbort)\n      cleanup()\n      const err: SpeechError = {\n        code: 'AUDIO_PLAYBACK',\n        message: `Audio playback failed (MediaError code ${audio.error?.code ?? 'unknown'})`,\n      }\n      reject(err)\n    }\n\n    signal?.addEventListener('abort', onAbort, { once: true })\n\n    audio.play().catch((e: unknown) => {\n      signal?.removeEventListener('abort', onAbort)\n      cleanup()\n      const err: SpeechError = {\n        code: 'AUDIO_PLAYBACK',\n        message: 'audio.play() rejected — browser may require a user gesture first',\n        cause: e,\n      }\n      reject(err)\n    })\n  })\n}\n\n/**\n * Converts a non-2xx fetch Response into a typed SpeechError and throws it.\n */\nexport async function throwIfNotOk(response: Response): Promise<void> {\n  if (response.ok) return\n\n  const body = await response.text().catch(() => '')\n  const code = response.status === 429 ? 'RATE_LIMIT' : 'API_ERROR'\n\n  const err: SpeechError = {\n    code,\n    message: `HTTP ${response.status} ${response.statusText}`,\n    cause: body || undefined,\n  }\n  throw err\n}\n"],"names":[],"mappings":";AAUO,SAAS,cAAc,MAAY,QAAqC;AAC7E,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,UAAM,QAAQ,IAAI,MAAM,GAAG;AAE3B,UAAM,UAAU,MAAM;AACpB,UAAI,gBAAgB,GAAG;AACvB,YAAM,UAAU;AAChB,YAAM,UAAU;AAAA,IAClB;AAEA,QAAI,iCAAQ,SAAS;AACnB,cAAA;AACA,cAAA;AACA;AAAA,IACF;AAEA,UAAM,UAAU,MAAM;AACpB,YAAM,MAAA;AACN,cAAA;AACA,cAAA;AAAA,IACF;AAEA,UAAM,UAAU,MAAM;AACpB,uCAAQ,oBAAoB,SAAS;AACrC,cAAA;AACA,cAAA;AAAA,IACF;AAEA,UAAM,UAAU,MAAM;;AACpB,uCAAQ,oBAAoB,SAAS;AACrC,cAAA;AACA,YAAM,MAAmB;AAAA,QACvB,MAAM;AAAA,QACN,SAAS,4CAA0C,WAAM,UAAN,mBAAa,SAAQ,SAAS;AAAA,MAAA;AAEnF,aAAO,GAAG;AAAA,IACZ;AAEA,qCAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM;AAEnD,UAAM,KAAA,EAAO,MAAM,CAAC,MAAe;AACjC,uCAAQ,oBAAoB,SAAS;AACrC,cAAA;AACA,YAAM,MAAmB;AAAA,QACvB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAET,aAAO,GAAG;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AACH;AAKA,eAAsB,aAAa,UAAmC;AACpE,MAAI,SAAS,GAAI;AAEjB,QAAM,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,EAAE;AACjD,QAAM,OAAO,SAAS,WAAW,MAAM,eAAe;AAEtD,QAAM,MAAmB;AAAA,IACvB;AAAA,IACA,SAAS,QAAQ,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,IACvD,OAAO,QAAQ;AAAA,EAAA;AAEjB,QAAM;AACR;;;"}