@ssml-builder-js/azure-tts-client 2.9.0 → 2.11.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ssml-builder-js/azure-tts-client
2
2
 
3
+ ## 2.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Release v2.11.0 with Azure voice catalog APIs and synchronization tools, expanded editor voice support, and stricter background audio validation.
8
+
9
+ ## 2.10.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Release v2.10.0 with expanded Azure SSML extension attributes, stricter background audio and multi-talker validation, and updated package entrypoint documentation.
14
+
3
15
  ## 2.9.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.d.mts CHANGED
@@ -42,4 +42,29 @@ declare class AzureTtsClient {
42
42
 
43
43
  declare function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer>;
44
44
 
45
- export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type TtsConfig, synthesizeSpeech };
45
+ interface FetchAzureVoiceCatalogOptions {
46
+ apiKey: string;
47
+ region: string | string[];
48
+ }
49
+ interface AzureVoiceCatalogVoice {
50
+ name: string;
51
+ locale: string;
52
+ secondaryLocales?: readonly string[];
53
+ styles?: readonly string[];
54
+ regions: readonly string[];
55
+ status?: "ga" | "preview" | "deprecated";
56
+ }
57
+ interface FetchedAzureVoiceCatalogMetadata {
58
+ voiceCount: number;
59
+ generatedAt: string;
60
+ apiVersion: string;
61
+ regions: readonly string[];
62
+ }
63
+ interface AzureVoiceCatalog {
64
+ voices: readonly AzureVoiceCatalogVoice[];
65
+ metadata: FetchedAzureVoiceCatalogMetadata;
66
+ }
67
+ /** Fetches and deduplicates the current Azure Speech voice catalog for one or more regions. */
68
+ declare function fetchAzureVoiceCatalog(options: FetchAzureVoiceCatalogOptions): Promise<AzureVoiceCatalog>;
69
+
70
+ export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type AzureVoiceCatalog, type AzureVoiceCatalogVoice, type FetchAzureVoiceCatalogOptions, type FetchedAzureVoiceCatalogMetadata, type TtsConfig, fetchAzureVoiceCatalog, synthesizeSpeech };
package/dist/index.d.ts CHANGED
@@ -42,4 +42,29 @@ declare class AzureTtsClient {
42
42
 
43
43
  declare function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer>;
44
44
 
45
- export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type TtsConfig, synthesizeSpeech };
45
+ interface FetchAzureVoiceCatalogOptions {
46
+ apiKey: string;
47
+ region: string | string[];
48
+ }
49
+ interface AzureVoiceCatalogVoice {
50
+ name: string;
51
+ locale: string;
52
+ secondaryLocales?: readonly string[];
53
+ styles?: readonly string[];
54
+ regions: readonly string[];
55
+ status?: "ga" | "preview" | "deprecated";
56
+ }
57
+ interface FetchedAzureVoiceCatalogMetadata {
58
+ voiceCount: number;
59
+ generatedAt: string;
60
+ apiVersion: string;
61
+ regions: readonly string[];
62
+ }
63
+ interface AzureVoiceCatalog {
64
+ voices: readonly AzureVoiceCatalogVoice[];
65
+ metadata: FetchedAzureVoiceCatalogMetadata;
66
+ }
67
+ /** Fetches and deduplicates the current Azure Speech voice catalog for one or more regions. */
68
+ declare function fetchAzureVoiceCatalog(options: FetchAzureVoiceCatalogOptions): Promise<AzureVoiceCatalog>;
69
+
70
+ export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type AzureVoiceCatalog, type AzureVoiceCatalogVoice, type FetchAzureVoiceCatalogOptions, type FetchedAzureVoiceCatalogMetadata, type TtsConfig, fetchAzureVoiceCatalog, synthesizeSpeech };
package/dist/index.js CHANGED
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  AzureTtsClient: () => AzureTtsClient,
41
41
  AzureTtsError: () => AzureTtsError,
42
42
  AzureTtsSdkError: () => AzureTtsSdkError,
43
+ fetchAzureVoiceCatalog: () => fetchAzureVoiceCatalog,
43
44
  synthesizeSpeech: () => synthesizeSpeech
44
45
  });
45
46
  module.exports = __toCommonJS(index_exports);
@@ -225,11 +226,90 @@ var AzureTtsClient = class {
225
226
  }
226
227
  };
227
228
  _options = new WeakMap();
229
+
230
+ // src/voiceCatalog.ts
231
+ var AZURE_VOICE_API_VERSION = "2025-10-01";
232
+ function stringValue(value) {
233
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
234
+ }
235
+ function stringList(value) {
236
+ if (!Array.isArray(value)) return [];
237
+ return [...new Set(value.map(stringValue).filter((item) => item !== void 0))];
238
+ }
239
+ function normalizeStatus(value) {
240
+ const status = stringValue(value)?.toLowerCase();
241
+ if (status === "preview" || status === "deprecated" || status === "ga") return status;
242
+ return void 0;
243
+ }
244
+ function normalizeRegions(region) {
245
+ const regions = Array.isArray(region) ? region : [region];
246
+ const result = [...new Set(regions.map((item) => item.trim()).filter(Boolean))];
247
+ if (result.length === 0) throw new TypeError("At least one Azure Speech region is required.");
248
+ return result;
249
+ }
250
+ async function fetchRegionVoices(region, apiKey) {
251
+ const endpoint = `https://${encodeURIComponent(region)}.tts.speech.microsoft.com/cognitiveservices/voices/list`;
252
+ const response = await fetch(endpoint, {
253
+ headers: {
254
+ Accept: "application/json",
255
+ "Ocp-Apim-Subscription-Key": apiKey
256
+ }
257
+ });
258
+ if (!response.ok) {
259
+ throw new Error(`Azure List Voices API request failed for region "${region}" with HTTP ${response.status}.`);
260
+ }
261
+ const payload = await response.json();
262
+ if (!Array.isArray(payload)) throw new Error(`Azure List Voices API returned an invalid response for "${region}".`);
263
+ return payload.filter((item) => Boolean(item && typeof item === "object"));
264
+ }
265
+ async function fetchAzureVoiceCatalog(options) {
266
+ if (!options || typeof options.apiKey !== "string" || !options.apiKey.trim())
267
+ throw new TypeError("An Azure Speech API key is required.");
268
+ const regions = normalizeRegions(options.region);
269
+ const payloads = await Promise.all(regions.map((region) => fetchRegionVoices(region, options.apiKey)));
270
+ const voices = /* @__PURE__ */ new Map();
271
+ for (let regionIndex = 0; regionIndex < payloads.length; regionIndex += 1) {
272
+ const region = regions[regionIndex];
273
+ for (const record of payloads[regionIndex]) {
274
+ const name = stringValue(record.ShortName) ?? stringValue(record.Name);
275
+ const locale = stringValue(record.Locale);
276
+ if (!name || !locale) continue;
277
+ const key = name.toLowerCase();
278
+ const existing = voices.get(key);
279
+ const secondaryLocales = stringList(record.SecondaryLocaleList);
280
+ const styles = stringList(record.StyleList);
281
+ const status = normalizeStatus(record.Status);
282
+ const merged = {
283
+ name: existing?.name ?? name,
284
+ locale: existing?.locale ?? locale,
285
+ regions: [.../* @__PURE__ */ new Set([...existing?.regions ?? [], region])]
286
+ };
287
+ const mergedSecondaryLocales = [.../* @__PURE__ */ new Set([...existing?.secondaryLocales ?? [], ...secondaryLocales])];
288
+ if (mergedSecondaryLocales.length > 0) merged.secondaryLocales = mergedSecondaryLocales;
289
+ const mergedStyles = [.../* @__PURE__ */ new Set([...existing?.styles ?? [], ...styles])];
290
+ if (mergedStyles.length > 0) merged.styles = mergedStyles;
291
+ if (status) merged.status = status;
292
+ else if (existing?.status) merged.status = existing.status;
293
+ voices.set(key, merged);
294
+ }
295
+ }
296
+ const sortedVoices = [...voices.values()].sort((first, second) => first.name.localeCompare(second.name));
297
+ return {
298
+ voices: sortedVoices,
299
+ metadata: {
300
+ voiceCount: sortedVoices.length,
301
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
302
+ apiVersion: AZURE_VOICE_API_VERSION,
303
+ regions
304
+ }
305
+ };
306
+ }
228
307
  // Annotate the CommonJS export names for ESM import in node:
