ssml-builder-js 2.5.0 → 2.7.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/README.md +74 -2
- package/dist/{chunk-4RHHW33A.mjs → chunk-D6ITDU3A.mjs} +135 -24
- package/dist/chunk-D6ITDU3A.mjs.map +1 -0
- package/dist/{chunk-ALFYHSCR.mjs → chunk-QJTVHANI.mjs} +1 -1
- package/dist/chunk-QJTVHANI.mjs.map +1 -0
- package/dist/core.d.mts +23 -1
- package/dist/core.d.ts +23 -1
- package/dist/core.js +136 -23
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +5 -1
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +136 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-4RHHW33A.mjs.map +0 -1
- package/dist/chunk-ALFYHSCR.mjs.map +0 -1
package/dist/elements.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AudioElement, AzureSsmlValidationOptions, AzureValidationOptions, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, parseSsml, validateAzureSsml, validateSsml } from './core.mjs';
|
|
1
|
+
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml } from './core.mjs';
|
|
2
2
|
|
|
3
3
|
interface TtsConfig {
|
|
4
4
|
signal?: AbortSignal;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AudioElement, AzureSsmlValidationOptions, AzureValidationOptions, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, parseSsml, validateAzureSsml, validateSsml } from './core.js';
|
|
1
|
+
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml } from './core.js';
|
|
2
2
|
|
|
3
3
|
interface TtsConfig {
|
|
4
4
|
signal?: AbortSignal;
|
package/dist/index.js
CHANGED
|
@@ -40,10 +40,12 @@ __export(src_exports, {
|
|
|
40
40
|
AzureTtsClient: () => AzureTtsClient,
|
|
41
41
|
AzureTtsError: () => AzureTtsError,
|
|
42
42
|
AzureTtsSdkError: () => AzureTtsSdkError,
|
|
43
|
+
areAzureLanguagesEquivalent: () => areAzureLanguagesEquivalent,
|
|
43
44
|
buildPartialSsml: () => buildPartialSsml,
|
|
44
45
|
buildSsml: () => buildSsml,
|
|
45
46
|
extractSsmlText: () => extractSsmlText,
|
|
46
47
|
mapSsmlTextNodes: () => mapSsmlTextNodes,
|
|
48
|
+
normalizeAzureLanguage: () => normalizeAzureLanguage,
|
|
47
49
|
parseSsml: () => parseSsml,
|
|
48
50
|
synthesizeSpeech: () => synthesizeSpeech,
|
|
49
51
|
validateAzureSsml: () => validateAzureSsml,
|
|
@@ -1241,29 +1243,123 @@ function location(source, offset) {
|
|
|
1241
1243
|
const line = before.split("\n").length;
|
|
1242
1244
|
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
1243
1245
|
}
|
|
1244
|
-
function addDiagnostic(diagnostics, source, offset, message, severity = "error") {
|
|
1245
|
-
diagnostics.push({ ...location(source, offset), message, severity });
|
|
1246
|
+
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code) {
|
|
1247
|
+
diagnostics.push({ ...location(source, offset), message, severity, ...code ? { code } : {} });
|
|
1246
1248
|
}
|
|
1247
1249
|
function attr(token, name) {
|
|
1248
1250
|
return token.attributes.get(name.toLowerCase());
|
|
1249
1251
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1252
|
+
var ADDITIONAL_VOICE_DEFINITIONS = [
|
|
1253
|
+
{ name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" },
|
|
1254
|
+
{ name: "es-ES-ElviraNeural", locale: "es-ES" },
|
|
1255
|
+
{ name: "th-TH-PremwadeeNeural", locale: "th-TH" },
|
|
1256
|
+
{ name: "fil-PH-AngeloNeural", locale: "fil-PH" },
|
|
1257
|
+
{ name: "vi-VN-HoaiMyNeural", locale: "vi-VN" },
|
|
1258
|
+
{ name: "id-ID-GadisNeural", locale: "id-ID" },
|
|
1259
|
+
{ name: "ms-MY-YasminNeural", locale: "ms-MY" }
|
|
1260
|
+
];
|
|
1261
|
+
var DEFAULT_LANGUAGE_ALIASES = {
|
|
1262
|
+
"zh-CN": ["zh-Hans"],
|
|
1263
|
+
"zh-TW": ["zh-Hant"]
|
|
1264
|
+
};
|
|
1265
|
+
function canonicalLanguageTag(language) {
|
|
1266
|
+
const trimmed = language.trim();
|
|
1267
|
+
if (!trimmed) return "";
|
|
1268
|
+
try {
|
|
1269
|
+
return new Intl.Locale(trimmed).toString().toLowerCase();
|
|
1270
|
+
} catch {
|
|
1271
|
+
return trimmed.toLowerCase();
|
|
1259
1272
|
}
|
|
1260
|
-
|
|
1273
|
+
}
|
|
1274
|
+
function createLanguageNormalizer(options) {
|
|
1275
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
1276
|
+
const addAliasGroup = (canonical, values) => {
|
|
1277
|
+
const normalizedCanonical = canonicalLanguageTag(canonical);
|
|
1278
|
+
if (!normalizedCanonical) return;
|
|
1279
|
+
aliases.set(normalizedCanonical, normalizedCanonical);
|
|
1280
|
+
for (const value of values) {
|
|
1281
|
+
const normalizedValue = canonicalLanguageTag(value);
|
|
1282
|
+
if (normalizedValue) aliases.set(normalizedValue, normalizedCanonical);
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
for (const [canonical, values] of Object.entries(DEFAULT_LANGUAGE_ALIASES)) addAliasGroup(canonical, values);
|
|
1286
|
+
for (const [canonical, valueOrValues] of Object.entries(options.languageAliases ?? {}))
|
|
1287
|
+
addAliasGroup(canonical, typeof valueOrValues === "string" ? [valueOrValues] : valueOrValues);
|
|
1288
|
+
return (language) => {
|
|
1289
|
+
const customValue = options.normalizeLanguage ? options.normalizeLanguage(language) : language;
|
|
1290
|
+
const normalized = canonicalLanguageTag(customValue);
|
|
1291
|
+
return aliases.get(normalized) ?? normalized;
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function normalizeAzureLanguage(language, options = {}) {
|
|
1295
|
+
return createLanguageNormalizer(options)(language);
|
|
1296
|
+
}
|
|
1297
|
+
function areAzureLanguagesEquivalent(first, second, options = {}) {
|
|
1298
|
+
const normalize = createLanguageNormalizer(options);
|
|
1299
|
+
const normalizedFirst = normalize(first);
|
|
1300
|
+
const normalizedSecond = normalize(second);
|
|
1301
|
+
if (normalizedFirst === normalizedSecond) return true;
|
|
1302
|
+
return normalizedFirst === languagePart(normalizedFirst) && languagePart(normalizedFirst) === languagePart(normalizedSecond);
|
|
1303
|
+
}
|
|
1304
|
+
function voiceLocalePrefix(voiceName) {
|
|
1305
|
+
const match = /^(?<language>[A-Za-z]{2,3})-(?<region>[A-Za-z]{2}|\d{3})(?:-|$)/.exec(voiceName.trim());
|
|
1306
|
+
if (!match?.groups) return void 0;
|
|
1307
|
+
const tag = `${match.groups.language}-${match.groups.region}`;
|
|
1308
|
+
return {
|
|
1309
|
+
language: match.groups.language.toLowerCase(),
|
|
1310
|
+
region: match.groups.region.toLowerCase(),
|
|
1311
|
+
tag
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
function definitionFromStyleMap(voiceName, styles) {
|
|
1315
|
+
return {
|
|
1316
|
+
name: voiceName,
|
|
1317
|
+
locale: voiceLocalePrefix(voiceName)?.tag ?? "",
|
|
1318
|
+
styles
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
function normalizeVoiceCatalog(options) {
|
|
1322
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
1323
|
+
for (const [name, styles] of Object.entries(EXPRESS_AS_STYLES)) {
|
|
1324
|
+
definitions.set(name.toLowerCase(), definitionFromStyleMap(name, styles));
|
|
1325
|
+
}
|
|
1326
|
+
for (const definition of ADDITIONAL_VOICE_DEFINITIONS) definitions.set(definition.name.toLowerCase(), definition);
|
|
1327
|
+
for (const definition of options.voiceCatalog ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
1328
|
+
for (const definition of options.voiceDefinitions ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
1329
|
+
for (const definition of options.customVoiceDefinitions ?? [])
|
|
1330
|
+
definitions.set(definition.name.toLowerCase(), definition);
|
|
1331
|
+
for (const [voiceName, styles] of Object.entries(options.customVoiceStyleMap ?? {})) {
|
|
1332
|
+
const key = voiceName.toLowerCase();
|
|
1333
|
+
const current = definitions.get(key);
|
|
1334
|
+
definitions.set(key, {
|
|
1335
|
+
...current ?? definitionFromStyleMap(voiceName, styles),
|
|
1336
|
+
name: current?.name ?? voiceName,
|
|
1337
|
+
styles: styles.map((style) => style.toLowerCase())
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
return definitions;
|
|
1261
1341
|
}
|
|
1262
1342
|
function diagnosticSeverity(policy) {
|
|
1263
1343
|
if (policy === "ignore") return void 0;
|
|
1264
1344
|
return policy === "error" ? "error" : "warning";
|
|
1265
1345
|
}
|
|
1266
|
-
function
|
|
1346
|
+
function languagePart(language) {
|
|
1347
|
+
try {
|
|
1348
|
+
return new Intl.Locale(language).language.toLowerCase();
|
|
1349
|
+
} catch {
|
|
1350
|
+
return language.split("-")[0]?.toLowerCase() ?? "";
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
function definitionMatchesLanguage(definition, voiceName, language, normalizeLanguage) {
|
|
1354
|
+
const candidateLanguages = definition ? [definition.locale, ...definition.secondaryLocales ?? []].filter(Boolean) : [voiceLocalePrefix(voiceName)?.tag ?? ""];
|
|
1355
|
+
if (candidateLanguages.length === 0 || !language.trim()) return void 0;
|
|
1356
|
+
const normalizedLanguage = normalizeLanguage(language);
|
|
1357
|
+
const normalizedCandidates = candidateLanguages.map(normalizeLanguage);
|
|
1358
|
+
if (normalizedCandidates.includes(normalizedLanguage)) return true;
|
|
1359
|
+
if (!normalizedLanguage || !normalizedCandidates.some(Boolean)) return void 0;
|
|
1360
|
+
return normalizedLanguage === languagePart(normalizedLanguage) ? normalizedCandidates.some((candidate) => languagePart(candidate) === normalizedLanguage) : false;
|
|
1361
|
+
}
|
|
1362
|
+
function validateElement(token, source, diagnostics, voiceName, options, voiceCatalog) {
|
|
1267
1363
|
const name = token.name.toLowerCase();
|
|
1268
1364
|
if (name === "voice" && !attr(token, "name")?.trim())
|
|
1269
1365
|
addDiagnostic(diagnostics, source, token.start, '<voice> requires a non-empty "name" attribute.');
|
|
@@ -1305,17 +1401,19 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceSt
|
|
|
1305
1401
|
const role = attr(token, "role");
|
|
1306
1402
|
if (role && !ALLOWED_ROLES.has(role))
|
|
1307
1403
|
addDiagnostic(diagnostics, source, token.start, `Unsupported <mstts:express-as role> value "${role}".`);
|
|
1308
|
-
const
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1404
|
+
const definition = voiceName ? voiceCatalog.get(voiceName.toLowerCase()) : void 0;
|
|
1405
|
+
const supportedStyles = definition?.styles;
|
|
1406
|
+
const severity = diagnosticSeverity(options.unsupportedStylePolicy ?? options.unknownVoicePolicy ?? "warn");
|
|
1407
|
+
if (style && definition && !supportedStyles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase()) && severity)
|
|
1311
1408
|
addDiagnostic(
|
|
1312
1409
|
diagnostics,
|
|
1313
1410
|
source,
|
|
1314
1411
|
token.start,
|
|
1315
1412
|
`Unknown style "${style}" is not supported by voice "${voiceName}" according to the configured voice style map.`,
|
|
1316
|
-
severity
|
|
1413
|
+
severity,
|
|
1414
|
+
"azure-unsupported-style"
|
|
1317
1415
|
);
|
|
1318
|
-
if (style && voiceName && !
|
|
1416
|
+
if (style && voiceName && !definition && severity)
|
|
1319
1417
|
addDiagnostic(
|
|
1320
1418
|
diagnostics,
|
|
1321
1419
|
source,
|
|
@@ -1420,23 +1518,36 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1420
1518
|
"Azure SSML requires at least one <voice> element under <speak>."
|
|
1421
1519
|
);
|
|
1422
1520
|
const voiceName = voices[0] ? attr(voices[0], "name") : void 0;
|
|
1423
|
-
const
|
|
1521
|
+
const voiceCatalog = normalizeVoiceCatalog(options);
|
|
1522
|
+
const normalizeLanguage = createLanguageNormalizer(options);
|
|
1424
1523
|
const policySeverity = diagnosticSeverity(options.unknownVoicePolicy ?? "warn");
|
|
1425
1524
|
const voicesToValidate = options.validateNestedVoices === false ? voices.slice(0, 1) : voices;
|
|
1426
1525
|
for (const token of voicesToValidate) {
|
|
1427
1526
|
const name = attr(token, "name")?.trim();
|
|
1428
|
-
|
|
1527
|
+
const language = attr(token, "xml:lang")?.trim() || (speak ? attr(speak, "xml:lang")?.trim() : void 0);
|
|
1528
|
+
const definition = name ? voiceCatalog.get(name.toLowerCase()) : void 0;
|
|
1529
|
+
if (name && !definition && policySeverity)
|
|
1530
|
+
addDiagnostic(
|
|
1531
|
+
diagnostics,
|
|
1532
|
+
ssml,
|
|
1533
|
+
token.start,
|
|
1534
|
+
`Unknown voice "${name}" is not registered in the voice catalog.`,
|
|
1535
|
+
policySeverity,
|
|
1536
|
+
"azure-unknown-voice"
|
|
1537
|
+
);
|
|
1538
|
+
if (name && language && definitionMatchesLanguage(definition, name, language, normalizeLanguage) === false)
|
|
1429
1539
|
addDiagnostic(
|
|
1430
1540
|
diagnostics,
|
|
1431
1541
|
ssml,
|
|
1432
1542
|
token.start,
|
|
1433
|
-
`
|
|
1434
|
-
|
|
1543
|
+
`Voice "${name}" does not match language "${language}"; the voice name prefix indicates a different language or region.`,
|
|
1544
|
+
"warning",
|
|
1545
|
+
"azure-locale-mismatch"
|
|
1435
1546
|
);
|
|
1436
1547
|
}
|
|
1437
1548
|
for (const token of tokens) {
|
|
1438
1549
|
const tokenVoiceName = options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
1439
|
-
validateElement(token, ssml, diagnostics, tokenVoiceName, options,
|
|
1550
|
+
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
1440
1551
|
}
|
|
1441
1552
|
return diagnostics;
|
|
1442
1553
|
}
|
|
@@ -1627,10 +1738,12 @@ _options = new WeakMap();
|
|
|
1627
1738
|
AzureTtsClient,
|
|
1628
1739
|
AzureTtsError,
|
|
1629
1740
|
AzureTtsSdkError,
|
|
1741
|
+
areAzureLanguagesEquivalent,
|
|
1630
1742
|
buildPartialSsml,
|
|
1631
1743
|
buildSsml,
|
|
1632
1744
|
extractSsmlText,
|
|
1633
1745
|
mapSsmlTextNodes,
|
|
1746
|
+
normalizeAzureLanguage,
|
|
1634
1747
|
parseSsml,
|
|
1635
1748
|
synthesizeSpeech,
|
|
1636
1749
|
validateAzureSsml,
|