ssml-builder-js 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.js CHANGED
@@ -44,6 +44,8 @@ __export(src_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,
@@ -71,6 +73,7 @@ __export(src_exports, {
71
73
  parseSsml: () => parseSsml,
72
74
  resolveMergeAudioFormat: () => resolveMergeAudioFormat,
73
75
  resolveMimeType: () => resolveMimeType,
76
+ serializeChunkError: () => serializeChunkError,
74
77
  splitSsmlDocument: () => splitSsmlDocument,
75
78
  synthesizeSpeech: () => synthesizeSpeech,
76
79
  synthesizeSsml: () => synthesizeSsml,
@@ -2803,6 +2806,15 @@ var SynthesisTimeoutError = class extends Error {
2803
2806
  this.name = "SynthesisTimeoutError";
2804
2807
  }
2805
2808
  };
2809
+ var IncompleteChunkSetError = class extends Error {
2810
+ constructor(totalChunks, missingChunkIndices) {
2811
+ super(`Cannot merge an incomplete chunk set; missing chunk indices: ${missingChunkIndices.join(", ")}.`);
2812
+ this.kind = "incomplete-chunk-set";
2813
+ this.name = "IncompleteChunkSetError";
2814
+ this.totalChunks = totalChunks;
2815
+ this.missingChunkIndices = [...missingChunkIndices];
2816
+ }
2817
+ };
2806
2818
  var MergeError = class extends Error {
2807
2819
  constructor(message, cause) {
2808
2820
  super(message);
@@ -2827,8 +2839,32 @@ var UnsupportedMergeFormatError = class extends Error {
2827
2839
  this.format = format;
2828
2840
  }
2829
2841
  };
2842
+ function serializeChunkError(error, phase, isOriginalFailure) {
2843
+ const message = error instanceof Error ? error.message : String(error);
2844
+ const status = error instanceof AzureTtsError ? error.status : void 0;
2845
+ const kind = error && typeof error === "object" && "kind" in error ? String(error.kind) : "";
2846
+ 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";
2847
+ const details = {};
2848
+ if (error instanceof AzureTtsError) {
2849
+ details.statusText = error.statusText;
2850
+ if (error.requestId) details.requestId = error.requestId;
2851
+ }
2852
+ if (error instanceof IncompleteChunkSetError) {
2853
+ details.totalChunks = error.totalChunks;
2854
+ details.missingChunkIndices = [...error.missingChunkIndices];
2855
+ }
2856
+ return {
2857
+ code,
2858
+ phase,
2859
+ message,
2860
+ isOriginalFailure,
2861
+ 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)),
2862
+ ...status !== void 0 && status > 0 ? { httpStatus: status } : {},
2863
+ ...Object.keys(details).length > 0 ? { details } : {}
2864
+ };
2865
+ }
2830
2866
  function toSynthesisError(error) {
2831
- if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError)
2867
+ if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError || error instanceof IncompleteChunkSetError)
2832
2868
  return error;
2833
2869
  const message = error instanceof Error ? error.message : String(error);
2834
2870
  if (/cancel|abort/i.test(message)) return new SynthesisCancelledError(message);
@@ -2840,6 +2876,55 @@ function createSpeechSdkError(error) {
2840
2876
  return new AzureTtsSdkError(message);
2841
2877
  }
2842
2878
 
2879
+ // packages/azure-tts-client/src/deadline.ts
2880
+ var _controller, _parent, _onParentAbort, _timer, _timedOut;
2881
+ var DeadlineController = class {
2882
+ constructor(totalJobMs, parent) {
2883
+ __privateAdd(this, _controller, new AbortController());
2884
+ __privateAdd(this, _parent);
2885
+ __privateAdd(this, _onParentAbort);
2886
+ __privateAdd(this, _timer);
2887
+ __privateAdd(this, _timedOut, false);
2888
+ __privateSet(this, _parent, parent);
2889
+ __privateSet(this, _onParentAbort, () => __privateGet(this, _controller).abort());
2890
+ this.deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
2891
+ this.signal = this.deadlineAtMs === void 0 && parent ? parent : __privateGet(this, _controller).signal;
2892
+ if (parent?.aborted) __privateGet(this, _controller).abort();
2893
+ parent?.addEventListener("abort", __privateGet(this, _onParentAbort), { once: true });
2894
+ if (this.deadlineAtMs !== void 0) {
2895
+ __privateSet(this, _timer, setTimeout(
2896
+ () => {
2897
+ __privateSet(this, _timedOut, true);
2898
+ __privateGet(this, _controller).abort();
2899
+ },
2900
+ Math.max(0, this.deadlineAtMs - Date.now())
2901
+ ));
2902
+ }
2903
+ }
2904
+ get timedOut() {
2905
+ return __privateGet(this, _timedOut) || this.deadlineAtMs !== void 0 && this.remainingMs <= 0;
2906
+ }
2907
+ get remainingMs() {
2908
+ return this.deadlineAtMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, this.deadlineAtMs - Date.now());
2909
+ }
2910
+ throwIfExpired() {
2911
+ if (this.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
2912
+ if (this.signal.aborted) throw new Error("Speech synthesis was cancelled.");
2913
+ }
2914
+ abort() {
2915
+ __privateGet(this, _controller).abort();
2916
+ }
2917
+ dispose() {
2918
+ if (__privateGet(this, _timer)) clearTimeout(__privateGet(this, _timer));
2919
+ __privateGet(this, _parent)?.removeEventListener("abort", __privateGet(this, _onParentAbort));
2920
+ }
2921
+ };
2922
+ _controller = new WeakMap();
2923
+ _parent = new WeakMap();
2924
+ _onParentAbort = new WeakMap();
2925
+ _timer = new WeakMap();
2926
+ _timedOut = new WeakMap();
2927
+
2843
2928
  // packages/azure-tts-client/src/synthesis.ts
2844
2929
  var SpeechSDK2 = __toESM(require("microsoft-cognitiveservices-speech-sdk"));
