ssml-builder-js 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.mjs CHANGED
@@ -93,6 +93,15 @@ var SynthesisTimeoutError = class extends Error {
93
93
  this.name = "SynthesisTimeoutError";
94
94
  }
95
95
  };
96
+ var IncompleteChunkSetError = class extends Error {
97
+ constructor(totalChunks, missingChunkIndices) {
98
+ super(`Cannot merge an incomplete chunk set; missing chunk indices: ${missingChunkIndices.join(", ")}.`);
99
+ this.kind = "incomplete-chunk-set";
100
+ this.name = "IncompleteChunkSetError";
101
+ this.totalChunks = totalChunks;
102
+ this.missingChunkIndices = [...missingChunkIndices];
103
+ }
104
+ };
96
105
  var MergeError = class extends Error {
97
106
  constructor(message, cause) {
98
107
  super(message);
@@ -117,8 +126,32 @@ var UnsupportedMergeFormatError = class extends Error {
117
126
  this.format = format;
118
127
  }
119
128
  };
129
+ function serializeChunkError(error, phase, isOriginalFailure) {
130
+ const message = error instanceof Error ? error.message : String(error);
131
+ const status = error instanceof AzureTtsError ? error.status : void 0;
132
+ const kind = error && typeof error === "object" && "kind" in error ? String(error.kind) : "";
133
+ 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";
134
+ const details = {};
135
+ if (error instanceof AzureTtsError) {
136
+ details.statusText = error.statusText;
137
+ if (error.requestId) details.requestId = error.requestId;
138
+ }
139
+ if (error instanceof IncompleteChunkSetError) {
140
+ details.totalChunks = error.totalChunks;
141
+ details.missingChunkIndices = [...error.missingChunkIndices];
142
+ }
143
+ return {
144
+ code,
145
+ phase,
146
+ message,
147
+ isOriginalFailure,
148
+ 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)),
149
+ ...status !== void 0 && status > 0 ? { httpStatus: status } : {},
150
+ ...Object.keys(details).length > 0 ? { details } : {}
151
+ };
152
+ }
120
153
  function toSynthesisError(error) {
121
- if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError)
154
+ if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError || error instanceof IncompleteChunkSetError)
122
155
  return error;
123
156
  const message = error instanceof Error ? error.message : String(error);
124
157
  if (/cancel|abort/i.test(message)) return new SynthesisCancelledError(message);
@@ -130,6 +163,55 @@ function createSpeechSdkError(error) {
130
163
  return new AzureTtsSdkError(message);
131
164
  }
132
165
 
166
+ // packages/azure-tts-client/src/deadline.ts
167
+ var _controller, _parent, _onParentAbort, _timer, _timedOut;
168
+ var DeadlineController = class {
169
+ constructor(totalJobMs, parent) {
170
+ __privateAdd(this, _controller, new AbortController());
171
+ __privateAdd(this, _parent);
172
+ __privateAdd(this, _onParentAbort);
173
+ __privateAdd(this, _timer);
174
+ __privateAdd(this, _timedOut, false);
175
+ __privateSet(this, _parent, parent);
176
+ __privateSet(this, _onParentAbort, () => __privateGet(this, _controller).abort());
177
+ this.deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
178
+ this.signal = this.deadlineAtMs === void 0 && parent ? parent : __privateGet(this, _controller).signal;
179
+ if (parent?.aborted) __privateGet(this, _controller).abort();
180
+ parent?.addEventListener("abort", __privateGet(this, _onParentAbort), { once: true });
181
+ if (this.deadlineAtMs !== void 0) {
182
+ __privateSet(this, _timer, setTimeout(
183
+ () => {
184
+ __privateSet(this, _timedOut, true);
185
+ __privateGet(this, _controller).abort();
186
+ },
187
+ Math.max(0, this.deadlineAtMs - Date.now())
188
+ ));
189
+ }
190
+ }
191
+ get timedOut() {
192
+ return __privateGet(this, _timedOut) || this.deadlineAtMs !== void 0 && this.remainingMs <= 0;
193
+ }
194
+ get remainingMs() {
195
+ return this.deadlineAtMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, this.deadlineAtMs - Date.now());
196
+ }
197
+ throwIfExpired() {
198
+ if (this.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
199
+ if (this.signal.aborted) throw new Error("Speech synthesis was cancelled.");
200
+ }
201
+ abort() {
202
+ __privateGet(this, _controller).abort();
203
+ }
204
+ dispose() {
205
+ if (__privateGet(this, _timer)) clearTimeout(__privateGet(this, _timer));
206
+ __privateGet(this, _parent)?.removeEventListener("abort", __privateGet(this, _onParentAbort));
207
+ }
208
+ };
209
+ _controller = new WeakMap();
210
+ _parent = new WeakMap();
211
+ _onParentAbort = new WeakMap();
212
+ _timer = new WeakMap();
213
+ _timedOut = new WeakMap();
214
+
133
215
  // packages/azure-tts-client/src/synthesis.ts
134
216
  import * as SpeechSDK2 from "microsoft-cognitiveservices-speech-sdk";
135
217
 
@@ -211,18 +293,23 @@ function createSpeechConfig(config) {
211
293
  }
212
294
 
