ssml-builder-js 2.14.0 → 2.15.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 +6 -4
- package/dist/{chunk-UZSCXPC5.mjs → chunk-BDY2Q2JL.mjs} +2 -2
- package/dist/{chunk-AQ55MOPU.mjs → chunk-FXUM45ZY.mjs} +305 -173
- package/dist/chunk-FXUM45ZY.mjs.map +1 -0
- package/dist/{chunk-QFIBPCO4.mjs → chunk-WXFLUCLR.mjs} +164 -17
- package/dist/chunk-WXFLUCLR.mjs.map +1 -0
- package/dist/core.d.mts +48 -17
- package/dist/core.d.ts +48 -17
- package/dist/core.js +303 -170
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +38 -16
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +2 -2
- package/dist/{index.d-CUXRwSw0.d.mts → index.d-8BvkB9gz.d.mts} +22 -2
- package/dist/{index.d-CUXRwSw0.d.ts → index.d-8BvkB9gz.d.ts} +22 -2
- package/dist/index.d.mts +114 -18
- package/dist/index.d.ts +114 -18
- package/dist/index.js +1783 -1281
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +307 -86
- 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 +115 -28
- 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-BDY2Q2JL.mjs.map} +0 -0
|
@@ -925,6 +925,130 @@ function buildPartialSsml(textOrOptions, context) {
|
|
|
925
925
|
}
|
|
926
926
|
return serializePartialSsml(textOrOptions.text, textOrOptions);
|
|
927
927
|
}
|
|
928
|
+
function decodeXmlText(value) {
|
|
929
|
+
return value.replace(/&(?:amp|apos|gt|lt|quot);|&#(?:x[\da-f]+|\d+);/gi, (entity) => {
|
|
930
|
+
if (entity === "&") return "&";
|
|
931
|
+
if (entity === "'") return "'";
|
|
932
|
+
if (entity === ">") return ">";
|
|
933
|
+
if (entity === "<") return "<";
|
|
934
|
+
if (entity === """) return '"';
|
|
935
|
+
const hexadecimal = entity.toLowerCase().startsWith("&#x");
|
|
936
|
+
const digits = entity.slice(hexadecimal ? 3 : 2, -1);
|
|
937
|
+
return String.fromCodePoint(Number.parseInt(digits, hexadecimal ? 16 : 10));
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function decodeXmlAttribute(value) {
|
|
941
|
+
return decodeXmlText(value);
|
|
942
|
+
}
|
|
943
|
+
function findTagEnd(source, start) {
|
|
944
|
+
let quote = "";
|
|
945
|
+
for (let index = start; index < source.length; index += 1) {
|
|
946
|
+
const character = source[index];
|
|
947
|
+
if (quote) {
|
|
948
|
+
if (character === quote) quote = "";
|
|
949
|
+
} else if (character === '"' || character === "'") {
|
|
950
|
+
quote = character;
|
|
951
|
+
} else if (character === ">") {
|
|
952
|
+
return index;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return source.length - 1;
|
|
956
|
+
}
|
|
957
|
+
function readTagName(tag) {
|
|
958
|
+
const match = /^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/.exec(tag);
|
|
959
|
+
return match?.[1];
|
|
960
|
+
}
|
|
961
|
+
function readTagAttributes(tag, name) {
|
|
962
|
+
const attributes = {};
|
|
963
|
+
const nameStart = tag.indexOf(name);
|
|
964
|
+
const attributeSource = tag.slice(nameStart + name.length, tag.length - 1).replace(/\/\s*$/, "");
|
|
965
|
+
const attributePattern = /([A-Za-z_][A-Za-z0-9_.:-]*)\s*=\s*(["'])([\s\S]*?)\2/g;
|
|
966
|
+
for (const match of attributeSource.matchAll(attributePattern)) {
|
|
967
|
+
attributes[match[1].toLowerCase()] = decodeXmlAttribute(match[3]);
|
|
968
|
+
}
|
|
969
|
+
return attributes;
|
|
970
|
+
}
|
|
971
|
+
function collectSourceMap(source) {
|
|
972
|
+
const segments = [];
|
|
973
|
+
const markers = [];
|
|
974
|
+
const elements = [];
|
|
975
|
+
let textOffset = 0;
|
|
976
|
+
let index = 0;
|
|
977
|
+
const textParts = [];
|
|
978
|
+
const addText = (value) => {
|
|
979
|
+
if (!value) return;
|
|
980
|
+
const parent = elements[elements.length - 1];
|
|
981
|
+
if (parent) parent.nextChildIndex += 1;
|
|
982
|
+
const sourceNodePath = parent?.path ?? ["speak"];
|
|
983
|
+
const start = textOffset;
|
|
984
|
+
textOffset += value.length;
|
|
985
|
+
textParts.push(value);
|
|
986
|
+
segments.push({ text: value, range: { start, end: textOffset }, sourceNodePath: [...sourceNodePath] });
|
|
987
|
+
};
|
|
988
|
+
while (index < source.length) {
|
|
989
|
+
if (source[index] !== "<") {
|
|
990
|
+
const end2 = source.indexOf("<", index);
|
|
991
|
+
const textEnd = end2 === -1 ? source.length : end2;
|
|
992
|
+
addText(decodeXmlText(source.slice(index, textEnd)));
|
|
993
|
+
index = textEnd;
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
if (source.startsWith("<!--", index)) {
|
|
997
|
+
const end2 = source.indexOf("-->", index + 4);
|
|
998
|
+
index = end2 === -1 ? source.length : end2 + 3;
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
if (source.startsWith("<![CDATA[", index)) {
|
|
1002
|
+
const contentStart = index + 9;
|
|
1003
|
+
const end2 = source.indexOf("]]>", contentStart);
|
|
1004
|
+
const contentEnd = end2 === -1 ? source.length : end2;
|
|
1005
|
+
addText(source.slice(contentStart, contentEnd));
|
|
1006
|
+
index = end2 === -1 ? source.length : end2 + 3;
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if (source.startsWith("<?", index)) {
|
|
1010
|
+
const end2 = source.indexOf("?>", index + 2);
|
|
1011
|
+
index = end2 === -1 ? source.length : end2 + 2;
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
const end = findTagEnd(source, index + 1);
|
|
1015
|
+
const rawTag = source.slice(index, end + 1);
|
|
1016
|
+
if (rawTag.startsWith("</")) {
|
|
1017
|
+
elements.pop();
|
|
1018
|
+
index = end + 1;
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
const name = readTagName(rawTag);
|
|
1022
|
+
if (!name) {
|
|
1023
|
+
index = end + 1;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
const parent = elements[elements.length - 1];
|
|
1027
|
+
const childIndex = parent?.nextChildIndex ?? 0;
|
|
1028
|
+
if (parent) parent.nextChildIndex += 1;
|
|
1029
|
+
const path = parent ? [...parent.path, `${name}[${childIndex}]`] : [name];
|
|
1030
|
+
const attributes = readTagAttributes(rawTag, name);
|
|
1031
|
+
const normalizedName = name.toLowerCase();
|
|
1032
|
+
if (normalizedName === "mark" || normalizedName === "bookmark") {
|
|
1033
|
+
const markerName = attributes[normalizedName === "mark" ? "name" : "mark"];
|
|
1034
|
+
if (markerName) {
|
|
1035
|
+
markers.push({
|
|
1036
|
+
kind: normalizedName,
|
|
1037
|
+
name: markerName,
|
|
1038
|
+
originalTextRange: { start: textOffset, end: textOffset },
|
|
1039
|
+
sourceNodePath: [...path]
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
if (!/\/\s*>$/.test(rawTag)) elements.push({ name, path, nextChildIndex: 0 });
|
|
1044
|
+
index = end + 1;
|
|
1045
|
+
}
|
|
1046
|
+
return { text: textParts.join(""), segments, markers };
|
|
1047
|
+
}
|
|
1048
|
+
function getSsmlSourceMap(ssml) {
|
|
1049
|
+
parseSsml(ssml);
|
|
1050
|
+
return collectSourceMap(ssml);
|
|
1051
|
+
}
|
|
928
1052
|
var PARSER_POSITION_SUFFIX = / at position (\d+)$/;
|
|
929
1053
|
function validateSsml(xmlString) {
|
|
930
1054
|
try {
|
|
@@ -1066,34 +1190,51 @@ function createAzureUrlValidatorRunner(validator, options = {}) {
|
|
|
1066
1190
|
const inFlight = /* @__PURE__ */ new Map();
|
|
1067
1191
|
const waiters = [];
|
|
1068
1192
|
let active = 0;
|
|
1069
|
-
const
|
|
1193
|
+
const configuredSignal = options.signal ?? new AbortController().signal;
|
|
1194
|
+
const acquire = async (signal) => {
|
|
1195
|
+
if (signal.aborted) throw new Error("URL validation was aborted.");
|
|
1070
1196
|
if (active < concurrency) {
|
|
1071
1197
|
active += 1;
|
|
1072
1198
|
return;
|
|
1073
1199
|
}
|
|
1074
|
-
await new Promise((resolve) =>
|
|
1200
|
+
await new Promise((resolve, reject) => {
|
|
1201
|
+
let waiter;
|
|
1202
|
+
const abortHandler = () => {
|
|
1203
|
+
const index = waiters.indexOf(waiter);
|
|
1204
|
+
if (index >= 0) waiters.splice(index, 1);
|
|
1205
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1206
|
+
reject(new Error("URL validation was aborted."));
|
|
1207
|
+
};
|
|
1208
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1209
|
+
waiter = () => {
|
|
1210
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1211
|
+
resolve();
|
|
1212
|
+
};
|
|
1213
|
+
waiters.push(waiter);
|
|
1214
|
+
});
|
|
1075
1215
|
active += 1;
|
|
1076
1216
|
};
|
|
1077
1217
|
const release = () => {
|
|
1078
1218
|
active -= 1;
|
|
1079
1219
|
waiters.shift()?.();
|
|
1080
1220
|
};
|
|
1081
|
-
const check = async (url, context) => {
|
|
1082
|
-
if (
|
|
1083
|
-
const
|
|
1221
|
+
const check = async (url, context, signal = configuredSignal) => {
|
|
1222
|
+
if (signal.aborted) throw new Error("URL validation was aborted.");
|
|
1223
|
+
const key = `${context.tag}:${context.attribute}:${url}`;
|
|
1224
|
+
const cached = cache.get(key);
|
|
1084
1225
|
if (cached !== void 0) return cached;
|
|
1085
|
-
const existing = inFlight.get(
|
|
1226
|
+
const existing = inFlight.get(key);
|
|
1086
1227
|
if (existing) return existing;
|
|
1087
1228
|
const promise = (async () => {
|
|
1088
|
-
await acquire();
|
|
1229
|
+
await acquire(signal);
|
|
1089
1230
|
try {
|
|
1090
|
-
if (
|
|
1091
|
-
const validation = Promise.resolve(validator(url, context));
|
|
1231
|
+
if (signal.aborted) throw new Error("URL validation was aborted.");
|
|
1232
|
+
const validation = Promise.resolve(validator(url, context, signal));
|
|
1092
1233
|
let timer;
|
|
1093
1234
|
let abortHandler;
|
|
1094
1235
|
const cancellation = new Promise((_resolve, reject) => {
|
|
1095
1236
|
abortHandler = () => reject(new Error("URL validation was aborted."));
|
|
1096
|
-
|
|
1237
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1097
1238
|
if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
|
|
1098
1239
|
timer = setTimeout(
|
|
1099
1240
|
() => reject(new Error(`URL validation timed out after ${options.timeoutMs} ms.`)),
|
|
@@ -1103,24 +1244,24 @@ function createAzureUrlValidatorRunner(validator, options = {}) {
|
|
|
1103
1244
|
});
|
|
1104
1245
|
try {
|
|
1105
1246
|
const result = await (timer || abortHandler ? Promise.race([validation, cancellation]) : validation);
|
|
1106
|
-
cache.set(
|
|
1247
|
+
cache.set(key, result);
|
|
1107
1248
|
return result;
|
|
1108
1249
|
} finally {
|
|
1109
1250
|
if (timer) clearTimeout(timer);
|
|
1110
|
-
if (abortHandler)
|
|
1251
|
+
if (abortHandler) signal.removeEventListener("abort", abortHandler);
|
|
1111
1252
|
}
|
|
1112
1253
|
} finally {
|
|
1113
1254
|
release();
|
|
1114
1255
|
}
|
|
1115
1256
|
})();
|
|
1116
|
-
inFlight.set(
|
|
1257
|
+
inFlight.set(key, promise);
|
|
1117
1258
|
try {
|
|
1118
1259
|
return await promise;
|
|
1119
1260
|
} finally {
|
|
1120
|
-
inFlight.delete(
|
|
1261
|
+
inFlight.delete(key);
|
|
1121
1262
|
}
|
|
1122
1263
|
};
|
|
1123
|
-
return (url, context) => check(url, context);
|
|
1264
|
+
return (url, context, signal) => check(url, context, signal ?? configuredSignal);
|
|
1124
1265
|
}
|
|
1125
1266
|
var ALLOWED_BREAK_STRENGTHS = /* @__PURE__ */ new Set(["none", "x-weak", "weak", "medium", "strong", "x-strong"]);
|
|
1126
1267
|
var ALLOWED_SAY_AS = /* @__PURE__ */ new Set([
|
|
@@ -1766,6 +1907,7 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1766
1907
|
...options.urlValidatorSignal ? { signal: options.urlValidatorSignal } : {},
|
|
1767
1908
|
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
1768
1909
|
});
|
|
1910
|
+
const validationSignal = options.urlValidatorSignal ?? options.urlValidation?.signal ?? new AbortController().signal;
|
|
1769
1911
|
let tokens;
|
|
1770
1912
|
try {
|
|
1771
1913
|
tokens = tokenizeElements(ssml);
|
|
@@ -1775,7 +1917,11 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1775
1917
|
const checks = tokens.flatMap(
|
|
1776
1918
|
(token) => urlAttributes(token).map(async ({ attribute, value }) => {
|
|
1777
1919
|
try {
|
|
1778
|
-
const result = await boundedValidator(
|
|
1920
|
+
const result = await boundedValidator(
|
|
1921
|
+
value,
|
|
1922
|
+
{ tag: token.name, attribute, ...options.sourceNodePath ? { sourceNodePath: options.sourceNodePath } : {} },
|
|
1923
|
+
validationSignal
|
|
1924
|
+
);
|
|
1779
1925
|
const valid = typeof result === "boolean" ? result : result.valid;
|
|
1780
1926
|
if (!valid) {
|
|
1781
1927
|
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
@@ -1810,7 +1956,8 @@ export {
|
|
|
1810
1956
|
buildSsml,
|
|
1811
1957
|
parseSsml,
|
|
1812
1958
|
buildPartialSsml,
|
|
1959
|
+
getSsmlSourceMap,
|
|
1813
1960
|
validateSsml,
|
|
1814
1961
|
validateAzureSsml
|
|
1815
1962
|
};
|
|
1816
|
-
//# sourceMappingURL=chunk-
|
|
1963
|
+
//# sourceMappingURL=chunk-WXFLUCLR.mjs.map
|