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

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
@@ -69,6 +69,15 @@ var SynthesisTimeoutError = class extends Error {
69
69
  this.name = "SynthesisTimeoutError";
70
70
  }
71
71
  };
72
+ var IncompleteChunkSetError = class extends Error {
73
+ constructor(totalChunks, missingChunkIndices) {
74
+ super(`Cannot merge an incomplete chunk set; missing chunk indices: ${missingChunkIndices.join(", ")}.`);
75
+ this.kind = "incomplete-chunk-set";
76
+ this.name = "IncompleteChunkSetError";
77
+ this.totalChunks = totalChunks;
78
+ this.missingChunkIndices = [...missingChunkIndices];
79
+ }
80
+ };
72
81
  var MergeError = class extends Error {
73
82
  constructor(message, cause) {
74
83
  super(message);
@@ -93,8 +102,32 @@ var UnsupportedMergeFormatError = class extends Error {
93
102
  this.format = format;
94
103
  }
95
104
  };
105
+ function serializeChunkError(error, phase, isOriginalFailure) {
106
+ const message = error instanceof Error ? error.message : String(error);
107
+ const status = error instanceof AzureTtsError ? error.status : void 0;
108
+ const kind = error && typeof error === "object" && "kind" in error ? String(error.kind) : "";
109
+ 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";
110
+ const details = {};
111
+ if (error instanceof AzureTtsError) {
112
+ details.statusText = error.statusText;
113
+ if (error.requestId) details.requestId = error.requestId;
114
+ }
115
+ if (error instanceof IncompleteChunkSetError) {
116
+ details.totalChunks = error.totalChunks;
117
+ details.missingChunkIndices = [...error.missingChunkIndices];
118
+ }
119
+ return {
120
+ code,
121
+ phase,
122
+ message,
123
+ isOriginalFailure,
124
+ 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)),
125
+ ...status !== void 0 && status > 0 ? { httpStatus: status } : {},
126
+ ...Object.keys(details).length > 0 ? { details } : {}
127
+ };
128
+ }
96
129
  function toSynthesisError(error) {
97
- if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError)
130
+ if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError || error instanceof IncompleteChunkSetError)
98
131
  return error;
99
132
  const message = error instanceof Error ? error.message : String(error);
100
133
  if (/cancel|abort/i.test(message)) return new SynthesisCancelledError(message);
@@ -106,6 +139,55 @@ function createSpeechSdkError(error) {
106
139
  return new AzureTtsSdkError(message);
107
140
  }
108
141
 
142
+ // src/deadline.ts
143
+ var _controller, _parent, _onParentAbort, _timer, _timedOut;
144
+ var DeadlineController = class {
145
+ constructor(totalJobMs, parent) {
146
+ __privateAdd(this, _controller, new AbortController());
147
+ __privateAdd(this, _parent);
148
+ __privateAdd(this, _onParentAbort);
149
+ __privateAdd(this, _timer);
150
+ __privateAdd(this, _timedOut, false);
151
+ __privateSet(this, _parent, parent);
152
+ __privateSet(this, _onParentAbort, () => __privateGet(this, _controller).abort());
153
+ this.deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
154
+ this.signal = this.deadlineAtMs === void 0 && parent ? parent : __privateGet(this, _controller).signal;
155
+ if (parent?.aborted) __privateGet(this, _controller).abort();
156
+ parent?.addEventListener("abort", __privateGet(this, _onParentAbort), { once: true });
157
+ if (this.deadlineAtMs !== void 0) {
158
+ __privateSet(this, _timer, setTimeout(
159
+ () => {
160
+ __privateSet(this, _timedOut, true);
161
+ __privateGet(this, _controller).abort();
162
+ },
163
+ Math.max(0, this.deadlineAtMs - Date.now())
164
+ ));
165
+ }
166
+ }
167
+ get timedOut() {
168
+ return __privateGet(this, _timedOut) || this.deadlineAtMs !== void 0 && this.remainingMs <= 0;
169
+ }
170
+ get remainingMs() {
171
+ return this.deadlineAtMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, this.deadlineAtMs - Date.now());
172
+ }
173
+ throwIfExpired() {
174
+ if (this.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
175
+ if (this.signal.aborted) throw new Error("Speech synthesis was cancelled.");
176
+ }
177
+ abort() {
178
+ __privateGet(this, _controller).abort();
179
+ }
180
+ dispose() {
181
+ if (__privateGet(this, _timer)) clearTimeout(__privateGet(this, _timer));
182
+ __privateGet(this, _parent)?.removeEventListener("abort", __privateGet(this, _onParentAbort));
183
+ }
184
+ };
185
+ _controller = new WeakMap();
186
+ _parent = new WeakMap();
187
+ _onParentAbort = new WeakMap();
188
+ _timer = new WeakMap();
189
+ _timedOut = new WeakMap();
190
+
109
191
  // src/synthesis.ts
110
192
  import * as SpeechSDK2 from "microsoft-cognitiveservices-speech-sdk";
111
193
  import { getSsmlSourceMap } from "@ssml-builder-js/ssml-core";
@@ -188,18 +270,23 @@ function createSpeechConfig(config) {
188
270
  }
189
271
 
190
272
  // src/synthesis.ts