213
295
  // packages/azure-tts-client/src/synthesis.ts
214
- function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT) {
296
+ function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT, options = {}) {
215
297
  const readAttribute = (name) => {
216
298
  const pattern = new RegExp(`(?:${name})\\s*=\\s*[\\"']([^\\"']*)`, "gi");
217
299
  return [...ssml.matchAll(pattern)].map((match) => match[1] ?? "").join("|");
218
300
  };
301
+ const headers = Object.fromEntries(
302
+ Object.entries(options.customHeaders ?? {}).sort(([first], [second]) => first.localeCompare(second))
303
+ );
219
304
  const payload = JSON.stringify({
220
305
  ssml,
221
306
  outputFormat,
222
- voice: readAttribute("(?:name|voice)"),
223
- language: readAttribute("(?:xml:lang|lang)"),
224
- rate: readAttribute("rate"),
225
- pitch: readAttribute("pitch")
307
+ region: options.region ?? "",
308
+ endpoint: options.endpoint ?? "",
309
+ voice: options.voice ?? readAttribute("(?:name|voice)"),
310
+ lang: options.lang ?? readAttribute("(?:xml:lang|lang)"),
311
+ customHeaders: headers,
312
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? options.schemaVersion ?? "2"
226
313
  });
227
314
  let hash = 0xcbf29ce484222325n;
228
315
  const mask = 0xffffffffffffffffn;
@@ -342,6 +429,129 @@ function parseMp3Specification(buffer, format) {
342
429
  }
343
430
  return void 0;
344
431
  }
