@ssml-builder-js/azure-tts-client 2.18.0 → 2.19.1

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.
package/dist/index.js CHANGED
@@ -44,6 +44,8 @@ __export(index_exports, {
44
44
  BatchChunkValidationError: () => BatchChunkValidationError,
45
45
  ChunkValidationError: () => ChunkValidationError,
46
46
  DEFAULT_OUTPUT_FORMAT: () => DEFAULT_OUTPUT_FORMAT,
47
+ DeadlineController: () => DeadlineController,
48
+ IncompleteChunkSetError: () => IncompleteChunkSetError,
47
49
  MergeError: () => MergeError,
48
50
  SynthesisCancelledError: () => SynthesisCancelledError,
49
51
  SynthesisTimeoutError: () => SynthesisTimeoutError,
@@ -57,6 +59,7 @@ __export(index_exports, {
57
59
  mergeSynthesisResults: () => mergeSynthesisResults,
58
60
  resolveMergeAudioFormat: () => resolveMergeAudioFormat,
59
61
  resolveMimeType: () => resolveMimeType,
62
+ serializeChunkError: () => serializeChunkError,
60
63
  synthesizeSpeech: () => synthesizeSpeech,
61
64
  synthesizeSsml: () => synthesizeSsml,
62
65
  synthesizeSsmlChunks: () => synthesizeSsmlChunks,
@@ -128,6 +131,15 @@ var SynthesisTimeoutError = class extends Error {
128
131
  this.name = "SynthesisTimeoutError";
129
132
  }
130
133
  };
134
+ var IncompleteChunkSetError = class extends Error {
135
+ constructor(totalChunks, missingChunkIndices) {
136
+ super(`Cannot merge an incomplete chunk set; missing chunk indices: ${missingChunkIndices.join(", ")}.`);
137
+ this.kind = "incomplete-chunk-set";
138
+ this.name = "IncompleteChunkSetError";
139
+ this.totalChunks = totalChunks;
140
+ this.missingChunkIndices = [...missingChunkIndices];
141
+ }
142
+ };
131
143
  var MergeError = class extends Error {
132
144
  constructor(message, cause) {
133
145
  super(message);
@@ -152,8 +164,32 @@ var UnsupportedMergeFormatError = class extends Error {
152
164
  this.format = format;
153
165
  }
154
166
  };
167
+ function serializeChunkError(error, phase, isOriginalFailure) {
168
+ const message = error instanceof Error ? error.message : String(error);
169
+ const status = error instanceof AzureTtsError ? error.status : void 0;
170
+ const kind = error && typeof error === "object" && "kind" in error ? String(error.kind) : "";
171
+ const code = kind === "validation-error" ? "VALIDATION_ERROR" : kind === "timeout" || /tim(?:e|ed) ?out|deadline/i.test(message) ? "TIMEOUT" : kind === "cancelled" || /cancel|abort/i.test(message) ? "CANCELLED" : kind === "audio-format-mismatch" || kind === "unsupported-format-error" ? "FORMAT_MISMATCH" : kind === "merge-error" || phase === "merge" ? "MERGE_ERROR" : "AZURE_API_ERROR";
172
+ const details = {};
173
+ if (error instanceof AzureTtsError) {
174
+ details.statusText = error.statusText;
175
+ if (error.requestId) details.requestId = error.requestId;
176
+ }
177
+ if (error instanceof IncompleteChunkSetError) {
178
+ details.totalChunks = error.totalChunks;
179
+ details.missingChunkIndices = [...error.missingChunkIndices];
180
+ }
181
+ return {
182
+ code,
183
+ phase,
184
+ message,
185
+ isOriginalFailure,
186
+ isRetryable: code === "AZURE_API_ERROR" && (status === 429 || status !== void 0 && status >= 500 || /network|connection|connect|socket|fetch failed|econn|etimedout|temporar|transient|unavailable/i.test(message)),
187
+ ...status !== void 0 && status > 0 ? { httpStatus: status } : {},
188
+ ...Object.keys(details).length > 0 ? { details } : {}
189
+ };
190
+ }
155
191
  function toSynthesisError(error) {
156
- if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError)
192
+ if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError || error instanceof IncompleteChunkSetError)
157
193
  return error;
158
194
  const message = error instanceof Error ? error.message : String(error);
159
195
  if (/cancel|abort/i.test(message)) return new SynthesisCancelledError(message);
@@ -165,6 +201,55 @@ function createSpeechSdkError(error) {
165
201
  return new AzureTtsSdkError(message);
166
202
  }
167
203
 
204
+ // src/deadline.ts
205
+ var _controller, _parent, _onParentAbort, _timer, _timedOut;
206
+ var DeadlineController = class {
207
+ constructor(totalJobMs, parent) {
208
+ __privateAdd(this, _controller, new AbortController());
209
+ __privateAdd(this, _parent);
210
+ __privateAdd(this, _onParentAbort);
211
+ __privateAdd(this, _timer);
212
+ __privateAdd(this, _timedOut, false);
213
+ __privateSet(this, _parent, parent);
214
+ __privateSet(this, _onParentAbort, () => __privateGet(this, _controller).abort());
215
+ this.deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
216
+ this.signal = this.deadlineAtMs === void 0 && parent ? parent : __privateGet(this, _controller).signal;
217
+ if (parent?.aborted) __privateGet(this, _controller).abort();
218
+ parent?.addEventListener("abort", __privateGet(this, _onParentAbort), { once: true });
219
+ if (this.deadlineAtMs !== void 0) {
220
+ __privateSet(this, _timer, setTimeout(
221
+ () => {
222
+ __privateSet(this, _timedOut, true);
223
+ __privateGet(this, _controller).abort();
224
+ },
225
+ Math.max(0, this.deadlineAtMs - Date.now())
226
+ ));
227
+ }
228
+ }
229
+ get timedOut() {
230
+ return __privateGet(this, _timedOut) || this.deadlineAtMs !== void 0 && this.remainingMs <= 0;
231
+ }
232
+ get remainingMs() {
233
+ return this.deadlineAtMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, this.deadlineAtMs - Date.now());
234
+ }
235
+ throwIfExpired() {
236
+ if (this.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
237
+ if (this.signal.aborted) throw new Error("Speech synthesis was cancelled.");
238
+ }
239
+ abort() {
240
+ __privateGet(this, _controller).abort();
241
+ }
242
+ dispose() {
243
+ if (__privateGet(this, _timer)) clearTimeout(__privateGet(this, _timer));
244
+ __privateGet(this, _parent)?.removeEventListener("abort", __privateGet(this, _onParentAbort));
245
+ }
246
+ };
247
+ _controller = new WeakMap();
248
+ _parent = new WeakMap();
249
+ _onParentAbort = new WeakMap();
250
+ _timer = new WeakMap();
251
+ _timedOut = new WeakMap();
252
+
168
253
  // src/synthesis.ts
169
254
  var SpeechSDK2 = __toESM(require("microsoft-cognitiveservices-speech-sdk"));
170
255
  var import_ssml_core = require("@ssml-builder-js/ssml-core");
@@ -247,18 +332,23 @@ function createSpeechConfig(config) {
247
332
  }
248
333
 
249
334
  // src/synthesis.ts
250
- function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT) {
335
+ function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT, options = {}) {
251
336
  const readAttribute = (name) => {
252
337
  const pattern = new RegExp(`(?:${name})\\s*=\\s*[\\"']([^\\"']*)`, "gi");
253
338
  return [...ssml.matchAll(pattern)].map((match) => match[1] ?? "").join("|");
254
339
  };
340
+ const headers = Object.fromEntries(
341
+ Object.entries(options.customHeaders ?? {}).sort(([first], [second]) => first.localeCompare(second))
342
+ );
255
343
  const payload = JSON.stringify({
256
344
  ssml,
257
345
  outputFormat,
258
- voice: readAttribute("(?:name|voice)"),
259
- language: readAttribute("(?:xml:lang|lang)"),
260
- rate: readAttribute("rate"),
261
- pitch: readAttribute("pitch")
346
+ region: options.region ?? "",
347
+ endpoint: options.endpoint ?? "",
348
+ voice: options.voice ?? readAttribute("(?:name|voice)"),
349
+ lang: options.lang ?? readAttribute("(?:xml:lang|lang)"),
350
+ customHeaders: headers,
351
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? options.schemaVersion ?? "2"
262
352
  });
263
353
  let hash = 0xcbf29ce484222325n;
264
354
  const mask = 0xffffffffffffffffn;
@@ -378,6 +468,129 @@ function parseMp3Specification(buffer, format) {
378
468
  }
379
469
  return void 0;
380
470
  }
471
+ function readEbmlVint(bytes, offset, preserveMarker) {
472
+ const first = bytes[offset];
473
+ if (first === void 0) throw new Error("Invalid EBML variable-length integer.");
474
+ let mask = 128;
475
+ let length = 1;
476
+ while (length <= 8 && (first & mask) === 0) {
477
+ mask >>= 1;
478
+ length += 1;
479
+ }
480
+ if (length > 8 || offset + length > bytes.byteLength) throw new Error("Truncated EBML variable-length integer.");
481
+ let value = preserveMarker ? first : first & mask - 1;
482
+ for (let index = 1; index < length; index += 1) value = value * 256 + (bytes[offset + index] ?? 0);
483
+ if (!preserveMarker && value === 2 ** (7 * length) - 1)
484
+ throw new Error("EBML unknown-size elements are not supported.");
485
+ return { value, length };
486
+ }
487
+ function readEbmlElement(bytes, offset) {
488
+ const id = readEbmlVint(bytes, offset, true);
489
+ const size = readEbmlVint(bytes, offset + id.length, false);
490
+ const dataStart = offset + id.length + size.length;
491
+ const dataEnd = dataStart + size.value;
492
+ if (dataEnd > bytes.byteLength) throw new Error("EBML element exceeds the audio buffer.");
493
+ return { id: id.value, dataStart, dataEnd };
494
+ }
495
+ function ebmlText(bytes, element) {
496
+ return new TextDecoder().decode(bytes.slice(element.dataStart, element.dataEnd));
497
+ }
498
+ function findEbmlElement(bytes, start, end, id) {
499
+ let offset = start;
500
+ while (offset < end) {
501
+ const element = readEbmlElement(bytes, offset);
502
+ if (element.id === id) return element;
503
+ offset = element.dataEnd;
504
+ }
505
+ if (offset !== end) throw new Error("Invalid EBML element boundary.");
506
+ return void 0;
507
+ }
508
+ function parseOggSpecification(buffer, format) {
509
+ const bytes = new Uint8Array(buffer);
510
+ let offset = 0;
511
+ let firstPayload;
512
+ let pages = 0;
513
+ while (offset < bytes.byteLength) {
514
+ if (offset + 27 > bytes.byteLength || !ascii(bytes, offset, "OggS")) throw new Error("Invalid Ogg page header.");
515
+ if (bytes[offset + 4] !== 0) throw new Error("Unsupported Ogg bitstream version.");
516
+ const segmentCount = bytes[offset + 26] ?? 0;
517
+ const lacingStart = offset + 27;
518
+ const payloadStart = lacingStart + segmentCount;
519
+ if (payloadStart > bytes.byteLength) throw new Error("Truncated Ogg segment table.");
520
+ const payloadLength = bytes.slice(lacingStart, payloadStart).reduce((total, value) => total + value, 0);
521
+ const pageEnd = payloadStart + payloadLength;
522
+ if (pageEnd > bytes.byteLength) throw new Error("Ogg page payload exceeds the audio buffer.");
523
+ if (pages === 0) firstPayload = bytes.slice(payloadStart, pageEnd);
524
+ offset = pageEnd;
525
+ pages += 1;
526
+ }
527
+ if (pages === 0 || !firstPayload || !ascii(firstPayload, 0, "OpusHead") || firstPayload.byteLength < 19)
528
+ throw new Error("Ogg audio must contain a valid OpusHead packet.");
529
+ const version = firstPayload[8];
530
+ const channels = firstPayload[9] ?? 0;
531
+ const sampleRate = new DataView(firstPayload.buffer, firstPayload.byteOffset, firstPayload.byteLength).getUint32(
532
+ 12,
533
+ true
534
+ );
535
+ if (version !== 1 || channels <= 0 || sampleRate <= 0) throw new Error("Invalid Ogg OpusHead stream parameters.");
536
+ return {
537
+ format,
538
+ mimeType: "audio/ogg",
539
+ codec: "opus",
540
+ sampleRate,
541
+ channels,
542
+ container: "ogg",
543
+ isVbr: true,
544
+ isCompressed: true
545
+ };
546
+ }
547
+ function parseWebmSpecification(buffer, format) {
548
+ const bytes = new Uint8Array(buffer);
549
+ const ebml = readEbmlElement(bytes, 0);
550
+ if (ebml.id !== 440786851) throw new Error("WebM audio must begin with an EBML header.");
551
+ const docType = findEbmlElement(bytes, ebml.dataStart, ebml.dataEnd, 17026);
552
+ if (!docType || ebmlText(bytes, docType).toLowerCase() !== "webm") throw new Error("EBML DocType must be webm.");
553
+ const segment = readEbmlElement(bytes, ebml.dataEnd);
554
+ if (segment.id !== 408125543) throw new Error("WebM audio must contain a Segment element.");
555
+ const tracks = findEbmlElement(bytes, segment.dataStart, segment.dataEnd, 374648427);
556
+ if (!tracks) throw new Error("WebM audio must contain a Tracks element.");
557
+ let offset = tracks.dataStart;
558
+ let opusTrack;
559
+ while (offset < tracks.dataEnd) {
560
+ const track = readEbmlElement(bytes, offset);
561
+ if (track.id === 174) {
562
+ const codec = findEbmlElement(bytes, track.dataStart, track.dataEnd, 134);
563
+ const trackType = findEbmlElement(bytes, track.dataStart, track.dataEnd, 131);
564
+ if (codec && ebmlText(bytes, codec) === "A_OPUS" && trackType && bytes[trackType.dataStart] === 2) {
565
+ opusTrack = track;
566
+ break;
567
+ }
568
+ }
569
+ offset = track.dataEnd;
570
+ }
571
+ if (!opusTrack) throw new Error("WebM tracks do not define an Opus audio track.");
572
+ const audio = findEbmlElement(bytes, opusTrack.dataStart, opusTrack.dataEnd, 225);
573
+ const sampling = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 181) : void 0;
574
+ const channels = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 159) : void 0;
575
+ const sampleRate = sampling ? new DataView(
576
+ bytes.buffer,
577
+ bytes.byteOffset + sampling.dataStart,
578
+ sampling.dataEnd - sampling.dataStart
579
+ ).getFloat64(0, false) : 0;
580
+ const channelCount = channels ? bytes[channels.dataEnd - 1] ?? 0 : 0;
581
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0 || channelCount <= 0)
582
+ throw new Error("WebM Opus audio track has invalid sampling or channel parameters.");
583
+ return {
584
+ format,
585
+ mimeType: "audio/webm",
586
+ codec: "opus",
587
+ sampleRate: Math.round(sampleRate),
588
+ channels: channelCount,
589
+ container: "webm",
590
+ isVbr: true,
591
+ isCompressed: true
592
+ };
593
+ }
381
594
  function inspectAudioSpecification(buffer, format) {
382
595
  if (isWavFormat(format) || ascii(new Uint8Array(buffer), 0, "RIFF")) {
383
596
  const parsed = parseWav(buffer);
@@ -402,7 +615,20 @@ function inspectAudioSpecification(buffer, format) {
402
615
  isCompressed: codec !== "pcm" && codec !== "unknown"
403
616
  };
404
617
  }
405
- if (isMp3Format(format)) return parseMp3Specification(buffer, format) ?? formatAudioSpecification(format);
618
+ if (isMp3Format(format)) {
619
+ const specification2 = parseMp3Specification(buffer, format);
620
+ return specification2 ?? formatAudioSpecification(format);
621
+ }
622
+ if (isOggFormat(format) || ascii(new Uint8Array(buffer), 0, "OggS")) {
623
+ const specification2 = parseOggSpecification(buffer, format);
624
+ validateContainerFormat(specification2, format);
625
+ return specification2;
626
+ }
627
+ if (isWebmFormat(format) || new Uint8Array(buffer)[0] === 26) {
628
+ const specification2 = parseWebmSpecification(buffer, format);
629
+ validateContainerFormat(specification2, format);
630
+ return specification2;
631
+ }
406
632
  const specification = formatAudioSpecification(format);
407
633
  if (specification.container === "raw") validateRawAudioBuffer(buffer, specification);
408
634
  return specification;
@@ -411,7 +637,20 @@ function validateRawAudioBuffer(buffer, specification) {
411
637
  if (specification.sampleRate <= 0 || specification.channels <= 0 || specification.bitDepth === void 0) {
412
638
  throw new Error(`RAW audio format "${specification.format}" does not define a complete audio specification.`);
413
639
  }
414
- if (specification.codec === "siren" || specification.codec === "silk") return;
640
+ if (specification.codec === "siren") return;
641
+ if (specification.codec === "silk") {
642
+ if (buffer.byteLength <= 9 || !ascii(new Uint8Array(buffer), 0, "#!SILK_V3"))
643
+ throw new Error("RAW SILK audio must contain a valid #!SILK_V3 payload header.");
644
+ return;
645
+ }
646
+ if (specification.codec === "opus" && buffer.byteLength === 0) throw new Error("RAW Opus audio cannot be empty.");
647
+ if (specification.codec === "opus") {
648
+ const packetCode = new Uint8Array(buffer)[0] ?? 0;
649
+ const frameCountCode = packetCode & 3;
650
+ if (packetCode >> 3 > 31 || buffer.byteLength < (frameCountCode === 3 ? 2 : 2) || frameCountCode === 3 && ((new Uint8Array(buffer)[1] ?? 0) & 63) === 0)
651
+ throw new Error("RAW Opus audio has an invalid packet framing header.");
652
+ return;
653
+ }
415
654
  const bytesPerFrame = specification.channels * Math.ceil(specification.bitDepth / 8);
416
655
  if (bytesPerFrame <= 0 || buffer.byteLength % bytesPerFrame !== 0) {
417
656
  throw new Error(
@@ -419,6 +658,15 @@ function validateRawAudioBuffer(buffer, specification) {
419
658
  );
420
659
  }
421
660
  }
661
+ function validateContainerFormat(specification, format) {
662
+ const expected = formatAudioSpecification(format);
663
+ if (expected.sampleRate > 0 && specification.sampleRate !== expected.sampleRate || expected.channels > 0 && specification.channels !== expected.channels || expected.codec !== "unknown" && specification.codec !== expected.codec) {
664
+ throw new AudioFormatMismatchError(`Audio container does not match the requested format "${format}".`, [
665
+ expected,
666
+ specification
667
+ ]);
668
+ }
669
+ }
422
670
  function validateAudioSpecifications(specs) {
423
671
  const first = specs[0];
424
672
  if (!first) return;
@@ -500,17 +748,29 @@ function stripMp3Tags(buffer) {
500
748
  function isMp3Format(format) {
501
749
  return /(?:mp3|mpeg)/i.test(format);
502
750
  }
751
+ function isOggFormat(format) {
752
+ return /ogg/i.test(format);
753
+ }
754
+ function isWebmFormat(format) {
755
+ return /webm/i.test(format);
756
+ }
503
757
  function isWavFormat(format) {
504
758
  return /(?:wav|wave|riff)/i.test(format);
505
759
  }
506
760
  function isRawFormat(format) {
507
761
  return /^raw(?:-|$)/i.test(format);
508
762
  }
509
- function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType) {
763
+ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType, allowExternalContainer) {
510
764
  if (!(merged instanceof ArrayBuffer) || merged.byteLength === 0) {
511
765
  throw new MergeError("The custom audio merger returned an empty or invalid audio buffer.");
512
766
  }
513
- const specification = inspectAudioSpecification(merged, format);
767
+ let specification;
768
+ try {
769
+ specification = inspectAudioSpecification(merged, format);
770
+ } catch (error) {
771
+ if (!allowExternalContainer) throw error;
772
+ specification = inputSpecs[0] ?? formatAudioSpecification(format);
773
+ }
514
774
  if (!outputMimeType.trim()) throw new MergeError("The custom audio merger output MIME type cannot be empty.");
515
775
  const firstInput = inputSpecs[0];
516
776
  if (firstInput && (specification.sampleRate !== firstInput.sampleRate || specification.channels !== firstInput.channels || specification.codec !== firstInput.codec || firstInput.bitDepth !== void 0 && specification.bitDepth !== firstInput.bitDepth)) {
@@ -530,6 +790,25 @@ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMi
530
790
  }
531
791
  return specification;
532
792
  }
793
+ async function withinDeadline(value, deadline) {
794
+ if (!deadline) return value;
795
+ deadline.throwIfExpired();
796
+ if (!Number.isFinite(deadline.remainingMs)) return value;
797
+ let timer;
798
+ try {
799
+ return await Promise.race([
800
+ Promise.resolve(value),
801
+ new Promise((_resolve, reject) => {
802
+ timer = setTimeout(
803
+ () => reject(new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.")),
804
+ deadline.remainingMs
805
+ );
806
+ })
807
+ ]);
808
+ } finally {
809
+ if (timer) clearTimeout(timer);
810
+ }
811
+ }
533
812
  function resolveMergeAudioFormat(format) {
534
813
  if (isWavFormat(format)) return "wav";
535
814
  if (isMp3Format(format)) return "mp3";
@@ -542,6 +821,7 @@ function canMergeAudioFormat(format) {
542
821
  function mergeAudioBuffers(buffers, options) {
543
822
  const format = typeof options === "string" ? options : options?.format;
544
823
  if (!format) throw new UnsupportedMergeFormatError("");
824
+ if (!canMergeAudioFormat(format)) throw new UnsupportedMergeFormatError(format);
545
825
  try {
546
826
  validateAudioSpecifications(buffers.map((buffer) => inspectAudioSpecification(buffer, format)));
547
827
  if (isWavFormat(format)) return mergeWavBuffers(buffers);
@@ -850,10 +1130,23 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry, deadline
850
1130
  }
851
1131
  }
852
1132
  async function synthesizeSsml(ssml, config) {
853
- const totalJobMs = config.timeouts?.totalJobMs;
854
- const deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
855
- if (!config.retryOptions) return synthesizeSsmlOnce(ssml, config);
856
- return synthesizeWithRetry(ssml, config, config.retryOptions, () => void 0, deadlineAtMs);
1133
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
1134
+ try {
1135
+ deadline.throwIfExpired();
1136
+ const synthesisConfig = {
1137
+ ...config,
1138
+ signal: deadline.signal,
1139
+ timeouts: config.timeouts ? { ...config.timeouts, totalJobMs: void 0 } : void 0
1140
+ };
1141
+ const result = config.retryOptions ? await synthesizeWithRetry(ssml, synthesisConfig, config.retryOptions, () => void 0, deadline.deadlineAtMs) : await synthesizeSsmlOnce(ssml, synthesisConfig);
1142
+ deadline.throwIfExpired();
1143
+ return result;
1144
+ } catch (error) {
1145
+ if (deadline.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
1146
+ throw error;
1147
+ } finally {
1148
+ deadline.dispose();
1149
+ }
857
1150
  }
858
1151
  function createAbortScope(parent, timeoutMs) {
859
1152
  const controller = new AbortController();
@@ -890,7 +1183,12 @@ async function synthesizeSsmlChunks(chunks, config) {
890
1183
  const totalChunks = chunks.length;
891
1184
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
892
1185
  const fingerprints = inputs.map(
893
- (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT)
1186
+ (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT, {
1187
+ region: config.region,
1188
+ endpoint: config.endpoint,
1189
+ customHeaders: config.customHeaders,
1190
+ fingerprintSchemaVersion: config.fingerprintSchemaVersion
1191
+ })
894
1192
  );
895
1193
  const results = new Array(totalChunks);
896
1194
  const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
@@ -914,6 +1212,7 @@ async function synthesizeSsmlChunks(chunks, config) {
914
1212
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
915
1213
  const jobStartedAt = Date.now();
916
1214
  const jobDeadlineAt = config.timeouts?.totalJobMs !== void 0 && config.timeouts.totalJobMs > 0 ? jobStartedAt + config.timeouts.totalJobMs : void 0;
1215
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
917
1216
  const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
918
1217
  const report = (event) => config.onProgress?.(event);
919
1218
  for (const [index, input] of inputs.entries()) {
@@ -954,7 +1253,7 @@ async function synthesizeSsmlChunks(chunks, config) {
954
1253
  input.ssml,
955
1254
  {
956
1255
  ...config,
957
- signal: scope.signal,
1256
+ signal: deadline.signal,
958
1257
  ...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
959
1258
  ...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
960
1259
  ...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
@@ -999,7 +1298,7 @@ async function synthesizeSsmlChunks(chunks, config) {
999
1298
  status: wasCancelled ? "cancelled" : "failed",
1000
1299
  isOriginalFailure: !wasCancelled,
1001
1300
  canResume: true,
1002
- error
1301
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
1003
1302
  };
1004
1303
  report({
1005
1304
  currentChunk: completed,
@@ -1019,13 +1318,19 @@ async function synthesizeSsmlChunks(chunks, config) {
1019
1318
  try {
1020
1319
  await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
1021
1320
  if (firstError) throw firstError;
1321
+ const missingChunkIndices = Array.from(
1322
+ { length: totalChunks },
1323
+ (_value, index) => results[index] === void 0 ? index : void 0
1324
+ ).filter((index) => index !== void 0);
1325
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(totalChunks, missingChunkIndices);
1022
1326
  const orderedResults = results.filter((result) => result !== void 0);
1023
1327
  return await mergeSynthesisResults(orderedResults, {
1024
1328
  format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
1025
- signal: scope.signal,
1329
+ signal: deadline.signal,
1026
1330
  customMerger: config.customMerger,
1027
1331
  outputMimeType: config.outputMimeType,
1028
- postMergeValidator: config.postMergeValidator
1332
+ postMergeValidator: config.postMergeValidator,
1333
+ deadline
1029
1334
  });
1030
1335
  } catch (error) {
1031
1336
  if (firstError && config.cancelOnFailure !== false) {
@@ -1053,6 +1358,7 @@ async function synthesizeSsmlChunks(chunks, config) {
1053
1358
  throw error;
1054
1359
  } finally {
1055
1360
  scope.dispose();
1361
+ deadline.dispose();
1056
1362
  }
1057
1363
  }
1058
1364
  function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
@@ -1130,19 +1436,33 @@ function mergeSynthesisResults(results, options) {
1130
1436
  const format = resolvedOptions?.format;
1131
1437
  if (!format) throw new UnsupportedMergeFormatError("");
1132
1438
  const buffers = results.map((result) => result.audioData);
1133
- const inputSpecs = results.map((result) => result.audioSpec ?? inspectAudioSpecification(result.audioData, format));
1439
+ const inputSpecs = results.map((result) => {
1440
+ if (result.audioSpec) return result.audioSpec;
1441
+ try {
1442
+ return inspectAudioSpecification(result.audioData, format);
1443
+ } catch (error) {
1444
+ if (resolvedOptions.customMerger) return formatAudioSpecification(format);
1445
+ throw error;
1446
+ }
1447
+ });
1134
1448
  validateAudioSpecifications(inputSpecs);
1135
- const signal = resolvedOptions.signal ?? new AbortController().signal;
1449
+ const deadline = resolvedOptions.deadline;
1450
+ deadline?.throwIfExpired();
1451
+ const signal = resolvedOptions.signal ?? deadline?.signal ?? new AbortController().signal;
1136
1452
  if (signal.aborted) throw new SynthesisCancelledError();
1137
1453
  if (resolvedOptions.customMerger) {
1138
- return Promise.resolve().then(
1139
- () => resolvedOptions.customMerger?.(buffers, {
1140
- format,
1141
- outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
1142
- inputSpecs,
1143
- signal
1144
- })
1454
+ return withinDeadline(
1455
+ Promise.resolve().then(
1456
+ () => resolvedOptions.customMerger?.(buffers, {
1457
+ format,
1458
+ outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
1459
+ inputSpecs,
1460
+ signal
1461
+ })
1462
+ ),
1463
+ deadline
1145
1464
  ).then((merged) => {
1465
+ deadline?.throwIfExpired();
1146
1466
  if (!merged) throw new MergeError("The custom audio merger returned no audio buffer.");
1147
1467
  if (signal.aborted) throw new SynthesisCancelledError();
1148
1468
  const mergedSpec = validateMergedAudioBuffer(
@@ -1150,7 +1470,8 @@ function mergeSynthesisResults(results, options) {
1150
1470
  format,
1151
1471
  buffers,
1152
1472
  inputSpecs,
1153
- resolvedOptions.outputMimeType ?? resolveMimeType(format)
1473
+ resolvedOptions.outputMimeType ?? resolveMimeType(format),
1474
+ true
1154
1475
  );
1155
1476
  const result = createMergedResult(results, merged, format, mergedSpec, resolvedOptions.outputMimeType);
1156
1477
  return Promise.resolve(
@@ -1161,6 +1482,7 @@ function mergeSynthesisResults(results, options) {
1161
1482
  signal
1162
1483
  })
1163
1484
  ).then((valid) => {
1485
+ deadline?.throwIfExpired();
1164
1486
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1165
1487
  return result;
1166
1488
  });
@@ -1171,6 +1493,7 @@ function mergeSynthesisResults(results, options) {
1171
1493
  });
1172
1494
  }
1173
1495
  try {
1496
+ deadline?.throwIfExpired();
1174
1497
  const result = createMergedResult(
1175
1498
  results,
1176
1499
  mergeAudioBuffers(buffers, { format }),
@@ -1186,12 +1509,14 @@ function mergeSynthesisResults(results, options) {
1186
1509
  signal
1187
1510
  });
1188
1511
  if (validation instanceof Promise)
1189
- return validation.then((valid) => {
1512
+ return withinDeadline(validation, deadline).then((valid) => {
1513
+ deadline?.throwIfExpired();
1190
1514
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1191
1515
  return result;
1192
1516
  });
1193
1517
  if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1194
1518
  }
1519
+ deadline?.throwIfExpired();
1195
1520
  return result;
1196
1521
  } catch (error) {
1197
1522
  if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
@@ -1337,47 +1662,55 @@ function sharedValidationOptions(options, signal) {
1337
1662
  };
1338
1663
  }
1339
1664
  async function synthesizeSsmlSafe(client, ssml, options = {}) {
1340
- const validationOptions = sharedValidationOptions(options.validation ?? options, options.signal);
1665
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
1666
+ const validationOptions = sharedValidationOptions(options.validation ?? options, deadline.signal);
1341
1667
  const diagnostics = await Promise.resolve((0, import_ssml_core2.validateAzureSsml)(ssml, validationOptions));
1342
- if (options.signal?.aborted) {
1343
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1668
+ if (deadline.signal.aborted) {
1669
+ const error = toSynthesisError(
1670
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
1671
+ );
1672
+ deadline.dispose();
1344
1673
  return failure(error);
1345
1674
  }
1346
1675
  const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
1347
1676
  if (errors.length > 0) {
1677
+ deadline.dispose();
1348
1678
  return failure({
1349
1679
  kind: "validation-error",
1350
1680
  message: "SSML validation failed; the Azure Speech API was not called.",
1351
1681
  diagnostics: errors
1352
1682
  });
1353
1683
  }
1354
- const jobScope = options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts.totalJobMs) : void 0;
1355
1684
  try {
1356
1685
  return {
1357
1686
  ok: true,
1358
1687
  success: true,
1359
1688
  status: "success",
1360
1689
  value: await client.synthesizeSsml(ssml, {
1361
- signal: jobScope?.signal ?? options.signal,
1690
+ signal: deadline.signal,
1362
1691
  timeoutMs: options.timeouts?.perChunkMs,
1363
- timeouts: options.timeouts
1692
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0
1364
1693
  })
1365
1694
  };
1366
1695
  } catch (error) {
1367
- if (jobScope?.timedOut()) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
1696
+ if (deadline.timedOut) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
1368
1697
  const synthesisError = toSynthesisError(error);
1369
1698
  return failure(synthesisError);
1370
1699
  } finally {
1371
- jobScope?.dispose();
1700
+ deadline.dispose();
1372
1701
  }
1373
1702
  }
1374
1703
  async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1704
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
1375
1705
  const validationOptions = sharedValidationOptions(
1376
1706
  { ...options.validation ?? options, timeouts: options.timeouts },
1377
- options.signal
1707
+ deadline.signal
1378
1708
  );
1379
- if (options.signal?.aborted) {
1380
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1709
+ if (deadline.signal.aborted) {
1710
+ const error = toSynthesisError(
1711
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
1712
+ );
1713
+ deadline.dispose();
1381
1714
  return failure(error);
1382
1715
  }
1383
1716
  const pending = (index, status, error) => {
@@ -1409,14 +1742,16 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1409
1742
  return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
1410
1743
  })
1411
1744
  );
1412
- if (options.signal?.aborted) {
1745
+ if (deadline.signal.aborted) {
1413
1746
  const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1747
+ deadline.dispose();
1414
1748
  return failure(error);
1415
1749
  }
1416
1750
  const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
1417
1751
  if (chunkDiagnostics.length > 0) {
1418
1752
  const error = new BatchChunkValidationError(chunkDiagnostics);
1419
1753
  for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
1754
+ deadline.dispose();
1420
1755
  return failure(error);
1421
1756
  }
1422
1757
  let fallbackJobScope;
@@ -1429,9 +1764,9 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1429
1764
  const value = await client.synthesizeChunks(normalizedChunks, {
1430
1765
  onProgress: options.onProgress,
1431
1766
  outputFormat: options.outputFormat,
1432
- signal: options.signal,
1767
+ signal: deadline.signal,
1433
1768
  timeoutMs: options.timeoutMs,
1434
- timeouts: options.timeouts,
1769
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0,
1435
1770
  sourceNodePath: options.sourceNodePath,
1436
1771
  concurrency: options.concurrency,
1437
1772
  retryOptions: options.retryOptions,
@@ -1441,12 +1776,19 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1441
1776
  customMerger: options.customMerger,
1442
1777
  outputMimeType: options.outputMimeType,
1443
1778
  postMergeValidator: options.postMergeValidator,
1444
- resumeValidation: options.resumeValidation
1779
+ resumeValidation: options.resumeValidation,
1780
+ customHeaders: options.customHeaders,
1781
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
1445
1782
  });
1446
1783
  return { ok: true, success: true, status: "success", value };
1447
1784
  }
1448
1785
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
1449
- const fingerprints = inputs.map((chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat));
1786
+ const fingerprints = inputs.map(
1787
+ (chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat, {
1788
+ customHeaders: options.customHeaders,
1789
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
1790
+ })
1791
+ );
1450
1792
  const results = new Array(chunks.length);
1451
1793
  const chunkStates = inputs.map((_chunk, chunkIndex) => ({
1452
1794
  chunkIndex,
@@ -1464,9 +1806,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1464
1806
  }
1465
1807
  const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
1466
1808
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
1467
- const jobStartedAt = Date.now();
1468
- const jobDeadlineAt = options.timeouts?.totalJobMs !== void 0 && options.timeouts.totalJobMs > 0 ? jobStartedAt + options.timeouts.totalJobMs : void 0;
1469
- const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
1809
+ const jobDeadlineAt = deadline.deadlineAtMs;
1810
+ const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(deadline.signal, void 0) : void 0;
1470
1811
  fallbackJobScope = jobScope;
1471
1812
  const failedIndices = /* @__PURE__ */ new Set();
1472
1813
  let firstError;
@@ -1490,8 +1831,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1490
1831
  const startedAt = Date.now();
1491
1832
  try {
1492
1833
  const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
1493
- const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
1494
- const chunkSignal = chunkScope?.signal ?? options.signal;
1834
+ const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? deadline.signal, chunkTimeout ?? options.timeoutMs) : void 0;
1835
+ const chunkSignal = chunkScope?.signal ?? deadline.signal;
1495
1836
  let result;
1496
1837
  try {
1497
1838
  result = await retryableSynthesis(
@@ -1518,7 +1859,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1518
1859
  jobDeadlineAt
1519
1860
  );
1520
1861
  } catch (error) {
1521
- if (chunkScope?.timedOut())
1862
+ if (chunkScope?.timedOut() || deadline.timedOut)
1522
1863
  throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
1523
1864
  throw error;
1524
1865
  } finally {
@@ -1579,15 +1920,15 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1579
1920
  durationMs: Date.now() - startedAt
1580
1921
  });
1581
1922
  } catch (error) {
1582
- const wasCancelled = firstError !== void 0 || Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
1583
- firstError ?? (firstError = error);
1923
+ const wasCancelled = firstError !== void 0 || !deadline.timedOut && Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
1924
+ firstError ?? (firstError = deadline.timedOut ? new Error("Speech synthesis timed out.") : error);
1584
1925
  if (!wasCancelled) failedIndices.add(index);
1585
1926
  chunkStates[index] = {
1586
1927
  chunkIndex: index,
1587
1928
  status: wasCancelled ? "cancelled" : "failed",
1588
1929
  isOriginalFailure: !wasCancelled,
1589
1930
  canResume: true,
1590
- error
1931
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
1591
1932
  };
1592
1933
  options.onProgress?.({
1593
1934
  currentChunk: completed,
@@ -1630,6 +1971,11 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1630
1971
  };
1631
1972
  throw error;
1632
1973
  }
1974
+ const missingChunkIndices = Array.from(
1975
+ { length: chunks.length },
1976
+ (_value, index) => results[index] === void 0 ? index : void 0
1977
+ ).filter((index) => index !== void 0);
1978
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(chunks.length, missingChunkIndices);
1633
1979
  const orderedResults = results.filter((result) => result !== void 0);
1634
1980
  return {
1635
1981
  ok: true,
@@ -1637,7 +1983,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1637
1983
  status: "success",
1638
1984
  value: await mergeSynthesisResults(orderedResults, {
1639
1985
  format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
1640
- signal: jobScope?.signal ?? options.signal,
1986
+ signal: jobScope?.signal ?? deadline.signal,
1641
1987
  customMerger: options.customMerger,
1642
1988
  outputMimeType: options.outputMimeType,
1643
1989
  postMergeValidator: options.postMergeValidator
@@ -1648,6 +1994,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1648
1994
  return failure(synthesisError, partialResultFrom(error));
1649
1995
  } finally {
1650
1996
  fallbackJobScope?.dispose();
1997
+ deadline.dispose();
1651
1998
  }
1652
1999
  }
1653
2000
  function withValidationSignal(options, signal) {
@@ -1668,7 +2015,16 @@ var AzureTtsClient = class {
1668
2015
  __privateSet(this, _options, options);
1669
2016
  }
1670
2017
  async synthesize(ssml) {
1671
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
2018
+ const {
2019
+ region,
2020
+ subscriptionKey,
2021
+ outputFormat,
2022
+ signal,
2023
+ timeoutMs,
2024
+ timeouts,
2025
+ customHeaders,
2026
+ fingerprintSchemaVersion
2027
+ } = __privateGet(this, _options);
1672
2028
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1673
2029
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
1674
2030
  const config = {
@@ -1679,12 +2035,23 @@ var AzureTtsClient = class {
1679
2035
  signal,
1680
2036
  timeoutMs,
1681
2037
  timeouts,
1682
- retryOptions: __privateGet(this, _options).retryOptions
2038
+ retryOptions: __privateGet(this, _options).retryOptions,
2039
+ customHeaders,
2040
+ fingerprintSchemaVersion
1683
2041
  };
1684
2042
  return synthesizeSpeech(ssml, config);
1685
2043
  }
1686
2044
  async synthesizeSsml(ssml, options = {}) {
1687
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
2045
+ const {
2046
+ region,
2047
+ subscriptionKey,
2048
+ outputFormat,
2049
+ signal,
2050
+ timeoutMs,
2051
+ timeouts,
2052
+ customHeaders,
2053
+ fingerprintSchemaVersion
2054
+ } = __privateGet(this, _options);
1688
2055
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1689
2056
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
1690
2057
  return synthesizeSsml(ssml, {
@@ -1703,11 +2070,22 @@ var AzureTtsClient = class {
1703
2070
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
1704
2071
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
1705
2072
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
1706
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
2073
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2074
+ customHeaders: options.customHeaders ?? customHeaders,
2075
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
1707
2076
  });
1708
2077
  }
1709
2078
  async synthesizeChunks(chunks, options = {}) {
1710
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
2079
+ const {
2080
+ region,
2081
+ subscriptionKey,
2082
+ outputFormat,
2083
+ signal,
2084
+ timeoutMs,
2085
+ timeouts,
2086
+ customHeaders,
2087
+ fingerprintSchemaVersion
2088
+ } = __privateGet(this, _options);
1711
2089
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1712
2090
  return synthesizeSsmlChunks(chunks, {
1713
2091
  endpoint,
@@ -1727,7 +2105,9 @@ var AzureTtsClient = class {
1727
2105
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
1728
2106
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
1729
2107
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
1730
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
2108
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2109
+ customHeaders: options.customHeaders ?? customHeaders,
2110
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
1731
2111
  });
1732
2112
  }
1733
2113
  async synthesizeSsmlSafe(ssml, options = {}) {
@@ -1742,7 +2122,14 @@ var AzureTtsClient = class {
1742
2122
  timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
1743
2123
  onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
1744
2124
  concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
1745
- retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
2125
+ retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
2126
+ cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
2127
+ customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
2128
+ outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
2129
+ postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
2130
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2131
+ customHeaders: options.customHeaders ?? __privateGet(this, _options).customHeaders,
2132
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? __privateGet(this, _options).fingerprintSchemaVersion
1746
2133
  });
1747
2134
  }
1748
2135
  async synthesizeSsmlChunksSafe(chunks, options = {}) {
@@ -1848,6 +2235,8 @@ async function fetchAzureVoiceCatalog(options) {
1848
2235
  BatchChunkValidationError,
1849
2236
  ChunkValidationError,
1850
2237
  DEFAULT_OUTPUT_FORMAT,
2238
+ DeadlineController,
2239
+ IncompleteChunkSetError,
1851
2240
  MergeError,
1852
2241
  SynthesisCancelledError,
1853
2242
  SynthesisTimeoutError,
@@ -1861,6 +2250,7 @@ async function fetchAzureVoiceCatalog(options) {
1861
2250
  mergeSynthesisResults,
1862
2251
  resolveMergeAudioFormat,
1863
2252
  resolveMimeType,
2253
+ serializeChunkError,
1864
2254
  synthesizeSpeech,
1865
2255
  synthesizeSsml,
1866
2256
  synthesizeSsmlChunks,