191
- function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT) {
273
+ function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT, options = {}) {
192
274
  const readAttribute = (name) => {
193
275
  const pattern = new RegExp(`(?:${name})\\s*=\\s*[\\"']([^\\"']*)`, "gi");
194
276
  return [...ssml.matchAll(pattern)].map((match) => match[1] ?? "").join("|");
195
277
  };
278
+ const headers = Object.fromEntries(
279
+ Object.entries(options.customHeaders ?? {}).sort(([first], [second]) => first.localeCompare(second))
280
+ );
196
281
  const payload = JSON.stringify({
197
282
  ssml,
198
283
  outputFormat,
199
- voice: readAttribute("(?:name|voice)"),
200
- language: readAttribute("(?:xml:lang|lang)"),
201
- rate: readAttribute("rate"),
202
- pitch: readAttribute("pitch")
284
+ region: options.region ?? "",
285
+ endpoint: options.endpoint ?? "",
286
+ voice: options.voice ?? readAttribute("(?:name|voice)"),
287
+ lang: options.lang ?? readAttribute("(?:xml:lang|lang)"),
288
+ customHeaders: headers,
289
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? options.schemaVersion ?? "2"
203
290
  });
204
291
  let hash = 0xcbf29ce484222325n;
205
292
  const mask = 0xffffffffffffffffn;
@@ -319,6 +406,129 @@ function parseMp3Specification(buffer, format) {
319
406
  }
320
407
  return void 0;
321
408
  }