229
308
  0 && (module.exports = {
230
309
  AzureTtsClient,
231
310
  AzureTtsError,
232
311
  AzureTtsSdkError,
312
+ fetchAzureVoiceCatalog,
233
313
  synthesizeSpeech
234
314
  });
235
315
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/synthesis.ts","../src/speechConfig.ts","../src/outputFormats.ts","../src/client.ts"],"sourcesContent":["/**\n * azure-tts-client: Azure Text-to-Speech client for SSML playback.\n */\n\nexport type { AzureTtsClientOptions, AzureTtsLogger, TtsConfig } from \"./types.ts\";\nexport { AzureTtsError, AzureTtsSdkError } from \"./errors.ts\";\nexport { AzureTtsClient } from \"./client.ts\";\nexport { synthesizeSpeech } from \"./synthesis.ts\";\n","export class AzureTtsError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly responseBody: string;\n readonly requestId: string | null;\n\n constructor(status: number, statusText: string, responseBody: string, requestId: string | null) {\n super(`Azure TTS request failed: ${status} ${statusText}`);\n this.name = \"AzureTtsError\";\n this.status = status;\n this.statusText = statusText;\n this.responseBody = responseBody;\n this.requestId = requestId;\n }\n}\n\nexport class AzureTtsSdkError extends AzureTtsError {\n readonly errorDetails: string;\n\n constructor(errorDetails: string) {\n super(0, \"Speech SDK\", errorDetails, null);\n this.name = \"AzureTtsSdkError\";\n this.message = `Azure TTS synthesis failed: ${errorDetails}`;\n this.errorDetails = errorDetails;\n }\n}\n\nexport function createSpeechSdkError(error: unknown): AzureTtsSdkError {\n const message = error instanceof Error ? error.message : String(error);\n return new AzureTtsSdkError(message);\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\nimport { createSpeechSdkError } from \"./errors.ts\";\nimport { createSpeechConfig } from \"./speechConfig.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nfunction closeSpeechResources(speechConfig: SpeechSDK.SpeechConfig, synthesizer: SpeechSDK.SpeechSynthesizer): void {\n try {\n synthesizer.close();\n } catch {}\n\n try {\n speechConfig.close();\n } catch {}\n}\n\nexport async function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer> {\n if (config.signal?.aborted) {\n throw createSpeechSdkError(\"Speech synthesis was cancelled.\");\n }\n\n const speechConfig = createSpeechConfig(config);\n const synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, null);\n\n return await new Promise<ArrayBuffer>((resolve, reject) => {\n let resourcesClosed = false;\n let settled = false;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let abortHandler: (() => void) | undefined;\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n if (abortHandler) config.signal?.removeEventListener(\"abort\", abortHandler);\n };\n const closeResources = () => {\n if (resourcesClosed) return;\n resourcesClosed = true;\n closeSpeechResources(speechConfig, synthesizer);\n };\n const rejectWithError = (error: unknown) => {\n if (settled) return;\n settled = true;\n cleanup();\n closeResources();\n reject(createSpeechSdkError(error));\n };\n\n const cb = (result: SpeechSDK.SpeechSynthesisResult) => {\n if (settled) return;\n const { reason, errorDetails } = result;\n if (reason !== SpeechSDK.ResultReason.SynthesizingAudioCompleted) {\n const err = errorDetails || `Speech synthesis failed with reason ${reason}.`;\n rejectWithError(err);\n return;\n }\n settled = true;\n cleanup();\n closeResources();\n resolve(result.audioData);\n };\n\n try {\n if (config.signal) {\n abortHandler = () => rejectWithError(\"Speech synthesis was cancelled.\");\n config.signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n if (config.timeoutMs !== undefined && config.timeoutMs > 0) {\n timeout = setTimeout(\n () => rejectWithError(`Speech synthesis timed out after ${config.timeoutMs} ms.`),\n config.timeoutMs,\n );\n }\n synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);\n } catch (error) {\n rejectWithError(error);\n }\n });\n}\n","import { SpeechConfig } from \"microsoft-cognitiveservices-speech-sdk\";\nimport { DEFAULT_OUTPUT_FORMAT, resolveOutputFormat } from \"./outputFormats.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nexport function resolveEndpoint(config: TtsConfig): string {\n const endpoint = config.endpoint?.trim() || \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n return endpoint.replace(/\\{region\\}/g, encodeURIComponent(config.region));\n}\n\nexport function createSpeechConfig(config: TtsConfig): SpeechConfig {\n const { outputFormat = DEFAULT_OUTPUT_FORMAT, subscriptionKey } = config;\n\n const endpoint = new URL(resolveEndpoint(config));\n const speechConfig = SpeechConfig.fromEndpoint(endpoint, subscriptionKey);\n speechConfig.speechSynthesisOutputFormat = resolveOutputFormat(outputFormat);\n return speechConfig;\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\n\nexport const DEFAULT_OUTPUT_FORMAT = \"audio-16khz-128kbitrate-mono-mp3\";\n\nconst OUTPUT_FORMATS: Record<string, SpeechSDK.SpeechSynthesisOutputFormat> = {\n \"raw-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoMULaw,\n \"riff-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16KbpsMonoSiren,\n \"audio-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16KbpsMonoSiren,\n \"audio-16khz-32kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3,\n \"audio-16khz-128kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz128KBitRateMonoMp3,\n \"audio-16khz-64kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz64KBitRateMonoMp3,\n \"audio-24khz-48kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz48KBitRateMonoMp3,\n \"audio-24khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz96KBitRateMonoMp3,\n \"audio-24khz-160kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3,\n \"raw-16khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoTrueSilk,\n \"riff-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16BitMonoPcm,\n \"riff-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz16BitMonoPcm,\n \"riff-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm,\n \"riff-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoMULaw,\n \"raw-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm,\n \"raw-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm,\n \"raw-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz16BitMonoPcm,\n \"ogg-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg16Khz16BitMonoOpus,\n \"ogg-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg24Khz16BitMonoOpus,\n \"raw-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw48Khz16BitMonoPcm,\n \"riff-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff48Khz16BitMonoPcm,\n \"audio-48khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz96KBitRateMonoMp3,\n \"audio-48khz-192kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz192KBitRateMonoMp3,\n \"ogg-48khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg48Khz16BitMonoOpus,\n \"webm-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm16Khz16BitMonoOpus,\n \"webm-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16BitMonoOpus,\n \"webm-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16Bit24KbpsMonoOpus,\n \"raw-24khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoTrueSilk,\n \"raw-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoALaw,\n \"riff-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoALaw,\n \"audio-16khz-16bit-32kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16Bit32KbpsMonoOpus,\n \"audio-24khz-16bit-48kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit48KbpsMonoOpus,\n \"audio-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit24KbpsMonoOpus,\n \"raw-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw22050Hz16BitMonoPcm,\n \"riff-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff22050Hz16BitMonoPcm,\n \"raw-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw44100Hz16BitMonoPcm,\n \"riff-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff44100Hz16BitMonoPcm,\n \"amr-wb-16000hz\": SpeechSDK.SpeechSynthesisOutputFormat.AmrWb16000Hz,\n \"g722-16khz-64kbps\": SpeechSDK.SpeechSynthesisOutputFormat.G72216Khz64Kbps,\n};\n\nexport function resolveOutputFormat(outputFormat: string): SpeechSDK.SpeechSynthesisOutputFormat {\n const resolvedFormat = OUTPUT_FORMATS[outputFormat];\n if (resolvedFormat === undefined) {\n throw new Error(`Unsupported Azure Speech output format: ${outputFormat}`);\n }\n\n return resolvedFormat;\n}\n","import { synthesizeSpeech } from \"./synthesis.ts\";\nimport type { AzureTtsClientOptions } from \"./types.ts\";\n\nconst ENDPOINT_TEMPLATE = \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n\nexport class AzureTtsClient {\n readonly #options: AzureTtsClientOptions;\n\n constructor(options: AzureTtsClientOptions) {\n this.#options = options;\n }\n\n async synthesize(ssml: string): Promise<ArrayBuffer> {\n const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;\n const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace(\"{region}\", region);\n this.#options.logger?.debug?.(\"Using Azure TTS endpoint:\", endpoint);\n\n const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };\n return synthesizeSpeech(ssml, config);\n }\n}\n"],"mappings":"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","names":["SpeechSDK"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/synthesis.ts","../src/speechConfig.ts","../src/outputFormats.ts","../src/client.ts","../src/voiceCatalog.ts"],"sourcesContent":["/**\n * azure-tts-client: Azure Text-to-Speech client for SSML playback.\n */\n\nexport type { AzureTtsClientOptions, AzureTtsLogger, TtsConfig } from \"./types.ts\";\nexport { AzureTtsError, AzureTtsSdkError } from \"./errors.ts\";\nexport { AzureTtsClient } from \"./client.ts\";\nexport { synthesizeSpeech } from \"./synthesis.ts\";\nexport { fetchAzureVoiceCatalog } from \"./voiceCatalog.ts\";\nexport type {\n AzureVoiceCatalog,\n AzureVoiceCatalogVoice,\n FetchedAzureVoiceCatalogMetadata,\n FetchAzureVoiceCatalogOptions,\n} from \"./voiceCatalog.ts\";\n","export class AzureTtsError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly responseBody: string;\n readonly requestId: string | null;\n\n constructor(status: number, statusText: string, responseBody: string, requestId: string | null) {\n super(`Azure TTS request failed: ${status} ${statusText}`);\n this.name = \"AzureTtsError\";\n this.status = status;\n this.statusText = statusText;\n this.responseBody = responseBody;\n this.requestId = requestId;\n }\n}\n\nexport class AzureTtsSdkError extends AzureTtsError {\n readonly errorDetails: string;\n\n constructor(errorDetails: string) {\n super(0, \"Speech SDK\", errorDetails, null);\n this.name = \"AzureTtsSdkError\";\n this.message = `Azure TTS synthesis failed: ${errorDetails}`;\n this.errorDetails = errorDetails;\n }\n}\n\nexport function createSpeechSdkError(error: unknown): AzureTtsSdkError {\n const message = error instanceof Error ? error.message : String(error);\n return new AzureTtsSdkError(message);\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\nimport { createSpeechSdkError } from \"./errors.ts\";\nimport { createSpeechConfig } from \"./speechConfig.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nfunction closeSpeechResources(speechConfig: SpeechSDK.SpeechConfig, synthesizer: SpeechSDK.SpeechSynthesizer): void {\n try {\n synthesizer.close();\n } catch {}\n\n try {\n speechConfig.close();\n } catch {}\n}\n\nexport async function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer> {\n if (config.signal?.aborted) {\n throw createSpeechSdkError(\"Speech synthesis was cancelled.\");\n }\n\n const speechConfig = createSpeechConfig(config);\n const synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, null);\n\n return await new Promise<ArrayBuffer>((resolve, reject) => {\n let resourcesClosed = false;\n let settled = false;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let abortHandler: (() => void) | undefined;\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n if (abortHandler) config.signal?.removeEventListener(\"abort\", abortHandler);\n };\n const closeResources = () => {\n if (resourcesClosed) return;\n resourcesClosed = true;\n closeSpeechResources(speechConfig, synthesizer);\n };\n const rejectWithError = (error: unknown) => {\n if (settled) return;\n settled = true;\n cleanup();\n closeResources();\n reject(createSpeechSdkError(error));\n };\n\n const cb = (result: SpeechSDK.SpeechSynthesisResult) => {\n if (settled) return;\n const { reason, errorDetails } = result;\n if (reason !== SpeechSDK.ResultReason.SynthesizingAudioCompleted) {\n const err = errorDetails || `Speech synthesis failed with reason ${reason}.`;\n rejectWithError(err);\n return;\n }\n settled = true;\n cleanup();\n closeResources();\n resolve(result.audioData);\n };\n\n try {\n if (config.signal) {\n abortHandler = () => rejectWithError(\"Speech synthesis was cancelled.\");\n config.signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n if (config.timeoutMs !== undefined && config.timeoutMs > 0) {\n timeout = setTimeout(\n () => rejectWithError(`Speech synthesis timed out after ${config.timeoutMs} ms.`),\n config.timeoutMs,\n );\n }\n synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);\n } catch (error) {\n rejectWithError(error);\n }\n });\n}\n","import { SpeechConfig } from \"microsoft-cognitiveservices-speech-sdk\";\nimport { DEFAULT_OUTPUT_FORMAT, resolveOutputFormat } from \"./outputFormats.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nexport function resolveEndpoint(config: TtsConfig): string {\n const endpoint = config.endpoint?.trim() || \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n return endpoint.replace(/\\{region\\}/g, encodeURIComponent(config.region));\n}\n\nexport function createSpeechConfig(config: TtsConfig): SpeechConfig {\n const { outputFormat = DEFAULT_OUTPUT_FORMAT, subscriptionKey } = config;\n\n const endpoint = new URL(resolveEndpoint(config));\n const speechConfig = SpeechConfig.fromEndpoint(endpoint, subscriptionKey);\n speechConfig.speechSynthesisOutputFormat = resolveOutputFormat(outputFormat);\n return speechConfig;\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\n\nexport const DEFAULT_OUTPUT_FORMAT = \"audio-16khz-128kbitrate-mono-mp3\";\n\nconst OUTPUT_FORMATS: Record<string, SpeechSDK.SpeechSynthesisOutputFormat> = {\n \"raw-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoMULaw,\n \"riff-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16KbpsMonoSiren,\n \"audio-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16KbpsMonoSiren,\n \"audio-16khz-32kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3,\n \"audio-16khz-128kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz128KBitRateMonoMp3,\n \"audio-16khz-64kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz64KBitRateMonoMp3,\n \"audio-24khz-48kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz48KBitRateMonoMp3,\n \"audio-24khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz96KBitRateMonoMp3,\n \"audio-24khz-160kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3,\n \"raw-16khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoTrueSilk,\n \"riff-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16BitMonoPcm,\n \"riff-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz16BitMonoPcm,\n \"riff-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm,\n \"riff-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoMULaw,\n \"raw-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm,\n \"raw-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm,\n \"raw-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz16BitMonoPcm,\n \"ogg-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg16Khz16BitMonoOpus,\n \"ogg-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg24Khz16BitMonoOpus,\n \"raw-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw48Khz16BitMonoPcm,\n \"riff-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff48Khz16BitMonoPcm,\n \"audio-48khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz96KBitRateMonoMp3,\n \"audio-48khz-192kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz192KBitRateMonoMp3,\n \"ogg-48khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg48Khz16BitMonoOpus,\n \"webm-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm16Khz16BitMonoOpus,\n \"webm-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16BitMonoOpus,\n \"webm-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16Bit24KbpsMonoOpus,\n \"raw-24khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoTrueSilk,\n \"raw-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoALaw,\n \"riff-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoALaw,\n \"audio-16khz-16bit-32kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16Bit32KbpsMonoOpus,\n \"audio-24khz-16bit-48kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit48KbpsMonoOpus,\n \"audio-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit24KbpsMonoOpus,\n \"raw-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw22050Hz16BitMonoPcm,\n \"riff-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff22050Hz16BitMonoPcm,\n \"raw-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw44100Hz16BitMonoPcm,\n \"riff-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff44100Hz16BitMonoPcm,\n \"amr-wb-16000hz\": SpeechSDK.SpeechSynthesisOutputFormat.AmrWb16000Hz,\n \"g722-16khz-64kbps\": SpeechSDK.SpeechSynthesisOutputFormat.G72216Khz64Kbps,\n};\n\nexport function resolveOutputFormat(outputFormat: string): SpeechSDK.SpeechSynthesisOutputFormat {\n const resolvedFormat = OUTPUT_FORMATS[outputFormat];\n if (resolvedFormat === undefined) {\n throw new Error(`Unsupported Azure Speech output format: ${outputFormat}`);\n }\n\n return resolvedFormat;\n}\n","import { synthesizeSpeech } from \"./synthesis.ts\";\nimport type { AzureTtsClientOptions } from \"./types.ts\";\n\nconst ENDPOINT_TEMPLATE = \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n\nexport class AzureTtsClient {\n readonly #options: AzureTtsClientOptions;\n\n constructor(options: AzureTtsClientOptions) {\n this.#options = options;\n }\n\n async synthesize(ssml: string): Promise<ArrayBuffer> {\n const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;\n const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace(\"{region}\", region);\n this.#options.logger?.debug?.(\"Using Azure TTS endpoint:\", endpoint);\n\n const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };\n return synthesizeSpeech(ssml, config);\n }\n}\n","const AZURE_VOICE_API_VERSION = \"2025-10-01\";\n\nexport interface FetchAzureVoiceCatalogOptions {\n apiKey: string;\n region: string | string[];\n}\n\nexport interface AzureVoiceCatalogVoice {\n name: string;\n locale: string;\n secondaryLocales?: readonly string[];\n styles?: readonly string[];\n regions: readonly string[];\n status?: \"ga\" | \"preview\" | \"deprecated\";\n}\n\nexport interface FetchedAzureVoiceCatalogMetadata {\n voiceCount: number;\n generatedAt: string;\n apiVersion: string;\n regions: readonly string[];\n}\n\nexport interface AzureVoiceCatalog {\n voices: readonly AzureVoiceCatalogVoice[];\n metadata: FetchedAzureVoiceCatalogMetadata;\n}\n\ninterface AzureVoiceApiRecord {\n Locale?: unknown;\n Name?: unknown;\n SecondaryLocaleList?: unknown;\n ShortName?: unknown;\n Status?: unknown;\n StyleList?: unknown;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction stringList(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return [...new Set(value.map(stringValue).filter((item): item is string => item !== undefined))];\n}\n\nfunction normalizeStatus(value: unknown): AzureVoiceCatalogVoice[\"status\"] {\n const status = stringValue(value)?.toLowerCase();\n if (status === \"preview\" || status === \"deprecated\" || status === \"ga\") return status;\n return undefined;\n}\n\nfunction normalizeRegions(region: string | string[]): string[] {\n const regions = Array.isArray(region) ? region : [region];\n const result = [...new Set(regions.map((item) => item.trim()).filter(Boolean))];\n if (result.length === 0) throw new TypeError(\"At least one Azure Speech region is required.\");\n return result;\n}\n\nasync function fetchRegionVoices(region: string, apiKey: string): Promise<AzureVoiceApiRecord[]> {\n const endpoint = `https://${encodeURIComponent(region)}.tts.speech.microsoft.com/cognitiveservices/voices/list`;\n const response = await fetch(endpoint, {\n headers: {\n Accept: \"application/json\",\n \"Ocp-Apim-Subscription-Key\": apiKey,\n },\n });\n if (!response.ok) {\n throw new Error(`Azure List Voices API request failed for region \"${region}\" with HTTP ${response.status}.`);\n }\n const payload: unknown = await response.json();\n if (!Array.isArray(payload)) throw new Error(`Azure List Voices API returned an invalid response for \"${region}\".`);\n return payload.filter((item): item is AzureVoiceApiRecord => Boolean(item && typeof item === \"object\"));\n}\n\n/** Fetches and deduplicates the current Azure Speech voice catalog for one or more regions. */\nexport async function fetchAzureVoiceCatalog(options: FetchAzureVoiceCatalogOptions): Promise<AzureVoiceCatalog> {\n if (!options || typeof options.apiKey !== \"string\" || !options.apiKey.trim())\n throw new TypeError(\"An Azure Speech API key is required.\");\n const regions = normalizeRegions(options.region);\n const payloads = await Promise.all(regions.map((region) => fetchRegionVoices(region, options.apiKey)));\n const voices = new Map<string, AzureVoiceCatalogVoice>();\n\n for (let regionIndex = 0; regionIndex < payloads.length; regionIndex += 1) {\n const region = regions[regionIndex];\n for (const record of payloads[regionIndex]) {\n const name = stringValue(record.ShortName) ?? stringValue(record.Name);\n const locale = stringValue(record.Locale);\n if (!name || !locale) continue;\n const key = name.toLowerCase();\n const existing = voices.get(key);\n const secondaryLocales = stringList(record.SecondaryLocaleList);\n const styles = stringList(record.StyleList);\n const status = normalizeStatus(record.Status);\n const merged: AzureVoiceCatalogVoice = {\n name: existing?.name ?? name,\n locale: existing?.locale ?? locale,\n regions: [...new Set([...(existing?.regions ?? []), region])],\n };\n const mergedSecondaryLocales = [...new Set([...(existing?.secondaryLocales ?? []), ...secondaryLocales])];\n if (mergedSecondaryLocales.length > 0) merged.secondaryLocales = mergedSecondaryLocales;\n const mergedStyles = [...new Set([...(existing?.styles ?? []), ...styles])];\n if (mergedStyles.length > 0) merged.styles = mergedStyles;\n if (status) merged.status = status;\n else if (existing?.status) merged.status = existing.status;\n voices.set(key, merged);\n }\n }\n\n const sortedVoices = [...voices.values()].sort((first, second) => first.name.localeCompare(second.name));\n return {\n voices: sortedVoices,\n metadata: {\n voiceCount: sortedVoices.length,\n generatedAt: new Date().toISOString(),\n apiVersion: AZURE_VOICE_API_VERSION,\n regions,\n },\n };\n}\n"],"mappings":"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","names":["SpeechSDK"]}
package/dist/index.mjs CHANGED
@@ -187,10 +187,89 @@ var AzureTtsClient = class {
187
187
  }
