ssml-builder-js 2.15.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/dist/core.js CHANGED
@@ -43,6 +43,7 @@ __export(core_exports, {
43
43
  parseSsml: () => parseSsml,
44
44
  splitSsmlDocument: () => splitSsmlDocument,
45
45
  validateAzureSsml: () => validateAzureSsml,
46
+ validateAzureSsmlChunks: () => validateAzureSsmlChunks,
46
47
  validateSsml: () => validateSsml,
47
48
  validateSsmlStructureIntegrity: () => validateSsmlStructureIntegrity
48
49
  });
@@ -2059,6 +2060,7 @@ function tokenizeElements(source) {
2059
2060
  if (parent) parent.childElementCount += 1;
2060
2061
  const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
2061
2062
  const tokenName = nameMatch[1];
2063
+ const path = parent ? [...parent.path, `${tokenName}[${childElementIndex ?? 0}]`] : [tokenName];
2062
2064
  const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
2063
2065
  tokens.push({
2064
2066
  attributes,
@@ -2069,12 +2071,14 @@ function tokenizeElements(source) {
2069
2071
  parentName: parent?.name,
2070
2072
  parentVoiceName,
2071
2073
  selfClosing,
2072
- start
2074
+ start,
2075
+ path
2073
2076
  });
2074
2077
  if (!selfClosing) {
2075
2078
  openElements.push({
2076
2079
  childElementCount: 0,
2077
2080
  name: tokenName,
2081
+ path,
2078
2082
  voiceName: tokenVoiceName
2079
2083
  });
2080
2084
  }
@@ -2087,13 +2091,14 @@ function location(source, offset) {
2087
2091
  const line = before.split("\n").length;
2088
2092
  return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
2089
2093
  }
2090
- function addDiagnostic(diagnostics, source, offset, message, severity = "error", code) {
2094
+ function addDiagnostic(diagnostics, source, offset, message, severity = "error", code, metadata = {}) {
2091
2095
  diagnostics.push({
2092
2096
  ...location(source, offset),
2093
2097
  message,
2094
2098
  severity,
2095
2099
  source: "ssml-static-validator",
2096
- ...code ? { code } : {}
2100
+ ...code ? { code } : {},
2101
+ ...metadata
2097
2102
  });
2098
2103
  }
2099
2104
  function isSupportedProsodyRate(value) {
@@ -2566,9 +2571,11 @@ function validateAzureSsmlStatic(ssml, options = {}) {
2566
2571
  for (const token of tokens) {
2567
2572
  const tokenName = token.name.toLowerCase();
2568
2573
  const tokenVoiceName = tokenName === "voice" ? attr(token, "name")?.trim() : tokenName === "mstts:turn" ? attr(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
2574
+ const tokenDiagnosticStart = diagnostics.length;
2569
2575
  validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
2570
2576
  const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
2571
2577
  validateVoiceFeatureMatrix(token, ssml, diagnostics, tokenVoiceName, definition);
2578
+ annotateTokenDiagnostics(diagnostics, tokenDiagnosticStart, token, options, tokenVoiceName);
2572
2579
  if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
2573
2580
  addDiagnostic(
2574
2581
  diagnostics,
@@ -2590,12 +2597,30 @@ function urlAttributes(token) {
2590
2597
  return value === void 0 ? [] : [{ attribute, value }];
2591
2598
  });
2592
2599
  }
2600
+ function annotateTokenDiagnostics(diagnostics, startIndex, token, options, voiceName) {
2601
+ const attributes = [...token.attributes.keys()];
2602
+ for (const diagnostic of diagnostics.slice(startIndex)) {
2603
+ const attributeName = attributes.find(
2604
+ (attribute) => new RegExp(`(?:<[^> ]+\\s+|")${attribute}(?:"|>|\\s)`, "i").test(diagnostic.message)
2605
+ );
2606
+ const nodePath = options.sourceNodePath ? [...options.sourceNodePath] : [...token.path];
2607
+ Object.assign(diagnostic, {
2608
+ range: { start: token.start, end: token.end + 1 },
2609
+ tagName: token.name,
2610
+ ...attributeName ? { attributeName } : {},
2611
+ ...voiceName ? { voiceName } : {},
2612
+ ...options.chunkIndex !== void 0 ? { chunkIndex: options.chunkIndex } : {},
2613
+ nodePath,
2614
+ targetNodePath: [...token.path]
2615
+ });
2616
+ }
2617
+ }
2593
2618
  function validateAzureSsml(ssml, options = {}) {
2594
2619
  const diagnostics = validateAzureSsmlStatic(ssml, options);
2595
2620
  const validator = options.urlValidator ?? options.customUrlValidator;
2596
2621
  if (!validator || typeof ssml !== "string") return diagnostics;
2597
2622
  const runnerOptions = options.urlValidation ?? {};
2598
- const boundedValidator = createAzureUrlValidatorRunner(validator, {
2623
+ const boundedValidator = options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
2599
2624
  ...runnerOptions,
2600
2625
  ...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
2601
2626
  ...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
@@ -2620,26 +2645,46 @@ function validateAzureSsml(ssml, options = {}) {
2620
2645
  const valid = typeof result === "boolean" ? result : result.valid;
2621
2646
  if (!valid) {
2622
2647
  const reason = typeof result === "boolean" ? void 0 : result.reason;
2648
+ const diagnosticStart = diagnostics.length;
2623
2649
  addDiagnostic(
2624
2650
  diagnostics,
2625
2651
  ssml,
2626
2652
  token.start,
2627
2653
  `<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
2628
2654
  );
2655
+ annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
2629
2656
  }
2630
2657
  } catch (error) {
2631
2658
  const reason = error instanceof Error ? error.message : String(error);
2659
+ const diagnosticStart = diagnostics.length;
2632
2660
  addDiagnostic(
2633
2661
  diagnostics,
2634
2662
  ssml,
2635
2663
  token.start,
2636
2664
  `<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
2637
2665
  );
2666
+ annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
2638
2667
  }
2639
2668
  })
2640
2669
  );
2641
2670
  return Promise.all(checks).then(() => diagnostics);
2642
2671
  }
2672
+ async function validateAzureSsmlChunks(chunks, options = {}) {
2673
+ const validator = options.urlValidator ?? options.customUrlValidator;
2674
+ const sharedOptions = validator ? {
2675
+ ...options,
2676
+ urlValidatorRunner: options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
2677
+ ...options.urlValidation ?? {},
2678
+ ...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
2679
+ ...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
2680
+ ...options.urlValidatorSignal ? { signal: options.urlValidatorSignal } : {},
2681
+ ...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
2682
+ })
2683
+ } : options;
2684
+ return Promise.all(
2685
+ chunks.map((chunk, chunkIndex) => Promise.resolve(validateAzureSsml(chunk, { ...sharedOptions, chunkIndex })))
2686
+ );
2687
+ }
2643
2688
 
2644
2689
  // packages/ssml-core/src/generated/azureVoiceCatalog.ts
2645
2690
  var AZURE_VOICE_CATALOG_METADATA = {
@@ -2675,6 +2720,7 @@ var getBuiltInVoiceCatalogMetadata = getAzureVoiceCatalogMetadata;
2675
2720
  parseSsml,
2676
2721
  splitSsmlDocument,
2677
2722
  validateAzureSsml,
2723
+ validateAzureSsmlChunks,
2678
2724
  validateSsml,
2679
2725
  validateSsmlStructureIntegrity
2680
2726
  });