@ssml-builder-js/azure-tts-client 2.15.0 → 2.17.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/src/client.ts CHANGED
@@ -19,16 +19,16 @@ export class AzureTtsClient {
19
19
  }
20
20
 
21
21
  async synthesize(ssml: string): Promise<ArrayBuffer> {
22
- const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;
22
+ const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = this.#options;
23
23
  const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
24
24
  this.#options.logger?.debug?.("Using Azure TTS endpoint:", endpoint);
25
25
 
26
- const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };
26
+ const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts };
27
27
  return synthesizeSpeech(ssml, config);
28
28
  }
29
29
 
30
30
  async synthesizeSsml(ssml: string, options: Partial<TtsConfig> = {}): Promise<SsmlSynthesisResult> {
31
- const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;
31
+ const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = this.#options;
32
32
  const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
33
33
  this.#options.logger?.debug?.("Using Azure TTS endpoint:", endpoint);
34
34
 
@@ -39,6 +39,7 @@ export class AzureTtsClient {
39
39
  outputFormat: options.outputFormat ?? outputFormat,
40
40
  signal: options.signal ?? signal,
41
41
  timeoutMs: options.timeoutMs ?? timeoutMs,
42
+ timeouts: options.timeouts ?? timeouts,
42
43
  sourceNodePath: options.sourceNodePath,
43
44
  sourceTextSegments: options.sourceTextSegments,
44
45
  sourceMarkers: options.sourceMarkers,
@@ -49,7 +50,7 @@ export class AzureTtsClient {
49
50
  chunks: readonly (SsmlSynthesisChunk | string)[],
50
51
  options: SynthesizeChunksOptions = {},
51
52
  ): Promise<SsmlSynthesisResult> {
52
- const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;
53
+ const { region, subscriptionKey, outputFormat, signal, timeoutMs, timeouts } = this.#options;
53
54
  const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
54
55
  return synthesizeSsmlChunks(chunks, {
55
56
  endpoint,
@@ -58,8 +59,17 @@ export class AzureTtsClient {
58
59
  outputFormat: options.outputFormat ?? outputFormat,
59
60
  signal: options.signal ?? signal,
60
61
  timeoutMs: options.timeoutMs ?? timeoutMs,
62
+ timeouts: options.timeouts ?? timeouts,
61
63
  sourceNodePath: options.sourceNodePath,
62
64
  onProgress: options.onProgress ?? this.#options.onProgress,
65
+ concurrency: options.concurrency ?? this.#options.concurrency,
66
+ retryOptions: options.retryOptions ?? this.#options.retryOptions,
67
+ cancelOnFailure: options.cancelOnFailure,
68
+ resumeChunks: options.resumeChunks,
69
+ resumeChunkIndices: options.resumeChunkIndices,
70
+ customMerger: options.customMerger,
71
+ outputMimeType: options.outputMimeType,
72
+ postMergeValidator: options.postMergeValidator,
63
73
  });
64
74
  }
65
75
 
@@ -76,7 +86,10 @@ export class AzureTtsClient {
76
86
  outputFormat: options.outputFormat ?? this.#options.outputFormat,
77
87
  signal: options.signal ?? this.#options.signal,
78
88
  timeoutMs: options.timeoutMs ?? this.#options.timeoutMs,
89
+ timeouts: options.timeouts ?? this.#options.timeouts,
79
90
  onProgress: options.onProgress ?? this.#options.onProgress,
91
+ concurrency: options.concurrency ?? this.#options.concurrency,
92
+ retryOptions: options.retryOptions ?? this.#options.retryOptions,
80
93
  });
81
94
  }
82
95
 
package/src/errors.ts CHANGED
@@ -2,6 +2,7 @@ export type SynthesisErrorKind =
2
2
  | "validation-error"
3
3
  | "azure-api-error"
4
4
  | "merge-error"
5
+ | "audio-format-mismatch"
5
6
  | "unsupported-format-error"
6
7
  | "cancelled"
7
8
  | "timeout";
@@ -12,17 +13,59 @@ export class AzureTtsError extends Error {
12
13
  readonly statusText: string;
13
14
  readonly responseBody: string;
14
15
  readonly requestId: string | null;
16
+ readonly retryAfterMs?: number;
15
17
 
16
- constructor(status: number, statusText: string, responseBody: string, requestId: string | null) {
18
+ constructor(
19
+ status: number,
20
+ statusText: string,
21
+ responseBody: string,
22
+ requestId: string | null,
23
+ responseHeaders?: Headers | Readonly<Record<string, string>>,
24
+ ) {
17
25
  super(`Azure TTS request failed: ${status} ${statusText}`);
18
26
  this.name = "AzureTtsError";
19
27
  this.status = status;
20
28
  this.statusText = statusText;
21
29
  this.responseBody = responseBody;
22
30
  this.requestId = requestId;
31
+ const value =
32
+ responseHeaders instanceof Headers
33
+ ? responseHeaders.get("retry-after")
34
+ : (responseHeaders?.["retry-after"] ?? responseHeaders?.["Retry-After"]);
35
+ const seconds = value ? Number(value.trim()) : NaN;
36
+ const date = value ? Date.parse(value) : NaN;
37
+ if (Number.isFinite(seconds) && seconds >= 0) this.retryAfterMs = seconds * 1000;
38
+ else if (Number.isFinite(date)) this.retryAfterMs = Math.max(0, date - Date.now());
23
39
  }
24
40
  }
25
41
 
42
+ /** Reads Retry-After from an error-like value, returning milliseconds when present. */
43
+ export function getRetryAfterDelayMs(error: unknown): number | undefined {
44
+ if (error instanceof AzureTtsError && error.retryAfterMs !== undefined) return error.retryAfterMs;
45
+ if (!error || typeof error !== "object") return undefined;
46
+ const candidate = error as { retryAfterMs?: unknown; headers?: unknown; response?: unknown };
47
+ if (typeof candidate.retryAfterMs === "number" && candidate.retryAfterMs >= 0) return candidate.retryAfterMs;
48
+ const headers = candidate.headers ?? (candidate.response as { headers?: unknown } | undefined)?.headers;
49
+ if (headers instanceof Headers) {
50
+ const value = headers.get("retry-after");
51
+ if (!value) return undefined;
52
+ const seconds = Number(value.trim());
53
+ if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;
54
+ const date = Date.parse(value);
55
+ return Number.isFinite(date) ? Math.max(0, date - Date.now()) : undefined;
56
+ }
57
+ if (headers && typeof headers === "object") {
58
+ const value =
59
+ (headers as Record<string, unknown>)["retry-after"] ?? (headers as Record<string, unknown>)["Retry-After"];
60
+ if (typeof value !== "string") return undefined;
61
+ const seconds = Number(value.trim());
62
+ if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;
63
+ const date = Date.parse(value);
64
+ return Number.isFinite(date) ? Math.max(0, date - Date.now()) : undefined;
65
+ }
66
+ return undefined;
67
+ }
68
+
26
69
  export class AzureTtsSdkError extends AzureTtsError {
27
70
  readonly errorDetails: string;
28
71
 
@@ -63,6 +106,18 @@ export class MergeError extends Error {
63
106
  }
64
107
  }
65
108
 
109
+ /** Thrown when chunk headers describe incompatible audio streams. */
110
+ export class AudioFormatMismatchError extends Error {
111
+ readonly kind = "audio-format-mismatch" as const;
112
+ readonly inputSpecs: readonly AudioSpecification[];
113
+
114
+ constructor(message: string, inputSpecs: readonly AudioSpecification[] = []) {
115
+ super(message);
116
+ this.name = "AudioFormatMismatchError";
117
+ this.inputSpecs = inputSpecs;
118
+ }
119
+ }
120
+
66
121
  /** Thrown when audio buffers require container re-multiplexing before they can be merged. */
67
122
  export class UnsupportedMergeFormatError extends Error {
68
123
  readonly kind = "unsupported-format-error" as const;
@@ -78,16 +133,24 @@ export class UnsupportedMergeFormatError extends Error {
78
133
  export type AzureTtsSynthesisError =
79
134
  | AzureTtsError
80
135
  | MergeError
136
+ | AudioFormatMismatchError
81
137
  | UnsupportedMergeFormatError
82
138
  | SynthesisCancelledError
83
139
  | SynthesisTimeoutError;
84
140
 
85
141
  export function toSynthesisError(
86
142
  error: unknown,
87
- ): AzureTtsError | MergeError | UnsupportedMergeFormatError | SynthesisCancelledError | SynthesisTimeoutError {
143
+ ):
144
+ | AzureTtsError
145
+ | MergeError
146
+ | AudioFormatMismatchError
147
+ | UnsupportedMergeFormatError
148
+ | SynthesisCancelledError
149
+ | SynthesisTimeoutError {
88
150
  if (
89
151
  error instanceof AzureTtsError ||
90
152
  error instanceof MergeError ||
153
+ error instanceof AudioFormatMismatchError ||
91
154
  error instanceof UnsupportedMergeFormatError ||
92
155
  error instanceof SynthesisCancelledError ||
93
156
  error instanceof SynthesisTimeoutError
@@ -103,3 +166,4 @@ export function createSpeechSdkError(error: unknown): AzureTtsSdkError {
103
166
  const message = error instanceof Error ? error.message : String(error);
104
167
  return new AzureTtsSdkError(message);
105
168
  }
169
+ import type { AudioSpecification } from "./types.ts";
package/src/index.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  export type {
6
6
  AzureTtsClientOptions,
7
7
  AzureTtsLogger,
8
+ AudioSpecification,
9
+ MappingStatus,
8
10
  MergedSynthesisResult,
9
11
  SsmlSynthesisBookmark,
10
12
  SsmlSynthesisBoundary,
@@ -14,15 +16,25 @@ export type {
14
16
  SynthesisProgressEvent,
15
17
  SynthesizeChunksOptions,
16
18
  SynthesisChunkStatus,
19
+ RetryOptions,
20
+ SynthesisTimeouts,
21
+ SynthesizedChunk,
22
+ PartialChunkSynthesisResult,
23
+ PartialSynthesisResult,
24
+ CustomMergerContext,
25
+ CustomAudioMerger,
26
+ PostMergeValidator,
17
27
  TtsConfig,
18
28
  } from "./types.ts";
19
29
  export {
20
30
  AzureTtsError,
21
31
  AzureTtsSdkError,
32
+ AudioFormatMismatchError,
22
33
  MergeError,
23
34
  SynthesisCancelledError,
24
35
  SynthesisTimeoutError,
25
36
  UnsupportedMergeFormatError,
37
+ getRetryAfterDelayMs,
26
38
  } from "./errors.ts";
27
39
  export type { AzureTtsSynthesisError, SynthesisErrorKind } from "./errors.ts";
28
40
  export { AzureTtsClient } from "./client.ts";
@@ -32,13 +44,24 @@ export {
32
44
  canMergeAudioFormat,
33
45
  mergeAudioBuffers,
34
46
  mergeSynthesisResults,
47
+ inspectAudioSpecification,
35
48
  resolveMergeAudioFormat,
36
49
  synthesizeSsmlChunks,
37
50
  } from "./synthesis.ts";
38
- export type { MergeAudioFormat, MergeAudioOptions, MergeSynthesisOptions } from "./synthesis.ts";
51
+ export type {
52
+ InputAudioSpecs,
53
+ MergeAudioFormat,
54
+ MergeAudioOptions,
55
+ MergeSynthesisOptions,
56
+ } from "./synthesis.ts";
39
57
  export { DEFAULT_OUTPUT_FORMAT, resolveMimeType } from "./outputFormats.ts";
40
58
  export type { AzureTtsOutputFormat } from "./outputFormats.ts";
41
- export { ChunkValidationError, synthesizeSsmlChunksSafe, synthesizeSsmlSafe } from "./safe.ts";
59
+ export {
60
+ BatchChunkValidationError,
61
+ ChunkValidationError,
62
+ synthesizeSsmlChunksSafe,
63
+ synthesizeSsmlSafe,
64
+ } from "./safe.ts";
42
65
  export type {
43
66
  AzureApiErrorResult,
44
67
  Result,
@@ -51,6 +74,7 @@ export type {
51
74
  SsmlSynthesisChunksSafeResult,
52
75
  SynthesizeSsmlChunksSafeOptions,
53
76
  ValidationErrorResult,
77
+ ChunkDiagnostics,
54
78
  } from "./safe.ts";
55
79
  export { fetchAzureVoiceCatalog } from "./voiceCatalog.ts";
56
80
  export type {