188
188
  };
189
189
  _options = new WeakMap();
190
+
191
+ // src/voiceCatalog.ts
192
+ var AZURE_VOICE_API_VERSION = "2025-10-01";
193
+ function stringValue(value) {
194
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
195
+ }
196
+ function stringList(value) {
197
+ if (!Array.isArray(value)) return [];
198
+ return [...new Set(value.map(stringValue).filter((item) => item !== void 0))];
199
+ }
200
+ function normalizeStatus(value) {
201
+ const status = stringValue(value)?.toLowerCase();
202
+ if (status === "preview" || status === "deprecated" || status === "ga") return status;
203
+ return void 0;
204
+ }
205
+ function normalizeRegions(region) {
206
+ const regions = Array.isArray(region) ? region : [region];
207
+ const result = [...new Set(regions.map((item) => item.trim()).filter(Boolean))];
208
+ if (result.length === 0) throw new TypeError("At least one Azure Speech region is required.");
209
+ return result;
210
+ }
211
+ async function fetchRegionVoices(region, apiKey) {
212
+ const endpoint = `https://${encodeURIComponent(region)}.tts.speech.microsoft.com/cognitiveservices/voices/list`;
213
+ const response = await fetch(endpoint, {
214
+ headers: {
215
+ Accept: "application/json",
216
+ "Ocp-Apim-Subscription-Key": apiKey
217
+ }
218
+ });
219
+ if (!response.ok) {
220
+ throw new Error(`Azure List Voices API request failed for region "${region}" with HTTP ${response.status}.`);
221
+ }
222
+ const payload = await response.json();
223
+ if (!Array.isArray(payload)) throw new Error(`Azure List Voices API returned an invalid response for "${region}".`);
224
+ return payload.filter((item) => Boolean(item && typeof item === "object"));
225
+ }
226
+ async function fetchAzureVoiceCatalog(options) {
227
+ if (!options || typeof options.apiKey !== "string" || !options.apiKey.trim())
228
+ throw new TypeError("An Azure Speech API key is required.");
229
+ const regions = normalizeRegions(options.region);
230
+ const payloads = await Promise.all(regions.map((region) => fetchRegionVoices(region, options.apiKey)));
231
+ const voices = /* @__PURE__ */ new Map();
232
+ for (let regionIndex = 0; regionIndex < payloads.length; regionIndex += 1) {
233
+ const region = regions[regionIndex];
234
+ for (const record of payloads[regionIndex]) {
235
+ const name = stringValue(record.ShortName) ?? stringValue(record.Name);
236
+ const locale = stringValue(record.Locale);
237
+ if (!name || !locale) continue;
238
+ const key = name.toLowerCase();
239
+ const existing = voices.get(key);
240
+ const secondaryLocales = stringList(record.SecondaryLocaleList);
241
+ const styles = stringList(record.StyleList);
242
+ const status = normalizeStatus(record.Status);
243
+ const merged = {
244
+ name: existing?.name ?? name,
245
+ locale: existing?.locale ?? locale,
246
+ regions: [.../* @__PURE__ */ new Set([...existing?.regions ?? [], region])]
247
+ };
248
+ const mergedSecondaryLocales = [.../* @__PURE__ */ new Set([...existing?.secondaryLocales ?? [], ...secondaryLocales])];
249
+ if (mergedSecondaryLocales.length > 0) merged.secondaryLocales = mergedSecondaryLocales;
250
+ const mergedStyles = [.../* @__PURE__ */ new Set([...existing?.styles ?? [], ...styles])];
251
+ if (mergedStyles.length > 0) merged.styles = mergedStyles;
252
+ if (status) merged.status = status;
253
+ else if (existing?.status) merged.status = existing.status;
254
+ voices.set(key, merged);
255
+ }
256
+ }
257
+ const sortedVoices = [...voices.values()].sort((first, second) => first.name.localeCompare(second.name));
258
+ return {
259
+ voices: sortedVoices,
260
+ metadata: {
261
+ voiceCount: sortedVoices.length,
262
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
263
+ apiVersion: AZURE_VOICE_API_VERSION,
264
+ regions
265
+ }
266
+ };
267
+ }
190
268
  export {
191
269
  AzureTtsClient,
192
270
  AzureTtsError,
193
271
  AzureTtsSdkError,
272
+ fetchAzureVoiceCatalog,
194
273
  synthesizeSpeech
195
274
  };
