@verbatra/sdk 0.5.0-next.0 → 0.5.0-next.2

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/index.d.cts CHANGED
@@ -36,7 +36,7 @@ type TranslationEntry$1 = Readonly<z.infer<typeof translationEntrySchema$1>>;
36
36
  * Result of comparing source placeholders against translated-output placeholders as MULTISETS
37
37
  * (counts matter). Always returned (never thrown) for an ordinary mismatch.
38
38
  */
39
- interface PlaceholderIntegrityResult {
39
+ interface PlaceholderIntegrityResult$1 {
40
40
  /** True when nothing is missing or extra: the multisets match, regardless of order. */
41
41
  readonly matches: boolean;
42
42
  /** Occurrences present in source but absent from the translation, one per dropped occurrence. Sorted. */
@@ -57,6 +57,13 @@ type Tone = "formal" | "informal" | "neutral";
57
57
  * derives placeholders itself.
58
58
  */
59
59
  type PlaceholderExtractor = (value: string) => readonly string[];
60
+ /**
61
+ * Compares a source and translated value's placeholders directly, branch-aware, instead of
62
+ * independently extracting each side's flat placeholder list first. Supplied by the caller (the SDK, for
63
+ * a format whose adapter defines one, for example ICU plural/select) so it matches the entries' format;
64
+ * ai-providers never derives it itself and never parses a specific format's message syntax.
65
+ */
66
+ type PlaceholderComparator = (source: string, translated: string) => PlaceholderIntegrityResult$1;
60
67
  /**
61
68
  * A batch translation request. Format- and provider-neutral: it carries no prompt,
62
69
  * model, key, or other provider-specific field. The placeholder extractor is
@@ -75,20 +82,62 @@ interface TranslateRequest {
75
82
  readonly tone?: Tone;
76
83
  /** Mandatory placeholder extractor; the output integrity check runs against it. */
77
84
  readonly extractPlaceholders: PlaceholderExtractor;
85
+ /**
86
+ * Optional branch-aware placeholder comparator. When present, the output integrity check uses it
87
+ * instead of independently extracting each side's placeholders with {@link extractPlaceholders} and
88
+ * diffing the flat lists. Absent for a format with no plural/select sub-message structure.
89
+ */
90
+ readonly comparePlaceholders?: PlaceholderComparator;
91
+ /**
92
+ * Optional cancellation signal for this batch. When aborted, an in-flight provider call rejects
93
+ * with the abort, unwrapped, instead of a {@link ProviderError} (see `guardProviderCall`). Not a
94
+ * plain-data field: it is never validated by `requestDataSchema` and never sent to a provider.
95
+ */
96
+ readonly signal?: AbortSignal;
78
97
  }
79
98
  /** Token usage, when the provider reports it. Absent for providers without tokens (DeepL). */
80
99
  interface Usage {
81
100
  readonly inputTokens: number;
82
101
  readonly outputTokens: number;
83
102
  }
84
- /** Result of a batch translation: per-key values and per-key integrity outcomes. */
103
+ /**
104
+ * Stable codes for a provider's graceful-degradation notices. These are returned DATA on a
105
+ * successful result, NOT thrown:
106
+ *
107
+ * - `FORMALITY_DOWNGRADED`: a requested formality was not applied (DeepL's free tier does not
108
+ * support it).
109
+ * - `GLOSSARY_IGNORED`: a supplied generic glossary term map was not applied (DeepL only applies a
110
+ * native glossary id, never a term map).
111
+ * - `PLACEHOLDER_UNSUPPORTED`: at least one placeholder- or ICU-bearing entry was left untranslated
112
+ * because the provider cannot preserve those tokens; such entries are withheld (absent from the
113
+ * result maps) rather than sent to the provider and mangled.
114
+ */
115
+ type ProviderNoticeCode = "FORMALITY_DOWNGRADED" | "GLOSSARY_IGNORED" | "PLACEHOLDER_UNSUPPORTED";
116
+ /**
117
+ * An observable, structured signal that something was gracefully degraded (not an
118
+ * error). Carries only a stable code and a static message, never a key or content.
119
+ * Surfaced as result data, never thrown; callers inspect it but need not treat it as a failure.
120
+ */
121
+ interface ProviderNotice {
122
+ /** The stable {@link ProviderNoticeCode} for what was degraded. */
123
+ readonly code: ProviderNoticeCode;
124
+ /** A static, safe description; never a key or translatable content. */
125
+ readonly message: string;
126
+ }
127
+ /** Result of a batch translation: per-key values, per-key integrity outcomes, and any notices. */
85
128
  interface TranslateResult {
86
129
  /** The translated value for each requested key. */
87
130
  readonly values: ReadonlyMap<string, string>;
88
131
  /** The placeholder-integrity outcome for each key (source vs translated placeholder sets). */
89
- readonly integrity: ReadonlyMap<string, PlaceholderIntegrityResult>;
132
+ readonly integrity: ReadonlyMap<string, PlaceholderIntegrityResult$1>;
90
133
  /** Token usage when the provider reports it; absent for token-less providers. */
91
134
  readonly usage?: Usage;
135
+ /**
136
+ * Graceful-degradation notices for this batch. Every provider populates this as a present array:
137
+ * DeepL reports real notices (for example `GLOSSARY_IGNORED`); an LLM provider with nothing to
138
+ * report returns an empty array rather than omitting the field.
139
+ */
140
+ readonly notices?: readonly ProviderNotice[];
92
141
  }
93
142
  /**
94
143
  * The single contract every provider implements. It is narrow enough that a machine-translation API like
@@ -147,28 +196,6 @@ interface TranslationProvider {
147
196
  * are still accepted. This drives editor completions only; the runtime schema validates.
148
197
  */
149
198
  type AnthropicModel = Anthropic.Messages.Model;
150
- /**
151
- * Stable codes for the DeepL graceful-degradation notices, DeepL only. These are returned DATA on a
152
- * successful result, NOT thrown:
153
- *
154
- * - `FORMALITY_DOWNGRADED`: a requested formality was not applied (the free tier does not support it).
155
- * - `GLOSSARY_IGNORED`: a supplied generic glossary was not applied.
156
- * - `PLACEHOLDER_UNSUPPORTED`: at least one placeholder- or ICU-bearing entry was left untranslated
157
- * because DeepL cannot preserve those tokens; such entries are withheld (absent from the result maps)
158
- * rather than sent to DeepL and mangled.
159
- */
160
- type ProviderNoticeCode = "FORMALITY_DOWNGRADED" | "GLOSSARY_IGNORED" | "PLACEHOLDER_UNSUPPORTED";
161
- /**
162
- * An observable, structured signal that something was gracefully degraded (not an
163
- * error). Carries only a stable code and a static message, never a key or content.
164
- * Surfaced as result data, never thrown; callers inspect it but need not treat it as a failure.
165
- */
166
- interface ProviderNotice {
167
- /** The stable {@link ProviderNoticeCode} for what was degraded. */
168
- readonly code: ProviderNoticeCode;
169
- /** A static, safe description; never a key or translatable content. */
170
- readonly message: string;
171
- }
172
199
 
173
200
  /**
174
201
  * The Gemini authoring model type, sourced from the installed SDK's own model union so
@@ -187,8 +214,9 @@ type OpenAiModel = OpenAI.ChatModel;
187
214
 
188
215
  /**
189
216
  * The provider section of the config: a discriminated union over the provider id, reusing each
190
- * provider's own config schema. There is no key field anywhere in this union; the provider reads its
191
- * API key from the environment at construction.
217
+ * provider's own config schema. There is no key field anywhere in this union except
218
+ * openai-compatible's optional `apiKeyEnvVar`, which names an environment variable rather than carrying
219
+ * a key value; every provider still reads its actual key from the environment at construction.
192
220
  */
193
221
  declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
194
222
  id: z.ZodLiteral<"anthropic">;
@@ -213,6 +241,14 @@ declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
213
241
  options: z.ZodObject<{
214
242
  glossaryId: z.ZodOptional<z.ZodString>;
215
243
  }, z.core.$strict>;
244
+ }, z.core.$strip>, z.ZodObject<{
245
+ id: z.ZodLiteral<"openai-compatible">;
246
+ options: z.ZodObject<{
247
+ baseUrl: z.ZodURL;
248
+ model: z.ZodString;
249
+ maxOutputTokens: z.ZodNumber;
250
+ apiKeyEnvVar: z.ZodOptional<z.ZodString>;
251
+ }, z.core.$strict>;
216
252
  }, z.core.$strip>], "id">;