409
+ function readEbmlVint(bytes, offset, preserveMarker) {
410
+ const first = bytes[offset];
411
+ if (first === void 0) throw new Error("Invalid EBML variable-length integer.");
412
+ let mask = 128;
413
+ let length = 1;
414
+ while (length <= 8 && (first & mask) === 0) {
415
+ mask >>= 1;
416
+ length += 1;
417
+ }
418
+ if (length > 8 || offset + length > bytes.byteLength) throw new Error("Truncated EBML variable-length integer.");
419
+ let value = preserveMarker ? first : first & mask - 1;
420
+ for (let index = 1; index < length; index += 1) value = value * 256 + (bytes[offset + index] ?? 0);
421
+ if (!preserveMarker && value === 2 ** (7 * length) - 1)
422
+ throw new Error("EBML unknown-size elements are not supported.");
423
+ return { value, length };
424
+ }
425
+ function readEbmlElement(bytes, offset) {
426
+ const id = readEbmlVint(bytes, offset, true);
427
+ const size = readEbmlVint(bytes, offset + id.length, false);
428
+ const dataStart = offset + id.length + size.length;
429
+ const dataEnd = dataStart + size.value;
430
+ if (dataEnd > bytes.byteLength) throw new Error("EBML element exceeds the audio buffer.");
431
+ return { id: id.value, dataStart, dataEnd };
432
+ }
433
+ function ebmlText(bytes, element) {
434
+ return new TextDecoder().decode(bytes.slice(element.dataStart, element.dataEnd));
435
+ }
436
+ function findEbmlElement(bytes, start, end, id) {
437
+ let offset = start;
438
+ while (offset < end) {
439
+ const element = readEbmlElement(bytes, offset);
440
+ if (element.id === id) return element;
441
+ offset = element.dataEnd;
442
+ }
443
+ if (offset !== end) throw new Error("Invalid EBML element boundary.");
444
+ return void 0;
445
+ }
446
+ function parseOggSpecification(buffer, format) {
447
+ const bytes = new Uint8Array(buffer);
448
+ let offset = 0;
449
+ let firstPayload;
450
+ let pages = 0;
451
+ while (offset < bytes.byteLength) {
452
+ if (offset + 27 > bytes.byteLength || !ascii(bytes, offset, "OggS")) throw new Error("Invalid Ogg page header.");
453
+ if (bytes[offset + 4] !== 0) throw new Error("Unsupported Ogg bitstream version.");
454
+ const segmentCount = bytes[offset + 26] ?? 0;
455
+ const lacingStart = offset + 27;
456
+ const payloadStart = lacingStart + segmentCount;
457
+ if (payloadStart > bytes.byteLength) throw new Error("Truncated Ogg segment table.");
458
+ const payloadLength = bytes.slice(lacingStart, payloadStart).reduce((total, value) => total + value, 0);
459
+ const pageEnd = payloadStart + payloadLength;
460
+ if (pageEnd > bytes.byteLength) throw new Error("Ogg page payload exceeds the audio buffer.");
461
+ if (pages === 0) firstPayload = bytes.slice(payloadStart, pageEnd);
462
+ offset = pageEnd;
463
+ pages += 1;
464
+ }
465
+ if (pages === 0 || !firstPayload || !ascii(firstPayload, 0, "OpusHead") || firstPayload.byteLength < 19)
466
+ throw new Error("Ogg audio must contain a valid OpusHead packet.");
467
+ const version = firstPayload[8];
468
+ const channels = firstPayload[9] ?? 0;
469
+ const sampleRate = new DataView(firstPayload.buffer, firstPayload.byteOffset, firstPayload.byteLength).getUint32(
470
+ 12,
471
+ true
472
+ );
473
+ if (version !== 1 || channels <= 0 || sampleRate <= 0) throw new Error("Invalid Ogg OpusHead stream parameters.");
474
+ return {
475
+ format,
476
+ mimeType: "audio/ogg",
477
+ codec: "opus",
478
+ sampleRate,
479
+ channels,
480
+ container: "ogg",
481
+ isVbr: true,
482
+ isCompressed: true
483
+ };
484
+ }
485
+ function parseWebmSpecification(buffer, format) {
486
+ const bytes = new Uint8Array(buffer);
487
+ const ebml = readEbmlElement(bytes, 0);
488
+ if (ebml.id !== 440786851) throw new Error("WebM audio must begin with an EBML header.");
489
+ const docType = findEbmlElement(bytes, ebml.dataStart, ebml.dataEnd, 17026);
490
+ if (!docType || ebmlText(bytes, docType).toLowerCase() !== "webm") throw new Error("EBML DocType must be webm.");
491
+ const segment = readEbmlElement(bytes, ebml.dataEnd);
492
+ if (segment.id !== 408125543) throw new Error("WebM audio must contain a Segment element.");
493
+ const tracks = findEbmlElement(bytes, segment.dataStart, segment.dataEnd, 374648427);
494
+ if (!tracks) throw new Error("WebM audio must contain a Tracks element.");
495
+ let offset = tracks.dataStart;
496
+ let opusTrack;
497
+ while (offset < tracks.dataEnd) {
498
+ const track = readEbmlElement(bytes, offset);
499
+ if (track.id === 174) {
500
+ const codec = findEbmlElement(bytes, track.dataStart, track.dataEnd, 134);
501
+ const trackType = findEbmlElement(bytes, track.dataStart, track.dataEnd, 131);
502
+ if (codec && ebmlText(bytes, codec) === "A_OPUS" && trackType && bytes[trackType.dataStart] === 2) {
503
+ opusTrack = track;
504
+ break;
505
+ }
506
+ }
507
+ offset = track.dataEnd;
508
+ }
509
+ if (!opusTrack) throw new Error("WebM tracks do not define an Opus audio track.");
510
+ const audio = findEbmlElement(bytes, opusTrack.dataStart, opusTrack.dataEnd, 225);
511
+ const sampling = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 181) : void 0;
512
+ const channels = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 159) : void 0;
513
+ const sampleRate = sampling ? new DataView(
514
+ bytes.buffer,
515
+ bytes.byteOffset + sampling.dataStart,
516
+ sampling.dataEnd - sampling.dataStart
517
+ ).getFloat64(0, false) : 0;
518
+ const channelCount = channels ? bytes[channels.dataEnd - 1] ?? 0 : 0;
519
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0 || channelCount <= 0)
520
+ throw new Error("WebM Opus audio track has invalid sampling or channel parameters.");
521
+ return {
522
+ format,
523
+ mimeType: "audio/webm",
524
+ codec: "opus",
525
+ sampleRate: Math.round(sampleRate),
526
+ channels: channelCount,
527
+ container: "webm",
528
+ isVbr: true,
529
+ isCompressed: true
530
+ };
531
+ }
322
532
  function inspectAudioSpecification(buffer, format) {
323
533
  if (isWavFormat(format) || ascii(new Uint8Array(buffer), 0, "RIFF")) {
324
534
  const parsed = parseWav(buffer);
@@ -343,7 +553,20 @@ function inspectAudioSpecification(buffer, format) {
343
553
  isCompressed: codec !== "pcm" && codec !== "unknown"
344
554
  };
345
555
  }
346
- if (isMp3Format(format)) return parseMp3Specification(buffer, format) ?? formatAudioSpecification(format);
556
+ if (isMp3Format(format)) {
557
+ const specification2 = parseMp3Specification(buffer, format);
558
+ return specification2 ?? formatAudioSpecification(format);
559
+ }
560
+ if (isOggFormat(format) || ascii(new Uint8Array(buffer), 0, "OggS")) {
561
+ const specification2 = parseOggSpecification(buffer, format);
562
+ validateContainerFormat(specification2, format);
563
+ return specification2;
564
+ }
565
+ if (isWebmFormat(format) || new Uint8Array(buffer)[0] === 26) {
566
+ const specification2 = parseWebmSpecification(buffer, format);
567
+ validateContainerFormat(specification2, format);
568
+ return specification2;
569
+ }
347
570
  const specification = formatAudioSpecification(format);
348
571
  if (specification.container === "raw") validateRawAudioBuffer(buffer, specification);
349
572
  return specification;
@@ -352,7 +575,20 @@ function validateRawAudioBuffer(buffer, specification) {
352
575
  if (specification.sampleRate <= 0 || specification.channels <= 0 || specification.bitDepth === void 0) {
353
576
  throw new Error(`RAW audio format "${specification.format}" does not define a complete audio specification.`);
354
577
  }
355
- if (specification.codec === "siren" || specification.codec === "silk") return;
578
+ if (specification.codec === "siren") return;
579
+ if (specification.codec === "silk") {
580
+ if (buffer.byteLength <= 9 || !ascii(new Uint8Array(buffer), 0, "#!SILK_V3"))
581
+ throw new Error("RAW SILK audio must contain a valid #!SILK_V3 payload header.");
582
+ return;
583
+ }
584
+ if (specification.codec === "opus" && buffer.byteLength === 0) throw new Error("RAW Opus audio cannot be empty.");
585
+ if (specification.codec === "opus") {
586
+ const packetCode = new Uint8Array(buffer)[0] ?? 0;
587
+ const frameCountCode = packetCode & 3;
588
+ if (packetCode >> 3 > 31 || buffer.byteLength < (frameCountCode === 3 ? 2 : 2) || frameCountCode === 3 && ((new Uint8Array(buffer)[1] ?? 0) & 63) === 0)
589
+ throw new Error("RAW Opus audio has an invalid packet framing header.");
590
+ return;
591
+ }
356
592
  const bytesPerFrame = specification.channels * Math.ceil(specification.bitDepth / 8);
357
593
  if (bytesPerFrame <= 0 || buffer.byteLength % bytesPerFrame !== 0) {
358
594
  throw new Error(
@@ -360,6 +596,15 @@ function validateRawAudioBuffer(buffer, specification) {
360
596
  );
361
597
  }
362
598
  }
599
+ function validateContainerFormat(specification, format) {
600
+ const expected = formatAudioSpecification(format);
601
+ if (expected.sampleRate > 0 && specification.sampleRate !== expected.sampleRate || expected.channels > 0 && specification.channels !== expected.channels || expected.codec !== "unknown" && specification.codec !== expected.codec) {
602
+ throw new AudioFormatMismatchError(`Audio container does not match the requested format "${format}".`, [
603
+ expected,
604
+ specification
605
+ ]);
606
+ }
607
+ }
363
608
  function validateAudioSpecifications(specs) {
364
609
  const first = specs[0];
365
610
  if (!first) return;
@@ -441,17 +686,29 @@ function stripMp3Tags(buffer) {
441
686
  function isMp3Format(format) {
442
687
  return /(?:mp3|mpeg)/i.test(format);
443
688
  }
689
+ function isOggFormat(format) {
690
+ return /ogg/i.test(format);
691
+ }
692
+ function isWebmFormat(format) {
693
+ return /webm/i.test(format);
694
+ }
444
695
  function isWavFormat(format) {
445
696
  return /(?:wav|wave|riff)/i.test(format);
446
697
  }
447
698
  function isRawFormat(format) {
448
699
  return /^raw(?:-|$)/i.test(format);
449
700
  }
450
- function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType) {
701
+ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType, allowExternalContainer) {
451
702
  if (!(merged instanceof ArrayBuffer) || merged.byteLength === 0) {
452
703
  throw new MergeError("The custom audio merger returned an empty or invalid audio buffer.");
453
704
  }
454
- const specification = inspectAudioSpecification(merged, format);
705
+ let specification;
706
+ try {
707
+ specification = inspectAudioSpecification(merged, format);
708
+ } catch (error) {
709
+ if (!allowExternalContainer) throw error;
710
+ specification = inputSpecs[0] ?? formatAudioSpecification(format);
711
+ }
455
712
  if (!outputMimeType.trim()) throw new MergeError("The custom audio merger output MIME type cannot be empty.");
456
713
  const firstInput = inputSpecs[0];
457
714
  if (firstInput && (specification.sampleRate !== firstInput.sampleRate || specification.channels !== firstInput.channels || specification.codec !== firstInput.codec || firstInput.bitDepth !== void 0 && specification.bitDepth !== firstInput.bitDepth)) {
@@ -471,6 +728,25 @@ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMi
471
728
  }
472
729
  return specification;
473
730
  }
731
+ async function withinDeadline(value, deadline) {
732
+ if (!deadline) return value;
733
+ deadline.throwIfExpired();
734
+ if (!Number.isFinite(deadline.remainingMs)) return value;
735
+ let timer;
736
+ try {
737
+ return await Promise.race([
738
+ Promise.resolve(value),
739
+ new Promise((_resolve, reject) => {
740
+ timer = setTimeout(
741
+ () => reject(new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.")),
742
+ deadline.remainingMs
743
+ );
744
+ })
745
+ ]);
746
+ } finally {
747
+ if (timer) clearTimeout(timer);
748
+ }
749
+ }
474
750
  function resolveMergeAudioFormat(format) {
475
751
  if (isWavFormat(format)) return "wav";
476
752
  if (isMp3Format(format)) return "mp3";
@@ -483,6 +759,7 @@ function canMergeAudioFormat(format) {
483
759
  function mergeAudioBuffers(buffers, options) {
484
760
  const format = typeof options === "string" ? options : options?.format;
485
761
  if (!format) throw new UnsupportedMergeFormatError("");
762
+ if (!canMergeAudioFormat(format)) throw new UnsupportedMergeFormatError(format);
486
763
  try {
487
764
  validateAudioSpecifications(buffers.map((buffer) => inspectAudioSpecification(buffer, format)));
488
765
  if (isWavFormat(format)) return mergeWavBuffers(buffers);
@@ -791,10 +1068,23 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry, deadline
791
1068
  }
792
1069
  }
793
1070
  async function synthesizeSsml(ssml, config) {
794
- const totalJobMs = config.timeouts?.totalJobMs;
795
- const deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
796
- if (!config.retryOptions) return synthesizeSsmlOnce(ssml, config);
797
- return synthesizeWithRetry(ssml, config, config.retryOptions, () => void 0, deadlineAtMs);
1071
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
1072
+ try {
1073
+ deadline.throwIfExpired();
1074
+ const synthesisConfig = {
1075
+ ...config,
1076
+ signal: deadline.signal,
1077
+ timeouts: config.timeouts ? { ...config.timeouts, totalJobMs: void 0 } : void 0
1078
+ };
1079
+ const result = config.retryOptions ? await synthesizeWithRetry(ssml, synthesisConfig, config.retryOptions, () => void 0, deadline.deadlineAtMs) : await synthesizeSsmlOnce(ssml, synthesisConfig);
1080
+ deadline.throwIfExpired();
1081
+ return result;
1082
+ } catch (error) {
1083
+ if (deadline.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
1084
+ throw error;
1085
+ } finally {
1086
+ deadline.dispose();
1087
+ }
798
1088
  }
799
1089
  function createAbortScope(parent, timeoutMs) {
800
1090
  const controller = new AbortController();
@@ -831,7 +1121,12 @@ async function synthesizeSsmlChunks(chunks, config) {
831
1121
  const totalChunks = chunks.length;
832
1122
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
833
1123
  const fingerprints = inputs.map(
834
- (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT)
1124
+ (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT, {
1125
+ region: config.region,
1126
+ endpoint: config.endpoint,
1127
+ customHeaders: config.customHeaders,
1128
+ fingerprintSchemaVersion: config.fingerprintSchemaVersion
1129
+ })
835
1130
  );
836
1131
  const results = new Array(totalChunks);
837
1132
  const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
@@ -855,6 +1150,7 @@ async function synthesizeSsmlChunks(chunks, config) {
855
1150
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
856
1151
  const jobStartedAt = Date.now();
857
1152
  const jobDeadlineAt = config.timeouts?.totalJobMs !== void 0 && config.timeouts.totalJobMs > 0 ? jobStartedAt + config.timeouts.totalJobMs : void 0;
1153
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
858
1154
  const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
859
1155
  const report = (event) => config.onProgress?.(event);
860
1156
  for (const [index, input] of inputs.entries()) {
@@ -895,7 +1191,7 @@ async function synthesizeSsmlChunks(chunks, config) {
895
1191
  input.ssml,
896
1192
  {
897
1193
  ...config,
898
- signal: scope.signal,
1194
+ signal: deadline.signal,
899
1195
  ...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
900
1196
  ...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
901
1197
  ...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
@@ -940,7 +1236,7 @@ async function synthesizeSsmlChunks(chunks, config) {
940
1236
  status: wasCancelled ? "cancelled" : "failed",
941
1237
  isOriginalFailure: !wasCancelled,
942
1238
  canResume: true,
943
- error
1239
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
944
1240
  };
945
1241
  report({
946
1242
  currentChunk: completed,
@@ -960,13 +1256,19 @@ async function synthesizeSsmlChunks(chunks, config) {
960
1256
  try {
961
1257
  await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
962
1258
  if (firstError) throw firstError;
1259
+ const missingChunkIndices = Array.from(
1260
+ { length: totalChunks },
1261
+ (_value, index) => results[index] === void 0 ? index : void 0
1262
+ ).filter((index) => index !== void 0);
1263
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(totalChunks, missingChunkIndices);
963
1264
  const orderedResults = results.filter((result) => result !== void 0);
964
1265
  return await mergeSynthesisResults(orderedResults, {
965
1266
  format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
966
- signal: scope.signal,
1267
+ signal: deadline.signal,
967
1268
  customMerger: config.customMerger,
968
1269
  outputMimeType: config.outputMimeType,
969
- postMergeValidator: config.postMergeValidator
1270
+ postMergeValidator: config.postMergeValidator,
1271
+ deadline
970
1272
  });
971
1273
  } catch (error) {
972
1274
  if (firstError && config.cancelOnFailure !== false) {
@@ -994,6 +1296,7 @@ async function synthesizeSsmlChunks(chunks, config) {
994
1296
  throw error;
995
1297
  } finally {
996
1298
  scope.dispose();
1299
+ deadline.dispose();
997
1300
  }
998
1301
  }
999
1302
  function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
@@ -1071,19 +1374,33 @@ function mergeSynthesisResults(results, options) {
1071
1374
  const format = resolvedOptions?.format;
1072
1375
  if (!format) throw new UnsupportedMergeFormatError("");
1073
1376
  const buffers = results.map((result) => result.audioData);
1074
- const inputSpecs = results.map((result) => result.audioSpec ?? inspectAudioSpecification(result.audioData, format));
1377
+ const inputSpecs = results.map((result) => {
1378
+ if (result.audioSpec) return result.audioSpec;
1379
+ try {
1380
+ return inspectAudioSpecification(result.audioData, format);
1381
+ } catch (error) {
1382
+ if (resolvedOptions.customMerger) return formatAudioSpecification(format);
1383
+ throw error;
1384
+ }
1385
+ });
1075
1386
  validateAudioSpecifications(inputSpecs);
1076
- const signal = resolvedOptions.signal ?? new AbortController().signal;
1387
+ const deadline = resolvedOptions.deadline;
1388
+ deadline?.throwIfExpired();
1389
+ const signal = resolvedOptions.signal ?? deadline?.signal ?? new AbortController().signal;
1077
1390
  if (signal.aborted) throw new SynthesisCancelledError();
1078
1391
  if (resolvedOptions.customMerger) {
1079
- return Promise.resolve().then(
1080
- () => resolvedOptions.customMerger?.(buffers, {
1081
- format,
1082
- outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
1083
- inputSpecs,
1084
- signal
1085
- })
1392
+ return withinDeadline(
1393
+ Promise.resolve().then(
1394
+ () => resolvedOptions.customMerger?.(buffers, {
1395
+ format,
1396
+ outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
1397
+ inputSpecs,
1398
+ signal
1399
+ })
1400
+ ),
1401
+ deadline
1086
1402
  ).then((merged) => {
1403
+ deadline?.throwIfExpired();
1087
1404
  if (!merged) throw new MergeError("The custom audio merger returned no audio buffer.");
1088
1405
  if (signal.aborted) throw new SynthesisCancelledError();
1089
1406
  const mergedSpec = validateMergedAudioBuffer(
@@ -1091,7 +1408,8 @@ function mergeSynthesisResults(results, options) {
1091
1408
  format,
1092
1409
  buffers,
1093
1410
  inputSpecs,
1094
- resolvedOptions.outputMimeType ?? resolveMimeType(format)
1411
+ resolvedOptions.outputMimeType ?? resolveMimeType(format),
1412
+ true
1095
1413
  );
1096
1414
  const result = createMergedResult(results, merged, format, mergedSpec, resolvedOptions.outputMimeType);
1097
1415
  return Promise.resolve(
@@ -1102,6 +1420,7 @@ function mergeSynthesisResults(results, options) {
1102
1420
  signal
1103
1421
  })
1104
1422
  ).then((valid) => {
1423
+ deadline?.throwIfExpired();
1105
1424
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1106
1425
  return result;
1107
1426
  });
@@ -1112,6 +1431,7 @@ function mergeSynthesisResults(results, options) {
1112
1431
  });
1113
1432
  }
1114
1433
  try {
1434
+ deadline?.throwIfExpired();
1115
1435
  const result = createMergedResult(
1116
1436
  results,
1117
1437
  mergeAudioBuffers(buffers, { format }),
@@ -1127,12 +1447,14 @@ function mergeSynthesisResults(results, options) {
1127
1447
  signal
1128
1448
  });
1129
1449
  if (validation instanceof Promise)
1130
- return validation.then((valid) => {
1450
+ return withinDeadline(validation, deadline).then((valid) => {
1451
+ deadline?.throwIfExpired();
1131
1452
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1132
1453
  return result;
1133
1454
  });
1134
1455
  if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
1135
1456
  }
1457
+ deadline?.throwIfExpired();
1136
1458
  return result;
1137
1459
  } catch (error) {
1138
1460
  if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
@@ -1281,47 +1603,55 @@ function sharedValidationOptions(options, signal) {
1281
1603
  };
1282
1604
  }
1283
1605
  async function synthesizeSsmlSafe(client, ssml, options = {}) {
1284
- const validationOptions = sharedValidationOptions(options.validation ?? options, options.signal);
1606
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
1607
+ const validationOptions = sharedValidationOptions(options.validation ?? options, deadline.signal);
1285
1608
  const diagnostics = await Promise.resolve(validateAzureSsml(ssml, validationOptions));
1286
- if (options.signal?.aborted) {
1287
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1609
+ if (deadline.signal.aborted) {
1610
+ const error = toSynthesisError(
1611
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
1612
+ );
1613
+ deadline.dispose();
1288
1614
  return failure(error);
1289
1615
  }
1290
1616
  const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
1291
1617
  if (errors.length > 0) {
1618
+ deadline.dispose();
1292
1619
  return failure({
1293
1620
  kind: "validation-error",
1294
1621
  message: "SSML validation failed; the Azure Speech API was not called.",
1295
1622
  diagnostics: errors
1296
1623
  });
1297
1624
  }
1298
- const jobScope = options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts.totalJobMs) : void 0;
1299
1625
  try {
1300
1626
  return {
1301
1627
  ok: true,
1302
1628
  success: true,
1303
1629
  status: "success",
1304
1630
  value: await client.synthesizeSsml(ssml, {
1305
- signal: jobScope?.signal ?? options.signal,
1631
+ signal: deadline.signal,
1306
1632
  timeoutMs: options.timeouts?.perChunkMs,
1307
- timeouts: options.timeouts
1633
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0
1308
1634
  })
1309
1635
  };
1310
1636
  } catch (error) {
1311
- if (jobScope?.timedOut()) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
1637
+ if (deadline.timedOut) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
1312
1638
  const synthesisError = toSynthesisError(error);
1313
1639
  return failure(synthesisError);
1314
1640
  } finally {
1315
- jobScope?.dispose();
1641
+ deadline.dispose();
1316
1642
  }
1317
1643
  }
1318
1644
  async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1645
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
1319
1646
  const validationOptions = sharedValidationOptions(
1320
1647
  { ...options.validation ?? options, timeouts: options.timeouts },
1321
- options.signal
1648
+ deadline.signal
1322
1649
  );
1323
- if (options.signal?.aborted) {
1324
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1650
+ if (deadline.signal.aborted) {
1651
+ const error = toSynthesisError(
1652
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
1653
+ );
1654
+ deadline.dispose();
1325
1655
  return failure(error);
1326
1656
  }
1327
1657
  const pending = (index, status, error) => {
@@ -1353,14 +1683,16 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1353
1683
  return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
1354
1684
  })
1355
1685
  );
1356
- if (options.signal?.aborted) {
1686
+ if (deadline.signal.aborted) {
1357
1687
  const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
1688
+ deadline.dispose();
1358
1689
  return failure(error);
1359
1690
  }
1360
1691
  const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
1361
1692
  if (chunkDiagnostics.length > 0) {
1362
1693
  const error = new BatchChunkValidationError(chunkDiagnostics);
1363
1694
  for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
1695
+ deadline.dispose();
1364
1696
  return failure(error);
1365
1697
  }
1366
1698
  let fallbackJobScope;
@@ -1373,9 +1705,9 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1373
1705
  const value = await client.synthesizeChunks(normalizedChunks, {
1374
1706
  onProgress: options.onProgress,
1375
1707
  outputFormat: options.outputFormat,
1376
- signal: options.signal,
1708
+ signal: deadline.signal,
1377
1709
  timeoutMs: options.timeoutMs,
1378
- timeouts: options.timeouts,
1710
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0,
1379
1711
  sourceNodePath: options.sourceNodePath,
1380
1712
  concurrency: options.concurrency,
1381
1713
  retryOptions: options.retryOptions,
@@ -1385,12 +1717,19 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1385
1717
  customMerger: options.customMerger,
1386
1718
  outputMimeType: options.outputMimeType,
1387
1719
  postMergeValidator: options.postMergeValidator,
1388
- resumeValidation: options.resumeValidation
1720
+ resumeValidation: options.resumeValidation,
1721
+ customHeaders: options.customHeaders,
1722
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
1389
1723
  });
1390
1724
  return { ok: true, success: true, status: "success", value };
1391
1725
  }
1392
1726
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
1393
- const fingerprints = inputs.map((chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat));
1727
+ const fingerprints = inputs.map(
1728
+ (chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat, {
1729
+ customHeaders: options.customHeaders,
1730
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
1731
+ })
1732
+ );
1394
1733
  const results = new Array(chunks.length);
1395
1734
  const chunkStates = inputs.map((_chunk, chunkIndex) => ({
1396
1735
  chunkIndex,
@@ -1408,9 +1747,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1408
1747
  }
1409
1748
  const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
1410
1749
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
1411
- const jobStartedAt = Date.now();
1412
- const jobDeadlineAt = options.timeouts?.totalJobMs !== void 0 && options.timeouts.totalJobMs > 0 ? jobStartedAt + options.timeouts.totalJobMs : void 0;
1413
- const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
1750
+ const jobDeadlineAt = deadline.deadlineAtMs;
1751
+ const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(deadline.signal, void 0) : void 0;
1414
1752
  fallbackJobScope = jobScope;
1415
1753
  const failedIndices = /* @__PURE__ */ new Set();
1416
1754
  let firstError;
@@ -1434,8 +1772,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1434
1772
  const startedAt = Date.now();
1435
1773
  try {
1436
1774
  const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
1437
- const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
1438
- const chunkSignal = chunkScope?.signal ?? options.signal;
1775
+ const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? deadline.signal, chunkTimeout ?? options.timeoutMs) : void 0;
1776
+ const chunkSignal = chunkScope?.signal ?? deadline.signal;
1439
1777
  let result;
1440
1778
  try {
1441
1779
  result = await retryableSynthesis(
@@ -1462,7 +1800,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1462
1800
  jobDeadlineAt
1463
1801
  );
1464
1802
  } catch (error) {
1465
- if (chunkScope?.timedOut())
1803
+ if (chunkScope?.timedOut() || deadline.timedOut)
1466
1804
  throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
1467
1805
  throw error;
1468
1806
  } finally {
@@ -1523,15 +1861,15 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1523
1861
  durationMs: Date.now() - startedAt
1524
1862
  });
1525
1863
  } catch (error) {
1526
- const wasCancelled = firstError !== void 0 || Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
1527
- firstError ?? (firstError = error);
1864
+ const wasCancelled = firstError !== void 0 || !deadline.timedOut && Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
1865
+ firstError ?? (firstError = deadline.timedOut ? new Error("Speech synthesis timed out.") : error);
1528
1866
  if (!wasCancelled) failedIndices.add(index);
1529
1867
  chunkStates[index] = {
1530
1868
  chunkIndex: index,
1531
1869
  status: wasCancelled ? "cancelled" : "failed",
1532
1870
  isOriginalFailure: !wasCancelled,
1533
1871
  canResume: true,
1534
- error
1872
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
1535
1873
  };
1536
1874
  options.onProgress?.({
1537
1875
  currentChunk: completed,
@@ -1574,6 +1912,11 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1574
1912
  };
1575
1913
  throw error;
1576
1914
  }
1915
+ const missingChunkIndices = Array.from(
1916
+ { length: chunks.length },
1917
+ (_value, index) => results[index] === void 0 ? index : void 0
1918
+ ).filter((index) => index !== void 0);
1919
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(chunks.length, missingChunkIndices);
1577
1920
  const orderedResults = results.filter((result) => result !== void 0);
1578
1921
  return {
1579
1922
  ok: true,
@@ -1581,7 +1924,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1581
1924
  status: "success",
1582
1925
  value: await mergeSynthesisResults(orderedResults, {
1583
1926
  format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
1584
- signal: jobScope?.signal ?? options.signal,
1927
+ signal: jobScope?.signal ?? deadline.signal,
1585
1928
  customMerger: options.customMerger,
1586
1929
  outputMimeType: options.outputMimeType,
1587
1930
  postMergeValidator: options.postMergeValidator
@@ -1592,6 +1935,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
1592
1935
  return failure(synthesisError, partialResultFrom(error));
1593
1936
  } finally {
1594
1937
  fallbackJobScope?.dispose();
1938
+ deadline.dispose();
1595
1939
  }
1596
1940
  }
1597
1941
  function withValidationSignal(options, signal) {
@@ -1612,7 +1956,16 @@ var AzureTtsClient = class {
1612
1956
  __privateSet(this, _options, options);
1613
1957
  }
1614
1958
  async synthesize(ssml) {
1615
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
1959
+ const {
1960
+ region,
1961
+ subscriptionKey,
1962
+ outputFormat,
1963
+ signal,
1964
+ timeoutMs,
1965
+ timeouts,
1966
+ customHeaders,
1967
+ fingerprintSchemaVersion
1968
+ } = __privateGet(this, _options);
1616
1969
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1617
1970
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
1618
1971
  const config = {
@@ -1623,12 +1976,23 @@ var AzureTtsClient = class {
1623
1976
  signal,
1624
1977
  timeoutMs,
1625
1978
  timeouts,
1626
- retryOptions: __privateGet(this, _options).retryOptions
1979
+ retryOptions: __privateGet(this, _options).retryOptions,
1980
+ customHeaders,
1981
+ fingerprintSchemaVersion
1627
1982
  };
1628
1983
  return synthesizeSpeech(ssml, config);
1629
1984
  }
1630
1985
  async synthesizeSsml(ssml, options = {}) {
1631
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
1986
+ const {
1987
+ region,
1988
+ subscriptionKey,
1989
+ outputFormat,
1990
+ signal,
1991
+ timeoutMs,
1992
+ timeouts,
1993
+ customHeaders,
1994
+ fingerprintSchemaVersion
1995
+ } = __privateGet(this, _options);
1632
1996
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1633
1997
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
1634
1998
  return synthesizeSsml(ssml, {
@@ -1647,11 +2011,22 @@ var AzureTtsClient = class {
1647
2011
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
1648
2012
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
1649
2013
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
1650
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
2014
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2015
+ customHeaders: options.customHeaders ?? customHeaders,
2016
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
1651
2017
  });
1652
2018
  }
1653
2019
  async synthesizeChunks(chunks, options = {}) {
1654
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
2020
+ const {
2021
+ region,
2022
+ subscriptionKey,
2023
+ outputFormat,
2024
+ signal,
2025
+ timeoutMs,
2026
+ timeouts,
2027
+ customHeaders,
2028
+ fingerprintSchemaVersion
2029
+ } = __privateGet(this, _options);
1655
2030
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
1656
2031
  return synthesizeSsmlChunks(chunks, {
1657
2032
  endpoint,
@@ -1671,7 +2046,9 @@ var AzureTtsClient = class {
1671
2046
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
1672
2047
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
1673
2048
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
1674
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
2049
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2050
+ customHeaders: options.customHeaders ?? customHeaders,
2051
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
1675
2052
  });
1676
2053
  }
1677
2054
  async synthesizeSsmlSafe(ssml, options = {}) {
@@ -1686,7 +2063,14 @@ var AzureTtsClient = class {
1686
2063
  timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
1687
2064
  onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
1688
2065
  concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
1689
- retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
2066
+ retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
2067
+ cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
2068
+ customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
2069
+ outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
2070
+ postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
2071
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
2072
+ customHeaders: options.customHeaders ?? __privateGet(this, _options).customHeaders,
2073
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? __privateGet(this, _options).fingerprintSchemaVersion
1690
2074
  });
1691
2075
  }
1692
2076
  async synthesizeSsmlChunksSafe(chunks, options = {}) {
@@ -1791,6 +2175,8 @@ export {
1791
2175
  BatchChunkValidationError,
1792
2176
  ChunkValidationError,
1793
2177
  DEFAULT_OUTPUT_FORMAT,
2178
+ DeadlineController,
2179
+ IncompleteChunkSetError,
1794
2180
  MergeError,
1795
2181
  SynthesisCancelledError,
1796
2182
  SynthesisTimeoutError,
@@ -1804,6 +2190,7 @@ export {
1804
2190
  mergeSynthesisResults,
1805
2191
  resolveMergeAudioFormat,
1806
2192
  resolveMimeType,
2193
+ serializeChunkError,
1807
2194
  synthesizeSpeech,
1808
2195
  synthesizeSsml,
1809
2196
  synthesizeSsmlChunks,