@clipwright/core 0.19.0 → 0.20.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/__fixtures__/queue-promises.d.ts +5 -84
  3. package/dist/__fixtures__/queue-promises.js +7 -84
  4. package/dist/__fixtures__/queue-promises.js.map +1 -1
  5. package/dist/account-actors.d.ts +25 -25
  6. package/dist/account-actors.js +25 -25
  7. package/dist/account-actors.js.map +1 -1
  8. package/dist/account.d.ts +4 -10
  9. package/dist/account.js +6 -12
  10. package/dist/account.js.map +1 -1
  11. package/dist/actor-bounds.d.ts +4 -4
  12. package/dist/actor-bounds.js +4 -4
  13. package/dist/actor-bounds.js.map +1 -1
  14. package/dist/actor-qualities.d.ts +3 -3
  15. package/dist/actor-qualities.js +3 -3
  16. package/dist/actor-qualities.js.map +1 -1
  17. package/dist/actors.d.ts +8 -8
  18. package/dist/actors.js +9 -9
  19. package/dist/actors.js.map +1 -1
  20. package/dist/api-failure.d.ts +28 -129
  21. package/dist/api-failure.js +52 -225
  22. package/dist/api-failure.js.map +1 -1
  23. package/dist/aspect.d.ts +26 -96
  24. package/dist/aspect.js +32 -108
  25. package/dist/aspect.js.map +1 -1
  26. package/dist/contract-dispositions.d.ts +42 -167
  27. package/dist/contract-dispositions.js +54 -184
  28. package/dist/contract-dispositions.js.map +1 -1
  29. package/dist/endpoints.d.ts +5 -5
  30. package/dist/endpoints.js +3 -3
  31. package/dist/endpoints.js.map +1 -1
  32. package/dist/errors.d.ts +33 -101
  33. package/dist/errors.js +42 -108
  34. package/dist/errors.js.map +1 -1
  35. package/dist/faceless-cut-style.d.ts +115 -0
  36. package/dist/faceless-cut-style.js +109 -0
  37. package/dist/faceless-cut-style.js.map +1 -0
  38. package/dist/faceless-image-qualification.d.ts +97 -0
  39. package/dist/faceless-image-qualification.js +145 -0
  40. package/dist/faceless-image-qualification.js.map +1 -0
  41. package/dist/faceless-short-61.d.ts +83 -0
  42. package/dist/faceless-short-61.js +152 -0
  43. package/dist/faceless-short-61.js.map +1 -0
  44. package/dist/faceless-storyboard-timing.d.ts +40 -0
  45. package/dist/faceless-storyboard-timing.js +97 -0
  46. package/dist/faceless-storyboard-timing.js.map +1 -0
  47. package/dist/faceless-storyboard-v2-plan.d.ts +3 -0
  48. package/dist/faceless-storyboard-v2-plan.js +112 -0
  49. package/dist/faceless-storyboard-v2-plan.js.map +1 -0
  50. package/dist/faceless-storyboard-v2-timing.d.ts +52 -0
  51. package/dist/faceless-storyboard-v2-timing.js +136 -0
  52. package/dist/faceless-storyboard-v2-timing.js.map +1 -0
  53. package/dist/faceless-storyboard-v2.d.ts +44 -0
  54. package/dist/faceless-storyboard-v2.js +43 -0
  55. package/dist/faceless-storyboard-v2.js.map +1 -0
  56. package/dist/faceless-storyboard.d.ts +55 -0
  57. package/dist/faceless-storyboard.js +105 -0
  58. package/dist/faceless-storyboard.js.map +1 -0
  59. package/dist/idempotency.d.ts +8 -44
  60. package/dist/idempotency.js +10 -44
  61. package/dist/idempotency.js.map +1 -1
  62. package/dist/index.js +21 -25
  63. package/dist/index.js.map +1 -1
  64. package/dist/long-form-pricing.d.ts +4 -4
  65. package/dist/long-form-pricing.js +4 -4
  66. package/dist/long-form-pricing.js.map +1 -1
  67. package/dist/long-form-profile.d.ts +2 -2
  68. package/dist/long-form-profile.js +2 -2
  69. package/dist/long-form-profile.js.map +1 -1
  70. package/dist/pacing.d.ts +1 -1
  71. package/dist/pacing.js +1 -1
  72. package/dist/pacing.js.map +1 -1
  73. package/dist/public-endpoints.d.ts +9 -11
  74. package/dist/public-endpoints.js +11 -13
  75. package/dist/public-endpoints.js.map +1 -1
  76. package/dist/rate-limit.d.ts +23 -130
  77. package/dist/rate-limit.js +27 -132
  78. package/dist/rate-limit.js.map +1 -1
  79. package/dist/render-backend.d.ts +30 -145
  80. package/dist/render-backend.js +4 -27
  81. package/dist/render-backend.js.map +1 -1
  82. package/dist/run-preflight.d.ts +4 -4
  83. package/dist/run-preflight.js +10 -10
  84. package/dist/run-preflight.js.map +1 -1
  85. package/dist/run-static-warnings.d.ts +8 -8
  86. package/dist/run-static-warnings.js +24 -24
  87. package/dist/run-static-warnings.js.map +1 -1
  88. package/dist/runs.d.ts +18 -55
  89. package/dist/runs.js +26 -94
  90. package/dist/runs.js.map +1 -1
  91. package/dist/script-integrity.d.ts +6 -7
  92. package/dist/script-integrity.js +7 -8
  93. package/dist/script-integrity.js.map +1 -1
  94. package/dist/semantic-inserts.d.ts +4 -4
  95. package/dist/semantic-inserts.js +6 -6
  96. package/dist/semantic-inserts.js.map +1 -1
  97. package/dist/skills.d.ts +33 -107
  98. package/dist/skills.js +69 -160
  99. package/dist/skills.js.map +1 -1
  100. package/dist/tts-backend.d.ts +13 -42
  101. package/dist/tts-backend.js +2 -10
  102. package/dist/tts-backend.js.map +1 -1
  103. package/dist/uploads.d.ts +4 -4
  104. package/dist/uploads.js +4 -4
  105. package/dist/uploads.js.map +1 -1
  106. package/dist/vendor-response.d.ts +17 -80
  107. package/dist/vendor-response.js +19 -67
  108. package/dist/vendor-response.js.map +1 -1
  109. package/dist/voice-admission.d.ts +5 -5
  110. package/dist/voice-admission.js +6 -6
  111. package/dist/voice-admission.js.map +1 -1
  112. package/dist/voice-catalog-data.d.ts +2 -2
  113. package/dist/voice-catalog-data.js +2 -2
  114. package/dist/voice-catalog-data.js.map +1 -1
  115. package/dist/voice-catalog.d.ts +5 -5
  116. package/dist/voice-catalog.js +6 -6
  117. package/dist/voice-catalog.js.map +1 -1
  118. package/dist/voice-listing.d.ts +4 -4
  119. package/dist/voice-listing.js +6 -6
  120. package/dist/voice-listing.js.map +1 -1
  121. package/dist/voices.d.ts +40 -82
  122. package/dist/voices.js +56 -99
  123. package/dist/voices.js.map +1 -1
  124. package/package.json +1 -1
