ssml-builder-js 2.12.0 → 2.14.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 +34 -1
- package/dist/{chunk-4BVNAUVR.mjs → chunk-AQ55MOPU.mjs} +246 -21
- package/dist/chunk-AQ55MOPU.mjs.map +1 -0
- package/dist/chunk-QFIBPCO4.mjs +1816 -0
- package/dist/chunk-QFIBPCO4.mjs.map +1 -0
- package/dist/{chunk-FHDFRM2F.mjs → chunk-UZSCXPC5.mjs} +248 -1722
- package/dist/chunk-UZSCXPC5.mjs.map +1 -0
- package/dist/core.d.mts +62 -3
- package/dist/core.d.ts +62 -3
- package/dist/core.js +246 -20
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +196 -8
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +6 -4
- package/dist/elements.mjs.map +1 -1
- package/dist/index.d-CUXRwSw0.d.mts +232 -0
- package/dist/index.d-CUXRwSw0.d.ts +232 -0
- package/dist/index.d.mts +151 -2
- package/dist/index.d.ts +151 -2
- package/dist/index.js +2264 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +479 -4
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +65 -163
- package/dist/react.d.ts +65 -163
- package/dist/react.js +498 -22
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +162 -18
- package/dist/react.mjs.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-4BVNAUVR.mjs.map +0 -1
- package/dist/chunk-FHDFRM2F.mjs.map +0 -1
package/dist/core.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
areAzureLanguagesEquivalent,
|
|
3
3
|
buildPartialSsml,
|
|
4
4
|
buildSsml,
|
|
5
|
+
createAzureUrlValidatorRunner,
|
|
5
6
|
extractSsmlText,
|
|
6
7
|
extractSsmlTranslatableText,
|
|
7
8
|
fromPlainTextToSsml,
|
|
@@ -15,12 +16,13 @@ import {
|
|
|
15
16
|
validateAzureSsml,
|
|
16
17
|
validateSsml,
|
|
17
18
|
validateSsmlStructureIntegrity
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-AQ55MOPU.mjs";
|
|
19
20
|
import "./chunk-6S5ODO6A.mjs";
|
|
20
21
|
export {
|
|
21
22
|
areAzureLanguagesEquivalent,
|
|
22
23
|
buildPartialSsml,
|
|
23
24
|
buildSsml,
|
|
25
|
+
createAzureUrlValidatorRunner,
|
|
24
26
|
extractSsmlText,
|
|
25
27
|
extractSsmlTranslatableText,
|
|
26
28
|
fromPlainTextToSsml,
|
package/dist/elements.js
CHANGED
|
@@ -1017,6 +1017,69 @@ var AZURE_VOICE_DEFINITIONS = [
|
|
|
1017
1017
|
},
|
|
1018
1018
|
{ name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" }
|
|
1019
1019
|
];
|
|
1020
|
+
function createAzureUrlValidatorRunner(validator, options = {}) {
|
|
1021
|
+
if (typeof validator !== "function") throw new TypeError("A URL validator function is required.");
|
|
1022
|
+
const concurrency = options.concurrency === void 0 ? Infinity : Number.isFinite(options.concurrency) ? Math.max(1, Math.floor(options.concurrency)) : Infinity;
|
|
1023
|
+
const cache = options.cache ?? /* @__PURE__ */ new Map();
|
|
1024
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
1025
|
+
const waiters = [];
|
|
1026
|
+
let active = 0;
|
|
1027
|
+
const acquire = async () => {
|
|
1028
|
+
if (active < concurrency) {
|
|
1029
|
+
active += 1;
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
await new Promise((resolve) => waiters.push(resolve));
|
|
1033
|
+
active += 1;
|
|
1034
|
+
};
|
|
1035
|
+
const release = () => {
|
|
1036
|
+
active -= 1;
|
|
1037
|
+
waiters.shift()?.();
|
|
1038
|
+
};
|
|
1039
|
+
const check = async (url, context) => {
|
|
1040
|
+
if (options.signal?.aborted) throw new Error("URL validation was aborted.");
|
|
1041
|
+
const cached = cache.get(url);
|
|
1042
|
+
if (cached !== void 0) return cached;
|
|
1043
|
+
const existing = inFlight.get(url);
|
|
1044
|
+
if (existing) return existing;
|
|
1045
|
+
const promise = (async () => {
|
|
1046
|
+
await acquire();
|
|
1047
|
+
try {
|
|
1048
|
+
if (options.signal?.aborted) throw new Error("URL validation was aborted.");
|
|
1049
|
+
const validation = Promise.resolve(validator(url, context));
|
|
1050
|
+
let timer;
|
|
1051
|
+
let abortHandler;
|
|
1052
|
+
const cancellation = new Promise((_resolve, reject) => {
|
|
1053
|
+
abortHandler = () => reject(new Error("URL validation was aborted."));
|
|
1054
|
+
options.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
1055
|
+
if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
|
|
1056
|
+
timer = setTimeout(
|
|
1057
|
+
() => reject(new Error(`URL validation timed out after ${options.timeoutMs} ms.`)),
|
|
1058
|
+
options.timeoutMs
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
try {
|
|
1063
|
+
const result = await (timer || abortHandler ? Promise.race([validation, cancellation]) : validation);
|
|
1064
|
+
cache.set(url, result);
|
|
1065
|
+
return result;
|
|
1066
|
+
} finally {
|
|
1067
|
+
if (timer) clearTimeout(timer);
|
|
1068
|
+
if (abortHandler) options.signal?.removeEventListener("abort", abortHandler);
|
|
1069
|
+
}
|
|
1070
|
+
} finally {
|
|
1071
|
+
release();
|
|
1072
|
+
}
|
|
1073
|
+
})();
|
|
1074
|
+
inFlight.set(url, promise);
|
|
1075
|
+
try {
|
|
1076
|
+
return await promise;
|
|
1077
|
+
} finally {
|
|
1078
|
+
inFlight.delete(url);
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
return (url, context) => check(url, context);
|
|
1082
|
+
}
|
|
1020
1083
|
var ALLOWED_BREAK_STRENGTHS = /* @__PURE__ */ new Set(["none", "x-weak", "weak", "medium", "strong", "x-strong"]);
|
|
1021
1084
|
var ALLOWED_SAY_AS = /* @__PURE__ */ new Set([
|
|
1022
1085
|
"characters",
|
|
@@ -1301,23 +1364,23 @@ function validateVoiceFeatureMatrix(token, source, diagnostics, voiceName, defin
|
|
|
1301
1364
|
);
|
|
1302
1365
|
}
|
|
1303
1366
|
}
|
|
1304
|
-
function validateAudioSource(token, source, diagnostics, options,
|
|
1367
|
+
function validateAudioSource(token, source, diagnostics, options, elementName3) {
|
|
1305
1368
|
const src = attr(token, "src");
|
|
1306
1369
|
if (!src) {
|
|
1307
|
-
addDiagnostic(diagnostics, source, token.start, `<${
|
|
1370
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName3}> requires a "src" attribute.`);
|
|
1308
1371
|
return;
|
|
1309
1372
|
}
|
|
1310
1373
|
let parsed;
|
|
1311
1374
|
try {
|
|
1312
1375
|
parsed = new URL(src);
|
|
1313
1376
|
} catch {
|
|
1314
|
-
addDiagnostic(diagnostics, source, token.start, `<${
|
|
1377
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName3} src> must be an absolute HTTP(S) URL.`);
|
|
1315
1378
|
return;
|
|
1316
1379
|
}
|
|
1317
1380
|
if (parsed.username || parsed.password)
|
|
1318
|
-
addDiagnostic(diagnostics, source, token.start, `<${
|
|
1381
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName3} src> must not contain URL credentials.`);
|
|
1319
1382
|
if (parsed.protocol !== "https:" && !(options.allowHttpAudio && parsed.protocol === "http:"))
|
|
1320
|
-
addDiagnostic(diagnostics, source, token.start, `<${
|
|
1383
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName3} src> must use HTTPS.`);
|
|
1321
1384
|
const isAllowedOrigin = options.allowedAudioOrigins?.some((allowedOrigin) => {
|
|
1322
1385
|
try {
|
|
1323
1386
|
const configured = new URL(allowedOrigin);
|
|
@@ -1329,13 +1392,13 @@ function validateAudioSource(token, source, diagnostics, options, elementName2)
|
|
|
1329
1392
|
}
|
|
1330
1393
|
}) ?? false;
|
|
1331
1394
|
if (options.allowedAudioOrigins && !isAllowedOrigin)
|
|
1332
|
-
addDiagnostic(diagnostics, source, token.start, `<${
|
|
1395
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName3} src> origin "${parsed.origin}" is not allowed.`);
|
|
1333
1396
|
else if (!isAllowedOrigin && !options.allowExternalAudio)
|
|
1334
1397
|
addDiagnostic(
|
|
1335
1398
|
diagnostics,
|
|
1336
1399
|
source,
|
|
1337
1400
|
token.start,
|
|
1338
|
-
`<${
|
|
1401
|
+
`<${elementName3} src> external origin "${parsed.origin}" is blocked by default; set allowExternalAudio to true or provide allowedAudioOrigins.`
|
|
1339
1402
|
);
|
|
1340
1403
|
}
|
|
1341
1404
|
function validateElement(token, source, diagnostics, voiceName, options, voiceCatalog) {
|
|
@@ -1517,7 +1580,7 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceCa
|
|
|
1517
1580
|
addDiagnostic(diagnostics, source, token.start, "<mstts:backgroundaudio> must be self-closing.");
|
|
1518
1581
|
}
|
|
1519
1582
|
}
|
|
1520
|
-
function
|
|
1583
|
+
function validateAzureSsmlStatic(ssml, options = {}) {
|
|
1521
1584
|
const diagnostics = [];
|
|
1522
1585
|
if (typeof ssml !== "string") {
|
|
1523
1586
|
return [
|
|
@@ -1641,6 +1704,59 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1641
1704
|
}
|
|
1642
1705
|
return diagnostics;
|
|
1643
1706
|
}
|
|
1707
|
+
function urlAttributes(token) {
|
|
1708
|
+
const tag = canonicalTagName(token.name);
|
|
1709
|
+
const attributes = tag === "audio" || tag === "mstts:backgroundaudio" ? ["src"] : tag === "lexicon" ? ["uri"] : tag === "mstts:voiceconversion" ? ["url"] : [];
|
|
1710
|
+
return attributes.flatMap((attribute) => {
|
|
1711
|
+
const value = attr(token, attribute);
|
|
1712
|
+
return value === void 0 ? [] : [{ attribute, value }];
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
function validateAzureSsml(ssml, options = {}) {
|
|
1716
|
+
const diagnostics = validateAzureSsmlStatic(ssml, options);
|
|
1717
|
+
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
1718
|
+
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
1719
|
+
const runnerOptions = options.urlValidation ?? {};
|
|
1720
|
+
const boundedValidator = createAzureUrlValidatorRunner(validator, {
|
|
1721
|
+
...runnerOptions,
|
|
1722
|
+
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
1723
|
+
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
1724
|
+
...options.urlValidatorSignal ? { signal: options.urlValidatorSignal } : {},
|
|
1725
|
+
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
1726
|
+
});
|
|
1727
|
+
let tokens;
|
|
1728
|
+
try {
|
|
1729
|
+
tokens = tokenizeElements(ssml);
|
|
1730
|
+
} catch {
|
|
1731
|
+
return diagnostics;
|
|
1732
|
+
}
|
|
1733
|
+
const checks = tokens.flatMap(
|
|
1734
|
+
(token) => urlAttributes(token).map(async ({ attribute, value }) => {
|
|
1735
|
+
try {
|
|
1736
|
+
const result = await boundedValidator(value, { tag: token.name, attribute });
|
|
1737
|
+
const valid = typeof result === "boolean" ? result : result.valid;
|
|
1738
|
+
if (!valid) {
|
|
1739
|
+
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
1740
|
+
addDiagnostic(
|
|
1741
|
+
diagnostics,
|
|
1742
|
+
ssml,
|
|
1743
|
+
token.start,
|
|
1744
|
+
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
} catch (error) {
|
|
1748
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1749
|
+
addDiagnostic(
|
|
1750
|
+
diagnostics,
|
|
1751
|
+
ssml,
|
|
1752
|
+
token.start,
|
|
1753
|
+
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
})
|
|
1757
|
+
);
|
|
1758
|
+
return Promise.all(checks).then(() => diagnostics);
|
|
1759
|
+
}
|
|
1644
1760
|
var AZURE_VOICE_CATALOG_METADATA = {
|
|
1645
1761
|
apiVersion: "2025-10-01",
|
|
1646
1762
|
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
@@ -3578,6 +3694,42 @@ var SSML_HOVER_COPY = {
|
|
|
3578
3694
|
parameters: {
|
|
3579
3695
|
value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
|
|
3580
3696
|
}
|
|
3697
|
+
},
|
|
3698
|
+
"mstts:dialog": { title: "\u30C0\u30A4\u30A2\u30ED\u30B0", description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002", parameters: {} },
|
|
3699
|
+
"mstts:turn": {
|
|
3700
|
+
title: "\u8A71\u8005\u30BF\u30FC\u30F3",
|
|
3701
|
+
description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
3702
|
+
parameters: {
|
|
3703
|
+
voice: { title: "voice", description: "\u3053\u306E\u30BF\u30FC\u30F3\u3067\u4F7F\u7528\u3059\u308B Azure \u97F3\u58F0\u540D\u3002" },
|
|
3704
|
+
speaker: { title: "speaker", description: "\u8A71\u8005\u8B58\u5225\u5B50\u3002" }
|
|
3705
|
+
}
|
|
3706
|
+
},
|
|
3707
|
+
"mstts:backgroundaudio": {
|
|
3708
|
+
title: "\u80CC\u666F\u97F3\u58F0",
|
|
3709
|
+
description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
3710
|
+
parameters: { src: { title: "src", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u306E HTTPS URL\u3002" } }
|
|
3711
|
+
},
|
|
3712
|
+
"mstts:ttsembedding": {
|
|
3713
|
+
title: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
|
|
3714
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
|
|
3715
|
+
parameters: { speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" } }
|
|
3716
|
+
},
|
|
3717
|
+
"mstts:embedding": {
|
|
3718
|
+
title: "\u57CB\u3081\u8FBC\u307F",
|
|
3719
|
+
description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
3720
|
+
parameters: {
|
|
3721
|
+
id: { title: "id", description: "\u57CB\u3081\u8FBC\u307F ID\u3002" },
|
|
3722
|
+
speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
|
|
3723
|
+
}
|
|
3724
|
+
},
|
|
3725
|
+
"mstts:voiceconversion": {
|
|
3726
|
+
title: "\u97F3\u58F0\u5909\u63DB",
|
|
3727
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
3728
|
+
parameters: {
|
|
3729
|
+
url: { title: "url", description: "\u97F3\u58F0\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB URL\u3002" },
|
|
3730
|
+
profile: { title: "profile", description: "\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u3002" },
|
|
3731
|
+
speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
|
|
3732
|
+
}
|
|
3581
3733
|
}
|
|
3582
3734
|
}
|
|
3583
3735
|
},
|
|
@@ -3742,6 +3894,42 @@ var SSML_HOVER_COPY = {
|
|
|
3742
3894
|
parameters: {
|
|
3743
3895
|
value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
|
|
3744
3896
|
}
|
|
3897
|
+
},
|
|
3898
|
+
"mstts:dialog": { title: "Dialog", description: "Groups multiple speaker turns.", parameters: {} },
|
|
3899
|
+
"mstts:turn": {
|
|
3900
|
+
title: "Dialog turn",
|
|
3901
|
+
description: "Specifies a speaker and utterance within a dialog.",
|
|
3902
|
+
parameters: {
|
|
3903
|
+
voice: { title: "voice", description: "The Azure voice used by this turn." },
|
|
3904
|
+
speaker: { title: "speaker", description: "The speaker identifier." }
|
|
3905
|
+
}
|
|
3906
|
+
},
|
|
3907
|
+
"mstts:backgroundaudio": {
|
|
3908
|
+
title: "Background audio",
|
|
3909
|
+
description: "Specifies an audio file played behind synthesized speech.",
|
|
3910
|
+
parameters: { src: { title: "src", description: "The HTTPS URL of the audio file." } }
|
|
3911
|
+
},
|
|
3912
|
+
"mstts:ttsembedding": {
|
|
3913
|
+
title: "TTS embedding",
|
|
3914
|
+
description: "Embeds custom voice or speaker profile metadata.",
|
|
3915
|
+
parameters: { speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." } }
|
|
3916
|
+
},
|
|
3917
|
+
"mstts:embedding": {
|
|
3918
|
+
title: "Embedding",
|
|
3919
|
+
description: "Specifies speaker embedding metadata.",
|
|
3920
|
+
parameters: {
|
|
3921
|
+
id: { title: "id", description: "The embedding ID." },
|
|
3922
|
+
speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
|
|
3923
|
+
}
|
|
3924
|
+
},
|
|
3925
|
+
"mstts:voiceconversion": {
|
|
3926
|
+
title: "Voice conversion",
|
|
3927
|
+
description: "Specifies custom voice conversion metadata.",
|
|
3928
|
+
parameters: {
|
|
3929
|
+
url: { title: "url", description: "The voice conversion profile URL." },
|
|
3930
|
+
profile: { title: "profile", description: "The conversion profile." },
|
|
3931
|
+
speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
|
|
3932
|
+
}
|
|
3745
3933
|
}
|
|
3746
3934
|
}
|
|
3747
3935
|
}
|