2845
2930
 
@@ -4615,18 +4700,23 @@ function createSpeechConfig(config) {
4615
4700
  }
4616
4701
 
4617
4702
  // packages/azure-tts-client/src/synthesis.ts
4618
- function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT) {
4703
+ function computeChunkFingerprint(ssml, outputFormat = DEFAULT_OUTPUT_FORMAT, options = {}) {
4619
4704
  const readAttribute3 = (name) => {
4620
4705
  const pattern = new RegExp(`(?:${name})\\s*=\\s*[\\"']([^\\"']*)`, "gi");
4621
4706
  return [...ssml.matchAll(pattern)].map((match) => match[1] ?? "").join("|");
4622
4707
  };
4708
+ const headers = Object.fromEntries(
4709
+ Object.entries(options.customHeaders ?? {}).sort(([first], [second]) => first.localeCompare(second))
4710
+ );
4623
4711
  const payload = JSON.stringify({
4624
4712
  ssml,
4625
4713
  outputFormat,
4626
- voice: readAttribute3("(?:name|voice)"),
4627
- language: readAttribute3("(?:xml:lang|lang)"),
4628
- rate: readAttribute3("rate"),
4629
- pitch: readAttribute3("pitch")
4714
+ region: options.region ?? "",
4715
+ endpoint: options.endpoint ?? "",
4716
+ voice: options.voice ?? readAttribute3("(?:name|voice)"),
4717
+ lang: options.lang ?? readAttribute3("(?:xml:lang|lang)"),
4718
+ customHeaders: headers,
4719
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? options.schemaVersion ?? "2"
4630
4720
  });
4631
4721
  let hash = 0xcbf29ce484222325n;
4632
4722
  const mask = 0xffffffffffffffffn;
@@ -4746,6 +4836,129 @@ function parseMp3Specification(buffer, format) {
4746
4836
  }
4747
4837
  return void 0;
4748
4838
  }
4839
+ function readEbmlVint(bytes, offset, preserveMarker) {
4840
+ const first = bytes[offset];
4841
+ if (first === void 0) throw new Error("Invalid EBML variable-length integer.");
4842
+ let mask = 128;
4843
+ let length = 1;
4844
+ while (length <= 8 && (first & mask) === 0) {
4845
+ mask >>= 1;
4846
+ length += 1;
4847
+ }
4848
+ if (length > 8 || offset + length > bytes.byteLength) throw new Error("Truncated EBML variable-length integer.");
4849
+ let value = preserveMarker ? first : first & mask - 1;
4850
+ for (let index = 1; index < length; index += 1) value = value * 256 + (bytes[offset + index] ?? 0);
4851
+ if (!preserveMarker && value === 2 ** (7 * length) - 1)
4852
+ throw new Error("EBML unknown-size elements are not supported.");
4853
+ return { value, length };
4854
+ }
4855
+ function readEbmlElement(bytes, offset) {
4856
+ const id = readEbmlVint(bytes, offset, true);
4857
+ const size = readEbmlVint(bytes, offset + id.length, false);
4858
+ const dataStart = offset + id.length + size.length;
4859
+ const dataEnd = dataStart + size.value;
4860
+ if (dataEnd > bytes.byteLength) throw new Error("EBML element exceeds the audio buffer.");
4861
+ return { id: id.value, dataStart, dataEnd };
4862
+ }
4863
+ function ebmlText(bytes, element) {
4864
+ return new TextDecoder().decode(bytes.slice(element.dataStart, element.dataEnd));
4865
+ }
4866
+ function findEbmlElement(bytes, start, end, id) {
4867
+ let offset = start;
4868
+ while (offset < end) {
4869
+ const element = readEbmlElement(bytes, offset);
4870
+ if (element.id === id) return element;
4871
+ offset = element.dataEnd;
4872
+ }
4873
+ if (offset !== end) throw new Error("Invalid EBML element boundary.");
4874
+ return void 0;
4875
+ }
4876
+ function parseOggSpecification(buffer, format) {
4877
+ const bytes = new Uint8Array(buffer);
4878
+ let offset = 0;
4879
+ let firstPayload;
4880
+ let pages = 0;
4881
+ while (offset < bytes.byteLength) {
4882
+ if (offset + 27 > bytes.byteLength || !ascii(bytes, offset, "OggS")) throw new Error("Invalid Ogg page header.");
4883
+ if (bytes[offset + 4] !== 0) throw new Error("Unsupported Ogg bitstream version.");
4884
+ const segmentCount = bytes[offset + 26] ?? 0;
4885
+ const lacingStart = offset + 27;
4886
+ const payloadStart = lacingStart + segmentCount;
4887
+ if (payloadStart > bytes.byteLength) throw new Error("Truncated Ogg segment table.");
4888
+ const payloadLength = bytes.slice(lacingStart, payloadStart).reduce((total, value) => total + value, 0);
4889
+ const pageEnd = payloadStart + payloadLength;
4890
+ if (pageEnd > bytes.byteLength) throw new Error("Ogg page payload exceeds the audio buffer.");
4891
+ if (pages === 0) firstPayload = bytes.slice(payloadStart, pageEnd);
4892
+ offset = pageEnd;
4893
+ pages += 1;
4894
+ }
4895
+ if (pages === 0 || !firstPayload || !ascii(firstPayload, 0, "OpusHead") || firstPayload.byteLength < 19)
4896
+ throw new Error("Ogg audio must contain a valid OpusHead packet.");
4897
+ const version = firstPayload[8];
4898
+ const channels = firstPayload[9] ?? 0;
4899
+ const sampleRate = new DataView(firstPayload.buffer, firstPayload.byteOffset, firstPayload.byteLength).getUint32(
4900
+ 12,
4901
+ true
4902
+ );
4903
+ if (version !== 1 || channels <= 0 || sampleRate <= 0) throw new Error("Invalid Ogg OpusHead stream parameters.");
4904
+ return {
4905
+ format,
4906
+ mimeType: "audio/ogg",
4907
+ codec: "opus",
4908
+ sampleRate,
4909
+ channels,
4910
+ container: "ogg",
4911
+ isVbr: true,
4912
+ isCompressed: true
4913
+ };
4914
+ }
4915
+ function parseWebmSpecification(buffer, format) {
4916
+ const bytes = new Uint8Array(buffer);
4917
+ const ebml = readEbmlElement(bytes, 0);
4918
+ if (ebml.id !== 440786851) throw new Error("WebM audio must begin with an EBML header.");
4919
+ const docType = findEbmlElement(bytes, ebml.dataStart, ebml.dataEnd, 17026);
4920
+ if (!docType || ebmlText(bytes, docType).toLowerCase() !== "webm") throw new Error("EBML DocType must be webm.");
4921
+ const segment = readEbmlElement(bytes, ebml.dataEnd);
4922
+ if (segment.id !== 408125543) throw new Error("WebM audio must contain a Segment element.");
4923
+ const tracks = findEbmlElement(bytes, segment.dataStart, segment.dataEnd, 374648427);
4924
+ if (!tracks) throw new Error("WebM audio must contain a Tracks element.");
4925
+ let offset = tracks.dataStart;
4926
+ let opusTrack;
4927
+ while (offset < tracks.dataEnd) {
4928
+ const track = readEbmlElement(bytes, offset);
4929
+ if (track.id === 174) {
4930
+ const codec = findEbmlElement(bytes, track.dataStart, track.dataEnd, 134);
4931
+ const trackType = findEbmlElement(bytes, track.dataStart, track.dataEnd, 131);
4932
+ if (codec && ebmlText(bytes, codec) === "A_OPUS" && trackType && bytes[trackType.dataStart] === 2) {
4933
+ opusTrack = track;
4934
+ break;
4935
+ }
4936
+ }
4937
+ offset = track.dataEnd;
4938
+ }
4939
+ if (!opusTrack) throw new Error("WebM tracks do not define an Opus audio track.");
4940
+ const audio = findEbmlElement(bytes, opusTrack.dataStart, opusTrack.dataEnd, 225);
4941
+ const sampling = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 181) : void 0;
4942
+ const channels = audio ? findEbmlElement(bytes, audio.dataStart, audio.dataEnd, 159) : void 0;
4943
+ const sampleRate = sampling ? new DataView(
4944
+ bytes.buffer,
4945
+ bytes.byteOffset + sampling.dataStart,
4946
+ sampling.dataEnd - sampling.dataStart
4947
+ ).getFloat64(0, false) : 0;
4948
+ const channelCount = channels ? bytes[channels.dataEnd - 1] ?? 0 : 0;
4949
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0 || channelCount <= 0)
4950
+ throw new Error("WebM Opus audio track has invalid sampling or channel parameters.");
4951
+ return {
4952
+ format,
4953
+ mimeType: "audio/webm",
4954
+ codec: "opus",
4955
+ sampleRate: Math.round(sampleRate),
4956
+ channels: channelCount,
4957
+ container: "webm",
4958
+ isVbr: true,
4959
+ isCompressed: true
4960
+ };
4961
+ }
4749
4962
  function inspectAudioSpecification(buffer, format) {
4750
4963
  if (isWavFormat(format) || ascii(new Uint8Array(buffer), 0, "RIFF")) {
4751
4964
  const parsed = parseWav(buffer);
@@ -4770,7 +4983,20 @@ function inspectAudioSpecification(buffer, format) {
4770
4983
  isCompressed: codec !== "pcm" && codec !== "unknown"
4771
4984
  };
4772
4985
  }
4773
- if (isMp3Format(format)) return parseMp3Specification(buffer, format) ?? formatAudioSpecification(format);
4986
+ if (isMp3Format(format)) {
4987
+ const specification2 = parseMp3Specification(buffer, format);
4988
+ return specification2 ?? formatAudioSpecification(format);
4989
+ }
4990
+ if (isOggFormat(format) || ascii(new Uint8Array(buffer), 0, "OggS")) {
4991
+ const specification2 = parseOggSpecification(buffer, format);
4992
+ validateContainerFormat(specification2, format);
4993
+ return specification2;
4994
+ }
4995
+ if (isWebmFormat(format) || new Uint8Array(buffer)[0] === 26) {
4996
+ const specification2 = parseWebmSpecification(buffer, format);
4997
+ validateContainerFormat(specification2, format);
4998
+ return specification2;
4999
+ }
4774
5000
  const specification = formatAudioSpecification(format);
4775
5001
  if (specification.container === "raw") validateRawAudioBuffer(buffer, specification);
4776
5002
  return specification;
@@ -4779,7 +5005,20 @@ function validateRawAudioBuffer(buffer, specification) {
4779
5005
  if (specification.sampleRate <= 0 || specification.channels <= 0 || specification.bitDepth === void 0) {
4780
5006
  throw new Error(`RAW audio format "${specification.format}" does not define a complete audio specification.`);
4781
5007
  }
4782
- if (specification.codec === "siren" || specification.codec === "silk") return;
5008
+ if (specification.codec === "siren") return;
5009
+ if (specification.codec === "silk") {
5010
+ if (buffer.byteLength <= 9 || !ascii(new Uint8Array(buffer), 0, "#!SILK_V3"))
5011
+ throw new Error("RAW SILK audio must contain a valid #!SILK_V3 payload header.");
5012
+ return;
5013
+ }
5014
+ if (specification.codec === "opus" && buffer.byteLength === 0) throw new Error("RAW Opus audio cannot be empty.");
5015
+ if (specification.codec === "opus") {
5016
+ const packetCode = new Uint8Array(buffer)[0] ?? 0;
5017
+ const frameCountCode = packetCode & 3;
5018
+ if (packetCode >> 3 > 31 || buffer.byteLength < (frameCountCode === 3 ? 2 : 2) || frameCountCode === 3 && ((new Uint8Array(buffer)[1] ?? 0) & 63) === 0)
5019
+ throw new Error("RAW Opus audio has an invalid packet framing header.");
5020
+ return;
5021
+ }
4783
5022
  const bytesPerFrame = specification.channels * Math.ceil(specification.bitDepth / 8);
4784
5023
  if (bytesPerFrame <= 0 || buffer.byteLength % bytesPerFrame !== 0) {
4785
5024
  throw new Error(
@@ -4787,6 +5026,15 @@ function validateRawAudioBuffer(buffer, specification) {
4787
5026
  );
4788
5027
  }
4789
5028
  }
5029
+ function validateContainerFormat(specification, format) {
5030
+ const expected = formatAudioSpecification(format);
5031
+ if (expected.sampleRate > 0 && specification.sampleRate !== expected.sampleRate || expected.channels > 0 && specification.channels !== expected.channels || expected.codec !== "unknown" && specification.codec !== expected.codec) {
5032
+ throw new AudioFormatMismatchError(`Audio container does not match the requested format "${format}".`, [
5033
+ expected,
5034
+ specification
5035
+ ]);
5036
+ }
5037
+ }
4790
5038
  function validateAudioSpecifications(specs) {
4791
5039
  const first = specs[0];
4792
5040
  if (!first) return;
@@ -4868,17 +5116,29 @@ function stripMp3Tags(buffer) {
4868
5116
  function isMp3Format(format) {
4869
5117
  return /(?:mp3|mpeg)/i.test(format);
4870
5118
  }
5119
+ function isOggFormat(format) {
5120
+ return /ogg/i.test(format);
5121
+ }
5122
+ function isWebmFormat(format) {
5123
+ return /webm/i.test(format);
5124
+ }
4871
5125
  function isWavFormat(format) {
4872
5126
  return /(?:wav|wave|riff)/i.test(format);
4873
5127
  }
4874
5128
  function isRawFormat(format) {
4875
5129
  return /^raw(?:-|$)/i.test(format);
4876
5130
  }
4877
- function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType) {
5131
+ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMimeType, allowExternalContainer) {
4878
5132
  if (!(merged instanceof ArrayBuffer) || merged.byteLength === 0) {
4879
5133
  throw new MergeError("The custom audio merger returned an empty or invalid audio buffer.");
4880
5134
  }
4881
- const specification = inspectAudioSpecification(merged, format);
5135
+ let specification;
5136
+ try {
5137
+ specification = inspectAudioSpecification(merged, format);
5138
+ } catch (error) {
5139
+ if (!allowExternalContainer) throw error;
5140
+ specification = inputSpecs[0] ?? formatAudioSpecification(format);
5141
+ }
4882
5142
  if (!outputMimeType.trim()) throw new MergeError("The custom audio merger output MIME type cannot be empty.");
4883
5143
  const firstInput = inputSpecs[0];
4884
5144
  if (firstInput && (specification.sampleRate !== firstInput.sampleRate || specification.channels !== firstInput.channels || specification.codec !== firstInput.codec || firstInput.bitDepth !== void 0 && specification.bitDepth !== firstInput.bitDepth)) {
@@ -4898,6 +5158,25 @@ function validateMergedAudioBuffer(merged, format, buffers, inputSpecs, outputMi
4898
5158
  }
4899
5159
  return specification;
4900
5160
  }
5161
+ async function withinDeadline(value, deadline) {
5162
+ if (!deadline) return value;
5163
+ deadline.throwIfExpired();
5164
+ if (!Number.isFinite(deadline.remainingMs)) return value;
5165
+ let timer;
5166
+ try {
5167
+ return await Promise.race([
5168
+ Promise.resolve(value),
5169
+ new Promise((_resolve, reject) => {
5170
+ timer = setTimeout(
5171
+ () => reject(new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.")),
5172
+ deadline.remainingMs
5173
+ );
5174
+ })
5175
+ ]);
5176
+ } finally {
5177
+ if (timer) clearTimeout(timer);
5178
+ }
5179
+ }
4901
5180
  function resolveMergeAudioFormat(format) {
4902
5181
  if (isWavFormat(format)) return "wav";
4903
5182
  if (isMp3Format(format)) return "mp3";
@@ -4910,6 +5189,7 @@ function canMergeAudioFormat(format) {
4910
5189
  function mergeAudioBuffers(buffers, options) {
4911
5190
  const format = typeof options === "string" ? options : options?.format;
4912
5191
  if (!format) throw new UnsupportedMergeFormatError("");
5192
+ if (!canMergeAudioFormat(format)) throw new UnsupportedMergeFormatError(format);
4913
5193
  try {
4914
5194
  validateAudioSpecifications(buffers.map((buffer) => inspectAudioSpecification(buffer, format)));
4915
5195
  if (isWavFormat(format)) return mergeWavBuffers(buffers);
@@ -5218,10 +5498,23 @@ async function synthesizeWithRetry(ssml, config, retryOptions, onRetry, deadline
5218
5498
  }
5219
5499
  }
5220
5500
  async function synthesizeSsml(ssml, config) {
5221
- const totalJobMs = config.timeouts?.totalJobMs;
5222
- const deadlineAtMs = totalJobMs !== void 0 && totalJobMs > 0 ? Date.now() + totalJobMs : void 0;
5223
- if (!config.retryOptions) return synthesizeSsmlOnce(ssml, config);
5224
- return synthesizeWithRetry(ssml, config, config.retryOptions, () => void 0, deadlineAtMs);
5501
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
5502
+ try {
5503
+ deadline.throwIfExpired();
5504
+ const synthesisConfig = {
5505
+ ...config,
5506
+ signal: deadline.signal,
5507
+ timeouts: config.timeouts ? { ...config.timeouts, totalJobMs: void 0 } : void 0
5508
+ };
5509
+ const result = config.retryOptions ? await synthesizeWithRetry(ssml, synthesisConfig, config.retryOptions, () => void 0, deadline.deadlineAtMs) : await synthesizeSsmlOnce(ssml, synthesisConfig);
5510
+ deadline.throwIfExpired();
5511
+ return result;
5512
+ } catch (error) {
5513
+ if (deadline.timedOut) throw new SynthesisTimeoutError("Speech synthesis exceeded the total job deadline.");
5514
+ throw error;
5515
+ } finally {
5516
+ deadline.dispose();
5517
+ }
5225
5518
  }
5226
5519
  function createAbortScope(parent, timeoutMs) {
5227
5520
  const controller = new AbortController();
@@ -5258,7 +5551,12 @@ async function synthesizeSsmlChunks(chunks, config) {
5258
5551
  const totalChunks = chunks.length;
5259
5552
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
5260
5553
  const fingerprints = inputs.map(
5261
- (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT)
5554
+ (chunk) => computeChunkFingerprint(chunk.ssml, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT, {
5555
+ region: config.region,
5556
+ endpoint: config.endpoint,
5557
+ customHeaders: config.customHeaders,
5558
+ fingerprintSchemaVersion: config.fingerprintSchemaVersion
5559
+ })
5262
5560
  );
5263
5561
  const results = new Array(totalChunks);
5264
5562
  const cachedChunks = new Map((config.resumeChunks ?? []).map((chunk) => [chunk.chunkIndex, chunk]));
@@ -5282,6 +5580,7 @@ async function synthesizeSsmlChunks(chunks, config) {
5282
5580
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
5283
5581
  const jobStartedAt = Date.now();
5284
5582
  const jobDeadlineAt = config.timeouts?.totalJobMs !== void 0 && config.timeouts.totalJobMs > 0 ? jobStartedAt + config.timeouts.totalJobMs : void 0;
5583
+ const deadline = new DeadlineController(config.timeouts?.totalJobMs, config.signal);
5285
5584
  const scope = createAbortScope(config.signal, config.timeouts?.totalJobMs);
5286
5585
  const report = (event) => config.onProgress?.(event);
5287
5586
  for (const [index, input] of inputs.entries()) {
@@ -5322,7 +5621,7 @@ async function synthesizeSsmlChunks(chunks, config) {
5322
5621
  input.ssml,
5323
5622
  {
5324
5623
  ...config,
5325
- signal: scope.signal,
5624
+ signal: deadline.signal,
5326
5625
  ...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
5327
5626
  ...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
5328
5627
  ...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
@@ -5367,7 +5666,7 @@ async function synthesizeSsmlChunks(chunks, config) {
5367
5666
  status: wasCancelled ? "cancelled" : "failed",
5368
5667
  isOriginalFailure: !wasCancelled,
5369
5668
  canResume: true,
5370
- error
5669
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
5371
5670
  };
5372
5671
  report({
5373
5672
  currentChunk: completed,
@@ -5387,13 +5686,19 @@ async function synthesizeSsmlChunks(chunks, config) {
5387
5686
  try {
5388
5687
  await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
5389
5688
  if (firstError) throw firstError;
5689
+ const missingChunkIndices = Array.from(
5690
+ { length: totalChunks },
5691
+ (_value, index) => results[index] === void 0 ? index : void 0
5692
+ ).filter((index) => index !== void 0);
5693
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(totalChunks, missingChunkIndices);
5390
5694
  const orderedResults = results.filter((result) => result !== void 0);
5391
5695
  return await mergeSynthesisResults(orderedResults, {
5392
5696
  format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
5393
- signal: scope.signal,
5697
+ signal: deadline.signal,
5394
5698
  customMerger: config.customMerger,
5395
5699
  outputMimeType: config.outputMimeType,
5396
- postMergeValidator: config.postMergeValidator
5700
+ postMergeValidator: config.postMergeValidator,
5701
+ deadline
5397
5702
  });
5398
5703
  } catch (error) {
5399
5704
  if (firstError && config.cancelOnFailure !== false) {
@@ -5421,6 +5726,7 @@ async function synthesizeSsmlChunks(chunks, config) {
5421
5726
  throw error;
5422
5727
  } finally {
5423
5728
  scope.dispose();
5729
+ deadline.dispose();
5424
5730
  }
5425
5731
  }
5426
5732
  function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
@@ -5498,19 +5804,33 @@ function mergeSynthesisResults(results, options) {
5498
5804
  const format = resolvedOptions?.format;
5499
5805
  if (!format) throw new UnsupportedMergeFormatError("");
5500
5806
  const buffers = results.map((result) => result.audioData);
5501
- const inputSpecs = results.map((result) => result.audioSpec ?? inspectAudioSpecification(result.audioData, format));
5807
+ const inputSpecs = results.map((result) => {
5808
+ if (result.audioSpec) return result.audioSpec;
5809
+ try {
5810
+ return inspectAudioSpecification(result.audioData, format);
5811
+ } catch (error) {
5812
+ if (resolvedOptions.customMerger) return formatAudioSpecification(format);
5813
+ throw error;
5814
+ }
5815
+ });
5502
5816
  validateAudioSpecifications(inputSpecs);
5503
- const signal = resolvedOptions.signal ?? new AbortController().signal;
5817
+ const deadline = resolvedOptions.deadline;
5818
+ deadline?.throwIfExpired();
5819
+ const signal = resolvedOptions.signal ?? deadline?.signal ?? new AbortController().signal;
5504
5820
  if (signal.aborted) throw new SynthesisCancelledError();
5505
5821
  if (resolvedOptions.customMerger) {
5506
- return Promise.resolve().then(
5507
- () => resolvedOptions.customMerger?.(buffers, {
5508
- format,
5509
- outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
5510
- inputSpecs,
5511
- signal
5512
- })
5822
+ return withinDeadline(
5823
+ Promise.resolve().then(
5824
+ () => resolvedOptions.customMerger?.(buffers, {
5825
+ format,
5826
+ outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
5827
+ inputSpecs,
5828
+ signal
5829
+ })
5830
+ ),
5831
+ deadline
5513
5832
  ).then((merged) => {
5833
+ deadline?.throwIfExpired();
5514
5834
  if (!merged) throw new MergeError("The custom audio merger returned no audio buffer.");
5515
5835
  if (signal.aborted) throw new SynthesisCancelledError();
5516
5836
  const mergedSpec = validateMergedAudioBuffer(
@@ -5518,7 +5838,8 @@ function mergeSynthesisResults(results, options) {
5518
5838
  format,
5519
5839
  buffers,
5520
5840
  inputSpecs,
5521
- resolvedOptions.outputMimeType ?? resolveMimeType(format)
5841
+ resolvedOptions.outputMimeType ?? resolveMimeType(format),
5842
+ true
5522
5843
  );
5523
5844
  const result = createMergedResult(results, merged, format, mergedSpec, resolvedOptions.outputMimeType);
5524
5845
  return Promise.resolve(
@@ -5529,6 +5850,7 @@ function mergeSynthesisResults(results, options) {
5529
5850
  signal
5530
5851
  })
5531
5852
  ).then((valid) => {
5853
+ deadline?.throwIfExpired();
5532
5854
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
5533
5855
  return result;
5534
5856
  });
@@ -5539,6 +5861,7 @@ function mergeSynthesisResults(results, options) {
5539
5861
  });
5540
5862
  }
5541
5863
  try {
5864
+ deadline?.throwIfExpired();
5542
5865
  const result = createMergedResult(
5543
5866
  results,
5544
5867
  mergeAudioBuffers(buffers, { format }),
@@ -5554,12 +5877,14 @@ function mergeSynthesisResults(results, options) {
5554
5877
  signal
5555
5878
  });
5556
5879
  if (validation instanceof Promise)
5557
- return validation.then((valid) => {
5880
+ return withinDeadline(validation, deadline).then((valid) => {
5881
+ deadline?.throwIfExpired();
5558
5882
  if (valid === false) throw new MergeError("The post-merge validator rejected the merged audio.");
5559
5883
  return result;
5560
5884
  });
5561
5885
  if (validation === false) throw new MergeError("The post-merge validator rejected the merged audio.");
5562
5886
  }
5887
+ deadline?.throwIfExpired();
5563
5888
  return result;
5564
5889
  } catch (error) {
5565
5890
  if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
@@ -5704,47 +6029,55 @@ function sharedValidationOptions(options, signal) {
5704
6029
  };
5705
6030
  }
5706
6031
  async function synthesizeSsmlSafe(client, ssml, options = {}) {
5707
- const validationOptions = sharedValidationOptions(options.validation ?? options, options.signal);
6032
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
6033
+ const validationOptions = sharedValidationOptions(options.validation ?? options, deadline.signal);
5708
6034
  const diagnostics = await Promise.resolve(validateAzureSsml2(ssml, validationOptions));
5709
- if (options.signal?.aborted) {
5710
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
6035
+ if (deadline.signal.aborted) {
6036
+ const error = toSynthesisError(
6037
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
6038
+ );
6039
+ deadline.dispose();
5711
6040
  return failure(error);
5712
6041
  }
5713
6042
  const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
5714
6043
  if (errors.length > 0) {
6044
+ deadline.dispose();
5715
6045
  return failure({
5716
6046
  kind: "validation-error",
5717
6047
  message: "SSML validation failed; the Azure Speech API was not called.",
5718
6048
  diagnostics: errors
5719
6049
  });
5720
6050
  }
5721
- const jobScope = options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts.totalJobMs) : void 0;
5722
6051
  try {
5723
6052
  return {
5724
6053
  ok: true,
5725
6054
  success: true,
5726
6055
  status: "success",
5727
6056
  value: await client.synthesizeSsml(ssml, {
5728
- signal: jobScope?.signal ?? options.signal,
6057
+ signal: deadline.signal,
5729
6058
  timeoutMs: options.timeouts?.perChunkMs,
5730
- timeouts: options.timeouts
6059
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0
5731
6060
  })
5732
6061
  };
5733
6062
  } catch (error) {
5734
- if (jobScope?.timedOut()) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
6063
+ if (deadline.timedOut) return failure(toSynthesisError(new Error("Speech synthesis timed out.")));
5735
6064
  const synthesisError = toSynthesisError(error);
5736
6065
  return failure(synthesisError);
5737
6066
  } finally {
5738
- jobScope?.dispose();
6067
+ deadline.dispose();
5739
6068
  }
5740
6069
  }
5741
6070
  async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
6071
+ const deadline = new DeadlineController(options.timeouts?.totalJobMs, options.signal);
5742
6072
  const validationOptions = sharedValidationOptions(
5743
6073
  { ...options.validation ?? options, timeouts: options.timeouts },
5744
- options.signal
6074
+ deadline.signal
5745
6075
  );
5746
- if (options.signal?.aborted) {
5747
- const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
6076
+ if (deadline.signal.aborted) {
6077
+ const error = toSynthesisError(
6078
+ new Error(deadline.timedOut ? "Speech synthesis timed out." : "Speech synthesis was cancelled.")
6079
+ );
6080
+ deadline.dispose();
5748
6081
  return failure(error);
5749
6082
  }
5750
6083
  const pending = (index, status, error) => {
@@ -5776,14 +6109,16 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5776
6109
  return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
5777
6110
  })
5778
6111
  );
5779
- if (options.signal?.aborted) {
6112
+ if (deadline.signal.aborted) {
5780
6113
  const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
6114
+ deadline.dispose();
5781
6115
  return failure(error);
5782
6116
  }
5783
6117
  const chunkDiagnostics = validations.map((diagnostics, chunkIndex) => ({ chunkIndex, diagnostics })).filter((entry) => entry.diagnostics.length > 0);
5784
6118
  if (chunkDiagnostics.length > 0) {
5785
6119
  const error = new BatchChunkValidationError(chunkDiagnostics);
5786
6120
  for (const entry of chunkDiagnostics) pending(entry.chunkIndex, "failed", error);
6121
+ deadline.dispose();
5787
6122
  return failure(error);
5788
6123
  }
5789
6124
  let fallbackJobScope;
@@ -5796,9 +6131,9 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5796
6131
  const value = await client.synthesizeChunks(normalizedChunks, {
5797
6132
  onProgress: options.onProgress,
5798
6133
  outputFormat: options.outputFormat,
5799
- signal: options.signal,
6134
+ signal: deadline.signal,
5800
6135
  timeoutMs: options.timeoutMs,
5801
- timeouts: options.timeouts,
6136
+ timeouts: options.timeouts ? { ...options.timeouts, totalJobMs: void 0 } : void 0,
5802
6137
  sourceNodePath: options.sourceNodePath,
5803
6138
  concurrency: options.concurrency,
5804
6139
  retryOptions: options.retryOptions,
@@ -5808,12 +6143,19 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5808
6143
  customMerger: options.customMerger,
5809
6144
  outputMimeType: options.outputMimeType,
5810
6145
  postMergeValidator: options.postMergeValidator,
5811
- resumeValidation: options.resumeValidation
6146
+ resumeValidation: options.resumeValidation,
6147
+ customHeaders: options.customHeaders,
6148
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
5812
6149
  });
5813
6150
  return { ok: true, success: true, status: "success", value };
5814
6151
  }
5815
6152
  const inputs = chunks.map((chunk) => typeof chunk === "string" ? { ssml: chunk } : chunk);
5816
- const fingerprints = inputs.map((chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat));
6153
+ const fingerprints = inputs.map(
6154
+ (chunk) => computeChunkFingerprint(chunk.ssml, options.outputFormat, {
6155
+ customHeaders: options.customHeaders,
6156
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion
6157
+ })
6158
+ );
5817
6159
  const results = new Array(chunks.length);
5818
6160
  const chunkStates = inputs.map((_chunk, chunkIndex) => ({
5819
6161
  chunkIndex,
@@ -5831,9 +6173,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5831
6173
  }
5832
6174
  const requestedIndices = options.resumeChunkIndices ? new Set(options.resumeChunkIndices.filter((index) => index >= 0 && index < chunks.length)) : void 0;
5833
6175
  const shouldSynthesize = (index) => (!cachedChunks.has(index) || invalidCachedIndices.has(index)) && (requestedIndices === void 0 || requestedIndices.has(index) || invalidCachedIndices.has(index));
5834
- const jobStartedAt = Date.now();
5835
- const jobDeadlineAt = options.timeouts?.totalJobMs !== void 0 && options.timeouts.totalJobMs > 0 ? jobStartedAt + options.timeouts.totalJobMs : void 0;
5836
- const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(options.signal, options.timeouts?.totalJobMs) : void 0;
6176
+ const jobDeadlineAt = deadline.deadlineAtMs;
6177
+ const jobScope = chunks.length > 1 || options.timeouts?.totalJobMs !== void 0 ? createSafeAbortScope(deadline.signal, void 0) : void 0;
5837
6178
  fallbackJobScope = jobScope;
5838
6179
  const failedIndices = /* @__PURE__ */ new Set();
5839
6180
  let firstError;
@@ -5857,8 +6198,8 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5857
6198
  const startedAt = Date.now();
5858
6199
  try {
5859
6200
  const chunkTimeout = options.timeouts?.chunkWithRetriesMs ?? options.timeouts?.perChunkMs;
5860
- const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? options.signal, chunkTimeout ?? options.timeoutMs) : void 0;
5861
- const chunkSignal = chunkScope?.signal ?? options.signal;
6201
+ const chunkScope = chunkTimeout !== void 0 || jobScope ? createSafeAbortScope(jobScope?.signal ?? deadline.signal, chunkTimeout ?? options.timeoutMs) : void 0;
6202
+ const chunkSignal = chunkScope?.signal ?? deadline.signal;
5862
6203
  let result;
5863
6204
  try {
5864
6205
  result = await retryableSynthesis(
@@ -5885,7 +6226,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5885
6226
  jobDeadlineAt
5886
6227
  );
5887
6228
  } catch (error) {
5888
- if (chunkScope?.timedOut())
6229
+ if (chunkScope?.timedOut() || deadline.timedOut)
5889
6230
  throw new Error(`Speech synthesis timed out after ${chunkTimeout ?? options.timeoutMs} ms.`);
5890
6231
  throw error;
5891
6232
  } finally {
@@ -5946,15 +6287,15 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5946
6287
  durationMs: Date.now() - startedAt
5947
6288
  });
5948
6289
  } catch (error) {
5949
- const wasCancelled = firstError !== void 0 || Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
5950
- firstError ?? (firstError = error);
6290
+ const wasCancelled = firstError !== void 0 || !deadline.timedOut && Boolean(jobScope?.signal.aborted && !jobScope?.timedOut());
6291
+ firstError ?? (firstError = deadline.timedOut ? new Error("Speech synthesis timed out.") : error);
5951
6292
  if (!wasCancelled) failedIndices.add(index);
5952
6293
  chunkStates[index] = {
5953
6294
  chunkIndex: index,
5954
6295
  status: wasCancelled ? "cancelled" : "failed",
5955
6296
  isOriginalFailure: !wasCancelled,
5956
6297
  canResume: true,
5957
- error
6298
+ error: serializeChunkError(error, "synthesis", !wasCancelled)
5958
6299
  };
5959
6300
  options.onProgress?.({
5960
6301
  currentChunk: completed,
@@ -5997,6 +6338,11 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
5997
6338
  };
5998
6339
  throw error;
5999
6340
  }
6341
+ const missingChunkIndices = Array.from(
6342
+ { length: chunks.length },
6343
+ (_value, index) => results[index] === void 0 ? index : void 0
6344
+ ).filter((index) => index !== void 0);
6345
+ if (missingChunkIndices.length > 0) throw new IncompleteChunkSetError(chunks.length, missingChunkIndices);
6000
6346
  const orderedResults = results.filter((result) => result !== void 0);
6001
6347
  return {
6002
6348
  ok: true,
@@ -6004,7 +6350,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
6004
6350
  status: "success",
6005
6351
  value: await mergeSynthesisResults(orderedResults, {
6006
6352
  format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
6007
- signal: jobScope?.signal ?? options.signal,
6353
+ signal: jobScope?.signal ?? deadline.signal,
6008
6354
  customMerger: options.customMerger,
6009
6355
  outputMimeType: options.outputMimeType,
6010
6356
  postMergeValidator: options.postMergeValidator
@@ -6015,6 +6361,7 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
6015
6361
  return failure(synthesisError, partialResultFrom(error));
6016
6362
  } finally {
6017
6363
  fallbackJobScope?.dispose();
6364
+ deadline.dispose();
6018
6365
  }
6019
6366
  }
6020
6367
  function withValidationSignal(options, signal) {
@@ -6035,7 +6382,16 @@ var AzureTtsClient = class {
6035
6382
  __privateSet(this, _options, options);
6036
6383
  }
6037
6384
  async synthesize(ssml) {
6038
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
6385
+ const {
6386
+ region,
6387
+ subscriptionKey,
6388
+ outputFormat,
6389
+ signal,
6390
+ timeoutMs,
6391
+ timeouts,
6392
+ customHeaders,
6393
+ fingerprintSchemaVersion
6394
+ } = __privateGet(this, _options);
6039
6395
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
6040
6396
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
6041
6397
  const config = {
@@ -6046,12 +6402,23 @@ var AzureTtsClient = class {
6046
6402
  signal,
6047
6403
  timeoutMs,
6048
6404
  timeouts,
6049
- retryOptions: __privateGet(this, _options).retryOptions
6405
+ retryOptions: __privateGet(this, _options).retryOptions,
6406
+ customHeaders,
6407
+ fingerprintSchemaVersion
6050
6408
  };
6051
6409
  return synthesizeSpeech(ssml, config);
6052
6410
  }
6053
6411
  async synthesizeSsml(ssml, options = {}) {
6054
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
6412
+ const {
6413
+ region,
6414
+ subscriptionKey,
6415
+ outputFormat,
6416
+ signal,
6417
+ timeoutMs,
6418
+ timeouts,
6419
+ customHeaders,
6420
+ fingerprintSchemaVersion
6421
+ } = __privateGet(this, _options);
6055
6422
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
6056
6423
  __privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
6057
6424
  return synthesizeSsml(ssml, {
@@ -6070,11 +6437,22 @@ var AzureTtsClient = class {
6070
6437
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
6071
6438
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
6072
6439
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
6073
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
6440
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
6441
+ customHeaders: options.customHeaders ?? customHeaders,
6442
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
6074
6443
  });
6075
6444
  }
6076
6445
  async synthesizeChunks(chunks, options = {}) {
6077
- const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = __privateGet(this, _options);
6446
+ const {
6447
+ region,
6448
+ subscriptionKey,
6449
+ outputFormat,
6450
+ signal,
6451
+ timeoutMs,
6452
+ timeouts,
6453
+ customHeaders,
6454
+ fingerprintSchemaVersion
6455
+ } = __privateGet(this, _options);
6078
6456
  const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
6079
6457
  return synthesizeSsmlChunks(chunks, {
6080
6458
  endpoint,
@@ -6094,7 +6472,9 @@ var AzureTtsClient = class {
6094
6472
  customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
6095
6473
  outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
6096
6474
  postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
6097
- resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation
6475
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
6476
+ customHeaders: options.customHeaders ?? customHeaders,
6477
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? fingerprintSchemaVersion
6098
6478
  });
6099
6479
  }
6100
6480
  async synthesizeSsmlSafe(ssml, options = {}) {
@@ -6109,7 +6489,14 @@ var AzureTtsClient = class {
6109
6489
  timeouts: options.timeouts ?? __privateGet(this, _options).timeouts,
6110
6490
  onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
6111
6491
  concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
6112
- retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
6492
+ retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions,
6493
+ cancelOnFailure: options.cancelOnFailure ?? __privateGet(this, _options).cancelOnFailure,
6494
+ customMerger: options.customMerger ?? __privateGet(this, _options).customMerger,
6495
+ outputMimeType: options.outputMimeType ?? __privateGet(this, _options).outputMimeType,
6496
+ postMergeValidator: options.postMergeValidator ?? __privateGet(this, _options).postMergeValidator,
6497
+ resumeValidation: options.resumeValidation ?? __privateGet(this, _options).resumeValidation,
6498
+ customHeaders: options.customHeaders ?? __privateGet(this, _options).customHeaders,
6499
+ fingerprintSchemaVersion: options.fingerprintSchemaVersion ?? __privateGet(this, _options).fingerprintSchemaVersion
6113
6500
  });
6114
6501
  }
6115
6502
  async synthesizeSsmlChunksSafe(chunks, options = {}) {
@@ -6215,6 +6602,8 @@ async function fetchAzureVoiceCatalog(options) {
6215
6602
  BatchChunkValidationError,
6216
6603
  ChunkValidationError,
6217
6604
  DEFAULT_OUTPUT_FORMAT,
6605
+ DeadlineController,
6606
+ IncompleteChunkSetError,
6218
6607
  MergeError,
6219
6608
  SynthesisCancelledError,
6220
6609
  SynthesisTimeoutError,
@@ -6242,6 +6631,7 @@ async function fetchAzureVoiceCatalog(options) {
6242
6631
  parseSsml,
6243
6632
  resolveMergeAudioFormat,
6244
6633
  resolveMimeType,
6634
+ serializeChunkError,
6245
6635
  splitSsmlDocument,
6246
6636
  synthesizeSpeech,
6247
6637
  synthesizeSsml,