ssml-builder-js 2.10.0 → 2.12.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 +100 -5
- package/bin/sync-voices.js +62 -0
- package/dist/{chunk-GW6CKHXF.mjs → chunk-4BVNAUVR.mjs} +177 -5
- package/dist/chunk-4BVNAUVR.mjs.map +1 -0
- package/dist/{chunk-BYIZQL2W.mjs → chunk-FHDFRM2F.mjs} +92 -14
- package/dist/chunk-FHDFRM2F.mjs.map +1 -0
- package/dist/core.d.mts +21 -3
- package/dist/core.d.ts +21 -3
- package/dist/core.js +177 -4
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +91 -13
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +1 -1
- package/dist/index.d.mts +55 -2
- package/dist/index.d.ts +55 -2
- package/dist/index.js +300 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +124 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.js +335 -15
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +245 -3
- package/dist/react.mjs.map +1 -1
- package/package.json +5 -1
- package/dist/chunk-BYIZQL2W.mjs.map +0 -1
- package/dist/chunk-GW6CKHXF.mjs.map +0 -1
package/dist/core.mjs
CHANGED
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
mapSsmlTextNodes,
|
|
12
12
|
normalizeAzureLanguage,
|
|
13
13
|
parseSsml,
|
|
14
|
+
splitSsmlDocument,
|
|
14
15
|
validateAzureSsml,
|
|
15
16
|
validateSsml,
|
|
16
17
|
validateSsmlStructureIntegrity
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-4BVNAUVR.mjs";
|
|
18
19
|
import "./chunk-6S5ODO6A.mjs";
|
|
19
20
|
export {
|
|
20
21
|
areAzureLanguagesEquivalent,
|
|
@@ -29,6 +30,7 @@ export {
|
|
|
29
30
|
mapSsmlTextNodes,
|
|
30
31
|
normalizeAzureLanguage,
|
|
31
32
|
parseSsml,
|
|
33
|
+
splitSsmlDocument,
|
|
32
34
|
validateAzureSsml,
|
|
33
35
|
validateSsml,
|
|
34
36
|
validateSsmlStructureIntegrity
|
package/dist/elements.js
CHANGED
|
@@ -956,6 +956,9 @@ var AZURE_VOICE_DEFINITIONS = [
|
|
|
956
956
|
},
|
|
957
957
|
{ name: "es-ES-ElviraNeural", locale: "es-ES" },
|
|
958
958
|
{ name: "fil-PH-AngeloNeural", locale: "fil-PH" },
|
|
959
|
+
{ name: "fil-PH-Angelo:DragonHDLatestNeural", locale: "fil-PH" },
|
|
960
|
+
{ name: "fil-PH-BlessicaNeural", locale: "fil-PH" },
|
|
961
|
+
{ name: "fil-PH-Blessica:DragonHDLatestNeural", locale: "fil-PH" },
|
|
959
962
|
{ name: "fr-FR-DeniseNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
960
963
|
{ name: "fr-FR-HenriNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
961
964
|
{ name: "id-ID-GadisNeural", locale: "id-ID" },
|
|
@@ -1050,6 +1053,18 @@ var ALLOWED_SILENCE_TYPES = /* @__PURE__ */ new Set([
|
|
|
1050
1053
|
"Enumerationcomma"
|
|
1051
1054
|
]);
|
|
1052
1055
|
var ALLOWED_VISEME_TYPES = /* @__PURE__ */ new Set(["redlips_front", "FacialExpression"]);
|
|
1056
|
+
var DEFAULT_PREVIEW_TAGS = /* @__PURE__ */ new Set(["mstts:voiceconversion"]);
|
|
1057
|
+
function featureStatusForTag(name, options) {
|
|
1058
|
+
const tagName = canonicalTagName(name);
|
|
1059
|
+
const configured = Object.entries(options.tagStatuses ?? {}).find(
|
|
1060
|
+
([candidate]) => canonicalTagName(candidate) === tagName
|
|
1061
|
+
)?.[1];
|
|
1062
|
+
if (configured) return configured;
|
|
1063
|
+
if ((options.previewTags ?? [...DEFAULT_PREVIEW_TAGS]).some((candidate) => canonicalTagName(candidate) === tagName))
|
|
1064
|
+
return "preview";
|
|
1065
|
+
if ((options.deprecatedTags ?? []).some((candidate) => canonicalTagName(candidate) === tagName)) return "deprecated";
|
|
1066
|
+
return void 0;
|
|
1067
|
+
}
|
|
1053
1068
|
function decodeAttribute(value) {
|
|
1054
1069
|
return value.replace(
|
|
1055
1070
|
/&(?:amp|apos|gt|lt|quot);/gi,
|
|
@@ -1118,6 +1133,7 @@ function tokenizeElements(source) {
|
|
|
1118
1133
|
attributes,
|
|
1119
1134
|
childElementIndex,
|
|
1120
1135
|
end,
|
|
1136
|
+
depth: openElements.length + 1,
|
|
1121
1137
|
name: tokenName,
|
|
1122
1138
|
parentName: parent?.name,
|
|
1123
1139
|
parentVoiceName,
|
|
@@ -1166,9 +1182,9 @@ function isValidAzureAudioDuration(value) {
|
|
|
1166
1182
|
return Number(clock[1]) > 0 || Number(clock[2]) > 0 || Number(clock[3]) > 0 || Number(clock[4] ?? 0) > 0;
|
|
1167
1183
|
}
|
|
1168
1184
|
function isValidAzureBackgroundAudioDuration(value) {
|
|
1169
|
-
const match = /^(\d+
|
|
1185
|
+
const match = /^(\d+)$/.exec(value.trim());
|
|
1170
1186
|
if (!match) return false;
|
|
1171
|
-
const milliseconds = Number(match[1])
|
|
1187
|
+
const milliseconds = Number(match[1]);
|
|
1172
1188
|
return Number.isFinite(milliseconds) && milliseconds >= 0 && milliseconds <= 1e4;
|
|
1173
1189
|
}
|
|
1174
1190
|
function attr(token, name) {
|
|
@@ -1298,13 +1314,18 @@ function validateAudioSource(token, source, diagnostics, options, elementName2)
|
|
|
1298
1314
|
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must be an absolute HTTP(S) URL.`);
|
|
1299
1315
|
return;
|
|
1300
1316
|
}
|
|
1317
|
+
if (parsed.username || parsed.password)
|
|
1318
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must not contain URL credentials.`);
|
|
1301
1319
|
if (parsed.protocol !== "https:" && !(options.allowHttpAudio && parsed.protocol === "http:"))
|
|
1302
1320
|
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must use HTTPS.`);
|
|
1303
1321
|
const isAllowedOrigin = options.allowedAudioOrigins?.some((allowedOrigin) => {
|
|
1304
1322
|
try {
|
|
1305
|
-
|
|
1323
|
+
const configured = new URL(allowedOrigin);
|
|
1324
|
+
if (configured.protocol !== "https:" && configured.protocol !== "http:" || configured.username || configured.password || configured.pathname !== "/" || configured.search || configured.hash)
|
|
1325
|
+
return false;
|
|
1326
|
+
return configured.origin === parsed.origin;
|
|
1306
1327
|
} catch {
|
|
1307
|
-
return
|
|
1328
|
+
return false;
|
|
1308
1329
|
}
|
|
1309
1330
|
}) ?? false;
|
|
1310
1331
|
if (options.allowedAudioOrigins && !isAllowedOrigin)
|
|
@@ -1319,6 +1340,25 @@ function validateAudioSource(token, source, diagnostics, options, elementName2)
|
|
|
1319
1340
|
}
|
|
1320
1341
|
function validateElement(token, source, diagnostics, voiceName, options, voiceCatalog) {
|
|
1321
1342
|
const name = token.name.toLowerCase();
|
|
1343
|
+
const tagStatus = featureStatusForTag(token.name, options);
|
|
1344
|
+
if (tagStatus === "preview")
|
|
1345
|
+
addDiagnostic(
|
|
1346
|
+
diagnostics,
|
|
1347
|
+
source,
|
|
1348
|
+
token.start,
|
|
1349
|
+
`<${token.name}> is an Azure Speech preview feature and may change or require preview access.`,
|
|
1350
|
+
"warning",
|
|
1351
|
+
"azure-preview-tag"
|
|
1352
|
+
);
|
|
1353
|
+
if (tagStatus === "deprecated")
|
|
1354
|
+
addDiagnostic(
|
|
1355
|
+
diagnostics,
|
|
1356
|
+
source,
|
|
1357
|
+
token.start,
|
|
1358
|
+
`<${token.name}> is deprecated by Azure Speech; migrate to a supported alternative.`,
|
|
1359
|
+
"info",
|
|
1360
|
+
"azure-deprecated-tag"
|
|
1361
|
+
);
|
|
1322
1362
|
if (name === "voice" && !attr(token, "name")?.trim())
|
|
1323
1363
|
addDiagnostic(diagnostics, source, token.start, '<voice> requires a non-empty "name" attribute.');
|
|
1324
1364
|
if (name === "break") {
|
|
@@ -1493,6 +1533,9 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1493
1533
|
const maxLength = options.maxLength ?? 1e4;
|
|
1494
1534
|
if (ssml.length > maxLength)
|
|
1495
1535
|
addDiagnostic(diagnostics, ssml, maxLength, `SSML exceeds the maximum length of ${maxLength} characters.`);
|
|
1536
|
+
if (options.maxXmlDepth !== void 0 && (!Number.isInteger(options.maxXmlDepth) || options.maxXmlDepth <= 0)) {
|
|
1537
|
+
addDiagnostic(diagnostics, ssml, 0, "maxXmlDepth must be a positive integer.");
|
|
1538
|
+
}
|
|
1496
1539
|
try {
|
|
1497
1540
|
parseSsml(ssml);
|
|
1498
1541
|
} catch (error) {
|
|
@@ -1502,6 +1545,18 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1502
1545
|
return diagnostics;
|
|
1503
1546
|
}
|
|
1504
1547
|
const tokens = tokenizeElements(ssml);
|
|
1548
|
+
if (options.maxXmlDepth !== void 0) {
|
|
1549
|
+
for (const token of tokens) {
|
|
1550
|
+
if (token.depth > options.maxXmlDepth) {
|
|
1551
|
+
addDiagnostic(
|
|
1552
|
+
diagnostics,
|
|
1553
|
+
ssml,
|
|
1554
|
+
token.start,
|
|
1555
|
+
`XML nesting depth ${token.depth} exceeds the configured maximum of ${options.maxXmlDepth}.`
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1505
1560
|
const speak = tokens.find((token) => token.name.toLowerCase() === "speak");
|
|
1506
1561
|
const voices = tokens.filter((token) => token.name.toLowerCase() === "voice");
|
|
1507
1562
|
const backgroundAudioTokens = tokens.filter((token) => token.name.toLowerCase() === "mstts:backgroundaudio");
|
|
@@ -1530,6 +1585,24 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1530
1585
|
const name = attr(token, "name")?.trim();
|
|
1531
1586
|
const language = attr(token, "xml:lang")?.trim() || (speak ? attr(speak, "xml:lang")?.trim() : void 0);
|
|
1532
1587
|
const definition = name ? voiceCatalog.get(name.toLowerCase()) : void 0;
|
|
1588
|
+
if (name && definition?.status === "preview")
|
|
1589
|
+
addDiagnostic(
|
|
1590
|
+
diagnostics,
|
|
1591
|
+
ssml,
|
|
1592
|
+
token.start,
|
|
1593
|
+
`Voice "${name}" is an Azure Speech preview voice and may change or require preview access.`,
|
|
1594
|
+
"warning",
|
|
1595
|
+
"azure-preview-voice"
|
|
1596
|
+
);
|
|
1597
|
+
if (name && definition?.status === "deprecated")
|
|
1598
|
+
addDiagnostic(
|
|
1599
|
+
diagnostics,
|
|
1600
|
+
ssml,
|
|
1601
|
+
token.start,
|
|
1602
|
+
`Voice "${name}" is deprecated by Azure Speech; migrate to a supported voice.`,
|
|
1603
|
+
"info",
|
|
1604
|
+
"azure-deprecated-voice"
|
|
1605
|
+
);
|
|
1533
1606
|
if (name && !definition && policySeverity)
|
|
1534
1607
|
addDiagnostic(
|
|
1535
1608
|
diagnostics,
|
|
@@ -2295,6 +2368,7 @@ var ssmlPresets_exports = {};
|
|
|
2295
2368
|
__export(ssmlPresets_exports, {
|
|
2296
2369
|
AUDIO_DURATION_DESCRIPTIONS: () => AUDIO_DURATION_DESCRIPTIONS,
|
|
2297
2370
|
AUDIO_DURATION_PRESETS: () => AUDIO_DURATION_PRESETS,
|
|
2371
|
+
BACKGROUND_AUDIO_DURATION_PRESETS: () => BACKGROUND_AUDIO_DURATION_PRESETS,
|
|
2298
2372
|
BREAK_STRENGTH_PRESETS: () => BREAK_STRENGTH_PRESETS,
|
|
2299
2373
|
BREAK_TIME_DESCRIPTIONS: () => BREAK_TIME_DESCRIPTIONS,
|
|
2300
2374
|
BREAK_TIME_PRESETS: () => BREAK_TIME_PRESETS,
|
|
@@ -2376,6 +2450,9 @@ var AZURE_VOICE_STYLE_MAP = {
|
|
|
2376
2450
|
],
|
|
2377
2451
|
"es-ES-ElviraNeural": [],
|
|
2378
2452
|
"fil-PH-AngeloNeural": [],
|
|
2453
|
+
"fil-PH-Angelo:DragonHDLatestNeural": [],
|
|
2454
|
+
"fil-PH-BlessicaNeural": [],
|
|
2455
|
+
"fil-PH-Blessica:DragonHDLatestNeural": [],
|
|
2379
2456
|
"fr-FR-DeniseNeural": ["cheerful", "sad"],
|
|
2380
2457
|
"fr-FR-HenriNeural": ["cheerful", "sad"],
|
|
2381
2458
|
"id-ID-GadisNeural": [],
|
|
@@ -2653,6 +2730,7 @@ var SAY_AS_PRESETS = [
|
|
|
2653
2730
|
var LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"];
|
|
2654
2731
|
var SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"];
|
|
2655
2732
|
var AUDIO_DURATION_PRESETS = ["5s", "10s", "30s"];
|
|
2733
|
+
var BACKGROUND_AUDIO_DURATION_PRESETS = ["0", "500", "1000", "3000", "10000"];
|
|
2656
2734
|
var SILENCE_TYPE_PRESETS = [
|
|
2657
2735
|
"Leading",
|
|
2658
2736
|
"Tailing",
|
|
@@ -2714,8 +2792,8 @@ var SSML_ATTRIBUTE_PRESETS = {
|
|
|
2714
2792
|
},
|
|
2715
2793
|
"mstts:backgroundaudio": {
|
|
2716
2794
|
volume: PROSODY_VOLUME_PRESETS,
|
|
2717
|
-
fadein:
|
|
2718
|
-
fadeout:
|
|
2795
|
+
fadein: BACKGROUND_AUDIO_DURATION_PRESETS,
|
|
2796
|
+
fadeout: BACKGROUND_AUDIO_DURATION_PRESETS
|
|
2719
2797
|
},
|
|
2720
2798
|
silence: {
|
|
2721
2799
|
type: SILENCE_TYPE_PRESETS,
|
|
@@ -3220,7 +3298,7 @@ var SSML_COMPLETION_SNIPPETS = [
|
|
|
3220
3298
|
},
|
|
3221
3299
|
{
|
|
3222
3300
|
label: "mstts:backgroundaudio",
|
|
3223
|
-
insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2
|
|
3301
|
+
insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2:70}" fadein="\${3:1000}" fadeout="\${4:1000}" />`
|
|
3224
3302
|
},
|
|
3225
3303
|
{
|
|
3226
3304
|
label: "mstts:ttsembedding",
|
|
@@ -3999,18 +4077,18 @@ var SSML_TAG_DEFINITIONS = [
|
|
|
3999
4077
|
},
|
|
4000
4078
|
{
|
|
4001
4079
|
name: "volume",
|
|
4002
|
-
description: "The background audio volume
|
|
4003
|
-
example: "
|
|
4080
|
+
description: "The background audio volume from 0 to 100.",
|
|
4081
|
+
example: "70"
|
|
4004
4082
|
},
|
|
4005
4083
|
{
|
|
4006
4084
|
name: "fadein",
|
|
4007
|
-
description: "The fade-in duration
|
|
4008
|
-
example: "
|
|
4085
|
+
description: "The fade-in duration in milliseconds from 0 to 10000.",
|
|
4086
|
+
example: "1000"
|
|
4009
4087
|
},
|
|
4010
4088
|
{
|
|
4011
4089
|
name: "fadeout",
|
|
4012
|
-
description: "The fade-out duration
|
|
4013
|
-
example: "
|
|
4090
|
+
description: "The fade-out duration in milliseconds from 0 to 10000.",
|
|
4091
|
+
example: "1000"
|
|
4014
4092
|
}
|
|
4015
4093
|
]
|
|
4016
4094
|
},
|