ssml-builder-js 2.15.0 → 2.17.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 +11 -3
- package/dist/{chunk-FXUM45ZY.mjs → chunk-2SYLELUT.mjs} +53 -6
- package/dist/chunk-2SYLELUT.mjs.map +1 -0
- package/dist/{chunk-WXFLUCLR.mjs → chunk-5AZWURHW.mjs} +37 -6
- package/dist/chunk-5AZWURHW.mjs.map +1 -0
- package/dist/{chunk-BDY2Q2JL.mjs → chunk-SLZ7PE6W.mjs} +2 -2
- package/dist/core.d.mts +24 -1
- package/dist/core.d.ts +24 -1
- package/dist/core.js +53 -5
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +35 -5
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +2 -2
- package/dist/{index.d-8BvkB9gz.d.mts → index.d-tpKoP1jl.d.mts} +27 -1
- package/dist/{index.d-8BvkB9gz.d.ts → index.d-tpKoP1jl.d.ts} +27 -1
- package/dist/index.d.mts +123 -9
- package/dist/index.d.ts +123 -9
- package/dist/index.js +874 -180
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +787 -172
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +5 -2
- package/dist/react.d.ts +5 -2
- package/dist/react.js +143 -29
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +110 -26
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FXUM45ZY.mjs.map +0 -1
- package/dist/chunk-WXFLUCLR.mjs.map +0 -1
- /package/dist/{chunk-BDY2Q2JL.mjs.map → chunk-SLZ7PE6W.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -165,7 +165,7 @@ const parsed = parseSsml(ssml);
|
|
|
165
165
|
| `fromPlainTextToSsml(text, options?)` | プレーンテキストを `<speak>`、段落 `<p>`、文 `<s>` を含む SSML に変換 |
|
|
166
166
|
| `validateSsmlStructureIntegrity(original, translated)` | 翻訳前後のタグ階層・タグ名・属性の一致を検証 |
|
|
167
167
|
| `validateAzureSsml(xml, options?)` | Azure Speech 向けの意味検証結果を Diagnostic 配列で返す(各診断に `source: "ssml-static-validator"` を付与) |
|
|
168
|
-
| `getAzureVoiceCatalogMetadata()` / `getBuiltInVoiceCatalogMetadata()` | 組み込み音声カタログの生成日時、API
|
|
168
|
+
| `getAzureVoiceCatalogMetadata()` / `getBuiltInVoiceCatalogMetadata()` | 組み込み音声カタログの生成日時、API バージョン、リージョン、収録数、有効期限、リージョン差分を返す |
|
|
169
169
|
|
|
170
170
|
### 3段階の検証モデル
|
|
171
171
|
|
|
@@ -670,7 +670,7 @@ The main `buildSsml` and `parseSsml` signatures are:
|
|
|
670
670
|
| `fromPlainTextToSsml(text, options?)` | Converts plain text into an initial `<speak>` document containing paragraphs (`<p>`) and sentences (`<s>`) |
|
|
671
671
|
| `validateSsmlStructureIntegrity(original, translated)` | Checks that tag hierarchy, element names, and attributes are unchanged after translation |
|
|
672
672
|
| `validateAzureSsml(xml, options?)` | Returns Azure Speech semantic-validation diagnostics (each diagnostic has `source: "ssml-static-validator"`) |
|
|
673
|
-
| `getAzureVoiceCatalogMetadata()` / `getBuiltInVoiceCatalogMetadata()` | Returns generation time, API version, regions,
|
|
673
|
+
| `getAzureVoiceCatalogMetadata()` / `getBuiltInVoiceCatalogMetadata()` | Returns generation time, API version, regions, voice count, expiry, and known regional differences for the bundled voice catalog |
|
|
674
674
|
|
|
675
675
|
### Three-stage validation model
|
|
676
676
|
|
|
@@ -913,7 +913,11 @@ result.bookmarks; // { name, audioOffsetMs }[]
|
|
|
913
913
|
|
|
914
914
|
長文を分割して合成する場合は `synthesizeSsmlChunks` または `AzureTtsClient.synthesizeChunks` を使います。音声バイナリを連結し、`boundaries`、`visemes`、`bookmarks` のオフセットを累積 `durationMs` 分だけ補正します。`synthesizeSsmlSafe(client, ssml, { validation })` は検証エラー時に Azure API を呼び出さず、`status: "validation-error"` / `"azure-api-error"` / `"success"` の結果を返します。
|
|
915
915
|
|
|
916
|
-
v2.15.0 では `synthesizeSsmlChunksSafe(client, chunks, options)` が全チャンクを事前検証し、`outputFormat`、`signal`、`timeoutMs`、`sourceNodePath` を各合成へ伝播します。`mergeAudioBuffers(buffers, { format })` と `mergeSynthesisResults(results, { format })` は形式指定を必須とし、結合結果には `mimeType` が含まれます。Ogg/WebM などは `customMerger` で外部 Muxer に委譲できます。エラーは `validation-error`、`azure-api-error`、`merge-error`、`unsupported-format-error`、`cancelled`、`timeout` の判別可能な `kind` を持ちます。同期イベントは個別の `sourceNodePath` と `originalTextRange` にマッピングされ、URL 検証コールバックには `AbortSignal` が渡されます。
|
|
916
|
+
v2.15.0 では `synthesizeSsmlChunksSafe(client, chunks, options)` が全チャンクを事前検証し、`outputFormat`、`signal`、`timeoutMs`、`sourceNodePath` を各合成へ伝播します。`mergeAudioBuffers(buffers, { format })` と `mergeSynthesisResults(results, { format })` は形式指定を必須とし、結合結果には `mimeType` が含まれます。Ogg/WebM などは `customMerger` で外部 Muxer に委譲できます。エラーは `validation-error`、`azure-api-error`、`merge-error`、`unsupported-format-error`、`cancelled`、`timeout` の判別可能な `kind` を持ちます。同期イベントは個別の `sourceNodePath` と `originalTextRange` にマッピングされ、URL 検証コールバックには `AbortSignal` が渡されます。
|
|
917
|
+
|
|
918
|
+
v2.16.0 では `concurrency` と `retryOptions`(429/5xx・ネットワーク障害のみ、Jitter 付き指数バックオフ)でチャンク合成を制御できます。`onProgress` には `retryAttempt`、`nextRetryDelayMs`、`isRetrying` が追加され、結合は常に `chunkIndex` 順です。`SsmlSynthesisResult.audioSpec` は WAV/MP3 ヘッダーから音声仕様を抽出し、チャンク間の仕様不一致は `AudioFormatMismatchError` になります。同期イベントには `mappingStatus`、Azure 診断には `nodePath`、`range`、`tagName`、`attributeName`、`voiceName`、`chunkIndex` が含まれます。`validateAzureSsmlChunks` は URL 検証プールを全チャンクで共有し、カスタム結合器には `inputSpecs` と `signal` を渡します。
|
|
919
|
+
|
|
920
|
+
v2.17.0 では `customMerger`、`outputMimeType`、`postMergeValidator` をチャンク合成の末尾まで構成でき、`BatchChunkValidationError` が全チャンクの診断と総エラー数を返します。`cancelOnFailure` と `resumeChunks` により成功済みバイナリを再利用でき、`timeouts`(URL 検証、チャンク、リトライ込みチャンク、ジョブ全体)を個別に設定できます。429 の `Retry-After` は指数バックオフより優先されます。`AudioSpecification` には `bitDepth`、`container`、`isVbr` が追加され、同期 `mappingStatus` は JSON 化後も保持されます。
|
|
917
921
|
|
|
918
922
|
`validateAzureSsml` の `urlValidation` オプションは URL の重複排除、キャッシュ、`concurrency`、`signal`、`timeoutMs` を制御します。
|
|
919
923
|
|
|
@@ -921,6 +925,10 @@ For long documents, use `synthesizeSsmlChunks` or `AzureTtsClient.synthesizeChun
|
|
|
921
925
|
|
|
922
926
|
In v2.15.0, `synthesizeSsmlChunksSafe(client, chunks, options)` validates every chunk before contacting Azure and propagates `outputFormat`, `signal`, `timeoutMs`, and `sourceNodePath` to each synthesis. `mergeAudioBuffers(buffers, { format })` and `mergeSynthesisResults(results, { format })` require an explicit format and merged results expose `mimeType`. Ogg and WebM can be delegated to an external Muxer through `customMerger`. Errors have discriminated `kind` values: `validation-error`, `azure-api-error`, `merge-error`, `unsupported-format-error`, `cancelled`, and `timeout`. Synchronization events receive individual `sourceNodePath` and `originalTextRange` mappings, and URL validators receive an `AbortSignal`.
|
|
923
927
|
|
|
928
|
+
In v2.16.0, use `concurrency` and `retryOptions` to control chunk synthesis; only 429/5xx and network failures are retried with jittered exponential backoff. Progress events include `retryAttempt`, `nextRetryDelayMs`, and `isRetrying`, while merging always follows `chunkIndex` order. `SsmlSynthesisResult.audioSpec` is extracted from WAV/MP3 headers, and incompatible chunk specs throw `AudioFormatMismatchError`. Synchronization events include `mappingStatus`, diagnostics include structured node/range/tag/attribute/voice/chunk fields, `validateAzureSsmlChunks` shares one URL validation pool, and custom mergers receive `inputSpecs` and an `AbortSignal`.
|
|
929
|
+
|
|
930
|
+
In v2.17.0, configure `customMerger`, `outputMimeType`, and `postMergeValidator` through the chunk synthesis pipeline. `BatchChunkValidationError` aggregates diagnostics and the total error count for every invalid chunk. `cancelOnFailure` and `resumeChunks` allow successful binary chunks to be reused, while `timeouts` independently bounds URL validation, individual chunks, retries, and the total job. HTTP 429 `Retry-After` takes priority over exponential backoff. `AudioSpecification` now includes `bitDepth`, `container`, and `isVbr`, and `mappingStatus` remains present after JSON serialization.
|
|
931
|
+
|
|
924
932
|
The `urlValidation` option of `validateAzureSsml` provides URL deduplication, in-memory caching, bounded `concurrency`, `signal`, and `timeoutMs` controls.
|
|
925
933
|
|
|
926
934
|
The public updater CLI is `npx ssml-builder sync-voices --region eastus --output ./azure-voices.json`. It reads the key from `AZURE_SPEECH_KEY` (or `--key`) and regions from `AZURE_SPEECH_REGION(S)` (or `--region(s)`).
|
|
@@ -2015,6 +2015,7 @@ function tokenizeElements(source) {
|
|
|
2015
2015
|
if (parent) parent.childElementCount += 1;
|
|
2016
2016
|
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
2017
2017
|
const tokenName = nameMatch[1];
|
|
2018
|
+
const path = parent ? [...parent.path, `${tokenName}[${childElementIndex ?? 0}]`] : [tokenName];
|
|
2018
2019
|
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
2019
2020
|
tokens.push({
|
|
2020
2021
|
attributes,
|
|
@@ -2025,12 +2026,14 @@ function tokenizeElements(source) {
|
|
|
2025
2026
|
parentName: parent?.name,
|
|
2026
2027
|
parentVoiceName,
|
|
2027
2028
|
selfClosing,
|
|
2028
|
-
start
|
|
2029
|
+
start,
|
|
2030
|
+
path
|
|
2029
2031
|
});
|
|
2030
2032
|
if (!selfClosing) {
|
|
2031
2033
|
openElements.push({
|
|
2032
2034
|
childElementCount: 0,
|
|
2033
2035
|
name: tokenName,
|
|
2036
|
+
path,
|
|
2034
2037
|
voiceName: tokenVoiceName
|
|
2035
2038
|
});
|
|
2036
2039
|
}
|
|
@@ -2043,13 +2046,14 @@ function location(source, offset) {
|
|
|
2043
2046
|
const line = before.split("\n").length;
|
|
2044
2047
|
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
2045
2048
|
}
|
|
2046
|
-
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code) {
|
|
2049
|
+
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code, metadata = {}) {
|
|
2047
2050
|
diagnostics.push({
|
|
2048
2051
|
...location(source, offset),
|
|
2049
2052
|
message,
|
|
2050
2053
|
severity,
|
|
2051
2054
|
source: "ssml-static-validator",
|
|
2052
|
-
...code ? { code } : {}
|
|
2055
|
+
...code ? { code } : {},
|
|
2056
|
+
...metadata
|
|
2053
2057
|
});
|
|
2054
2058
|
}
|
|
2055
2059
|
function isSupportedProsodyRate(value) {
|
|
@@ -2522,9 +2526,11 @@ function validateAzureSsmlStatic(ssml, options = {}) {
|
|
|
2522
2526
|
for (const token of tokens) {
|
|
2523
2527
|
const tokenName = token.name.toLowerCase();
|
|
2524
2528
|
const tokenVoiceName = tokenName === "voice" ? attr(token, "name")?.trim() : tokenName === "mstts:turn" ? attr(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
2529
|
+
const tokenDiagnosticStart = diagnostics.length;
|
|
2525
2530
|
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
2526
2531
|
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
2527
2532
|
validateVoiceFeatureMatrix(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
2533
|
+
annotateTokenDiagnostics(diagnostics, tokenDiagnosticStart, token, options, tokenVoiceName);
|
|
2528
2534
|
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
2529
2535
|
addDiagnostic(
|
|
2530
2536
|
diagnostics,
|
|
@@ -2546,12 +2552,30 @@ function urlAttributes(token) {
|
|
|
2546
2552
|
return value === void 0 ? [] : [{ attribute, value }];
|
|
2547
2553
|
});
|
|
2548
2554
|
}
|
|
2555
|
+
function annotateTokenDiagnostics(diagnostics, startIndex, token, options, voiceName) {
|
|
2556
|
+
const attributes = [...token.attributes.keys()];
|
|
2557
|
+
for (const diagnostic of diagnostics.slice(startIndex)) {
|
|
2558
|
+
const attributeName = attributes.find(
|
|
2559
|
+
(attribute) => new RegExp(`(?:<[^> ]+\\s+|")${attribute}(?:"|>|\\s)`, "i").test(diagnostic.message)
|
|
2560
|
+
);
|
|
2561
|
+
const nodePath = options.sourceNodePath ? [...options.sourceNodePath] : [...token.path];
|
|
2562
|
+
Object.assign(diagnostic, {
|
|
2563
|
+
range: { start: token.start, end: token.end + 1 },
|
|
2564
|
+
tagName: token.name,
|
|
2565
|
+
...attributeName ? { attributeName } : {},
|
|
2566
|
+
...voiceName ? { voiceName } : {},
|
|
2567
|
+
...options.chunkIndex !== void 0 ? { chunkIndex: options.chunkIndex } : {},
|
|
2568
|
+
nodePath,
|
|
2569
|
+
targetNodePath: [...token.path]
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2549
2573
|
function validateAzureSsml(ssml, options = {}) {
|
|
2550
2574
|
const diagnostics = validateAzureSsmlStatic(ssml, options);
|
|
2551
2575
|
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
2552
2576
|
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
2553
2577
|
const runnerOptions = options.urlValidation ?? {};
|
|
2554
|
-
const boundedValidator = createAzureUrlValidatorRunner(validator, {
|
|
2578
|
+
const boundedValidator = options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
|
|
2555
2579
|
...runnerOptions,
|
|
2556
2580
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
2557
2581
|
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
@@ -2576,33 +2600,55 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
2576
2600
|
const valid = typeof result === "boolean" ? result : result.valid;
|
|
2577
2601
|
if (!valid) {
|
|
2578
2602
|
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
2603
|
+
const diagnosticStart = diagnostics.length;
|
|
2579
2604
|
addDiagnostic(
|
|
2580
2605
|
diagnostics,
|
|
2581
2606
|
ssml,
|
|
2582
2607
|
token.start,
|
|
2583
2608
|
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
2584
2609
|
);
|
|
2610
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
2585
2611
|
}
|
|
2586
2612
|
} catch (error) {
|
|
2587
2613
|
const reason = error instanceof Error ? error.message : String(error);
|
|
2614
|
+
const diagnosticStart = diagnostics.length;
|
|
2588
2615
|
addDiagnostic(
|
|
2589
2616
|
diagnostics,
|
|
2590
2617
|
ssml,
|
|
2591
2618
|
token.start,
|
|
2592
2619
|
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
2593
2620
|
);
|
|
2621
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
2594
2622
|
}
|
|
2595
2623
|
})
|
|
2596
2624
|
);
|
|
2597
2625
|
return Promise.all(checks).then(() => diagnostics);
|
|
2598
2626
|
}
|
|
2627
|
+
async function validateAzureSsmlChunks(chunks, options = {}) {
|
|
2628
|
+
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
2629
|
+
const sharedOptions = validator ? {
|
|
2630
|
+
...options,
|
|
2631
|
+
urlValidatorRunner: options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
|
|
2632
|
+
...options.urlValidation ?? {},
|
|
2633
|
+
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
2634
|
+
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
2635
|
+
...options.urlValidatorSignal ? { signal: options.urlValidatorSignal } : {},
|
|
2636
|
+
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
2637
|
+
})
|
|
2638
|
+
} : options;
|
|
2639
|
+
return Promise.all(
|
|
2640
|
+
chunks.map((chunk, chunkIndex) => Promise.resolve(validateAzureSsml(chunk, { ...sharedOptions, chunkIndex })))
|
|
2641
|
+
);
|
|
2642
|
+
}
|
|
2599
2643
|
|
|
2600
2644
|
// packages/ssml-core/src/generated/azureVoiceCatalog.ts
|
|
2601
2645
|
var AZURE_VOICE_CATALOG_METADATA = {
|
|
2602
2646
|
apiVersion: "2025-10-01",
|
|
2603
2647
|
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
2604
2648
|
regions: [],
|
|
2605
|
-
voiceCount: AZURE_VOICE_DEFINITIONS.length
|
|
2649
|
+
voiceCount: AZURE_VOICE_DEFINITIONS.length,
|
|
2650
|
+
expiresAt: "2026-09-04T00:00:00.000Z",
|
|
2651
|
+
regionDiffs: {}
|
|
2606
2652
|
};
|
|
2607
2653
|
|
|
2608
2654
|
// packages/ssml-core/src/voiceCatalog.ts
|
|
@@ -2631,7 +2677,8 @@ export {
|
|
|
2631
2677
|
normalizeAzureLanguage,
|
|
2632
2678
|
areAzureLanguagesEquivalent,
|
|
2633
2679
|
validateAzureSsml,
|
|
2680
|
+
validateAzureSsmlChunks,
|
|
2634
2681
|
getAzureVoiceCatalogMetadata,
|
|
2635
2682
|
getBuiltInVoiceCatalogMetadata
|
|
2636
2683
|
};
|
|
2637
|
-
//# sourceMappingURL=chunk-
|
|
2684
|
+
//# sourceMappingURL=chunk-2SYLELUT.mjs.map
|