ssml-builder-js 2.14.0 → 2.16.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 +10 -4
- package/dist/{chunk-UZSCXPC5.mjs → chunk-F2EMU3HM.mjs} +2 -2
- package/dist/{chunk-QFIBPCO4.mjs → chunk-HI74FTKY.mjs} +197 -21
- package/dist/chunk-HI74FTKY.mjs.map +1 -0
- package/dist/{chunk-AQ55MOPU.mjs → chunk-NKLZGITR.mjs} +354 -177
- package/dist/chunk-NKLZGITR.mjs.map +1 -0
- package/dist/core.d.mts +67 -17
- package/dist/core.d.ts +67 -17
- package/dist/core.js +353 -174
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +5 -1
- package/dist/elements.js +70 -20
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +2 -2
- package/dist/{index.d-CUXRwSw0.d.mts → index.d-DR5Qz43p.d.mts} +37 -2
- package/dist/{index.d-CUXRwSw0.d.ts → index.d-DR5Qz43p.d.ts} +37 -2
- package/dist/index.d.mts +162 -19
- package/dist/index.d.ts +162 -19
- package/dist/index.js +2060 -1128
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +752 -174
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +11 -2
- package/dist/react.d.ts +11 -2
- package/dist/react.js +147 -32
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +79 -14
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-AQ55MOPU.mjs.map +0 -1
- package/dist/chunk-QFIBPCO4.mjs.map +0 -1
- /package/dist/{chunk-UZSCXPC5.mjs.map → chunk-F2EMU3HM.mjs.map} +0 -0
package/dist/core.mjs
CHANGED
|
@@ -8,15 +8,17 @@ import {
|
|
|
8
8
|
fromPlainTextToSsml,
|
|
9
9
|
getAzureVoiceCatalogMetadata,
|
|
10
10
|
getBuiltInVoiceCatalogMetadata,
|
|
11
|
+
getSsmlSourceMap,
|
|
11
12
|
isValidAzureAudioDuration,
|
|
12
13
|
mapSsmlTextNodes,
|
|
13
14
|
normalizeAzureLanguage,
|
|
14
15
|
parseSsml,
|
|
15
16
|
splitSsmlDocument,
|
|
16
17
|
validateAzureSsml,
|
|
18
|
+
validateAzureSsmlChunks,
|
|
17
19
|
validateSsml,
|
|
18
20
|
validateSsmlStructureIntegrity
|
|
19
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-NKLZGITR.mjs";
|
|
20
22
|
import "./chunk-6S5ODO6A.mjs";
|
|
21
23
|
export {
|
|
22
24
|
areAzureLanguagesEquivalent,
|
|
@@ -28,12 +30,14 @@ export {
|
|
|
28
30
|
fromPlainTextToSsml,
|
|
29
31
|
getAzureVoiceCatalogMetadata,
|
|
30
32
|
getBuiltInVoiceCatalogMetadata,
|
|
33
|
+
getSsmlSourceMap,
|
|
31
34
|
isValidAzureAudioDuration,
|
|
32
35
|
mapSsmlTextNodes,
|
|
33
36
|
normalizeAzureLanguage,
|
|
34
37
|
parseSsml,
|
|
35
38
|
splitSsmlDocument,
|
|
36
39
|
validateAzureSsml,
|
|
40
|
+
validateAzureSsmlChunks,
|
|
37
41
|
validateSsml,
|
|
38
42
|
validateSsmlStructureIntegrity
|
|
39
43
|
};
|
package/dist/elements.js
CHANGED
|
@@ -1024,34 +1024,51 @@ function createAzureUrlValidatorRunner(validator, options = {}) {
|
|
|
1024
1024
|
const inFlight = /* @__PURE__ */ new Map();
|
|
1025
1025
|
const waiters = [];
|
|
1026
1026
|
let active = 0;
|
|
1027
|
-
const
|
|
1027
|
+
const configuredSignal = options.signal ?? new AbortController().signal;
|
|
1028
|
+
const acquire = async (signal) => {
|
|
1029
|
+
if (signal.aborted) throw new Error("URL validation was aborted.");
|
|
1028
1030
|
if (active < concurrency) {
|
|
1029
1031
|
active += 1;
|
|
1030
1032
|
return;
|
|
1031
1033
|
}
|
|
1032
|
-
await new Promise((resolve) =>
|
|
1034
|
+
await new Promise((resolve, reject) => {
|
|
1035
|
+
let waiter;
|
|
1036
|
+
const abortHandler = () => {
|
|
1037
|
+
const index = waiters.indexOf(waiter);
|
|
1038
|
+
if (index >= 0) waiters.splice(index, 1);
|
|
1039
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1040
|
+
reject(new Error("URL validation was aborted."));
|
|
1041
|
+
};
|
|
1042
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1043
|
+
waiter = () => {
|
|
1044
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1045
|
+
resolve();
|
|
1046
|
+
};
|
|
1047
|
+
waiters.push(waiter);
|
|
1048
|
+
});
|
|
1033
1049
|
active += 1;
|
|
1034
1050
|
};
|
|
1035
1051
|
const release = () => {
|
|
1036
1052
|
active -= 1;
|
|
1037
1053
|
waiters.shift()?.();
|
|
1038
1054
|
};
|
|
1039
|
-
const check = async (url, context) => {
|
|
1040
|
-
if (
|
|
1041
|
-
const
|
|
1055
|
+
const check = async (url, context, signal = configuredSignal) => {
|
|
1056
|
+
if (signal.aborted) throw new Error("URL validation was aborted.");
|
|
1057
|
+
const key = `${context.tag}:${context.attribute}:${url}`;
|
|
1058
|
+
const cached = cache.get(key);
|
|
1042
1059
|
if (cached !== void 0) return cached;
|
|
1043
|
-
const existing = inFlight.get(
|
|
1060
|
+
const existing = inFlight.get(key);
|
|
1044
1061
|
if (existing) return existing;
|
|
1045
1062
|
const promise = (async () => {
|
|
1046
|
-
await acquire();
|
|
1063
|
+
await acquire(signal);
|
|
1047
1064
|
try {
|
|
1048
|
-
if (
|
|
1049
|
-
const validation = Promise.resolve(validator(url, context));
|
|
1065
|
+
if (signal.aborted) throw new Error("URL validation was aborted.");
|
|
1066
|
+
const validation = Promise.resolve(validator(url, context, signal));
|
|
1050
1067
|
let timer;
|
|
1051
1068
|
let abortHandler;
|
|
1052
1069
|
const cancellation = new Promise((_resolve, reject) => {
|
|
1053
1070
|
abortHandler = () => reject(new Error("URL validation was aborted."));
|
|
1054
|
-
|
|
1071
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1055
1072
|
if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
|
|
1056
1073
|
timer = setTimeout(
|
|
1057
1074
|
() => reject(new Error(`URL validation timed out after ${options.timeoutMs} ms.`)),
|
|
@@ -1061,24 +1078,24 @@ function createAzureUrlValidatorRunner(validator, options = {}) {
|
|
|
1061
1078
|
});
|
|
1062
1079
|
try {
|
|
1063
1080
|
const result = await (timer || abortHandler ? Promise.race([validation, cancellation]) : validation);
|
|
1064
|
-
cache.set(
|
|
1081
|
+
cache.set(key, result);
|
|
1065
1082
|
return result;
|
|
1066
1083
|
} finally {
|
|
1067
1084
|
if (timer) clearTimeout(timer);
|
|
1068
|
-
if (abortHandler)
|
|
1085
|
+
if (abortHandler) signal.removeEventListener("abort", abortHandler);
|
|
1069
1086
|
}
|
|
1070
1087
|
} finally {
|
|
1071
1088
|
release();
|
|
1072
1089
|
}
|
|
1073
1090
|
})();
|
|
1074
|
-
inFlight.set(
|
|
1091
|
+
inFlight.set(key, promise);
|
|
1075
1092
|
try {
|
|
1076
1093
|
return await promise;
|
|
1077
1094
|
} finally {
|
|
1078
|
-
inFlight.delete(
|
|
1095
|
+
inFlight.delete(key);
|
|
1079
1096
|
}
|
|
1080
1097
|
};
|
|
1081
|
-
return (url, context) => check(url, context);
|
|
1098
|
+
return (url, context, signal) => check(url, context, signal ?? configuredSignal);
|
|
1082
1099
|
}
|
|
1083
1100
|
var ALLOWED_BREAK_STRENGTHS = /* @__PURE__ */ new Set(["none", "x-weak", "weak", "medium", "strong", "x-strong"]);
|
|
1084
1101
|
var ALLOWED_SAY_AS = /* @__PURE__ */ new Set([
|
|
@@ -1191,6 +1208,7 @@ function tokenizeElements(source) {
|
|
|
1191
1208
|
if (parent) parent.childElementCount += 1;
|
|
1192
1209
|
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
1193
1210
|
const tokenName = nameMatch[1];
|
|
1211
|
+
const path = parent ? [...parent.path, `${tokenName}[${childElementIndex ?? 0}]`] : [tokenName];
|
|
1194
1212
|
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
1195
1213
|
tokens.push({
|
|
1196
1214
|
attributes,
|
|
@@ -1201,12 +1219,14 @@ function tokenizeElements(source) {
|
|
|
1201
1219
|
parentName: parent?.name,
|
|
1202
1220
|
parentVoiceName,
|
|
1203
1221
|
selfClosing,
|
|
1204
|
-
start
|
|
1222
|
+
start,
|
|
1223
|
+
path
|
|
1205
1224
|
});
|
|
1206
1225
|
if (!selfClosing) {
|
|
1207
1226
|
openElements.push({
|
|
1208
1227
|
childElementCount: 0,
|
|
1209
1228
|
name: tokenName,
|
|
1229
|
+
path,
|
|
1210
1230
|
voiceName: tokenVoiceName
|
|
1211
1231
|
});
|
|
1212
1232
|
}
|
|
@@ -1219,13 +1239,14 @@ function location(source, offset) {
|
|
|
1219
1239
|
const line = before.split("\n").length;
|
|
1220
1240
|
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
1221
1241
|
}
|
|
1222
|
-
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code2) {
|
|
1242
|
+
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code2, metadata = {}) {
|
|
1223
1243
|
diagnostics.push({
|
|
1224
1244
|
...location(source, offset),
|
|
1225
1245
|
message,
|
|
1226
1246
|
severity,
|
|
1227
1247
|
source: "ssml-static-validator",
|
|
1228
|
-
...code2 ? { code: code2 } : {}
|
|
1248
|
+
...code2 ? { code: code2 } : {},
|
|
1249
|
+
...metadata
|
|
1229
1250
|
});
|
|
1230
1251
|
}
|
|
1231
1252
|
function isSupportedProsodyRate(value) {
|
|
@@ -1688,9 +1709,11 @@ function validateAzureSsmlStatic(ssml, options = {}) {
|
|
|
1688
1709
|
for (const token of tokens) {
|
|
1689
1710
|
const tokenName = token.name.toLowerCase();
|
|
1690
1711
|
const tokenVoiceName = tokenName === "voice" ? attr(token, "name")?.trim() : tokenName === "mstts:turn" ? attr(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
1712
|
+
const tokenDiagnosticStart = diagnostics.length;
|
|
1691
1713
|
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
1692
1714
|
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
1693
1715
|
validateVoiceFeatureMatrix(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
1716
|
+
annotateTokenDiagnostics(diagnostics, tokenDiagnosticStart, token, options, tokenVoiceName);
|
|
1694
1717
|
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
1695
1718
|
addDiagnostic(
|
|
1696
1719
|
diagnostics,
|
|
@@ -1712,18 +1735,37 @@ function urlAttributes(token) {
|
|
|
1712
1735
|
return value === void 0 ? [] : [{ attribute, value }];
|
|
1713
1736
|
});
|
|
1714
1737
|
}
|
|
1738
|
+
function annotateTokenDiagnostics(diagnostics, startIndex, token, options, voiceName) {
|
|
1739
|
+
const attributes = [...token.attributes.keys()];
|
|
1740
|
+
for (const diagnostic of diagnostics.slice(startIndex)) {
|
|
1741
|
+
const attributeName = attributes.find(
|
|
1742
|
+
(attribute) => new RegExp(`(?:<[^> ]+\\s+|")${attribute}(?:"|>|\\s)`, "i").test(diagnostic.message)
|
|
1743
|
+
);
|
|
1744
|
+
const nodePath = options.sourceNodePath ? [...options.sourceNodePath] : [...token.path];
|
|
1745
|
+
Object.assign(diagnostic, {
|
|
1746
|
+
range: { start: token.start, end: token.end + 1 },
|
|
1747
|
+
tagName: token.name,
|
|
1748
|
+
...attributeName ? { attributeName } : {},
|
|
1749
|
+
...voiceName ? { voiceName } : {},
|
|
1750
|
+
...options.chunkIndex !== void 0 ? { chunkIndex: options.chunkIndex } : {},
|
|
1751
|
+
nodePath,
|
|
1752
|
+
targetNodePath: [...token.path]
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1715
1756
|
function validateAzureSsml(ssml, options = {}) {
|
|
1716
1757
|
const diagnostics = validateAzureSsmlStatic(ssml, options);
|
|
1717
1758
|
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
1718
1759
|
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
1719
1760
|
const runnerOptions = options.urlValidation ?? {};
|
|
1720
|
-
const boundedValidator = createAzureUrlValidatorRunner(validator, {
|
|
1761
|
+
const boundedValidator = options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
|
|
1721
1762
|
...runnerOptions,
|
|
1722
1763
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
1723
1764
|
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
1724
1765
|
...options.urlValidatorSignal ? { signal: options.urlValidatorSignal } : {},
|
|
1725
1766
|
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
1726
1767
|
});
|
|
1768
|
+
const validationSignal = options.urlValidatorSignal ?? options.urlValidation?.signal ?? new AbortController().signal;
|
|
1727
1769
|
let tokens;
|
|
1728
1770
|
try {
|
|
1729
1771
|
tokens = tokenizeElements(ssml);
|
|
@@ -1733,25 +1775,33 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1733
1775
|
const checks = tokens.flatMap(
|
|
1734
1776
|
(token) => urlAttributes(token).map(async ({ attribute, value }) => {
|
|
1735
1777
|
try {
|
|
1736
|
-
const result = await boundedValidator(
|
|
1778
|
+
const result = await boundedValidator(
|
|
1779
|
+
value,
|
|
1780
|
+
{ tag: token.name, attribute, ...options.sourceNodePath ? { sourceNodePath: options.sourceNodePath } : {} },
|
|
1781
|
+
validationSignal
|
|
1782
|
+
);
|
|
1737
1783
|
const valid = typeof result === "boolean" ? result : result.valid;
|
|
1738
1784
|
if (!valid) {
|
|
1739
1785
|
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
1786
|
+
const diagnosticStart = diagnostics.length;
|
|
1740
1787
|
addDiagnostic(
|
|
1741
1788
|
diagnostics,
|
|
1742
1789
|
ssml,
|
|
1743
1790
|
token.start,
|
|
1744
1791
|
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
1745
1792
|
);
|
|
1793
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
1746
1794
|
}
|
|
1747
1795
|
} catch (error) {
|
|
1748
1796
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1797
|
+
const diagnosticStart = diagnostics.length;
|
|
1749
1798
|
addDiagnostic(
|
|
1750
1799
|
diagnostics,
|
|
1751
1800
|
ssml,
|
|
1752
1801
|
token.start,
|
|
1753
1802
|
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
1754
1803
|
);
|
|
1804
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
1755
1805
|
}
|
|
1756
1806
|
})
|
|
1757
1807
|
);
|