217
253
  type ProviderConfig = z.infer<typeof providerConfigSchema>;
218
254
  type ProviderId = ProviderConfig["id"];
@@ -260,6 +296,14 @@ declare const verbatraConfigSchema: z.ZodObject<{
260
296
  options: z.ZodObject<{
261
297
  glossaryId: z.ZodOptional<z.ZodString>;
262
298
  }, z.core.$strict>;
299
+ }, z.core.$strip>, z.ZodObject<{
300
+ id: z.ZodLiteral<"openai-compatible">;
301
+ options: z.ZodObject<{
302
+ baseUrl: z.ZodURL;
303
+ model: z.ZodString;
304
+ maxOutputTokens: z.ZodNumber;
305
+ apiKeyEnvVar: z.ZodOptional<z.ZodString>;
306
+ }, z.core.$strict>;
263
307
  }, z.core.$strip>], "id">;
264
308
  glossary: z.ZodOptional<z.ZodUnion<readonly [z.ZodRecord<z.ZodString, z.ZodString>, z.ZodString]>>;
265
309
  tone: z.ZodOptional<z.ZodEnum<{
@@ -307,6 +351,9 @@ type AuthoringProviderVariant = {
307
351
  deepl: Extract<ProviderConfig, {
308
352
  id: "deepl";
309
353
  }>;
354
+ "openai-compatible": Extract<ProviderConfig, {
355
+ id: "openai-compatible";
356
+ }>;
310
357
  };
311
358
  /**
312
359
  * The authoring view of the whole config for a provider id: a {@link VerbatraConfigInput} whose
@@ -495,7 +542,8 @@ declare function loadConfig(options?: LoadConfigOptions): Promise<VerbatraConfig
495
542
  * - `SOURCE_UNREADABLE`: the source locale file is absent (thrown by `translate`, and by `watch` at startup).
496
543
  * - `SOURCE_INVALID`: the source locale file could not be read or parsed; wraps the adapter read error
497
544
  * (thrown by `translate`).
498
- * - `LOCK_FILE_INVALID`: the lock-file is present but corrupt or oversized (thrown by `translate`).
545
+ * - `LOCK_FILE_INVALID`: the lock-file is present but corrupt, oversized, or at an unsupported
546
+ * version (thrown by `translate`, `check`, `diff`, `exportWorkbook`, and `importWorkbook`).
499
547
  * - `LOCALE_FAILED` (NOT thrown): the fallback `code` recorded on a failed `LocaleSummary` when a
500
548
  * per-locale failure carries no string code of its own. See the surfaced-not-thrown distinction on
501
549
  * `translate`.
@@ -552,6 +600,21 @@ interface LocaleResource {
552
600
  readonly entries: ReadonlyMap<string, TranslationEntry>;
553
601
  }
554
602
 
603
+ /**
604
+ * Result of comparing source placeholders against translated-output placeholders as MULTISETS
605
+ * (counts matter). Always returned (never thrown) for an ordinary mismatch.
606
+ */
607
+ interface PlaceholderIntegrityResult {
608
+ /** True when nothing is missing or extra: the multisets match, regardless of order. */
609
+ readonly matches: boolean;
610
+ /** Occurrences present in source but absent from the translation, one per dropped occurrence. Sorted. */
611
+ readonly missing: readonly string[];
612
+ /** Occurrences present in the translation but absent from source, one per surplus occurrence. Sorted. */
613
+ readonly extra: readonly string[];
614
+ /** Same multiset present in both, but in a different order. */
615
+ readonly reordered: boolean;
616
+ }
617
+
555
618
  /**
556
619
  * The result of reading a file into core's intermediate representation.
557
620
  */
@@ -628,6 +691,18 @@ interface FormatAdapter {
628
691
  * unparseable value returns false.
629
692
  */
630
693
  validateMessage(value: string): boolean;
694
+ /**
695
+ * Optional branch-aware placeholder comparison for formats with plural/select sub-message structure.
696
+ * When present, callers use it instead of independently extracting each side's placeholders with
697
+ * {@link extractPlaceholders} and diffing the flat lists, since flattening a plural/select value loses
698
+ * which branch a placeholder came from. Absent for every non-ICU adapter, which is compared via
699
+ * `extractPlaceholders` plus `checkPlaceholders` as before.
700
+ *
701
+ * @param sourceValue - The source value.
702
+ * @param targetValue - The translated value to check against it.
703
+ * @returns The merged placeholder-integrity result across every branch. Does not throw.
704
+ */
705
+ comparePlaceholders?(sourceValue: string, targetValue: string): PlaceholderIntegrityResult;
631
706
  }
632
707
 
633
708
  /** Outcome of resolving an adapter for a file. Structured; never throws. */
@@ -876,11 +951,13 @@ interface LocaleSummary {
876
951
  */
877
952
  readonly integrityMismatches: readonly string[];
878
953
  /**
879
- * Keys withheld because the provider call itself failed (for example a revoked API key, a rate
880
- * limit, or a network timeout), as distinct from a translation that came back and failed the
881
- * placeholder-integrity check. Nothing was translated for these keys. The corresponding
882
- * {@link LocaleSummary.notices} entry carries the secret-free failure code and message. Empty in a
883
- * dry-run and empty unless a provider call threw.
954
+ * Keys withheld because nothing was translated for them this run, as distinct from a translation
955
+ * that came back and failed the placeholder-integrity check. This covers two causes: the provider
956
+ * call itself failed (for example a revoked API key, a rate limit, or a network timeout), in which
957
+ * case the corresponding {@link LocaleSummary.notices} entry carries the secret-free failure code
958
+ * and message; or the call succeeded but the key was still missing or duplicated in the response
959
+ * after the shared LLM layer's bounded reconcile repair round, in which case no notice is added.
960
+ * Empty in a dry-run.
884
961
  */
885
962
  readonly providerFailures: readonly string[];
886
963
  /**
@@ -1108,13 +1185,24 @@ interface LoadLockFileDeps {
1108
1185
  * @param input - The directory the lock-file resolves against.
1109
1186
  * @param deps - Optional composition seam (file system) for tests.
1110
1187
  * @returns The parsed lock-file, or an empty lock when none exists yet.
1111
- * @throws {@link SdkError} `LOCK_FILE_INVALID` when the lock-file is present but corrupt or oversized.
1188
+ * @throws {@link SdkError} `LOCK_FILE_INVALID` when the lock-file is present but corrupt, oversized,
1189
+ * or at an unsupported version.
1112
1190
  */
1113
1191
  declare function loadLockFile(input?: LoadLockFileInput, deps?: LoadLockFileDeps): Promise<LockFile>;
1114
1192
 
1115
1193
  /** The committed lock-file name, chosen to be obviously JSON and to not match a `*.lock` ignore rule. */
1116
1194
  declare const LOCK_FILE_NAME = "verbatra.lock.json";
1117
1195
 
1196
+ /**
1197
+ * The subset of {@link ProviderId} that `init` can scaffold: every provider with a single required
1198
+ * environment variable. "openai-compatible" is deliberately excluded: unlike every other provider it
1199
+ * has no single required environment variable, only a three-tier
1200
+ * apiKeyEnvVar/OPENAI_COMPATIBLE_API_KEY/placeholder fallback (see resolveOpenAiCompatibleKey in
1201
+ * @verbatra/ai-providers), so it does not fit the `providerEnv` table below and `init` scaffolding does
1202
+ * not offer it. This is the single source of truth for the exclusion; other packages import this type
1203
+ * rather than re-deriving `Exclude<ProviderId, "openai-compatible">` themselves.
1204
+ */
1205
+ type ScaffoldableProviderId = Exclude<ProviderId, "openai-compatible">;
1118
1206
  /**
1119
1207
  * Read-only metadata the CLI `init` scaffold derives its tables from, so the CLI never restates the
1120
1208
  * provider, env-var, model, or format truth owned by core and ai-providers.
@@ -1229,4 +1317,4 @@ interface WatchController {
1229
1317
  */
1230
1318
  declare function watch(input: WatchInput, deps?: WatchDeps): Promise<WatchController>;
1231
1319
 
1232
- export { type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleNotice, type LocaleSummary, type LockFile, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type ProviderConfig, type ProviderId, type RunSummary, type RunTranslate, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, exportWorkbook, importWorkbook, loadConfig, loadConfigWithMeta, loadLockFile, lockState, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
1320
+ export { type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleNotice, type LocaleSummary, type LockFile, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type ProviderConfig, type ProviderId, type RunSummary, type RunTranslate, type ScaffoldableProviderId, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, exportWorkbook, importWorkbook, loadConfig, loadConfigWithMeta, loadLockFile, lockState, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
package/dist/index.d.ts CHANGED
@@ -36,7 +36,7 @@ type TranslationEntry$1 = Readonly<z.infer<typeof translationEntrySchema$1>>;
36
36
  * Result of comparing source placeholders against translated-output placeholders as MULTISETS
37
37
  * (counts matter). Always returned (never thrown) for an ordinary mismatch.
38
38
  */
39
- interface PlaceholderIntegrityResult {
39
+ interface PlaceholderIntegrityResult$1 {
40
40
  /** True when nothing is missing or extra: the multisets match, regardless of order. */
41
41
  readonly matches: boolean;
42
42
  /** Occurrences present in source but absent from the translation, one per dropped occurrence. Sorted. */
@@ -57,6 +57,13 @@ type Tone = "formal" | "informal" | "neutral";
57
57
  * derives placeholders itself.
58
58
  */
59
59
  type PlaceholderExtractor = (value: string) => readonly string[];
60
+ /**
61
+ * Compares a source and translated value's placeholders directly, branch-aware, instead of
62
+ * independently extracting each side's flat placeholder list first. Supplied by the caller (the SDK, for
63
+ * a format whose adapter defines one, for example ICU plural/select) so it matches the entries' format;
64
+ * ai-providers never derives it itself and never parses a specific format's message syntax.
65
+ */
66
+ type PlaceholderComparator = (source: string, translated: string) => PlaceholderIntegrityResult$1;
60
67
  /**
61
68
  * A batch translation request. Format- and provider-neutral: it carries no prompt,
62
69
  * model, key, or other provider-specific field. The placeholder extractor is
@@ -75,20 +82,62 @@ interface TranslateRequest {
75
82
  readonly tone?: Tone;
76
83
  /** Mandatory placeholder extractor; the output integrity check runs against it. */
77
84
  readonly extractPlaceholders: PlaceholderExtractor;
85
+ /**
86
+ * Optional branch-aware placeholder comparator. When present, the output integrity check uses it
87
+ * instead of independently extracting each side's placeholders with {@link extractPlaceholders} and
88
+ * diffing the flat lists. Absent for a format with no plural/select sub-message structure.
89
+ */
90
+ readonly comparePlaceholders?: PlaceholderComparator;
91
+ /**
92
+ * Optional cancellation signal for this batch. When aborted, an in-flight provider call rejects
93
+ * with the abort, unwrapped, instead of a {@link ProviderError} (see `guardProviderCall`). Not a
94
+ * plain-data field: it is never validated by `requestDataSchema` and never sent to a provider.
95
+ */
96
+ readonly signal?: AbortSignal;
78
97
  }
79
98
  /** Token usage, when the provider reports it. Absent for providers without tokens (DeepL). */
80
99
  interface Usage {
81
100
  readonly inputTokens: number;
82
101
  readonly outputTokens: number;
83
102
  }
84
- /** Result of a batch translation: per-key values and per-key integrity outcomes. */
103
+ /**
104
+ * Stable codes for a provider's graceful-degradation notices. These are returned DATA on a
105
+ * successful result, NOT thrown:
106
+ *
107
+ * - `FORMALITY_DOWNGRADED`: a requested formality was not applied (DeepL's free tier does not
108
+ * support it).
109
+ * - `GLOSSARY_IGNORED`: a supplied generic glossary term map was not applied (DeepL only applies a
110
+ * native glossary id, never a term map).
111
+ * - `PLACEHOLDER_UNSUPPORTED`: at least one placeholder- or ICU-bearing entry was left untranslated
112
+ * because the provider cannot preserve those tokens; such entries are withheld (absent from the
113
+ * result maps) rather than sent to the provider and mangled.
114
+ */
115
+ type ProviderNoticeCode = "FORMALITY_DOWNGRADED" | "GLOSSARY_IGNORED" | "PLACEHOLDER_UNSUPPORTED";
116
+ /**
117
+ * An observable, structured signal that something was gracefully degraded (not an
118
+ * error). Carries only a stable code and a static message, never a key or content.
119
+ * Surfaced as result data, never thrown; callers inspect it but need not treat it as a failure.
120
+ */
121
+ interface ProviderNotice {
122
+ /** The stable {@link ProviderNoticeCode} for what was degraded. */
123
+ readonly code: ProviderNoticeCode;
124
+ /** A static, safe description; never a key or translatable content. */
125
+ readonly message: string;
126
+ }
127
+ /** Result of a batch translation: per-key values, per-key integrity outcomes, and any notices. */
85
128
  interface TranslateResult {
86
129
  /** The translated value for each requested key. */
87
130
  readonly values: ReadonlyMap<string, string>;
88
131
  /** The placeholder-integrity outcome for each key (source vs translated placeholder sets). */
89
- readonly integrity: ReadonlyMap<string, PlaceholderIntegrityResult>;
132
+ readonly integrity: ReadonlyMap<string, PlaceholderIntegrityResult$1>;
90
133
  /** Token usage when the provider reports it; absent for token-less providers. */
91
134
  readonly usage?: Usage;
135
+ /**
136
+ * Graceful-degradation notices for this batch. Every provider populates this as a present array:
137
+ * DeepL reports real notices (for example `GLOSSARY_IGNORED`); an LLM provider with nothing to
138
+ * report returns an empty array rather than omitting the field.
139
+ */
140
+ readonly notices?: readonly ProviderNotice[];
92
141
  }
93
142
  /**
94
143
  * The single contract every provider implements. It is narrow enough that a machine-translation API like
@@ -147,28 +196,6 @@ interface TranslationProvider {
147
196
  * are still accepted. This drives editor completions only; the runtime schema validates.
148
197
  */
149
198
  type AnthropicModel = Anthropic.Messages.Model;
150
- /**
151
- * Stable codes for the DeepL graceful-degradation notices, DeepL only. These are returned DATA on a
152
- * successful result, NOT thrown:
153
- *
154
- * - `FORMALITY_DOWNGRADED`: a requested formality was not applied (the free tier does not support it).
155
- * - `GLOSSARY_IGNORED`: a supplied generic glossary was not applied.
156
- * - `PLACEHOLDER_UNSUPPORTED`: at least one placeholder- or ICU-bearing entry was left untranslated
157
- * because DeepL cannot preserve those tokens; such entries are withheld (absent from the result maps)
158
- * rather than sent to DeepL and mangled.
159
- */
160
- type ProviderNoticeCode = "FORMALITY_DOWNGRADED" | "GLOSSARY_IGNORED" | "PLACEHOLDER_UNSUPPORTED";
161
- /**
162
- * An observable, structured signal that something was gracefully degraded (not an
163
- * error). Carries only a stable code and a static message, never a key or content.
164
- * Surfaced as result data, never thrown; callers inspect it but need not treat it as a failure.
165
- */
166
- interface ProviderNotice {
167
- /** The stable {@link ProviderNoticeCode} for what was degraded. */
168
- readonly code: ProviderNoticeCode;
169
- /** A static, safe description; never a key or translatable content. */
170
- readonly message: string;
171
- }
172
199
 
173
200
  /**
174
201
  * The Gemini authoring model type, sourced from the installed SDK's own model union so
@@ -187,8 +214,9 @@ type OpenAiModel = OpenAI.ChatModel;
187
214
 
188
215
  /**
189
216
  * The provider section of the config: a discriminated union over the provider id, reusing each
190
- * provider's own config schema. There is no key field anywhere in this union; the provider reads its
191
- * API key from the environment at construction.
217
+ * provider's own config schema. There is no key field anywhere in this union except
218
+ * openai-compatible's optional `apiKeyEnvVar`, which names an environment variable rather than carrying
219
+ * a key value; every provider still reads its actual key from the environment at construction.
192
220
  */
193
221
  declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
194
222
  id: z.ZodLiteral<"anthropic">;
@@ -213,6 +241,14 @@ declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
213
241
  options: z.ZodObject<{
214
242
  glossaryId: z.ZodOptional<z.ZodString>;
215
243
  }, z.core.$strict>;
244
+ }, z.core.$strip>, z.ZodObject<{
245
+ id: z.ZodLiteral<"openai-compatible">;
246
+ options: z.ZodObject<{
247
+ baseUrl: z.ZodURL;
248
+ model: z.ZodString;
249
+ maxOutputTokens: z.ZodNumber;
250
+ apiKeyEnvVar: z.ZodOptional<z.ZodString>;
251
+ }, z.core.$strict>;
216
252
  }, z.core.$strip>], "id">;
217
253
  type ProviderConfig = z.infer<typeof providerConfigSchema>;
218
254
  type ProviderId = ProviderConfig["id"];
@@ -260,6 +296,14 @@ declare const verbatraConfigSchema: z.ZodObject<{
260
296
  options: z.ZodObject<{
261
297
  glossaryId: z.ZodOptional<z.ZodString>;
262
298
  }, z.core.$strict>;
299
+ }, z.core.$strip>, z.ZodObject<{
300
+ id: z.ZodLiteral<"openai-compatible">;
301
+ options: z.ZodObject<{
302
+ baseUrl: z.ZodURL;
303
+ model: z.ZodString;
304
+ maxOutputTokens: z.ZodNumber;
305
+ apiKeyEnvVar: z.ZodOptional<z.ZodString>;
306
+ }, z.core.$strict>;
263
307
  }, z.core.$strip>], "id">;
264
308
  glossary: z.ZodOptional<z.ZodUnion<readonly [z.ZodRecord<z.ZodString, z.ZodString>, z.ZodString]>>;
265
309
  tone: z.ZodOptional<z.ZodEnum<{
@@ -307,6 +351,9 @@ type AuthoringProviderVariant = {
307
351
  deepl: Extract<ProviderConfig, {
308
352
  id: "deepl";
309
353
  }>;
354
+ "openai-compatible": Extract<ProviderConfig, {
355
+ id: "openai-compatible";
356
+ }>;
310
357
  };
311
358
  /**
312
359
  * The authoring view of the whole config for a provider id: a {@link VerbatraConfigInput} whose
@@ -495,7 +542,8 @@ declare function loadConfig(options?: LoadConfigOptions): Promise<VerbatraConfig
495
542
  * - `SOURCE_UNREADABLE`: the source locale file is absent (thrown by `translate`, and by `watch` at startup).
496
543
  * - `SOURCE_INVALID`: the source locale file could not be read or parsed; wraps the adapter read error
497
544
  * (thrown by `translate`).
498
- * - `LOCK_FILE_INVALID`: the lock-file is present but corrupt or oversized (thrown by `translate`).
545
+ * - `LOCK_FILE_INVALID`: the lock-file is present but corrupt, oversized, or at an unsupported
546
+ * version (thrown by `translate`, `check`, `diff`, `exportWorkbook`, and `importWorkbook`).
499
547
  * - `LOCALE_FAILED` (NOT thrown): the fallback `code` recorded on a failed `LocaleSummary` when a
500
548
  * per-locale failure carries no string code of its own. See the surfaced-not-thrown distinction on
501
549
  * `translate`.
@@ -552,6 +600,21 @@ interface LocaleResource {
552
600
  readonly entries: ReadonlyMap<string, TranslationEntry>;
553
601
  }
554
602
 
603
+ /**
604
+ * Result of comparing source placeholders against translated-output placeholders as MULTISETS
605
+ * (counts matter). Always returned (never thrown) for an ordinary mismatch.
606
+ */
607
+ interface PlaceholderIntegrityResult {
608
+ /** True when nothing is missing or extra: the multisets match, regardless of order. */
609
+ readonly matches: boolean;
610
+ /** Occurrences present in source but absent from the translation, one per dropped occurrence. Sorted. */
611
+ readonly missing: readonly string[];
612
+ /** Occurrences present in the translation but absent from source, one per surplus occurrence. Sorted. */
613
+ readonly extra: readonly string[];
614
+ /** Same multiset present in both, but in a different order. */
615
+ readonly reordered: boolean;
616
+ }
617
+
555
618
  /**
556
619
  * The result of reading a file into core's intermediate representation.
557
620
  */
@@ -628,6 +691,18 @@ interface FormatAdapter {
628
691
  * unparseable value returns false.
629
692
  */
630
693
  validateMessage(value: string): boolean;
694
+ /**
695
+ * Optional branch-aware placeholder comparison for formats with plural/select sub-message structure.
696
+ * When present, callers use it instead of independently extracting each side's placeholders with
697
+ * {@link extractPlaceholders} and diffing the flat lists, since flattening a plural/select value loses
698
+ * which branch a placeholder came from. Absent for every non-ICU adapter, which is compared via
699
+ * `extractPlaceholders` plus `checkPlaceholders` as before.
700
+ *
701
+ * @param sourceValue - The source value.
702
+ * @param targetValue - The translated value to check against it.
703
+ * @returns The merged placeholder-integrity result across every branch. Does not throw.
704
+ */
705
+ comparePlaceholders?(sourceValue: string, targetValue: string): PlaceholderIntegrityResult;
631
706
  }
632
707
 
633
708
  /** Outcome of resolving an adapter for a file. Structured; never throws. */
@@ -876,11 +951,13 @@ interface LocaleSummary {
876
951
  */
877
952
  readonly integrityMismatches: readonly string[];
878
953
  /**
879
- * Keys withheld because the provider call itself failed (for example a revoked API key, a rate
880
- * limit, or a network timeout), as distinct from a translation that came back and failed the
881
- * placeholder-integrity check. Nothing was translated for these keys. The corresponding
882
- * {@link LocaleSummary.notices} entry carries the secret-free failure code and message. Empty in a
883
- * dry-run and empty unless a provider call threw.
954
+ * Keys withheld because nothing was translated for them this run, as distinct from a translation
955
+ * that came back and failed the placeholder-integrity check. This covers two causes: the provider
956
+ * call itself failed (for example a revoked API key, a rate limit, or a network timeout), in which
957
+ * case the corresponding {@link LocaleSummary.notices} entry carries the secret-free failure code
958
+ * and message; or the call succeeded but the key was still missing or duplicated in the response
959
+ * after the shared LLM layer's bounded reconcile repair round, in which case no notice is added.
960
+ * Empty in a dry-run.
884
961
  */
885
962
  readonly providerFailures: readonly string[];
886
963
  /**
@@ -1108,13 +1185,24 @@ interface LoadLockFileDeps {
1108
1185
  * @param input - The directory the lock-file resolves against.
1109
1186
  * @param deps - Optional composition seam (file system) for tests.
1110
1187
  * @returns The parsed lock-file, or an empty lock when none exists yet.
1111
- * @throws {@link SdkError} `LOCK_FILE_INVALID` when the lock-file is present but corrupt or oversized.
1188
+ * @throws {@link SdkError} `LOCK_FILE_INVALID` when the lock-file is present but corrupt, oversized,
1189
+ * or at an unsupported version.
1112
1190
  */
1113
1191
  declare function loadLockFile(input?: LoadLockFileInput, deps?: LoadLockFileDeps): Promise<LockFile>;
1114
1192
 
1115
1193
  /** The committed lock-file name, chosen to be obviously JSON and to not match a `*.lock` ignore rule. */
1116
1194
  declare const LOCK_FILE_NAME = "verbatra.lock.json";
1117
1195
 
1196
+ /**
1197
+ * The subset of {@link ProviderId} that `init` can scaffold: every provider with a single required
1198
+ * environment variable. "openai-compatible" is deliberately excluded: unlike every other provider it
1199
+ * has no single required environment variable, only a three-tier
1200
+ * apiKeyEnvVar/OPENAI_COMPATIBLE_API_KEY/placeholder fallback (see resolveOpenAiCompatibleKey in
1201
+ * @verbatra/ai-providers), so it does not fit the `providerEnv` table below and `init` scaffolding does
1202
+ * not offer it. This is the single source of truth for the exclusion; other packages import this type
1203
+ * rather than re-deriving `Exclude<ProviderId, "openai-compatible">` themselves.
1204
+ */
1205
+ type ScaffoldableProviderId = Exclude<ProviderId, "openai-compatible">;
1118
1206
  /**
1119
1207
  * Read-only metadata the CLI `init` scaffold derives its tables from, so the CLI never restates the
1120
1208
  * provider, env-var, model, or format truth owned by core and ai-providers.
@@ -1229,4 +1317,4 @@ interface WatchController {
1229
1317
  */
1230
1318
  declare function watch(input: WatchInput, deps?: WatchDeps): Promise<WatchController>;
1231
1319
 
1232
- export { type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleNotice, type LocaleSummary, type LockFile, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type ProviderConfig, type ProviderId, type RunSummary, type RunTranslate, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, exportWorkbook, importWorkbook, loadConfig, loadConfigWithMeta, loadLockFile, lockState, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
1320
+ export { type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleNotice, type LocaleSummary, type LockFile, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type ProviderConfig, type ProviderId, type RunSummary, type RunTranslate, type ScaffoldableProviderId, SdkError, type SdkErrorCode, type SdkFs, type SdkNotice, type SdkNoticeCode, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, exportWorkbook, importWorkbook, loadConfig, loadConfigWithMeta, loadLockFile, lockState, scaffoldingMetadata, translate, verbatraConfigSchema, watch };