432
+ function readEbmlVint(bytes, offset, preserveMarker) {
433
+ const first = bytes[offset];
434
+ if (first === void 0) throw new Error("Invalid EBML variable-length integer.");
435
+ let mask = 128;
436
+ let length = 1;
437
+ while (length <= 8 && (first & mask) === 0) {
438
+ mask >>= 1;
439
+ length += 1;
440
+ }
441
+ if (length > 8 || offset + length > bytes.byteLength) throw new Error("Truncated EBML variable-length integer.");
442
+ let value = preserveMarker ? first : first & mask - 1;
443
+ for (let index = 1; index < length; index += 1) value = value * 256 + (bytes[offset + index] ?? 0);
444
+ if (!preserveMarker && value === 2 ** (7 * length) - 1)
445
+ throw new Error("EBML unknown-size elements are not supported.");
446
+ return { value, length };
447
+ }
448
+ function readEbmlElement(bytes, offset) {
449
+ const id = readEbmlVint(bytes, offset, true);
450
+ const size = readEbmlVint(bytes, offset + id.length, false);
451
+ const dataStart = offset + id.length + size.length;
452
+ const dataEnd = dataStart + size.value;
453
+ if (dataEnd > bytes.byteLength) throw new Error("EBML element exceeds the audio buffer.");
454
+ return { id: id.value, dataStart, dataEnd };
455
+ }
456
+ function ebmlText(bytes, element) {
457
+ return new TextDecoder().decode(bytes.slice(element.dataStart, element.dataEnd));
458
+ }
459
+ function findEbmlElement(bytes, start, end, id) {
460
+ let offset = start;
461
+ while (offset < end) {
462
+ const element = readEbmlElement(bytes, offset);
463
+ if (element.id === id) return element;
464
+ offset = element.dataEnd;
465
+ }
466
+ if (offset !== end) throw new Error("Invalid EBML element boundary.");
467
+ return void 0;
468
+ }
469
+ function parseOggSpecification(buffer, format) {
470
+ const bytes = new Uint8Array(buffer);
471
+ let offset = 0;
472
+ let firstPayload;
473
+ let pages = 0;
474
+ while (offset < bytes.byteLength) {
475
+ if (offset + 27 > bytes.byteLength || !ascii(bytes, offset, "OggS")) throw new Error("Invalid Ogg page header.");
476
+ if (bytes[offset + 4] !== 0) throw new Error("Unsupported Ogg bitstream version.");
477
+ const segmentCount = bytes[offset + 26] ?? 0;
478
+ const lacingStart = offset + 27;
479
+ const payloadStart = lacingStart + segmentCount;
480
+ if (payloadStart > bytes.byteLength) throw new Error("Truncated Ogg segment table.");
481
+ const payloadLength = bytes.slice(lacingStart, payloadStart).reduce((total, value) => total + value, 0);
482
+ const pageEnd = payloadStart + payloadLength;
483
+ if (pageEnd > bytes.byteLength) throw new Error("Ogg page payload exceeds the audio buffer.");
484
+ if (pages === 0) firstPayload = bytes.slice(payloadStart, pageEnd);
485
+ offset = pageEnd;
486
+ pages += 1;
487
+ }
488
+ if (pages === 0 || !firstPayload || !ascii(firstPayload, 0, "OpusHead") || firstPayload.byteLength < 19)
489
+ throw new Error("Ogg audio must contain a valid OpusHead packet.");
490
+ const version = firstPayload[8];
491
+ const channels = firstPayload[9] ?? 0;
492
+ const sampleRate = new DataView(firstPayload.buffer, firstPayload.byteOffset, firstPayload.byteLength).getUint32(
493
+ 12,
494
+ true
495
+ );
496
+ if (version !== 1 || channels <= 0 || sampleRate <= 0) throw new Error("Invalid Ogg OpusHead stream parameters.");
497
+ return {
498
+ format,
499
+ mimeType: "audio/ogg",
500
+ codec: "opus",
501
+ sampleRate,
502
+ channels,
503
+ container: "ogg",
504
+ isVbr: true,
505
+ isCompressed: true
506
+ };
507
+ }
508
+ function parseWebmSpecification(buffer, format) {
509
+ const bytes = new Uint8Array(buffer);
510
+ const ebml = readEbmlElement(bytes, 0);
511
+ if (ebml.id !== 440786851) throw new Error("WebM audio must begin with an EBML header.");
512
+ const docType = findEbmlElement(bytes, ebml.dataStart, ebml.dataEnd, 17026);
513
+ if (!docType || ebmlText(bytes, docType).toLowerCase() !== "webm") throw new Error("EBML DocType must be webm.");
514
+ const segment = readEbmlElement(bytes, ebml.dataEnd);
515
+ if (segment.id !== 408125543) throw new Error("WebM audio must contain a Segment element.");
516
+ const tracks = findEbmlElement(bytes, segment.dataStart, segment.dataEnd, 374648427);
517
+ if (!tracks) throw new Error("WebM audio must contain a Tracks element.");
518
+ let offset = tracks.dataStart;
519
+ let opusTrack;
520
+ while (offset < tracks.dataEnd) {
521
+ const track = readEbmlElement(bytes, offset);
522
+ if (track.id === 174) {
523
+ const codec = findEbmlElement(bytes, track.dataStart, track.dataEnd, 134);
524
+ const trackType = findEbmlElement(bytes, track.dataStart, track.dataEnd, 131);
525
+ if (codec && ebmlText(bytes, codec) === "A_OPUS" && trackType && bytes[trackType.dataStart] === 2) {
526
+ opusTrack = track;
527
+ break;
528
+ }
529
+ }
530
+ offset = track.dataEnd;
531
+ }
532
+ if (!opusTrack) throw new Error("WebM tracks do not define an Opus audio track.");
533
+ const audio = findEbmlElement(bytes, opusTrack.dataStart, opusTrack.dataEnd, 225);
534
+ const sampling = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 181) : void 0;
535
+ const channels = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 159) : void 0;
536
+ const sampleRate = sampling ? new DataView(
537
+ bytes.buffer,
538
+ bytes.byteOffset + sampling.dataStart,
539
+ sampling.dataEnd - sampling.dataStart
540
+ ).getFloat64(0, false) : 0;
541
+ const channelCount = channels ? bytes[channels.dataEnd - 1] ?? 0 : 0;
542
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0 || channelCount <= 0)
543
+ throw new Error("WebM Opus audio track has invalid sampling or channel parameters.");
544
+ return {
545
+ format,
546
+ mimeType: "audio/webm",
547
+ codec: "opus",
548
+ sampleRate: Math.round(sampleRate),
549
+ channels: channelCount,
550
+ container: "webm",
551
+ isVbr: true,
552
+ isCompressed: true
553
+ };
554
+ }
345
555
  function inspectAudioSpecification(buffer, format) {
346
556
  if (isWavFormat(format) || ascii(new Uint8Array(buffer), 0, "RIFF")) {
347
557
  const parsed = parseWav(buffer);
@@ -366,7 +576,20 @@ function inspectAudioSpecification(buffer, format) {
366
576
  isCompressed: codec !== "pcm" && codec !== "unknown"
367
577
  };
368
578
  }
369
- if (isMp3Format(format)) return parseMp3Specification(buffer, format) ?? formatAudioSpecification(format);
579
+ if (isMp3Format(format)) {
580
+ const specification2 = parseMp3Specification(buffer, format);
581
+ return specification2 ?? formatAudioSpecification(format);
582
+ }
583
+ if (isOggFormat(format) || ascii(new Uint8Array(buffer), 0, "OggS")) {
584
+ const specification2 = parseOggSpecification(buffer, format);
585
+ validateContainerFormat(specification2, format);
586
+ return specification2;
587
+ }
588
+ if (isWebmFormat(format) || new Uint8Array(buffer)[0] === 26) {
589
+ const specification2 = parseWebmSpecification(buffer, format);
590
+ validateContainerFormat(specification2, format);
591
+ return specification2;
592
+ }
370
593
  const specification = formatAudioSpecification(format);
371
594
  if (specification.container === "raw") validateRawAudioBuffer(buffer, specification);
372
595
  return specification;
@@ -375,7 +598,20 @@ function validateRawAudioBuffer(buffer, specification) {
375
598
  if (specification.sampleRate <= 0 || specification.channels <= 0 || specification.bitDepth === void 0) {
376
599
  throw new Error(`RAW audio format "${specification.format}" does not define a complete audio specification.`);
377
600
  }
378
- if (specification.codec === "siren" || specification.codec === "silk") return;
601
+ if (specification.codec === "siren") return;
602
+ if (specification.codec === "silk") {
603
+ if (buffer.byteLength <= 9 || !ascii(new Uint8Array(buffer), 0, "#!SILK_V3"))
604
+ throw new Error("RAW SILK audio must contain a valid #!SILK_V3 payload header.");
605
+ return;
606
+ }
607
+ if (specification.codec === "opus" && buffer.byteLength === 0) throw new Error("RAW Opus audio cannot be empty.");
608
+ if (specification.codec === "opus") {
609
+ const packetCode = new Uint8Array(buffer)[0] ?? 0;
610
+ const frameCountCode = packetCode & 3;
611
+ if (packetCode >> 3 > 31 || buffer.byteLength < (frameCountCode === 3 ? 2 : 2) || frameCountCode === 3 && ((new Uint8Array(buffer)[1] ?? 0) & 63) === 0)
612
+ throw new Error("RAW Opus audio has an invalid packet framing header.");
613
+ return;
614
+ }
379
615
  const bytesPerFrame = specification.channels * Math.ceil(specification.bitDepth / 8);
380
616
  if (bytesPerFrame <= 0 || buffer.byteLength % bytesPerFrame !== 0) {
381
617
  throw new Error(
@@ -383,6 +619,15 @@ function validateRawAudioBuffer(buffer, specification) {
383
619
  );
384
620
  }
385
621
  }
622
+ function validateContainerFormat(specification, format) {
623
+ const expected = formatAudioSpecification(format);
624
+ if (expected.sampleRate > 0 && specification.sampleRate !== expected.sampleRate || expected.channels > 0 && specification.channels !== expected.channels || expected.codec !== "unknown" && specification.codec !== expected.codec) {
625
+ throw new AudioFormatMismatchError(`Audio container does not match the requested format "${format}".`, [
626
+ expected,
627
+ specification
628
+ ]);
629
+ }
630
+ }
386
631
  function validateAudioSpecifications(specs) {
387
632
  const first = specs[0];
388
633
  if (!first) return;
@@ -464,17 +709,29 @@ function stripMp3Tags(buffer) {
464
709
  function isMp3Format(format) {
465
710
  return /(?:mp3|mpeg)/i.test(format);
466
711
  }
712
+ function isOggFormat(format) {
713
+ return /ogg/i.test(format);
714
+ }
715
+ function isWebmFormat(format) {
716
+ return /webm/i.test(format);
717
+ }
467
718
  function isWavFormat(format) {
468
719
  return /(?:wav|wave|riff)/i.test(format);
469
720
  }
470
721
  function isRawFormat(format) {
471
722
  return /^raw(?:-|$)/i.test(format);
472
723
  }
473
- function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType) {
724
+ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType, allowExternalContainer) {
474
725
  if (!(merged instanceof ArrayBuffer) || merged.byteLength === 0) {
475
726
  throw new MergeError("The custom audio merger returned an empty or invalid audio buffer.");
476
727
  }
477
- const specification = inspectAudioSpecification(merged, format);
728
+ let specification;
729
+ try {
730
+ specification = inspectAudioSpecification(merged, format);
731
+ } catch (error) {
732
+ if (!allowExternalContainer) throw error;
733
+ specification = inputSpecs[0] ?? formatAudioSpecification(format);
734
+ }
478
735
  if (!outputMimeType.trim()) throw new MergeError("The custom audio merger output MIME type cannot be empty.");
479
736
  const firstInput = inputSpecs[0];
480
737
  if (firstInput && (specification.sampleRate !== firstInput.sampleRate || specification.channels !== firstInput.channels || specification.codec !== firstInput.codec || firstInput.bitDepth !== void 0 && specification.bitDepth !== firstInput.bitDepth)) {
@@ -494,6 +751,25 @@ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMi
494
751
  }
495
752
  return specification;
496
753
  }
754
+ async function withinDeadline(value, deadline) {
755
+ if (!deadline) return value;
756
+ deadline.throwIfExpired();
757
+ if (!Number.isFinite(deadline.remainingMs)) return value;
758
+ let timer;
759
+ try {
760
+ return await Promise.race([
761
+ Promise.resolve(value),
762
+ new Promise((_resolve, reject) => {
763
+ timer = setTimeout(
764
+ () => reject(new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.")),
765
+ deadline.remainingMs
766
+ );
767
+ })
768
+ ]);
769
+ } finally {
770
+ if (timer) clearTimeout(timer);
771
+ }
772
+ }
497
773
  function resolveMergeAudioFormat(format) {
498
774
  if (isWavFormat(format)) return "wav";
499
775
  if (isMp3Format(format)) return "mp3";
@@ -506,6 +782,7 @@ function canMergeAudioFormat(format) {
506
782
  function mergeAudioBuffers(buffers, options) {
507
783
  const format = typeof options === "string" ? options : options?.format;
508
784
  if (!format) throw new UnsupportedMergeFormatError("");
785
+ if (!canMergeAudioFormat(format)) throw new UnsupportedMergeFormatError(format);
509
786
  try {
510
787
  validateAudioSpecifications(buffers.map((buffer) => inspectAudioSpecification(buffer, format)));
511
788
  if (isWavFormat(format)) return mergeWavBuffers(buffers);
@@ -814,10 +1091,23 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry, deadline
814
1091
  }
815
1092
  }
816
1093
  async function synthesizeSsml(ssml, config) {
817
- const totalJobMs = config.timeouts?.totalJobMs;
818
- const deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
819
- if (!config.retryOptions) return synthesizeSsmlOnce(ssml, config);
820
- return synthesizeWithRetry(ssml, config, config.retryOptions, () => void 0, deadlineAtMs);
1094
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
1095
+ try {
1096
+ deadline.throwIfExpired();
1097
+ const synthesisConfig = {
1098
+ ...config,
1099
+ signal: deadline.signal,
1100
+ timeouts: config.timeouts ? { ...config.timeouts, totalJobMs: void 0 } : void 0
1101
+ };
1102
+ const result = config.retryOptions ? await synthesizeWithRetry(ssml, synthesisConfig, config.retryOptions, () => void 0, deadline.deadlineAtMs) : await synthesizeSsmlOnce(ssml, synthesisConfig);
1103
+ deadline.throwIfExpired();
1104
+ return result;
1105
+ } catch (error) {
1106
+ if (deadline.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
1107
+ throw error;
1108
+ } finally {
1109
+ deadline.dispose();
1110
+ }
821
1111
  }
822
1112
  function createAbortScope(parent, timeoutMs) {
823
1113
  const controller = new AbortController();
@@ -854,7 +1144,12 @@ async function synthesizeSsmlChunks(chunks, config) {
854
1144
  const totalChunks = chunks.length;
855
1145
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
856
1146
  const fingerprints = inputs.map(
857
- (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT)
1147
+ (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT, {
1148
+ region: config.region,
1149
+ endpoint: config.endpoint,
1150
+ customHeaders: config.customHeaders,
1151
+ fingerprintSchemaVersion: config.fingerprintSchemaVersion
1152
+ })
858
1153
  );
859
1154
  const results = new Array(totalChunks);
860
1155
  const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
@@ -878,6 +1173,7 @@ async function synthesizeSsmlChunks(chunks, config) {
878
1173
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
879
1174
  const jobStartedAt = Date.now();
880
1175
  const jobDeadlineAt = config.timeouts?.totalJobMs !== void 0 && config.timeouts.totalJobMs > 0 ? jobStartedAt + config.timeouts.totalJobMs : void 0;
1176
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
881
1177
  const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
882
1178
  const report = (event) => config.onProgress?.(event);
883
1179
  for (const [index, input] of inputs.entries()) {
@@ -918,7 +1214,7 @@ async function synthesizeSsmlChunks(chunks, config) {
918
1214
  input.ssml,
919
1215
  {
920
1216
  ...config,
921
- signal: scope.signal,
1217
+ signal: deadline.signal,
922
1218
  ...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
923
1219
  ...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
924
1220
  ...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
@@ -963,7 +1259,7 @@ async function synthesizeSsmlChunks(chunks, config) {
963
1259
  status: wasCancelled ? "cancelled" : "failed",
964
1260
  isOriginalFailure: !wasCancelled,
965
1261
  canResume: true,
966
- error
1262
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
967
1263
  };
968
1264
  report({
969
1265
  currentChunk: completed,
@@ -983,13 +1279,19 @@ async function synthesizeSsmlChunks(chunks, config) {
983
1279
  try {
984
1280
  await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
985
1281
  if (firstError) throw firstError;
1282
+ const missingChunkIndices = Array.from(
1283
+ { length: totalChunks },
1284
+ (_value, index) => results[index] === void 0 ? index : void 0
1285
+ ).filter((index) => index !== void 0);
1286
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(totalChunks, missingChunkIndices);
986
1287
  const orderedResults = results.filter((result) => result !== void 0);
987
1288
  return await mergeSynthesisResults(orderedResults, {
988
1289
  format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
989
- signal: scope.signal,
1290
+ signal: deadline.signal,
990
1291
  customMerger: config.customMerger,
991
1292
  outputMimeType: config.outputMimeType,
992
- postMergeValidator: config.postMergeValidator
1293
+ postMergeValidator: config.postMergeValidator,
1294
+ deadline
993
1295
  });
994
1296
  } catch (error) {
995
1297
  if (firstError && config.cancelOnFailure !== false) {
@@ -1017,6 +1319,7 @@ async function synthesizeSsmlChunks(chunks, config) {
1017
1319
  throw error;
1018
1320
  } finally {
1019
1321
  scope.dispose();
1322
+ deadline.dispose();
1020
1323
  }
1021
1324
  }
1022
1325
  function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
@@ -1094,19 +1397,33 @@ function mergeSynthesisResults(results, options) {
1094
1397
  const format = resolvedOptions?.format;
1095
1398
  if (!format) throw new UnsupportedMergeFormatError("");
1096
1399
  const buffers = results.map((result) => result.audioData);
1097
- const inputSpecs = results.map((result) => result.audioSpec ?? inspectAudioSpecification(result.audioData, format));
1400
+ const inputSpecs = results.map((result) => {
1401
+ if (result.audioSpec) return result.audioSpec;
1402
+ try {
1403
+ return inspectAudioSpecification(result.audioData, format);
1404
+ } catch (error) {
1405
+ if (resolvedOptions.customMerger) return formatAudioSpecification(format);
1406
+ throw error;
1407
+ }
1408
+ });
1098
1409
  validateAudioSpecifications(inputSpecs);
1099
- const signal = resolvedOptions.signal ?? new AbortController().signal;
1410
+ const deadline = resolvedOptions.deadline;
1411
+ deadline?.throwIfExpired();
1412
+ const signal = resolvedOptions.signal ?? deadline?.signal ?? new AbortController().signal;
1100
1413
  if (signal.aborted) throw new SynthesisCancelledError();
1101
1414
  if (resolvedOptions.customMerger) {
1102
- return Promise.resolve().then(
1103
- () => resolvedOptions.customMerger?.(buffers, {
1104
- format,
1105
- outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
1106
- inputSpecs,
1107
- signal
1108
- })
1415
+ return withinDeadline(
1416
+ Promise.resolve().then(
1417
+ () => resolvedOptions.customMerger?.(buffers, {
1418
+ format,
1419
+ outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
1420
+ inputSpecs,
1421
+ signal
1422
+ })
1423
+ ),
1424
+ deadline
1109
1425
  ).then((merged) => {
1426
+ deadline?.throwIfExpired();
1110
1427
  if (!merged) throw new MergeError("The custom audio merger returned no audio buffer.");
1111
1428
  if (signal.aborted) throw new SynthesisCancelledError();
1112
1429
  const mergedSpec = validateMergedAudioBuffer(
@@ -1114,7 +1431,8 @@ function mergeSynthesisResults(results, options) {
1114
1431
  format,
1115
1432
  buffers,
1116
1433
  inputSpecs,
1117
- resolvedOptions.outputMimeType ?? resolveMimeType(format)
1434
+ resolvedOptions.outputMimeType ?? resolveMimeType(format),
1435
+ true
1118
1436
  );
1119
1437
  const result = createMergedResult(results, merged, format, mergedSpec, resolvedOptions.outputMimeType);
1120
1438
  return Promise.resolve(
@@ -1125,6 +1443,7 @@ function mergeSynthesisResults(results, options) {
1125
1443
  signal
1126
1444
  })
1127
1445
  ).then((valid) => {
1446
+ deadline?.throwIfExpired();
1128
1447
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1129
1448
  return result;
1130
1449
  });
@@ -1135,6 +1454,7 @@ function mergeSynthesisResults(results, options) {
1135
1454
  });
1136
1455
  }
1137
1456
  try {
1457
+ deadline?.throwIfExpired();
1138
1458
  const result = createMergedResult(
1139
1459
  results,
1140
1460
  mergeAudioBuffers(buffers, { format }),
@@ -1150,12 +1470,14 @@ function mergeSynthesisResults(results, options) {
1150
1470
  signal
1151
1471
  });
1152
1472
  if (validation instanceof Promise)
1153
- return validation.then((valid) => {
1473
+ return withinDeadline(validation, deadline).then((valid) => {
1474
+ deadline?.throwIfExpired();
1154
1475
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1155
1476
  return result;
1156
1477
  });
1157
1478
  if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1158
1479
  }
1480
+ deadline?.throwIfExpired();
1159
1481
  return result;
1160
1482
  } catch (error) {
1161
1483
  if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
@@ -1300,47 +1622,55 @@ function sharedValidationOptions(options, signal) {
1300
1622
  };
1301
1623
  }
1302
1624
  async function synthesizeSsmlSafe(client, ssml, options = {}) {
1303
- const validationOptions = sharedValidationOptions(options.validation ?? options, options.signal);
1625
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
1626
+ const validationOptions = sharedValidationOptions(options.validation ?? options, deadline.signal);
1304
1627
  const diagnostics = await Promise.resolve(validateAzureSsml2(ssml, validationOptions));
1305
- if (options.signal?.aborted) {
1306
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1628
+ if (deadline.signal.aborted) {
1629
+ const error = toSynthesisError(
1630
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
1631
+ );
1632
+ deadline.dispose();
1307
1633
  return failure(error);
1308
1634
  }
1309
1635
  const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
1310
1636
  if (errors.length > 0) {
1637
+ deadline.dispose();
1311
1638
  return failure({
1312
1639
  kind: "validation-error",
1313
1640
  message: "SSML validation failed; the Azure Speech API was not called.",
1314
1641
  diagnostics: errors
1315
1642
  });
1316
1643
  }
1317
- const jobScope = options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts.totalJobMs) : void 0;
1318
1644
  try {
1319
1645
  return {
1320
1646
  ok: true,
1321
1647
  success: true,
1322
1648
  status: "success",
1323
1649
  value: await client.synthesizeSsml(ssml, {
1324
- signal: jobScope?.signal ?? options.signal,
1650
+ signal: deadline.signal,
1325
1651
  timeoutMs: options.timeouts?.perChunkMs,
1326
- timeouts: options.timeouts
1652
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0
1327
1653
  })
1328
1654
  };
1329
1655
  } catch (error) {
1330
- if (jobScope?.timedOut()) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
1656
+ if (deadline.timedOut) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
1331
1657
  const synthesisError = toSynthesisError(error);
1332
1658
  return failure(synthesisError);
1333
1659
  } finally {
1334
- jobScope?.dispose();
1660
+ deadline.dispose();
1335
1661
  }
1336
1662
  }
1337
1663
  async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1664
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
1338
1665
  const validationOptions = sharedValidationOptions(
1339
1666
  { ...options.validation ?? options, timeouts: options.timeouts },
1340
- options.signal
1667
+ deadline.signal
1341
1668
  );
1342
- if (options.signal?.aborted) {
1343
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1669
+ if (deadline.signal.aborted) {
1670
+ const error = toSynthesisError(
1671
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
1672
+ );
1673
+ deadline.dispose();
1344
1674
  return failure(error);
1345
1675
  }
1346
1676
  const pending = (index, status, error) => {
@@ -1372,14 +1702,16 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1372
1702
  return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
1373
1703
  })
1374
1704
  );
1375
- if (options.signal?.aborted) {
1705
+ if (deadline.signal.aborted) {
1376
1706
  const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1707
+ deadline.dispose();
1377
1708
  return failure(error);
1378
1709
  }
1379
1710
  const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
1380
1711
  if (chunkDiagnostics.length > 0) {
1381
1712
  const error = new BatchChunkValidationError(chunkDiagnostics);
1382
1713
  for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
1714
+ deadline.dispose();
1383
1715
  return failure(error);
1384
1716
  }
1385
1717
  let fallbackJobScope;
@@ -1392,9 +1724,9 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1392
1724
  const value = await client.synthesizeChunks(normalizedChunks, {
1393
1725
  onProgress: options.onProgress,
1394
1726
  outputFormat: options.outputFormat,
1395
- signal: options.signal,
1727
+ signal: deadline.signal,
1396
1728
  timeoutMs: options.timeoutMs,
1397
- timeouts: options.timeouts,
1729
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0,
1398
1730
  sourceNodePath: options.sourceNodePath,
1399
1731
  concurrency: options.concurrency,
1400
1732
  retryOptions: options.retryOptions,
@@ -1404,12 +1736,19 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1404
1736
  customMerger: options.customMerger,
1405
1737
  outputMimeType: options.outputMimeType,
1406
1738
  postMergeValidator: options.postMergeValidator,
1407
- resumeValidation: options.resumeValidation
1739
+ resumeValidation: options.resumeValidation,
1740
+ customHeaders: options.customHeaders,
1741
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
1408
1742
  });
1409
1743
  return { ok: true, success: true, status: "success", value };
1410
1744
  }
1411
1745
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
1412
- const fingerprints = inputs.map((chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat));
1746
+ const fingerprints = inputs.map(
1747
+ (chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat, {
1748
+ customHeaders: options.customHeaders,
1749
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
1750
+ })
1751
+ );
1413
1752
  const results = new Array(chunks.length);
1414
1753
  const chunkStates = inputs.map((_chunk, chunkIndex) => ({
1415
1754
  chunkIndex,
@@ -1427,9 +1766,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1427
1766
  }
1428
1767
  const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
1429
1768
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
1430
- const jobStartedAt = Date.now();
1431
- const jobDeadlineAt = options.timeouts?.totalJobMs !== void 0 && options.timeouts.totalJobMs > 0 ? jobStartedAt + options.timeouts.totalJobMs : void 0;
1432
- const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
1769
+ const jobDeadlineAt = deadline.deadlineAtMs;
1770
+ const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(deadline.signal, void 0) : void 0;
1433
1771
  fallbackJobScope = jobScope;
1434
1772
  const failedIndices = /* @__PURE__ */ new Set();
1435
1773
  let firstError;
@@ -1453,8 +1791,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1453
1791
  const startedAt = Date.now();
1454
1792
  try {
1455
1793
  const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
1456
- const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
1457
- const chunkSignal = chunkScope?.signal ?? options.signal;
1794
+ const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? deadline.signal, chunkTimeout ?? options.timeoutMs) : void 0;
1795
+ const chunkSignal = chunkScope?.signal ?? deadline.signal;
1458
1796
  let result;
1459
1797
  try {
1460
1798
  result = await retryableSynthesis(
@@ -1481,7 +1819,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1481
1819
  jobDeadlineAt
1482
1820
  );
1483
1821
  } catch (error) {
1484
- if (chunkScope?.timedOut())
1822
+ if (chunkScope?.timedOut() || deadline.timedOut)
1485
1823
  throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
1486
1824
  throw error;
1487
1825
  } finally {
@@ -1542,15 +1880,15 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1542
1880
  durationMs: Date.now() - startedAt
1543
1881
  });
1544
1882
  } catch (error) {
1545
- const wasCancelled = firstError !== void 0 || Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
1546
- firstError ?? (firstError = error);
1883
+ const wasCancelled = firstError !== void 0 || !deadline.timedOut && Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
1884
+ firstError ?? (firstError = deadline.timedOut ? new Error("Speech synthesis timed out.") : error);
1547
1885
  if (!wasCancelled) failedIndices.add(index);
1548
1886
  chunkStates[index] = {
1549
1887
  chunkIndex: index,
1550
1888
  status: wasCancelled ? "cancelled" : "failed",
1551
1889
  isOriginalFailure: !wasCancelled,
1552
1890
  canResume: true,
1553
- error
1891
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
1554
1892
  };
1555
1893
  options.onProgress?.({
1556
1894
  currentChunk: completed,
@@ -1593,6 +1931,11 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1593
1931
  };
1594
1932
  throw error;
1595
1933
  }
1934
+ const missingChunkIndices = Array.from(
1935
+ { length: chunks.length },
1936
+ (_value, index) => results[index] === void 0 ? index : void 0
1937
+ ).filter((index) => index !== void 0);
1938
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(chunks.length, missingChunkIndices);
1596
1939
  const orderedResults = results.filter((result) => result !== void 0);
1597
1940
  return {
1598
1941
  ok: true,
@@ -1600,7 +1943,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1600
1943
  status: "success",
1601
1944
  value: await mergeSynthesisResults(orderedResults, {
1602
1945
  format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
1603
- signal: jobScope?.signal ?? options.signal,
1946
+ signal: jobScope?.signal ?? deadline.signal,
1604
1947
  customMerger: options.customMerger,
1605
1948
  outputMimeType: options.outputMimeType,
1606
1949
  postMergeValidator: options.postMergeValidator
@@ -1611,6 +1954,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1611
1954
  return failure(synthesisError, partialResultFrom(error));
1612
1955
  } finally {
1613
1956
  fallbackJobScope?.dispose();
1957
+ deadline.dispose();
1614
1958
  }
1615
1959
  }
1616
1960
  function withValidationSignal(options, signal) {
@@ -1631,7 +1975,16 @@ var AzureTtsClient = class {
1631
1975
  __privateSet(this, _options, options);
1632
1976
  }
1633
1977
  async synthesize(ssml) {
1634
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
1978
+ const {
1979
+ region,
1980
+ subscriptionKey,
1981
+ outputFormat,
1982
+ signal,
1983
+ timeoutMs,
1984
+ timeouts,
1985
+ customHeaders,
1986
+ fingerprintSchemaVersion
1987
+ } = __privateGet(this, _options);
1635
1988
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1636
1989
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
1637
1990
  const config = {
@@ -1642,12 +1995,23 @@ var AzureTtsClient = class {
1642
1995
  signal,
1643
1996
  timeoutMs,
1644
1997
  timeouts,
1645
- retryOptions: __privateGet(this, _options).retryOptions
1998
+ retryOptions: __privateGet(this, _options).retryOptions,
1999
+ customHeaders,
2000
+ fingerprintSchemaVersion
1646
2001
  };
1647
2002
  return synthesizeSpeech(ssml, config);
1648
2003
  }
1649
2004
  async synthesizeSsml(ssml, options = {}) {
1650
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
2005
+ const {
2006
+ region,
2007
+ subscriptionKey,
2008
+ outputFormat,
2009
+ signal,
2010
+ timeoutMs,
2011
+ timeouts,
2012
+ customHeaders,
2013
+ fingerprintSchemaVersion
2014
+ } = __privateGet(this, _options);
1651
2015
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1652
2016
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
1653
2017
  return synthesizeSsml(ssml, {
@@ -1666,11 +2030,22 @@ var AzureTtsClient = class {
1666
2030
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
1667
2031
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
1668
2032
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
1669
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
2033
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2034
+ customHeaders: options.customHeaders ?? customHeaders,
2035
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
1670
2036
  });
1671
2037
  }
1672
2038
  async synthesizeChunks(chunks, options = {}) {
1673
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
2039
+ const {
2040
+ region,
2041
+ subscriptionKey,
2042
+ outputFormat,
2043
+ signal,
2044
+ timeoutMs,
2045
+ timeouts,
2046
+ customHeaders,
2047
+ fingerprintSchemaVersion
2048
+ } = __privateGet(this, _options);
1674
2049
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1675
2050
  return synthesizeSsmlChunks(chunks, {
1676
2051
  endpoint,
@@ -1690,7 +2065,9 @@ var AzureTtsClient = class {
1690
2065
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
1691
2066
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
1692
2067
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
1693
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
2068
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2069
+ customHeaders: options.customHeaders ?? customHeaders,
2070
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
1694
2071
  });
1695
2072
  }
1696
2073
  async synthesizeSsmlSafe(ssml, options = {}) {
@@ -1705,7 +2082,14 @@ var AzureTtsClient = class {
1705
2082
  timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
1706
2083
  onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
1707
2084
  concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
1708
- retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
2085
+ retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
2086
+ cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
2087
+ customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
2088
+ outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
2089
+ postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
2090
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2091
+ customHeaders: options.customHeaders ?? __privateGet(this, _options).customHeaders,
2092
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? __privateGet(this, _options).fingerprintSchemaVersion
1709
2093
  });
1710
2094
  }
1711
2095
  async synthesizeSsmlChunksSafe(chunks, options = {}) {
@@ -1810,6 +2194,8 @@ export {
1810
2194
  BatchChunkValidationError,
1811
2195
  ChunkValidationError,
1812
2196
  DEFAULT_OUTPUT_FORMAT,
2197
+ DeadlineController,
2198
+ IncompleteChunkSetError,
1813
2199
  MergeError,
1814
2200
  SynthesisCancelledError,
1815
2201
  SynthesisTimeoutError,
@@ -1837,6 +2223,7 @@ export {
1837
2223
  parseSsml,
1838
2224
  resolveMergeAudioFormat,
1839
2225
  resolveMimeType,
2226
+ serializeChunkError,
1840
2227
  splitSsmlDocument,
1841
2228
  synthesizeSpeech,
1842
2229
  synthesizeSsml,