@@ -2,12 +2,12 @@ import { z } from "zod";
2
2
  import { CATALOG_LANGUAGES, normalizeVoiceLabel, VOICES_QUERY_FIELDS, voicesQueryShape, } from "./voice-catalog.js";
3
3
  import { TTS_MODEL_LANGUAGES, VOICE_CATALOG } from "./voice-catalog-data.js";
4
4
  import { resolveTtsModel, TTS_MODEL_CHAR_CAP, TTS_MODELS, VOICE_PRESETS, } from "./voices.js";
5
- // Подпуть, а не баррел: список читает каталог, а клиентам SDK он не нужен (docs/16).
6
- // Те же данные сверяет с вендором еженедельная проба воркера.
5
+ // A subpath, not the barrel: the list reads the catalog, which SDK clients do not need.
6
+ // A weekly worker probe checks the same data against the vendor.
7
7
  export { TTS_MODEL_LANGUAGES, VOICE_CATALOG } from "./voice-catalog-data.js";
8
8
  const isLive = (voice) => voice.retired_at === undefined;
9
- /** Модели, под которые вендор ВООБЩЕ выдаёт верификацию голосам библиотеки. */
10
- /** Выводится из каталога, а не перечисляется: разбор — `docs/16` (#208). */
9
+ /** Models for which the vendor verifies ANY library voice at all. Derived from the */
10
+ /** catalog, not listed. */
11
11
  export function vendorVerifiedModels(voices = VOICE_CATALOG.voices) {
12
12
  return TTS_MODELS.filter((model) => voices.some((voice) => isLive(voice) && voice.verified_models.includes(model)));
13
13
  }
@@ -15,7 +15,7 @@ function labelValues(voices, raw) {
15
15
  const values = voices.filter(isLive).map((voice) => normalizeVoiceLabel(raw(voice)));
16
16
  return [...new Set(values.filter((value) => value !== undefined))].sort();
17
17
  }
