ssml-builder-js 2.9.0 → 2.10.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 -6
- package/dist/{chunk-JNJVTEL6.mjs → chunk-BYIZQL2W.mjs} +77 -8
- package/dist/chunk-BYIZQL2W.mjs.map +1 -0
- package/dist/{chunk-7LCSH4SZ.mjs → chunk-GW6CKHXF.mjs} +90 -8
- package/dist/chunk-GW6CKHXF.mjs.map +1 -0
- package/dist/core.d.mts +7 -0
- package/dist/core.d.ts +7 -0
- package/dist/core.js +89 -7
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/elements.js +76 -7
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +1 -1
- package/dist/index.js +89 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.d.mts +7 -0
- package/dist/react.d.ts +7 -0
- package/dist/react.js +76 -7
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-7LCSH4SZ.mjs.map +0 -1
- package/dist/chunk-JNJVTEL6.mjs.map +0 -1
package/dist/index.mjs
CHANGED
package/dist/react.d.mts
CHANGED
|
@@ -113,6 +113,7 @@ interface SsmlDialogNode extends SsmlElementBase {
|
|
|
113
113
|
interface SsmlTurnNode extends SsmlElementBase {
|
|
114
114
|
type: "mstts:turn";
|
|
115
115
|
voice?: string;
|
|
116
|
+
speaker?: string;
|
|
116
117
|
}
|
|
117
118
|
interface SsmlBackgroundAudioNode extends SsmlElementBase {
|
|
118
119
|
type: "mstts:backgroundaudio";
|
|
@@ -126,12 +127,18 @@ interface SsmlBackgroundAudioNode extends SsmlElementBase {
|
|
|
126
127
|
}
|
|
127
128
|
interface MsttsTtsEmbeddingElement extends SsmlElementBase {
|
|
128
129
|
type: "mstts:ttsembedding";
|
|
130
|
+
speakerProfileId?: string;
|
|
129
131
|
}
|
|
130
132
|
interface MsttsEmbeddingElement extends SsmlElementBase {
|
|
131
133
|
type: "mstts:embedding";
|
|
134
|
+
id?: string;
|
|
135
|
+
speakerProfileId?: string;
|
|
132
136
|
}
|
|
133
137
|
interface MsttsVoiceConversionElement extends SsmlElementBase {
|
|
134
138
|
type: "mstts:voiceconversion";
|
|
139
|
+
url?: string;
|
|
140
|
+
profile?: string;
|
|
141
|
+
speakerProfileId?: string;
|
|
135
142
|
}
|
|
136
143
|
interface CustomElement extends SsmlElementBase {
|
|
137
144
|
type: "custom" | "element";
|
package/dist/react.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ interface SsmlDialogNode extends SsmlElementBase {
|
|
|
113
113
|
interface SsmlTurnNode extends SsmlElementBase {
|
|
114
114
|
type: "mstts:turn";
|
|
115
115
|
voice?: string;
|
|
116
|
+
speaker?: string;
|
|
116
117
|
}
|
|
117
118
|
interface SsmlBackgroundAudioNode extends SsmlElementBase {
|
|
118
119
|
type: "mstts:backgroundaudio";
|
|
@@ -126,12 +127,18 @@ interface SsmlBackgroundAudioNode extends SsmlElementBase {
|
|
|
126
127
|
}
|
|
127
128
|
interface MsttsTtsEmbeddingElement extends SsmlElementBase {
|
|
128
129
|
type: "mstts:ttsembedding";
|
|
130
|
+
speakerProfileId?: string;
|
|
129
131
|
}
|
|
130
132
|
interface MsttsEmbeddingElement extends SsmlElementBase {
|
|
131
133
|
type: "mstts:embedding";
|
|
134
|
+
id?: string;
|
|
135
|
+
speakerProfileId?: string;
|
|
132
136
|
}
|
|
133
137
|
interface MsttsVoiceConversionElement extends SsmlElementBase {
|
|
134
138
|
type: "mstts:voiceconversion";
|
|
139
|
+
url?: string;
|
|
140
|
+
profile?: string;
|
|
141
|
+
speakerProfileId?: string;
|
|
135
142
|
}
|
|
136
143
|
interface CustomElement extends SsmlElementBase {
|
|
137
144
|
type: "custom" | "element";
|
package/dist/react.js
CHANGED
|
@@ -1724,6 +1724,7 @@ var SSML_ATTRS = {
|
|
|
1724
1724
|
MSTTS_XMLNS: "xmlns:mstts",
|
|
1725
1725
|
NAME: "name",
|
|
1726
1726
|
VOICE: "voice",
|
|
1727
|
+
SPEAKER: "speaker",
|
|
1727
1728
|
EFFECT: "effect",
|
|
1728
1729
|
RATE: "rate",
|
|
1729
1730
|
PITCH: "pitch",
|
|
@@ -1754,6 +1755,11 @@ var SSML_ATTRS = {
|
|
|
1754
1755
|
ALIAS: "alias",
|
|
1755
1756
|
MARK: "mark",
|
|
1756
1757
|
URI: "uri",
|
|
1758
|
+
ID: "id",
|
|
1759
|
+
MODEL: "model",
|
|
1760
|
+
PROFILE: "profile",
|
|
1761
|
+
URL: "url",
|
|
1762
|
+
SPEAKER_PROFILE_ID: "speakerProfileId",
|
|
1757
1763
|
TYPE: "type",
|
|
1758
1764
|
VALUE: "value",
|
|
1759
1765
|
FADE_IN: "fadein",
|
|
@@ -1849,6 +1855,7 @@ function getAttributes(element) {
|
|
|
1849
1855
|
break;
|
|
1850
1856
|
case SSML_TAGS.MSTTS_TURN:
|
|
1851
1857
|
addAttribute(attributes, SSML_ATTRS.VOICE, element.voice);
|
|
1858
|
+
addAttribute(attributes, SSML_ATTRS.SPEAKER, element.speaker);
|
|
1852
1859
|
break;
|
|
1853
1860
|
case SSML_TAGS.MSTTS_BACKGROUND_AUDIO:
|
|
1854
1861
|
addAttribute(attributes, SSML_ATTRS.SRC, element.src);
|
|
@@ -1857,9 +1864,18 @@ function getAttributes(element) {
|
|
|
1857
1864
|
addAttribute(attributes, SSML_ATTRS.FADE_OUT, element.fadeOut ?? element.fadeout);
|
|
1858
1865
|
break;
|
|
1859
1866
|
case SSML_TAGS.MSTTS_DIALOG:
|
|
1867
|
+
break;
|
|
1860
1868
|
case SSML_TAGS.MSTTS_TTS_EMBEDDING:
|
|
1869
|
+
addAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID, element.speakerProfileId);
|
|
1870
|
+
break;
|
|
1861
1871
|
case SSML_TAGS.MSTTS_EMBEDDING:
|
|
1872
|
+
addAttribute(attributes, SSML_ATTRS.ID, element.id);
|
|
1873
|
+
addAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID, element.speakerProfileId);
|
|
1874
|
+
break;
|
|
1862
1875
|
case SSML_TAGS.MSTTS_VOICE_CONVERSION:
|
|
1876
|
+
addAttribute(attributes, SSML_ATTRS.URL, element.url);
|
|
1877
|
+
addAttribute(attributes, SSML_ATTRS.PROFILE, element.profile);
|
|
1878
|
+
addAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID, element.speakerProfileId);
|
|
1863
1879
|
break;
|
|
1864
1880
|
case SSML_TAGS.PARAGRAPH:
|
|
1865
1881
|
case SSML_TAGS.SENTENCE:
|
|
@@ -2432,7 +2448,9 @@ function convertElement(node) {
|
|
|
2432
2448
|
case SSML_TAGS.MSTTS_TURN: {
|
|
2433
2449
|
const element = { type: SSML_TAGS.MSTTS_TURN };
|
|
2434
2450
|
const voice = readAttribute(attributes, SSML_ATTRS.VOICE);
|
|
2451
|
+
const speaker = readAttribute(attributes, SSML_ATTRS.SPEAKER);
|
|
2435
2452
|
if (voice !== void 0) element.voice = voice;
|
|
2453
|
+
if (speaker !== void 0) element.speaker = speaker;
|
|
2436
2454
|
return finishElement(element, node, attributes);
|
|
2437
2455
|
}
|
|
2438
2456
|
case SSML_TAGS.MSTTS_BACKGROUND_AUDIO: {
|
|
@@ -2449,14 +2467,26 @@ function convertElement(node) {
|
|
|
2449
2467
|
}
|
|
2450
2468
|
case SSML_TAGS.MSTTS_TTS_EMBEDDING: {
|
|
2451
2469
|
const element = { type: SSML_TAGS.MSTTS_TTS_EMBEDDING };
|
|
2470
|
+
const speakerProfileId = readAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID);
|
|
2471
|
+
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
2452
2472
|
return finishElement(element, node, attributes);
|
|
2453
2473
|
}
|
|
2454
2474
|
case SSML_TAGS.MSTTS_EMBEDDING: {
|
|
2455
2475
|
const element = { type: SSML_TAGS.MSTTS_EMBEDDING };
|
|
2476
|
+
const id = readAttribute(attributes, SSML_ATTRS.ID);
|
|
2477
|
+
const speakerProfileId = readAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID);
|
|
2478
|
+
if (id !== void 0) element.id = id;
|
|
2479
|
+
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
2456
2480
|
return finishElement(element, node, attributes);
|
|
2457
2481
|
}
|
|
2458
2482
|
case SSML_TAGS.MSTTS_VOICE_CONVERSION: {
|
|
2459
2483
|
const element = { type: SSML_TAGS.MSTTS_VOICE_CONVERSION };
|
|
2484
|
+
const url = readAttribute(attributes, SSML_ATTRS.URL);
|
|
2485
|
+
const profile = readAttribute(attributes, SSML_ATTRS.PROFILE);
|
|
2486
|
+
const speakerProfileId = readAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID);
|
|
2487
|
+
if (url !== void 0) element.url = url;
|
|
2488
|
+
if (profile !== void 0) element.profile = profile;
|
|
2489
|
+
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
2460
2490
|
return finishElement(element, node, attributes);
|
|
2461
2491
|
}
|
|
2462
2492
|
default: {
|
|
@@ -2791,11 +2821,14 @@ function tokenizeElements(source) {
|
|
|
2791
2821
|
}
|
|
2792
2822
|
const selfClosing = /\/\s*>$/.test(raw);
|
|
2793
2823
|
const parent = openElements[openElements.length - 1];
|
|
2824
|
+
const childElementIndex = parent?.childElementCount;
|
|
2825
|
+
if (parent) parent.childElementCount += 1;
|
|
2794
2826
|
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
2795
2827
|
const tokenName = nameMatch[1];
|
|
2796
2828
|
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
2797
2829
|
tokens.push({
|
|
2798
2830
|
attributes,
|
|
2831
|
+
childElementIndex,
|
|
2799
2832
|
end,
|
|
2800
2833
|
name: tokenName,
|
|
2801
2834
|
parentName: parent?.name,
|
|
@@ -2805,6 +2838,7 @@ function tokenizeElements(source) {
|
|
|
2805
2838
|
});
|
|
2806
2839
|
if (!selfClosing) {
|
|
2807
2840
|
openElements.push({
|
|
2841
|
+
childElementCount: 0,
|
|
2808
2842
|
name: tokenName,
|
|
2809
2843
|
voiceName: tokenVoiceName
|
|
2810
2844
|
});
|
|
@@ -2843,6 +2877,12 @@ function isValidAzureAudioDuration(value) {
|
|
|
2843
2877
|
if (!clock) return false;
|
|
2844
2878
|
return Number(clock[1]) > 0 || Number(clock[2]) > 0 || Number(clock[3]) > 0 || Number(clock[4] ?? 0) > 0;
|
|
2845
2879
|
}
|
|
2880
|
+
function isValidAzureBackgroundAudioDuration(value) {
|
|
2881
|
+
const match = /^(\d+(?:\.\d+)?)(ms|s)?$/i.exec(value.trim());
|
|
2882
|
+
if (!match) return false;
|
|
2883
|
+
const milliseconds = Number(match[1]) * (match[2]?.toLowerCase() === "s" ? 1e3 : 1);
|
|
2884
|
+
return Number.isFinite(milliseconds) && milliseconds >= 0 && milliseconds <= 1e4;
|
|
2885
|
+
}
|
|
2846
2886
|
function attr(token, name) {
|
|
2847
2887
|
return token.attributes.get(name.toLowerCase());
|
|
2848
2888
|
}
|
|
@@ -2972,9 +3012,16 @@ function validateAudioSource(token, source, diagnostics, options, elementName2)
|
|
|
2972
3012
|
}
|
|
2973
3013
|
if (parsed.protocol !== "https:" && !(options.allowHttpAudio && parsed.protocol === "http:"))
|
|
2974
3014
|
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must use HTTPS.`);
|
|
2975
|
-
|
|
3015
|
+
const isAllowedOrigin = options.allowedAudioOrigins?.some((allowedOrigin) => {
|
|
3016
|
+
try {
|
|
3017
|
+
return new URL(allowedOrigin).origin === parsed.origin;
|
|
3018
|
+
} catch {
|
|
3019
|
+
return allowedOrigin === parsed.origin;
|
|
3020
|
+
}
|
|
3021
|
+
}) ?? false;
|
|
3022
|
+
if (options.allowedAudioOrigins && !isAllowedOrigin)
|
|
2976
3023
|
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> origin "${parsed.origin}" is not allowed.`);
|
|
2977
|
-
else if (!options.allowExternalAudio)
|
|
3024
|
+
else if (!isAllowedOrigin && !options.allowExternalAudio)
|
|
2978
3025
|
addDiagnostic(
|
|
2979
3026
|
diagnostics,
|
|
2980
3027
|
source,
|
|
@@ -3104,28 +3151,40 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceCa
|
|
|
3104
3151
|
validateAudioSource(token, source, diagnostics, options, "audio");
|
|
3105
3152
|
}
|
|
3106
3153
|
if (name === "mstts:turn") {
|
|
3107
|
-
if (!attr(token, "voice")?.trim())
|
|
3108
|
-
addDiagnostic(
|
|
3154
|
+
if (!attr(token, "voice")?.trim() && !attr(token, "speaker")?.trim())
|
|
3155
|
+
addDiagnostic(
|
|
3156
|
+
diagnostics,
|
|
3157
|
+
source,
|
|
3158
|
+
token.start,
|
|
3159
|
+
'<mstts:turn> requires a non-empty "voice" or "speaker" attribute.'
|
|
3160
|
+
);
|
|
3109
3161
|
if (token.parentName?.toLowerCase() !== "mstts:dialog")
|
|
3110
3162
|
addDiagnostic(diagnostics, source, token.start, "<mstts:turn> is only allowed directly inside <mstts:dialog>.");
|
|
3111
3163
|
}
|
|
3112
3164
|
if (name === "mstts:backgroundaudio") {
|
|
3113
3165
|
validateAudioSource(token, source, diagnostics, options, "mstts:backgroundaudio");
|
|
3114
3166
|
const volume = attr(token, "volume");
|
|
3115
|
-
if (volume &&
|
|
3167
|
+
if (volume !== void 0 && (!/^\d+(?:\.\d+)?$/.test(volume.trim()) || Number(volume) > 100))
|
|
3116
3168
|
addDiagnostic(diagnostics, source, token.start, `Unsupported <mstts:backgroundaudio volume> value "${volume}".`);
|
|
3117
3169
|
for (const [attribute, value] of [
|
|
3118
3170
|
["fadein", attr(token, "fadein")],
|
|
3119
3171
|
["fadeout", attr(token, "fadeout")]
|
|
3120
3172
|
]) {
|
|
3121
|
-
if (value && !
|
|
3173
|
+
if (value !== void 0 && !isValidAzureBackgroundAudioDuration(value))
|
|
3122
3174
|
addDiagnostic(
|
|
3123
3175
|
diagnostics,
|
|
3124
3176
|
source,
|
|
3125
3177
|
token.start,
|
|
3126
|
-
`<mstts:backgroundaudio ${attribute}> must be
|
|
3178
|
+
`<mstts:backgroundaudio ${attribute}> must be between 0 and 10000 milliseconds, for example "500ms" or "10s".`
|
|
3127
3179
|
);
|
|
3128
3180
|
}
|
|
3181
|
+
if (token.parentName?.toLowerCase() !== "speak" || token.childElementIndex !== 0)
|
|
3182
|
+
addDiagnostic(
|
|
3183
|
+
diagnostics,
|
|
3184
|
+
source,
|
|
3185
|
+
token.start,
|
|
3186
|
+
"<mstts:backgroundaudio> must be the first element directly under <speak>."
|
|
3187
|
+
);
|
|
3129
3188
|
if (!token.selfClosing)
|
|
3130
3189
|
addDiagnostic(diagnostics, source, token.start, "<mstts:backgroundaudio> must be self-closing.");
|
|
3131
3190
|
}
|
|
@@ -3157,6 +3216,16 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
3157
3216
|
const tokens = tokenizeElements(ssml);
|
|
3158
3217
|
const speak = tokens.find((token) => token.name.toLowerCase() === "speak");
|
|
3159
3218
|
const voices = tokens.filter((token) => token.name.toLowerCase() === "voice");
|
|
3219
|
+
const backgroundAudioTokens = tokens.filter((token) => token.name.toLowerCase() === "mstts:backgroundaudio");
|
|
3220
|
+
for (const [index, token] of backgroundAudioTokens.entries()) {
|
|
3221
|
+
if (index > 0)
|
|
3222
|
+
addDiagnostic(
|
|
3223
|
+
diagnostics,
|
|
3224
|
+
ssml,
|
|
3225
|
+
token.start,
|
|
3226
|
+
"An SSML document can contain at most one <mstts:backgroundaudio> element."
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3160
3229
|
if (!speak || voices.length === 0)
|
|
3161
3230
|
addDiagnostic(
|
|
3162
3231
|
diagnostics,
|