196
275
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/synthesis.ts","../src/speechConfig.ts","../src/outputFormats.ts","../src/client.ts"],"sourcesContent":["export class AzureTtsError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly responseBody: string;\n readonly requestId: string | null;\n\n constructor(status: number, statusText: string, responseBody: string, requestId: string | null) {\n super(`Azure TTS request failed: ${status} ${statusText}`);\n this.name = \"AzureTtsError\";\n this.status = status;\n this.statusText = statusText;\n this.responseBody = responseBody;\n this.requestId = requestId;\n }\n}\n\nexport class AzureTtsSdkError extends AzureTtsError {\n readonly errorDetails: string;\n\n constructor(errorDetails: string) {\n super(0, \"Speech SDK\", errorDetails, null);\n this.name = \"AzureTtsSdkError\";\n this.message = `Azure TTS synthesis failed: ${errorDetails}`;\n this.errorDetails = errorDetails;\n }\n}\n\nexport function createSpeechSdkError(error: unknown): AzureTtsSdkError {\n const message = error instanceof Error ? error.message : String(error);\n return new AzureTtsSdkError(message);\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\nimport { createSpeechSdkError } from \"./errors.ts\";\nimport { createSpeechConfig } from \"./speechConfig.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nfunction closeSpeechResources(speechConfig: SpeechSDK.SpeechConfig, synthesizer: SpeechSDK.SpeechSynthesizer): void {\n try {\n synthesizer.close();\n } catch {}\n\n try {\n speechConfig.close();\n } catch {}\n}\n\nexport async function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer> {\n if (config.signal?.aborted) {\n throw createSpeechSdkError(\"Speech synthesis was cancelled.\");\n }\n\n const speechConfig = createSpeechConfig(config);\n const synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, null);\n\n return await new Promise<ArrayBuffer>((resolve, reject) => {\n let resourcesClosed = false;\n let settled = false;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let abortHandler: (() => void) | undefined;\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n if (abortHandler) config.signal?.removeEventListener(\"abort\", abortHandler);\n };\n const closeResources = () => {\n if (resourcesClosed) return;\n resourcesClosed = true;\n closeSpeechResources(speechConfig, synthesizer);\n };\n const rejectWithError = (error: unknown) => {\n if (settled) return;\n settled = true;\n cleanup();\n closeResources();\n reject(createSpeechSdkError(error));\n };\n\n const cb = (result: SpeechSDK.SpeechSynthesisResult) => {\n if (settled) return;\n const { reason, errorDetails } = result;\n if (reason !== SpeechSDK.ResultReason.SynthesizingAudioCompleted) {\n const err = errorDetails || `Speech synthesis failed with reason ${reason}.`;\n rejectWithError(err);\n return;\n }\n settled = true;\n cleanup();\n closeResources();\n resolve(result.audioData);\n };\n\n try {\n if (config.signal) {\n abortHandler = () => rejectWithError(\"Speech synthesis was cancelled.\");\n config.signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n if (config.timeoutMs !== undefined && config.timeoutMs > 0) {\n timeout = setTimeout(\n () => rejectWithError(`Speech synthesis timed out after ${config.timeoutMs} ms.`),\n config.timeoutMs,\n );\n }\n synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);\n } catch (error) {\n rejectWithError(error);\n }\n });\n}\n","import { SpeechConfig } from \"microsoft-cognitiveservices-speech-sdk\";\nimport { DEFAULT_OUTPUT_FORMAT, resolveOutputFormat } from \"./outputFormats.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nexport function resolveEndpoint(config: TtsConfig): string {\n const endpoint = config.endpoint?.trim() || \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n return endpoint.replace(/\\{region\\}/g, encodeURIComponent(config.region));\n}\n\nexport function createSpeechConfig(config: TtsConfig): SpeechConfig {\n const { outputFormat = DEFAULT_OUTPUT_FORMAT, subscriptionKey } = config;\n\n const endpoint = new URL(resolveEndpoint(config));\n const speechConfig = SpeechConfig.fromEndpoint(endpoint, subscriptionKey);\n speechConfig.speechSynthesisOutputFormat = resolveOutputFormat(outputFormat);\n return speechConfig;\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\n\nexport const DEFAULT_OUTPUT_FORMAT = \"audio-16khz-128kbitrate-mono-mp3\";\n\nconst OUTPUT_FORMATS: Record<string, SpeechSDK.SpeechSynthesisOutputFormat> = {\n \"raw-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoMULaw,\n \"riff-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16KbpsMonoSiren,\n \"audio-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16KbpsMonoSiren,\n \"audio-16khz-32kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3,\n \"audio-16khz-128kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz128KBitRateMonoMp3,\n \"audio-16khz-64kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz64KBitRateMonoMp3,\n \"audio-24khz-48kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz48KBitRateMonoMp3,\n \"audio-24khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz96KBitRateMonoMp3,\n \"audio-24khz-160kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3,\n \"raw-16khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoTrueSilk,\n \"riff-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16BitMonoPcm,\n \"riff-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz16BitMonoPcm,\n \"riff-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm,\n \"riff-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoMULaw,\n \"raw-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm,\n \"raw-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm,\n \"raw-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz16BitMonoPcm,\n \"ogg-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg16Khz16BitMonoOpus,\n \"ogg-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg24Khz16BitMonoOpus,\n \"raw-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw48Khz16BitMonoPcm,\n \"riff-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff48Khz16BitMonoPcm,\n \"audio-48khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz96KBitRateMonoMp3,\n \"audio-48khz-192kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz192KBitRateMonoMp3,\n \"ogg-48khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg48Khz16BitMonoOpus,\n \"webm-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm16Khz16BitMonoOpus,\n \"webm-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16BitMonoOpus,\n \"webm-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16Bit24KbpsMonoOpus,\n \"raw-24khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoTrueSilk,\n \"raw-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoALaw,\n \"riff-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoALaw,\n \"audio-16khz-16bit-32kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16Bit32KbpsMonoOpus,\n \"audio-24khz-16bit-48kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit48KbpsMonoOpus,\n \"audio-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit24KbpsMonoOpus,\n \"raw-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw22050Hz16BitMonoPcm,\n \"riff-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff22050Hz16BitMonoPcm,\n \"raw-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw44100Hz16BitMonoPcm,\n \"riff-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff44100Hz16BitMonoPcm,\n \"amr-wb-16000hz\": SpeechSDK.SpeechSynthesisOutputFormat.AmrWb16000Hz,\n \"g722-16khz-64kbps\": SpeechSDK.SpeechSynthesisOutputFormat.G72216Khz64Kbps,\n};\n\nexport function resolveOutputFormat(outputFormat: string): SpeechSDK.SpeechSynthesisOutputFormat {\n const resolvedFormat = OUTPUT_FORMATS[outputFormat];\n if (resolvedFormat === undefined) {\n throw new Error(`Unsupported Azure Speech output format: ${outputFormat}`);\n }\n\n return resolvedFormat;\n}\n","import { synthesizeSpeech } from \"./synthesis.ts\";\nimport type { AzureTtsClientOptions } from \"./types.ts\";\n\nconst ENDPOINT_TEMPLATE = \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n\nexport class AzureTtsClient {\n readonly #options: AzureTtsClientOptions;\n\n constructor(options: AzureTtsClientOptions) {\n this.#options = options;\n }\n\n async synthesize(ssml: string): Promise<ArrayBuffer> {\n const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;\n const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace(\"{region}\", region);\n this.#options.logger?.debug?.(\"Using Azure TTS endpoint:\", endpoint);\n\n const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };\n return synthesizeSpeech(ssml, config);\n }\n}\n"],"mappings":"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","names":["SpeechSDK"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/synthesis.ts","../src/speechConfig.ts","../src/outputFormats.ts","../src/client.ts","../src/voiceCatalog.ts"],"sourcesContent":["export class AzureTtsError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly responseBody: string;\n readonly requestId: string | null;\n\n constructor(status: number, statusText: string, responseBody: string, requestId: string | null) {\n super(`Azure TTS request failed: ${status} ${statusText}`);\n this.name = \"AzureTtsError\";\n this.status = status;\n this.statusText = statusText;\n this.responseBody = responseBody;\n this.requestId = requestId;\n }\n}\n\nexport class AzureTtsSdkError extends AzureTtsError {\n readonly errorDetails: string;\n\n constructor(errorDetails: string) {\n super(0, \"Speech SDK\", errorDetails, null);\n this.name = \"AzureTtsSdkError\";\n this.message = `Azure TTS synthesis failed: ${errorDetails}`;\n this.errorDetails = errorDetails;\n }\n}\n\nexport function createSpeechSdkError(error: unknown): AzureTtsSdkError {\n const message = error instanceof Error ? error.message : String(error);\n return new AzureTtsSdkError(message);\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\nimport { createSpeechSdkError } from \"./errors.ts\";\nimport { createSpeechConfig } from \"./speechConfig.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nfunction closeSpeechResources(speechConfig: SpeechSDK.SpeechConfig, synthesizer: SpeechSDK.SpeechSynthesizer): void {\n try {\n synthesizer.close();\n } catch {}\n\n try {\n speechConfig.close();\n } catch {}\n}\n\nexport async function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer> {\n if (config.signal?.aborted) {\n throw createSpeechSdkError(\"Speech synthesis was cancelled.\");\n }\n\n const speechConfig = createSpeechConfig(config);\n const synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, null);\n\n return await new Promise<ArrayBuffer>((resolve, reject) => {\n let resourcesClosed = false;\n let settled = false;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let abortHandler: (() => void) | undefined;\n const cleanup = () => {\n if (timeout) clearTimeout(timeout);\n if (abortHandler) config.signal?.removeEventListener(\"abort\", abortHandler);\n };\n const closeResources = () => {\n if (resourcesClosed) return;\n resourcesClosed = true;\n closeSpeechResources(speechConfig, synthesizer);\n };\n const rejectWithError = (error: unknown) => {\n if (settled) return;\n settled = true;\n cleanup();\n closeResources();\n reject(createSpeechSdkError(error));\n };\n\n const cb = (result: SpeechSDK.SpeechSynthesisResult) => {\n if (settled) return;\n const { reason, errorDetails } = result;\n if (reason !== SpeechSDK.ResultReason.SynthesizingAudioCompleted) {\n const err = errorDetails || `Speech synthesis failed with reason ${reason}.`;\n rejectWithError(err);\n return;\n }\n settled = true;\n cleanup();\n closeResources();\n resolve(result.audioData);\n };\n\n try {\n if (config.signal) {\n abortHandler = () => rejectWithError(\"Speech synthesis was cancelled.\");\n config.signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n if (config.timeoutMs !== undefined && config.timeoutMs > 0) {\n timeout = setTimeout(\n () => rejectWithError(`Speech synthesis timed out after ${config.timeoutMs} ms.`),\n config.timeoutMs,\n );\n }\n synthesizer.speakSsmlAsync(ssml, cb, rejectWithError);\n } catch (error) {\n rejectWithError(error);\n }\n });\n}\n","import { SpeechConfig } from \"microsoft-cognitiveservices-speech-sdk\";\nimport { DEFAULT_OUTPUT_FORMAT, resolveOutputFormat } from \"./outputFormats.ts\";\nimport type { TtsConfig } from \"./types.ts\";\n\nexport function resolveEndpoint(config: TtsConfig): string {\n const endpoint = config.endpoint?.trim() || \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n return endpoint.replace(/\\{region\\}/g, encodeURIComponent(config.region));\n}\n\nexport function createSpeechConfig(config: TtsConfig): SpeechConfig {\n const { outputFormat = DEFAULT_OUTPUT_FORMAT, subscriptionKey } = config;\n\n const endpoint = new URL(resolveEndpoint(config));\n const speechConfig = SpeechConfig.fromEndpoint(endpoint, subscriptionKey);\n speechConfig.speechSynthesisOutputFormat = resolveOutputFormat(outputFormat);\n return speechConfig;\n}\n","import * as SpeechSDK from \"microsoft-cognitiveservices-speech-sdk\";\n\nexport const DEFAULT_OUTPUT_FORMAT = \"audio-16khz-128kbitrate-mono-mp3\";\n\nconst OUTPUT_FORMATS: Record<string, SpeechSDK.SpeechSynthesisOutputFormat> = {\n \"raw-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoMULaw,\n \"riff-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16KbpsMonoSiren,\n \"audio-16khz-16kbps-mono-siren\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16KbpsMonoSiren,\n \"audio-16khz-32kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3,\n \"audio-16khz-128kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz128KBitRateMonoMp3,\n \"audio-16khz-64kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz64KBitRateMonoMp3,\n \"audio-24khz-48kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz48KBitRateMonoMp3,\n \"audio-24khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz96KBitRateMonoMp3,\n \"audio-24khz-160kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3,\n \"raw-16khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoTrueSilk,\n \"riff-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff16Khz16BitMonoPcm,\n \"riff-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz16BitMonoPcm,\n \"riff-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm,\n \"riff-8khz-8bit-mono-mulaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoMULaw,\n \"raw-16khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm,\n \"raw-24khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm,\n \"raw-8khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz16BitMonoPcm,\n \"ogg-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg16Khz16BitMonoOpus,\n \"ogg-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg24Khz16BitMonoOpus,\n \"raw-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw48Khz16BitMonoPcm,\n \"riff-48khz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff48Khz16BitMonoPcm,\n \"audio-48khz-96kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz96KBitRateMonoMp3,\n \"audio-48khz-192kbitrate-mono-mp3\": SpeechSDK.SpeechSynthesisOutputFormat.Audio48Khz192KBitRateMonoMp3,\n \"ogg-48khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Ogg48Khz16BitMonoOpus,\n \"webm-16khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm16Khz16BitMonoOpus,\n \"webm-24khz-16bit-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16BitMonoOpus,\n \"webm-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Webm24Khz16Bit24KbpsMonoOpus,\n \"raw-24khz-16bit-mono-truesilk\": SpeechSDK.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoTrueSilk,\n \"raw-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Raw8Khz8BitMonoALaw,\n \"riff-8khz-8bit-mono-alaw\": SpeechSDK.SpeechSynthesisOutputFormat.Riff8Khz8BitMonoALaw,\n \"audio-16khz-16bit-32kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio16Khz16Bit32KbpsMonoOpus,\n \"audio-24khz-16bit-48kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit48KbpsMonoOpus,\n \"audio-24khz-16bit-24kbps-mono-opus\": SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz16Bit24KbpsMonoOpus,\n \"raw-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw22050Hz16BitMonoPcm,\n \"riff-22050hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff22050Hz16BitMonoPcm,\n \"raw-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Raw44100Hz16BitMonoPcm,\n \"riff-44100hz-16bit-mono-pcm\": SpeechSDK.SpeechSynthesisOutputFormat.Riff44100Hz16BitMonoPcm,\n \"amr-wb-16000hz\": SpeechSDK.SpeechSynthesisOutputFormat.AmrWb16000Hz,\n \"g722-16khz-64kbps\": SpeechSDK.SpeechSynthesisOutputFormat.G72216Khz64Kbps,\n};\n\nexport function resolveOutputFormat(outputFormat: string): SpeechSDK.SpeechSynthesisOutputFormat {\n const resolvedFormat = OUTPUT_FORMATS[outputFormat];\n if (resolvedFormat === undefined) {\n throw new Error(`Unsupported Azure Speech output format: ${outputFormat}`);\n }\n\n return resolvedFormat;\n}\n","import { synthesizeSpeech } from \"./synthesis.ts\";\nimport type { AzureTtsClientOptions } from \"./types.ts\";\n\nconst ENDPOINT_TEMPLATE = \"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1\";\n\nexport class AzureTtsClient {\n readonly #options: AzureTtsClientOptions;\n\n constructor(options: AzureTtsClientOptions) {\n this.#options = options;\n }\n\n async synthesize(ssml: string): Promise<ArrayBuffer> {\n const { region, subscriptionKey, outputFormat, signal, timeoutMs } = this.#options;\n const endpoint = this.#options.endpoint?.trim() || ENDPOINT_TEMPLATE.replace(\"{region}\", region);\n this.#options.logger?.debug?.(\"Using Azure TTS endpoint:\", endpoint);\n\n const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };\n return synthesizeSpeech(ssml, config);\n }\n}\n","const AZURE_VOICE_API_VERSION = \"2025-10-01\";\n\nexport interface FetchAzureVoiceCatalogOptions {\n apiKey: string;\n region: string | string[];\n}\n\nexport interface AzureVoiceCatalogVoice {\n name: string;\n locale: string;\n secondaryLocales?: readonly string[];\n styles?: readonly string[];\n regions: readonly string[];\n status?: \"ga\" | \"preview\" | \"deprecated\";\n}\n\nexport interface FetchedAzureVoiceCatalogMetadata {\n voiceCount: number;\n generatedAt: string;\n apiVersion: string;\n regions: readonly string[];\n}\n\nexport interface AzureVoiceCatalog {\n voices: readonly AzureVoiceCatalogVoice[];\n metadata: FetchedAzureVoiceCatalogMetadata;\n}\n\ninterface AzureVoiceApiRecord {\n Locale?: unknown;\n Name?: unknown;\n SecondaryLocaleList?: unknown;\n ShortName?: unknown;\n Status?: unknown;\n StyleList?: unknown;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction stringList(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return [...new Set(value.map(stringValue).filter((item): item is string => item !== undefined))];\n}\n\nfunction normalizeStatus(value: unknown): AzureVoiceCatalogVoice[\"status\"] {\n const status = stringValue(value)?.toLowerCase();\n if (status === \"preview\" || status === \"deprecated\" || status === \"ga\") return status;\n return undefined;\n}\n\nfunction normalizeRegions(region: string | string[]): string[] {\n const regions = Array.isArray(region) ? region : [region];\n const result = [...new Set(regions.map((item) => item.trim()).filter(Boolean))];\n if (result.length === 0) throw new TypeError(\"At least one Azure Speech region is required.\");\n return result;\n}\n\nasync function fetchRegionVoices(region: string, apiKey: string): Promise<AzureVoiceApiRecord[]> {\n const endpoint = `https://${encodeURIComponent(region)}.tts.speech.microsoft.com/cognitiveservices/voices/list`;\n const response = await fetch(endpoint, {\n headers: {\n Accept: \"application/json\",\n \"Ocp-Apim-Subscription-Key\": apiKey,\n },\n });\n if (!response.ok) {\n throw new Error(`Azure List Voices API request failed for region \"${region}\" with HTTP ${response.status}.`);\n }\n const payload: unknown = await response.json();\n if (!Array.isArray(payload)) throw new Error(`Azure List Voices API returned an invalid response for \"${region}\".`);\n return payload.filter((item): item is AzureVoiceApiRecord => Boolean(item && typeof item === \"object\"));\n}\n\n/** Fetches and deduplicates the current Azure Speech voice catalog for one or more regions. */\nexport async function fetchAzureVoiceCatalog(options: FetchAzureVoiceCatalogOptions): Promise<AzureVoiceCatalog> {\n if (!options || typeof options.apiKey !== \"string\" || !options.apiKey.trim())\n throw new TypeError(\"An Azure Speech API key is required.\");\n const regions = normalizeRegions(options.region);\n const payloads = await Promise.all(regions.map((region) => fetchRegionVoices(region, options.apiKey)));\n const voices = new Map<string, AzureVoiceCatalogVoice>();\n\n for (let regionIndex = 0; regionIndex < payloads.length; regionIndex += 1) {\n const region = regions[regionIndex];\n for (const record of payloads[regionIndex]) {\n const name = stringValue(record.ShortName) ?? stringValue(record.Name);\n const locale = stringValue(record.Locale);\n if (!name || !locale) continue;\n const key = name.toLowerCase();\n const existing = voices.get(key);\n const secondaryLocales = stringList(record.SecondaryLocaleList);\n const styles = stringList(record.StyleList);\n const status = normalizeStatus(record.Status);\n const merged: AzureVoiceCatalogVoice = {\n name: existing?.name ?? name,\n locale: existing?.locale ?? locale,\n regions: [...new Set([...(existing?.regions ?? []), region])],\n };\n const mergedSecondaryLocales = [...new Set([...(existing?.secondaryLocales ?? []), ...secondaryLocales])];\n if (mergedSecondaryLocales.length > 0) merged.secondaryLocales = mergedSecondaryLocales;\n const mergedStyles = [...new Set([...(existing?.styles ?? []), ...styles])];\n if (mergedStyles.length > 0) merged.styles = mergedStyles;\n if (status) merged.status = status;\n else if (existing?.status) merged.status = existing.status;\n voices.set(key, merged);\n }\n }\n\n const sortedVoices = [...voices.values()].sort((first, second) => first.name.localeCompare(second.name));\n return {\n voices: sortedVoices,\n metadata: {\n voiceCount: sortedVoices.length,\n generatedAt: new Date().toISOString(),\n apiVersion: AZURE_VOICE_API_VERSION,\n regions,\n },\n };\n}\n"],"mappings":"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","names":["SpeechSDK"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssml-builder-js/azure-tts-client",
3
- "version": "2.9.0",
3
+ "version": "2.11.0",
4
4
  "description": "Azure Text-to-Speech client using the Microsoft Speech SDK",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/index.ts CHANGED
@@ -6,3 +6,10 @@ export type { AzureTtsClientOptions, AzureTtsLogger, TtsConfig } from "./types.t
6
6
  export { AzureTtsError, AzureTtsSdkError } from "./errors.ts";
7
7
  export { AzureTtsClient } from "./client.ts";
8
8
  export { synthesizeSpeech } from "./synthesis.ts";
9
+ export { fetchAzureVoiceCatalog } from "./voiceCatalog.ts";
10
+ export type {
11
+ AzureVoiceCatalog,
12
+ AzureVoiceCatalogVoice,
13
+ FetchedAzureVoiceCatalogMetadata,
14
+ FetchAzureVoiceCatalogOptions,
15
+ } from "./voiceCatalog.ts";
@@ -0,0 +1,120 @@
1
+ const AZURE_VOICE_API_VERSION = "2025-10-01";
2
+
3
+ export interface FetchAzureVoiceCatalogOptions {
4
+ apiKey: string;
5
+ region: string | string[];
6
+ }
7
+
8
+ export interface AzureVoiceCatalogVoice {
9
+ name: string;
10
+ locale: string;
11
+ secondaryLocales?: readonly string[];
12
+ styles?: readonly string[];
13
+ regions: readonly string[];
14
+ status?: "ga" | "preview" | "deprecated";
15
+ }
16
+
17
+ export interface FetchedAzureVoiceCatalogMetadata {
18
+ voiceCount: number;
19
+ generatedAt: string;
20
+ apiVersion: string;
21
+ regions: readonly string[];
22
+ }
23
+
24
+ export interface AzureVoiceCatalog {
25
+ voices: readonly AzureVoiceCatalogVoice[];
26
+ metadata: FetchedAzureVoiceCatalogMetadata;
27
+ }
28
+
29
+ interface AzureVoiceApiRecord {
30
+ Locale?: unknown;
31
+ Name?: unknown;
32
+ SecondaryLocaleList?: unknown;
33
+ ShortName?: unknown;
34
+ Status?: unknown;
35
+ StyleList?: unknown;
36
+ }
37
+
38
+ function stringValue(value: unknown): string | undefined {
39
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
40
+ }
41
+
42
+ function stringList(value: unknown): string[] {
43
+ if (!Array.isArray(value)) return [];
44
+ return [...new Set(value.map(stringValue).filter((item): item is string => item !== undefined))];
45
+ }
46
+
47
+ function normalizeStatus(value: unknown): AzureVoiceCatalogVoice["status"] {
48
+ const status = stringValue(value)?.toLowerCase();
49
+ if (status === "preview" || status === "deprecated" || status === "ga") return status;
50
+ return undefined;
51
+ }
52
+
53
+ function normalizeRegions(region: string | string[]): string[] {
54
+ const regions = Array.isArray(region) ? region : [region];
55
+ const result = [...new Set(regions.map((item) => item.trim()).filter(Boolean))];
56
+ if (result.length === 0) throw new TypeError("At least one Azure Speech region is required.");
57
+ return result;
58
+ }
59
+
60
+ async function fetchRegionVoices(region: string, apiKey: string): Promise<AzureVoiceApiRecord[]> {
61
+ const endpoint = `https://${encodeURIComponent(region)}.tts.speech.microsoft.com/cognitiveservices/voices/list`;
62
+ const response = await fetch(endpoint, {
63
+ headers: {
64
+ Accept: "application/json",
65
+ "Ocp-Apim-Subscription-Key": apiKey,
66
+ },
67
+ });
68
+ if (!response.ok) {
69
+ throw new Error(`Azure List Voices API request failed for region "${region}" with HTTP ${response.status}.`);
70
+ }
71
+ const payload: unknown = await response.json();
72
+ if (!Array.isArray(payload)) throw new Error(`Azure List Voices API returned an invalid response for "${region}".`);
73
+ return payload.filter((item): item is AzureVoiceApiRecord => Boolean(item && typeof item === "object"));
74
+ }
75
+
76
+ /** Fetches and deduplicates the current Azure Speech voice catalog for one or more regions. */
77
+ export async function fetchAzureVoiceCatalog(options: FetchAzureVoiceCatalogOptions): Promise<AzureVoiceCatalog> {
78
+ if (!options || typeof options.apiKey !== "string" || !options.apiKey.trim())
79
+ throw new TypeError("An Azure Speech API key is required.");
80
+ const regions = normalizeRegions(options.region);
81
+ const payloads = await Promise.all(regions.map((region) => fetchRegionVoices(region, options.apiKey)));
82
+ const voices = new Map<string, AzureVoiceCatalogVoice>();
83
+
84
+ for (let regionIndex = 0; regionIndex < payloads.length; regionIndex += 1) {
85
+ const region = regions[regionIndex];
86
+ for (const record of payloads[regionIndex]) {
87
+ const name = stringValue(record.ShortName) ?? stringValue(record.Name);
88
+ const locale = stringValue(record.Locale);
89
+ if (!name || !locale) continue;
90
+ const key = name.toLowerCase();
91
+ const existing = voices.get(key);
92
+ const secondaryLocales = stringList(record.SecondaryLocaleList);
93
+ const styles = stringList(record.StyleList);
94
+ const status = normalizeStatus(record.Status);
95
+ const merged: AzureVoiceCatalogVoice = {
96
+ name: existing?.name ?? name,
97
+ locale: existing?.locale ?? locale,
98
+ regions: [...new Set([...(existing?.regions ?? []), region])],
99
+ };
100
+ const mergedSecondaryLocales = [...new Set([...(existing?.secondaryLocales ?? []), ...secondaryLocales])];
101
+ if (mergedSecondaryLocales.length > 0) merged.secondaryLocales = mergedSecondaryLocales;
102
+ const mergedStyles = [...new Set([...(existing?.styles ?? []), ...styles])];
103
+ if (mergedStyles.length > 0) merged.styles = mergedStyles;
104
+ if (status) merged.status = status;
105
+ else if (existing?.status) merged.status = existing.status;
106
+ voices.set(key, merged);
107
+ }
108
+ }
109
+
110
+ const sortedVoices = [...voices.values()].sort((first, second) => first.name.localeCompare(second.name));
111
+ return {
112
+ voices: sortedVoices,
113
+ metadata: {
114
+ voiceCount: sortedVoices.length,
115
+ generatedAt: new Date().toISOString(),
116
+ apiVersion: AZURE_VOICE_API_VERSION,
117
+ regions,
118
+ },
119
+ };
120
+ }
@@ -0,0 +1,51 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { fetchAzureVoiceCatalog } from "../src/index.ts";
4
+
5
+ test("fetchAzureVoiceCatalog fetches, merges, and annotates voices across regions", async () => {
6
+ const originalFetch = globalThis.fetch;
7
+ const requests: string[] = [];
8
+ globalThis.fetch = async (input) => {
9
+ const url = String(input);
10
+ requests.push(url);
11
+ const region = url.split(".")[0]?.replace("https://", "");
12
+ return new Response(
13
+ JSON.stringify(
14
+ region === "eastus"
15
+ ? [
16
+ { Locale: "fil-PH", ShortName: "fil-PH-BlessicaNeural", StyleList: ["cheerful"] },
17
+ { Locale: "en-US", Name: "en-US-JennyNeural", StyleList: ["chat"] },
18
+ ]
19
+ : [{ Locale: "fil-PH", ShortName: "fil-PH-BlessicaNeural", StyleList: ["sad"] }],
20
+ ),
21
+ { status: 200, headers: { "content-type": "application/json" } },
22
+ );
23
+ };
24
+ try {
25
+ const catalog = await fetchAzureVoiceCatalog({ apiKey: "secret", region: ["eastus", "japaneast"] });
26
+ assert.equal(requests.length, 2);
27
+ assert.deepEqual(catalog.metadata.regions, ["eastus", "japaneast"]);
28
+ assert.equal(catalog.metadata.voiceCount, 2);
29
+ assert.deepEqual(
30
+ catalog.voices.find(({ name }) => name === "fil-PH-BlessicaNeural"),
31
+ {
32
+ name: "fil-PH-BlessicaNeural",
33
+ locale: "fil-PH",
34
+ styles: ["cheerful", "sad"],
35
+ regions: ["eastus", "japaneast"],
36
+ },
37
+ );
38
+ } finally {
39
+ globalThis.fetch = originalFetch;
40
+ }
41
+ });
42
+
43
+ test("fetchAzureVoiceCatalog reports HTTP and payload failures", async () => {
44
+ const originalFetch = globalThis.fetch;
45
+ globalThis.fetch = async () => new Response("no", { status: 401 });
46
+ try {
47
+ await assert.rejects(fetchAzureVoiceCatalog({ apiKey: "secret", region: "eastus" }), /HTTP 401/);
48
+ } finally {
49
+ globalThis.fetch = originalFetch;
50
+ }
51
+ });