18
- /** Запрос `/v1/voices` на сервере: незнакомая метка — отказ со списком допустимых, а не пустой список. */
18
+ /** The server's `/v1/voices` query: an unfamiliar label is refused with the allowed list, not an empty result. */
19
19
  export function serverVoicesQueryFor(voices = VOICE_CATALOG.voices) {
20
20
  return z.object({
21
21
  ...voicesQueryShape,
@@ -25,7 +25,7 @@ export function serverVoicesQueryFor(voices = VOICE_CATALOG.voices) {
25
25
  }
26
26
  export const serverVoicesQuery = serverVoicesQueryFor();
27
27
  const presentText = (raw) => raw?.trim() || undefined;
28
- /** Пресеты, затем живые голоса каталога по языку и рангу; поле, которого у голоса нет, не совпадает ни с чем. */
28
+ /** Presets, then live catalog voices by language and rank; a field the voice lacks matches nothing. */
29
29
  export function listVoices(query, catalog = VOICE_CATALOG, modelLanguages = TTS_MODEL_LANGUAGES) {
30
30
  const languagesOf = (id) => modelLanguages.models.find((model) => model.id === id)?.languages ?? [];
31
31
  const supportedModels = (language) => TTS_MODELS.filter((id) => languagesOf(id).includes(language));
@@ -1 +1 @@
1
- {"version":3,"file":"voice-listing.js","sourceRoot":"","sources":["../src/voice-listing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"voice-listing.js","sourceRoot":"","sources":["../src/voice-listing.ts"],"names":[],"mappings":"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"}
package/dist/voices.d.ts CHANGED
@@ -1,41 +1,40 @@
1
1
  import { z } from "zod";
2
2
  import type { Gender } from "./default-actor.js";
3
- /** Модели синтеза ElevenLabs, которые контракт разрешает назвать (clipwright#101, #145). */
4
- /** `multilingual_v2` снята: по замеру #145 она не отвечает ни на один приём ударения. */
3
+ /** ElevenLabs speech models the contract lets callers name. `multilingual_v2` was dropped: */
4
+ /** it honored none of the stress-marking methods we measured. */
5
5
  export declare const TTS_MODELS: readonly ["eleven_v3", "eleven_flash_v2_5", "eleven_turbo_v2_5"];
6
6
  export type TtsModelId = (typeof TTS_MODELS)[number];
7
7
  export declare const DEFAULT_TTS_MODEL: TtsModelId;
8
- /** У v3 потолок вендорский. У 2.5 вендор даёт 40 000; 10 000 — наш потолок длины рана. */
8
+ /** The v3 cap is the vendor's. For 2.5 the vendor allows 40,000; 10,000 is our run-length cap. */
9
9
  export declare const TTS_MODEL_CHAR_CAP: Readonly<Record<TtsModelId, number>>;
10
- /** Знак ударения U+0301 читает только v3; flash тем же знаком ошибается втрое чаще (#145). */
10
+ /** Only v3 reads the U+0301 stress mark; flash misreads the same mark three times as often. */
11
11
  export declare const STRESS_MARK: string;
12
12
  export declare const STRESS_MARK_MODELS: ReadonlySet<TtsModelId>;
13
- /** «потОм» → «пото́м». Заглавная в начале слова — обычная заглавная; знак не удваивается. */
13
+ /** Turns an inner capital vowel into a stress mark. A word-initial capital stays a capital; */
14
+ /** an existing mark is not doubled. */
14
15
  export declare function stressMarksFromCapitals(text: string): string;
15
- /** Текст, который уходит вендору: разметка ударения — только для модели, которая её читает. */
16
+ /** The text sent to the vendor: stress marking only for a model that reads it. */
16
17
  export declare function speechTextFor(text: string, modelId: TtsModelId): string;
17
18
  export declare const MAX_SCRIPT_CHARS: number;
18
19
  export declare const SCRIPT_LENGTH_DESCRIPTION: string;
19
20
  export declare const STRESS_MARKING_DESCRIPTION: string;
20
- /** Длина считается по тексту, который уйдёт вендору, — со знаками ударения. */
21
+ /** Length is measured on the text sent to the vendor, stress marks included. */
21
22
  export declare function ttsScriptLimitError(text: string, modelId: string): string | undefined;
22
- /** Каталог голосовых пресетов — НАШИ имена и метадата, БЕЗ вендорских id: имена */
23
- /** порождают `tools/list` и типы SDK, id живут только в адаптере воркера. */
23
+ /** Voice preset catalog: OUR names and metadata, WITHOUT vendor ids. Names feed `tools/list` */
24
+ /** and SDK types; ids live only in the worker adapter. */
24
25
  export interface VoicePreset {
25
- /** Язык голоса в терминах BCP-47-подобного короткого кода (ru/en). */
26
+ /** Voice language as a short BCP-47-like code (ru/en). */
26
27
  language: string;
27
- /** Пол голоса. Опускается, только если он НАМ неизвестен: молчание вендора */
28
- /** про наш собственный клон незнанием не является (docs/18, Д50). */
28
+ /** Voice gender. Omitted only when WE do not know it: vendor silence about our own */
29
+ /** clone is not ignorance. */
29
30
  gender?: Gender;
30
- /** Человекочитаемое описание для list_voices (US-516). */
31
+ /** Human-readable description for list_voices. */
31
32
  description: string;
32
- /** Модель синтеза, на которую голос настроен; клиент может перебить полем `tts_model`. */
33
+ /** The speech model the voice is tuned for; clients may override with `tts_model`. */
33
34
  model: TtsModelId;
34
35
  }
35
- /**
36
- * Стартовый набор из 5 пресетов (условие плана §4). Порядок значим только для
37
- * презентации; дефолт задаётся отдельно через `DEFAULT_VOICE_PRESET`.
38
- */
36
+ /** The starter set of presets. Order matters only for presentation; the default is set */
37
+ /** separately by `DEFAULT_VOICE_PRESET`. */
39
38
  export declare const VOICE_PRESETS: {
40
39
  readonly owner_ru_clone: {
41
40
  readonly language: "ru";
@@ -68,17 +67,15 @@ export declare const VOICE_PRESETS: {
68
67
  readonly model: "eleven_v3";
69
68
  };
70
69
  };
71
- /**
72
- * Кортеж имён пресетов для `z.enum`. Выводится из ключей `VOICE_PRESETS`, чтобы
73
- * список имён и метадата не разъехались (правило проекта №2 — не дублировать).
74
- */
70
+ /** Preset name tuple for `z.enum`, derived from `VOICE_PRESETS` keys so names and */
71
+ /** metadata cannot drift. */
75
72
  export declare const VOICE_PRESET_NAMES: [keyof typeof VOICE_PRESETS, ...(keyof typeof VOICE_PRESETS)[]];
76
73
  export type VoicePresetName = keyof typeof VOICE_PRESETS;
77
- /** Форма имени голоса, а не список: каталог растёт на сервере без выпуска клиентов (#164). */
74
+ /** The shape of a voice name, not a list: the catalog grows on the server without client releases. */
78
75
  export declare const VOICE_NAME_PATTERN: RegExp;
79
76
  export declare function isVoicePresetName(name: string): name is VoicePresetName;
80
- /** Ответ `/v1/voices` — схема, а не догадка обеих сторон. Сервер отдаёт разобранное */
81
- /** ею тело, SDK разбирает им же: `Array.isArray` пропускал массив чего угодно. */
77
+ /** The `/v1/voices` response as a schema, not a guess on both sides: the server sends a */
78
+ /** body parsed by it and the SDK parses with it. `Array.isArray` let any array through. */
82
79
  export declare const voiceCatalogEntry: z.ZodObject<{
83
80
  name: z.ZodString;
84
81
  kind: z.ZodOptional<z.ZodString>;
@@ -135,28 +132,18 @@ export declare const voicesResponse: z.ZodObject<{
135
132
  }, z.core.$strip>>>;
136
133
  }, z.core.$strip>;
137
134
  export type VoicesResponse = z.infer<typeof voicesResponse>;
138
- /** Голос, когда пол актёра неизвестен; подставляет резолвер, а не схема (US-517). */
139
- /** История выбора дефолта — docs/16. */
135
+ /** The voice when the actor's gender is unknown; the resolver fills it in, not the schema. */
140
136
  export declare const DEFAULT_VOICE_PRESET: "george";
141
- /** Голос по умолчанию следует полу актёра (clipwright#183, docs/18, Д52). */
137
+ /** The default voice follows the actor's gender. */
142
138
  export declare const DEFAULT_VOICE_BY_GENDER: {
143
139
  readonly female: "sarah";
144
140
  readonly male: "george";
145
141
  };
146
- /**
147
- * Форма сырого `voice_id` вендора. НАМЕРЕННО терпимая (16–32 алфанумерик):
148
- * ElevenLabs не документирует формат id, наблюдаемые id — 20 символов, но
149
- * строгая длина 20 превратила бы будущий валидный id иной длины в ложный
150
- * отказ. Это лишь грубый фильтр мусора на входе; реальную проверку
151
- * существования id делает бесплатный префлайт в воркере (US-515).
152
- */
142
+ /** Shape of a raw vendor `voice_id`, deliberately loose (16–32 alphanumerics): the format is */
143
+ /** undocumented. A coarse input filter only; the worker's free preflight checks existence. */
153
144
  export declare const VOICE_ID_PATTERN: RegExp;
154
- /**
155
- * Результат разрешения выбора голоса — дискриминированное объединение. `preset`
156
- * несёт НАШЕ имя (адаптер превратит его в вендорский id через `PRESET_TO_VOICE_ID`),
157
- * `raw` — уже сырой вендорский id (escape hatch). Core НЕ знает вендорских id, и
158
- * это объединение — граница: дальше маппинг делает адаптер (принцип 5).
159
- */
145
+ /** Resolved voice selection. `preset` carries OUR name, mapped to a vendor id by the adapter; */
146
+ /** `raw` is already a vendor id. Core never knows vendor ids. */
160
147
  export type VoiceSelection = {
161
148
  kind: "preset";
162
149
  preset: VoicePresetName;
@@ -167,68 +154,39 @@ export type VoiceSelection = {
167
154
  kind: "raw";
168
155
  voiceId: string;
169
156
  };
170
- /** Приоритет: сырой `voice_id` > `voice` > голос по полу актёра > `DEFAULT_VOICE_PRESET`. */
171
- /** Пол даёт `voiceGenderFor` (actors.ts); взаимоисключение полей держит `makeUgcInput`. */
157
+ /** Priority: raw `voice_id` > `voice` > voice by actor gender > `DEFAULT_VOICE_PRESET`. */
158
+ /** Gender comes from `voiceGenderFor` (actors.ts); `makeUgcInput` enforces exclusivity. */
172
159
  export declare function resolveVoiceSelection(input: {
173
160
  voice?: string | undefined;
174
161
  voice_id?: string | undefined;
175
162
  }, gender?: Gender | undefined): VoiceSelection;
176
- /** Модель: явное `tts_model` → модель пресета → дефолт (сырой `voice_id` пресета не имеет). */
177
- /** Голос каталога говорит на дефолте, как пресеты (решение владельца #164). */
163
+ /** Model: explicit `tts_model` → the preset's model → default (a raw `voice_id` has no preset). */
164
+ /** A catalog voice speaks the default model, like the presets. */
178
165
  export declare function resolveTtsModel(input: {
179
166
  voice?: string | undefined;
180
167
  voice_id?: string | undefined;
181
168
  tts_model?: TtsModelId | undefined;
182
169
  }, gender?: Gender | undefined): TtsModelId;
183
- /**
184
- * Семейство письменности скрипта — грубый детектор для языкового warning'а
185
- * (US-517). Считает ТОЛЬКО буквенные символы: кириллица (U+0400–U+04FF) против
186
- * латиницы (A–Z/a–z). Знаки препинания, цифры, пробелы, эмодзи игнорируются —
187
- * они не различают язык.
188
- *
189
- * НЕЙТРАЛЬНАЯ ПОЛОСА (детерминированно, тестируемо):
190
- * - < 2 букв всего (пусто, одни цифры, единичная буква-случайность) → "neutral":
191
- * данных для суждения нет (это «мало букв» из плана §Р3, условие ii);
192
- * - ни одна сторона не набрала порога доминирования 60% (напр. смесь 50/50) →
193
- * "neutral": язык скрипта неоднозначен, warning выдавать не на чем.
194
- * Только уверенное доминирование (≥60%) даёт "cyrillic"/"latin".
195
- */
170
+ /** Script family for the language warning: counts only Cyrillic vs Latin letters. Fewer than */
171
+ /** 2 letters or no side reaching this 60% dominance gives "neutral". */
196
172
  export declare const SCRIPT_DOMINANCE_THRESHOLD = 0.6;
197
- /** Буквы каждого письма по отдельности. Их же складывает `detectScriptFamily`; */
198
- /** отдельно они нужны, чтобы отличить «мало» от «нет вовсе» (clipwright#131). */
173
+ /** Letters of each script separately. `detectScriptFamily` sums them; apart they tell */
174
+ /** "few" from "none at all". */
199
175
  export declare function scriptLetterCounts(script: string): {
200
176
  cyrillic: number;
201
177
  latin: number;
202
178
  };
203
179
  export declare function detectScriptFamily(script: string): "cyrillic" | "latin" | "neutral";
204
- /** Только путь QUOTE (US-517); почему не на пути рана — docs/18, Д41. */
180
+ /** QUOTE path only: a raw voice id is checked at run start, not at quote time. */
205
181
  export declare const RAW_VOICE_ID_VALIDATION_WARNING = "raw voice_id is verified against your account at run start, not at quote time";
206
- /**
207
- * Голосовые warnings по ТЕСТУ ДОПУСТИМОСТИ (план §Р3). Warning допустим, только
208
- * если ОДНОВРЕМЕННО: (i) причина — то, что клиент запросил ЯВНО (не дефолт, не наш
209
- * выбор); (ii) утверждение опирается на данные, которые ЕСТЬ (лейбл присутствует),
210
- * а не на их отсутствие; (iii) на дефолтном happy-path не срабатывает НИКОГДА.
211
- *
212
- * ПРАВИЛО СТАРШИНСТВА ПОДМЕНЫ: подмена (клиент попросил X, отдали Y) предупреждает
213
- * ВСЕГДА, независимо от (iii) — (iii) глушит уведомления о ПРАВИЛЬНОМ поведении, не
214
- * о деградированном. Канон такой подмены — 1080p→720p (render-backend.ts:144). В
215
- * ДАННОМ дизайне подмены голоса НЕТ: путь падает closed (сырой id не найден → бросок
216
- * до платного POST, US-515; неизвестное имя → 400 на границе enum), голос молча не
217
- * заменяется. Поэтому правило старшинства здесь не применяется — но зафиксировано,
218
- * чтобы будущая подмена не была ошибочно заглушена условием (iii).
219
- *
220
- * ПОЧЕМУ тест не подавляет обязательный warning про подмену №3 (принцип DR-2):
221
- * обязанность №3 крепится к НЕИСПОЛНЕННОМУ явному запросу (клиент попросил 1080p,
222
- * дали 720p) — это подмена, у неё старшинство над (iii). Здесь же голос либо
223
- * исполняется как запрошен, либо ран падает — деградированной выдачи под видом
224
- * успешной не бывает, глушить нечего.
225
- */
182
+ /** Voice warnings fire only when (i) the client chose explicitly, (ii) the claim rests on data */
183
+ /** present, (iii) never on the default happy path. A voice is never substituted, only failed. */
226
184
  export declare function buildVoiceWarnings(input: {
227
185
  voice?: string | undefined;
228
186
  voice_id?: string | undefined;
229
187
  script: string;
230
188
  }): string[];
231
- /** Модель, не читающая ударения, на тексте, где они важны. На умолчании (v3) молчит всегда. */
189
+ /** A model that ignores stress marks on text where they matter. Silent on the v3 default. */
232
190
  export declare function buildSpeechModelWarnings(input: {
233
191
  voice?: string | undefined;
234
192
  voice_id?: string | undefined;
package/dist/voices.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import { z } from "zod";
2
- /** Модели синтеза ElevenLabs, которые контракт разрешает назвать (clipwright#101, #145). */
3
- /** `multilingual_v2` снята: по замеру #145 она не отвечает ни на один приём ударения. */
2
+ /** ElevenLabs speech models the contract lets callers name. `multilingual_v2` was dropped: */
3
+ /** it honored none of the stress-marking methods we measured. */
4
4
  export const TTS_MODELS = ["eleven_v3", "eleven_flash_v2_5", "eleven_turbo_v2_5"];
5
5
  export const DEFAULT_TTS_MODEL = "eleven_v3";
6
- /** У v3 потолок вендорский. У 2.5 вендор даёт 40 000; 10 000 — наш потолок длины рана. */
6
+ /** The v3 cap is the vendor's. For 2.5 the vendor allows 40,000; 10,000 is our run-length cap. */
7
7
  export const TTS_MODEL_CHAR_CAP = {
8
8
  eleven_v3: 5_000,
9
9
  eleven_flash_v2_5: 10_000,
10
10
  eleven_turbo_v2_5: 10_000,
11
11
  };
12
- /** Знак ударения U+0301 читает только v3; flash тем же знаком ошибается втрое чаще (#145). */
12
+ /** Only v3 reads the U+0301 stress mark; flash misreads the same mark three times as often. */
13
13
  export const STRESS_MARK = String.fromCharCode(0x0301);
14
14
  export const STRESS_MARK_MODELS = new Set(["eleven_v3"]);
15
15
  const CYRILLIC_WORD = new RegExp(`[Ѐ-ӿ${STRESS_MARK}]+`, "g");
@@ -17,8 +17,8 @@ const CYRILLIC_STRESS_MARK = new RegExp(`[Ѐ-ӿ]${STRESS_MARK}`);
17
17
  const CYRILLIC_LOWERCASE = /[а-џ]/;
18
18
  const CYRILLIC_UPPERCASE = /[Ѐ-Я]/;
19
19
  const CYRILLIC_UPPER_VOWELS = "АЕЁИОУЫЭЮЯ";
20
- /** Позиция ударной заглавной: ровно одна заглавная после первой буквы, и она гласная. */
21
- /** Вторая заглавная или согласная — аббревиатура («ВУЗы», «СУПЕРцена»), не ударение. */
20
+ /** Index of the stressed capital: exactly one capital after the first letter, a vowel. */
21
+ /** A second capital or a consonant capital is an abbreviation, not stress. */
22
22
  function stressCapitalIndex(word) {
23
23
  if (!CYRILLIC_LOWERCASE.test(word))
24
24
  return -1;
@@ -36,7 +36,8 @@ function stressCapitalIndex(word) {
36
36
  }
37
37
  return found;
38
38
  }
39
- /** «потОм» → «пото́м». Заглавная в начале слова — обычная заглавная; знак не удваивается. */
39
+ /** Turns an inner capital vowel into a stress mark. A word-initial capital stays a capital; */
40
+ /** an existing mark is not doubled. */
40
41
  export function stressMarksFromCapitals(text) {
41
42
  return text.replace(CYRILLIC_WORD, (word) => {
42
43
  const i = stressCapitalIndex(word);
@@ -47,7 +48,7 @@ export function stressMarksFromCapitals(text) {
47
48
  return word.slice(0, i) + vowel.toLowerCase() + mark + word.slice(i + 1);
48
49
  });
49
50
  }
50
- /** Текст, который уходит вендору: разметка ударения — только для модели, которая её читает. */
51
+ /** The text sent to the vendor: stress marking only for a model that reads it. */
51
52
  export function speechTextFor(text, modelId) {
52
53
  return STRESS_MARK_MODELS.has(modelId) ? stressMarksFromCapitals(text) : text;
53
54
  }
@@ -62,7 +63,7 @@ export const STRESS_MARKING_DESCRIPTION = "Russian stress: write the stressed vo
62
63
  "read as stress, so write \"Яндекс Еда\", not \"ЯндексЕда\". " +
63
64
  "Tell users writing in Russian that they can mark stress this way. eleven_flash_v2_5 and " +
64
65
  "eleven_turbo_v2_5 cost less but misread stress marks: capitals reach them unchanged.";
65
- /** Длина считается по тексту, который уйдёт вендору, — со знаками ударения. */
66
+ /** Length is measured on the text sent to the vendor, stress marks included. */
66
67
  export function ttsScriptLimitError(text, modelId) {
67
68
  const model = TTS_MODELS.find((model) => model === modelId);
68
69
  if (!model)
@@ -78,19 +79,17 @@ export function ttsScriptLimitError(text, modelId) {
78
79
  }
79
80
  return undefined;
80
81
  }
81
- /**
82
- * Стартовый набор из 5 пресетов (условие плана §4). Порядок значим только для
83
- * презентации; дефолт задаётся отдельно через `DEFAULT_VOICE_PRESET`.
84
- */
82
+ /** The starter set of presets. Order matters only for presentation; the default is set */
83
+ /** separately by `DEFAULT_VOICE_PRESET`. */
85
84
  export const VOICE_PRESETS = {
86
85
  owner_ru_clone: {
87
86
  language: "ru",
88
- // Пол — НАШ факт о своём клоне, а не метадата вендора (docs/18, Д50).
87
+ // The gender is OUR fact about our own clone, not vendor metadata.
89
88
  gender: "male",
90
89
  description: "Клон голоса владельца (David-Dmitry-Ru); русский — на английском даёт акцент",
91
90
  model: "eleven_v3",
92
91
  },
93
- // Все пресеты — v3: решение владельца #145, выразительнее и единственная со знаком ударения.
92
+ // Every preset is v3: more expressive, and the only model that reads stress marks.
94
93
  sarah: {
95
94
  language: "en",
96
95
  gender: "female",
@@ -116,20 +115,18 @@ export const VOICE_PRESETS = {
116
115
  model: "eleven_v3",
117
116
  },
118
117
  };
119
- /**
120
- * Кортеж имён пресетов для `z.enum`. Выводится из ключей `VOICE_PRESETS`, чтобы
121
- * список имён и метадата не разъехались (правило проекта №2 — не дублировать).
122
- */
118
+ /** Preset name tuple for `z.enum`, derived from `VOICE_PRESETS` keys so names and */
119
+ /** metadata cannot drift. */
123
120
  export const VOICE_PRESET_NAMES = Object.keys(VOICE_PRESETS);
124
- /** Форма имени голоса, а не список: каталог растёт на сервере без выпуска клиентов (#164). */
121
+ /** The shape of a voice name, not a list: the catalog grows on the server without client releases. */
125
122
  export const VOICE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
126
123
  export function isVoicePresetName(name) {
127
124
  return Object.hasOwn(VOICE_PRESETS, name);
128
125
  }
129
- /** Ответ `/v1/voices` — схема, а не догадка обеих сторон. Сервер отдаёт разобранное */
130
- /** ею тело, SDK разбирает им же: `Array.isArray` пропускал массив чего угодно. */
126
+ /** The `/v1/voices` response as a schema, not a guess on both sides: the server sends a */
127
+ /** body parsed by it and the SDK parses with it. `Array.isArray` let any array through. */
131
128
  export const voiceCatalogEntry = z.object({
132
- // Строки, а не enum: незнакомое клиенту имя или метка не роняют `listVoices` (#164).
129
+ // Strings, not enums: an unfamiliar name or label does not break `listVoices`.
133
130
  name: z.string().min(1),
134
131
  kind: z.string().optional(),
135
132
  language: z.string().min(1).optional(),
@@ -142,7 +139,7 @@ export const voiceCatalogEntry = z.object({
142
139
  model: z.enum(TTS_MODELS),
143
140
  supported_models: z.array(z.string()).optional(),
144
141
  verified_models: z.array(z.string()).optional(),
145
- // Подпись временная; отсутствие пары значит «образца у голоса нет» (docs/16).
142
+ // The signature is temporary; a missing pair means the voice has no sample.
146
143
  preview_url: z.url().optional(),
147
144
  preview_expires_at: z.iso.datetime().optional(),
148
145
  });
@@ -150,18 +147,17 @@ export const voiceModelEntry = z.object({
150
147
  id: z.string().min(1),
151
148
  char_limit: z.number().int().positive(),
152
149
  languages: z.array(z.string()),
153
- /** Выдаёт ли вендор верификацию под эту модель хоть одному голосу библиотеки. */
154
- /** Пустой `verified_models` у модели, где `false`, о голосе не говорит (#208). */
150
+ /** Whether the vendor verifies any library voice for this model at all. An empty */
151
+ /** `verified_models` on a model where this is `false` says nothing about the voice. */
155
152
  vendor_verifies_voices: z.boolean().optional(),
156
153
  });
157
154
  export const voicesResponse = z.object({
158
155
  voices: z.array(voiceCatalogEntry),
159
156
  models: z.array(voiceModelEntry).optional(),
160
157
  });
161
- /** Голос, когда пол актёра неизвестен; подставляет резолвер, а не схема (US-517). */
162
- /** История выбора дефолта — docs/16. */
158
+ /** The voice when the actor's gender is unknown; the resolver fills it in, not the schema. */
163
159
  export const DEFAULT_VOICE_PRESET = "george";
164
- /** Голос по умолчанию следует полу актёра (clipwright#183, docs/18, Д52). */
160
+ /** The default voice follows the actor's gender. */
165
161
  export const DEFAULT_VOICE_BY_GENDER = {
166
162
  female: "sarah",
167
163
  male: "george",
@@ -169,30 +165,25 @@ export const DEFAULT_VOICE_BY_GENDER = {
169
165
  function defaultVoiceFor(gender) {
170
166
  return gender === undefined ? DEFAULT_VOICE_PRESET : DEFAULT_VOICE_BY_GENDER[gender];
171
167
  }
172
- /**
173
- * Форма сырого `voice_id` вендора. НАМЕРЕННО терпимая (16–32 алфанумерик):
174
- * ElevenLabs не документирует формат id, наблюдаемые id — 20 символов, но
175
- * строгая длина 20 превратила бы будущий валидный id иной длины в ложный
176
- * отказ. Это лишь грубый фильтр мусора на входе; реальную проверку
177
- * существования id делает бесплатный префлайт в воркере (US-515).
178
- */
168
+ /** Shape of a raw vendor `voice_id`, deliberately loose (16–32 alphanumerics): the format is */
169
+ /** undocumented. A coarse input filter only; the worker's free preflight checks existence. */
179
170
  export const VOICE_ID_PATTERN = /^[A-Za-z0-9]{16,32}$/;
180
- /** Приоритет: сырой `voice_id` > `voice` > голос по полу актёра > `DEFAULT_VOICE_PRESET`. */
181
- /** Пол даёт `voiceGenderFor` (actors.ts); взаимоисключение полей держит `makeUgcInput`. */
171
+ /** Priority: raw `voice_id` > `voice` > voice by actor gender > `DEFAULT_VOICE_PRESET`. */
172
+ /** Gender comes from `voiceGenderFor` (actors.ts); `makeUgcInput` enforces exclusivity. */
182
173
  export function resolveVoiceSelection(input, gender) {
183
174
  if (input.voice_id !== undefined) {
184
175
  return { kind: "raw", voiceId: input.voice_id };
185
176
  }
186
177
  if (input.voice !== undefined) {
187
- // Не пресет — слаг каталога: его id знает только сайдкар воркера, промах там — отказ (#164).
178
+ // Not a preset, so a catalog slug: only the worker sidecar knows its id; a miss there refuses.
188
179
  if (!isVoicePresetName(input.voice))
189
180
  return { kind: "catalog", slug: input.voice };
190
181
  return { kind: "preset", preset: input.voice };
191
182
  }
192
183
  return { kind: "preset", preset: defaultVoiceFor(gender) };
193
184
  }
194
- /** Модель: явное `tts_model` → модель пресета → дефолт (сырой `voice_id` пресета не имеет). */
195
- /** Голос каталога говорит на дефолте, как пресеты (решение владельца #164). */
185
+ /** Model: explicit `tts_model` → the preset's model → default (a raw `voice_id` has no preset). */
186
+ /** A catalog voice speaks the default model, like the presets. */
196
187
  export function resolveTtsModel(input, gender) {
197
188
  if (input.tts_model !== undefined)
198
189
  return input.tts_model;
@@ -201,22 +192,11 @@ export function resolveTtsModel(input, gender) {
201
192
  const voice = input.voice ?? defaultVoiceFor(gender);
202
193
  return isVoicePresetName(voice) ? VOICE_PRESETS[voice].model : DEFAULT_TTS_MODEL;
203
194
  }
204
- /**
205
- * Семейство письменности скрипта — грубый детектор для языкового warning'а
206
- * (US-517). Считает ТОЛЬКО буквенные символы: кириллица (U+0400–U+04FF) против
207
- * латиницы (A–Z/a–z). Знаки препинания, цифры, пробелы, эмодзи игнорируются —
208
- * они не различают язык.
209
- *
210
- * НЕЙТРАЛЬНАЯ ПОЛОСА (детерминированно, тестируемо):
211
- * - < 2 букв всего (пусто, одни цифры, единичная буква-случайность) → "neutral":
212
- * данных для суждения нет (это «мало букв» из плана §Р3, условие ii);
213
- * - ни одна сторона не набрала порога доминирования 60% (напр. смесь 50/50) →
214
- * "neutral": язык скрипта неоднозначен, warning выдавать не на чем.
215
- * Только уверенное доминирование (≥60%) даёт "cyrillic"/"latin".
216
- */
195
+ /** Script family for the language warning: counts only Cyrillic vs Latin letters. Fewer than */
196
+ /** 2 letters or no side reaching this 60% dominance gives "neutral". */
217
197
  export const SCRIPT_DOMINANCE_THRESHOLD = 0.6;
218
- /** Буквы каждого письма по отдельности. Их же складывает `detectScriptFamily`; */
219
- /** отдельно они нужны, чтобы отличить «мало» от «нет вовсе» (clipwright#131). */
198
+ /** Letters of each script separately. `detectScriptFamily` sums them; apart they tell */
199
+ /** "few" from "none at all". */
220
200
  export function scriptLetterCounts(script) {
221
201
  let cyrillic = 0;
222
202
  let latin = 0;
@@ -233,7 +213,7 @@ export function scriptLetterCounts(script) {
233
213
  export function detectScriptFamily(script) {
234
214
  const { cyrillic, latin } = scriptLetterCounts(script);
235
215
  const total = cyrillic + latin;
236
- // «Мало букв» → нейтрально: судить о языке не на чем (условие ii — данных нет).
216
+ // Too few letters: nothing to judge the language by.
237
217
  if (total < 2) {
238
218
  return "neutral";
239
219
  }
@@ -245,70 +225,47 @@ export function detectScriptFamily(script) {
245
225
  }
246
226
  return "neutral";
247
227
  }
248
- /**
249
- * Отображение нашего короткого языкового лейбла пресета в семейство письменности.
250
- * НАМЕРЕННО охватывает только языки, которые мы реально размечаем (ru/en): лейбл
251
- * вне этого набора трактуется как «нет данных для сравнения» и глушит warning
252
- * (условие ii — утверждение опирается на присутствующие данные, а не на догадку).
253
- */
228
+ /** Maps our short preset language label to a script family. Covers only ru/en on purpose: */
229
+ /** any other label means "no data to compare" and silences the warning. */
254
230
  const LANGUAGE_TO_SCRIPT_FAMILY = {
255
231
  ru: "cyrillic",
256
232
  en: "latin",
257
233
  };
258
- /** Только путь QUOTE (US-517); почему не на пути рана — docs/18, Д41. */
234
+ /** QUOTE path only: a raw voice id is checked at run start, not at quote time. */
259
235
  export const RAW_VOICE_ID_VALIDATION_WARNING = "raw voice_id is verified against your account at run start, not at quote time";
260
- /**
261
- * Голосовые warnings по ТЕСТУ ДОПУСТИМОСТИ (план §Р3). Warning допустим, только
262
- * если ОДНОВРЕМЕННО: (i) причина — то, что клиент запросил ЯВНО (не дефолт, не наш
263
- * выбор); (ii) утверждение опирается на данные, которые ЕСТЬ (лейбл присутствует),
264
- * а не на их отсутствие; (iii) на дефолтном happy-path не срабатывает НИКОГДА.
265
- *
266
- * ПРАВИЛО СТАРШИНСТВА ПОДМЕНЫ: подмена (клиент попросил X, отдали Y) предупреждает
267
- * ВСЕГДА, независимо от (iii) — (iii) глушит уведомления о ПРАВИЛЬНОМ поведении, не
268
- * о деградированном. Канон такой подмены — 1080p→720p (render-backend.ts:144). В
269
- * ДАННОМ дизайне подмены голоса НЕТ: путь падает closed (сырой id не найден → бросок
270
- * до платного POST, US-515; неизвестное имя → 400 на границе enum), голос молча не
271
- * заменяется. Поэтому правило старшинства здесь не применяется — но зафиксировано,
272
- * чтобы будущая подмена не была ошибочно заглушена условием (iii).
273
- *
274
- * ПОЧЕМУ тест не подавляет обязательный warning про подмену №3 (принцип DR-2):
275
- * обязанность №3 крепится к НЕИСПОЛНЕННОМУ явному запросу (клиент попросил 1080p,
276
- * дали 720p) — это подмена, у неё старшинство над (iii). Здесь же голос либо
277
- * исполняется как запрошен, либо ран падает — деградированной выдачи под видом
278
- * успешной не бывает, глушить нечего.
279
- */
236
+ /** Voice warnings fire only when (i) the client chose explicitly, (ii) the claim rests on data */
237
+ /** present, (iii) never on the default happy path. A voice is never substituted, only failed. */
280
238
  export function buildVoiceWarnings(input) {
281
- // Сырой escape-hatch id: причина явная (i), но проверка существования отложена
282
- // до рана. На quote префлайта нет — предупреждаем об отложенной валидации.
239
+ // A raw id is an explicit choice, but its existence is checked at run start,
240
+ // so the quote warns about deferred validation.
283
241
  if (input.voice_id !== undefined) {
284
242
  return [RAW_VOICE_ID_VALIDATION_WARNING];
285
243
  }
286
- // Условие (iii): голос не задан явно → языковых warning'ов нет никогда (анти-шум US-517).
287
- // Голос по полу актёра обсуждает `buildActorGenderWarnings` (actors.ts).
244
+ // No explicit voice: no language warnings ever. The gender-based voice is
245
+ // covered by `buildActorGenderWarnings` (actors.ts).
288
246
  if (input.voice === undefined) {
289
247
  return [];
290
248
  }
291
- // Defensive: до вызова zod уже сузил voice до валидного имени enum, но чистая
292
- // функция не полагается на это. Неизвестное имя → нет пресета → нет данных → [].
249
+ // Defensive: zod already narrowed voice, but a pure function does not rely on it.
250
+ // An unknown name has no preset and so no data.
293
251
  if (!isVoicePresetName(input.voice)) {
294
252
  return [];
295
253
  }
296
254
  const preset = VOICE_PRESETS[input.voice];
297
- // Условие (ii): сравнивать можно только при ПРИСУТСТВУЮЩЕМ пригодном лейбле.
298
- // Язык вне размеченного набора (ru/en) — «нет данных» → молчим, не выдумываем.
255
+ // Compare only against a PRESENT usable label; a language outside ru/en means
256
+ // no data, so stay silent instead of guessing.
299
257
  const voiceFamily = LANGUAGE_TO_SCRIPT_FAMILY[preset.language];
300
258
  if (voiceFamily === undefined) {
301
259
  return [];
302
260
  }
303
- // Условие (i)+(ii) выполнены: причина — явно выбранный пресет, а суждение
304
- // опирается на присутствующий лейбл против ДЕТЕКТИРОВАННОГО семейства скрипта.
305
- // Нейтральный/неоднозначный скрипт не противоречит ничему → молчим (ii).
261
+ // An explicit preset against the detected script family. A neutral or mixed
262
+ // script contradicts nothing.
306
263
  const scriptFamily = detectScriptFamily(input.script);
307
264
  if (scriptFamily === "neutral" || scriptFamily === voiceFamily) {
308
265
  return [];
309
266
  }
310
- // «БУКВ НЕТ ВОВСЕ» И «НЕ ТОТ ЯЗЫК» — РАЗНЫЕ СОВЕТЫ ЧИТАТЕЛЮ (clipwright#131):
311
- // первое чинят текстом или доставкой, второе — выбором голоса.
267
+ // "No letters at all" and "wrong language" call for different fixes: the text or
268
+ // its delivery versus the voice choice.
312
269
  const counts = scriptLetterCounts(input.script);
313
270
  if (counts[voiceFamily] === 0) {
314
271
  return [
@@ -321,12 +278,12 @@ export function buildVoiceWarnings(input) {
321
278
  `voice "${input.voice}" is labeled ${preset.language}, but the script looks ${scriptFamily}; it will be voiced in a ${preset.language} voice`,
322
279
  ];
323
280
  }
324
- /** Модель, не читающая ударения, на тексте, где они важны. На умолчании (v3) молчит всегда. */
281
+ /** A model that ignores stress marks on text where they matter. Silent on the v3 default. */
325
282
  export function buildSpeechModelWarnings(input, gender) {
326
283
  const model = resolveTtsModel(input, gender);
327
284
  if (STRESS_MARK_MODELS.has(model))
328
285
  return [];
329
- // Знак после латиницы — акцент французского или испанского текста (NFD), не ударение.
286
+ // A mark after a Latin letter is a French or Spanish accent (NFD), not stress.
330
287
  const marked = CYRILLIC_STRESS_MARK.test(input.script) || stressMarksFromCapitals(input.script) !== input.script;
331
288
  if (marked) {
332
289
  return [