@verbatra/sdk 0.5.0-next.3 → 0.5.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 +13 -1
- package/dist/index.cjs +1246 -238
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +573 -45
- package/dist/index.d.ts +573 -45
- package/dist/index.js +1238 -237
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.d.ts
CHANGED
|
@@ -3,19 +3,6 @@ import Anthropic from '@anthropic-ai/sdk';
|
|
|
3
3
|
import { Interactions } from '@google/genai';
|
|
4
4
|
import OpenAI from 'openai';
|
|
5
5
|
|
|
6
|
-
/** Zod schema accepting exactly one of {@link SUPPORTED_FORMATS}. */
|
|
7
|
-
declare const supportedFormatSchema$1: z.ZodEnum<{
|
|
8
|
-
"i18next-json": "i18next-json";
|
|
9
|
-
"vue-i18n-json": "vue-i18n-json";
|
|
10
|
-
"next-intl-json": "next-intl-json";
|
|
11
|
-
"ngx-translate-json": "ngx-translate-json";
|
|
12
|
-
xliff: "xliff";
|
|
13
|
-
yaml: "yaml";
|
|
14
|
-
arb: "arb";
|
|
15
|
-
}>;
|
|
16
|
-
/** One of the supported source formats; a member of {@link SUPPORTED_FORMATS}. */
|
|
17
|
-
type SupportedFormat$1 = z.infer<typeof supportedFormatSchema$1>;
|
|
18
|
-
|
|
19
6
|
/**
|
|
20
7
|
* A single, format-neutral translation unit. Placeholders are supplied already
|
|
21
8
|
* extracted; core never derives them from the value.
|
|
@@ -124,6 +111,24 @@ interface ProviderNotice {
|
|
|
124
111
|
/** A static, safe description; never a key or translatable content. */
|
|
125
112
|
readonly message: string;
|
|
126
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Stable codes for a derived, per-key "needs review" signal. This is verbatra's own computed
|
|
116
|
+
* assessment, never a raw model self-score: four are recomputable from plain source/translated
|
|
117
|
+
* values (see the shared heuristic function), and `PROVIDER_DEGRADED` is applied by the caller from
|
|
118
|
+
* a provider notice.
|
|
119
|
+
*
|
|
120
|
+
* - `LENGTH_RATIO_OUTLIER`: the translated value's length is far shorter or longer than the source's.
|
|
121
|
+
* - `EQUALS_SOURCE`: the translated value equals the source value verbatim.
|
|
122
|
+
* - `GLOSSARY_TERM_MISSED`: a configured glossary target term did not appear in the translation.
|
|
123
|
+
* - `INTEGRITY_REORDERED`: the placeholder set matched but landed in a different order.
|
|
124
|
+
* - `PROVIDER_DEGRADED`: the batch this key came from carried a graceful-degradation notice.
|
|
125
|
+
*/
|
|
126
|
+
type ReviewReasonCode = "LENGTH_RATIO_OUTLIER" | "EQUALS_SOURCE" | "GLOSSARY_TERM_MISSED" | "INTEGRITY_REORDERED" | "PROVIDER_DEGRADED";
|
|
127
|
+
/** A key flagged for human review, carrying every reason code that applies. */
|
|
128
|
+
interface ReviewFlag {
|
|
129
|
+
readonly status: "review";
|
|
130
|
+
readonly reasons: readonly ReviewReasonCode[];
|
|
131
|
+
}
|
|
127
132
|
/** Result of a batch translation: per-key values, per-key integrity outcomes, and any notices. */
|
|
128
133
|
interface TranslateResult {
|
|
129
134
|
/** The translated value for each requested key. */
|
|
@@ -138,6 +143,11 @@ interface TranslateResult {
|
|
|
138
143
|
* report returns an empty array rather than omitting the field.
|
|
139
144
|
*/
|
|
140
145
|
readonly notices?: readonly ProviderNotice[];
|
|
146
|
+
/**
|
|
147
|
+
* Derived per-key review flags for this batch. A key absent from the map is implicitly "ok"; a
|
|
148
|
+
* present key carries one or more {@link ReviewReasonCode}s. Optional and additive.
|
|
149
|
+
*/
|
|
150
|
+
readonly reviewFlags?: ReadonlyMap<string, ReviewFlag>;
|
|
141
151
|
}
|
|
142
152
|
/**
|
|
143
153
|
* The single contract every provider implements. It is narrow enough that a machine-translation API like
|
|
@@ -212,6 +222,19 @@ type GeminiModel = Interactions.Model;
|
|
|
212
222
|
*/
|
|
213
223
|
type OpenAiModel = OpenAI.ChatModel;
|
|
214
224
|
|
|
225
|
+
/** Zod schema accepting exactly one of {@link SUPPORTED_FORMATS}. */
|
|
226
|
+
declare const supportedFormatSchema$1: z.ZodEnum<{
|
|
227
|
+
"i18next-json": "i18next-json";
|
|
228
|
+
"vue-i18n-json": "vue-i18n-json";
|
|
229
|
+
"next-intl-json": "next-intl-json";
|
|
230
|
+
"ngx-translate-json": "ngx-translate-json";
|
|
231
|
+
xliff: "xliff";
|
|
232
|
+
yaml: "yaml";
|
|
233
|
+
arb: "arb";
|
|
234
|
+
}>;
|
|
235
|
+
/** One of the supported source formats; a member of {@link SUPPORTED_FORMATS}. */
|
|
236
|
+
type SupportedFormat$1 = z.infer<typeof supportedFormatSchema$1>;
|
|
237
|
+
|
|
215
238
|
/**
|
|
216
239
|
* The provider section of the config: a discriminated union over the provider id, reusing each
|
|
217
240
|
* provider's own config schema. There is no key field anywhere in this union except
|
|
@@ -250,7 +273,9 @@ declare const providerConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
250
273
|
apiKeyEnvVar: z.ZodOptional<z.ZodString>;
|
|
251
274
|
}, z.core.$strict>;
|
|
252
275
|
}, z.core.$strip>], "id">;
|
|
276
|
+
/** One validated provider section: the provider id plus that provider's own options. */
|
|
253
277
|
type ProviderConfig = z.infer<typeof providerConfigSchema>;
|
|
278
|
+
/** The closed set of configurable provider ids. */
|
|
254
279
|
type ProviderId = ProviderConfig["id"];
|
|
255
280
|
|
|
256
281
|
/**
|
|
@@ -314,6 +339,11 @@ declare const verbatraConfigSchema: z.ZodObject<{
|
|
|
314
339
|
prune: z.ZodOptional<z.ZodBoolean>;
|
|
315
340
|
generatePlurals: z.ZodOptional<z.ZodBoolean>;
|
|
316
341
|
maxBatchSize: z.ZodOptional<z.ZodNumber>;
|
|
342
|
+
maxTokens: z.ZodOptional<z.ZodNumber>;
|
|
343
|
+
budgetBehavior: z.ZodOptional<z.ZodEnum<{
|
|
344
|
+
warn: "warn";
|
|
345
|
+
stop: "stop";
|
|
346
|
+
}>>;
|
|
317
347
|
}, z.core.$strict>;
|
|
318
348
|
/**
|
|
319
349
|
* The as-authored (or as-parsed) shape of the verbatra configuration, straight from the schema: `glossary`
|
|
@@ -332,7 +362,14 @@ type VerbatraConfig = Omit<VerbatraConfigInput, "glossary"> & {
|
|
|
332
362
|
glossary?: Readonly<Record<string, string>>;
|
|
333
363
|
};
|
|
334
364
|
|
|
365
|
+
/**
|
|
366
|
+
* A provider's known model literals, with the open `string` arm stripped, so the authoring field
|
|
367
|
+
* accepts only that provider's models. Static authoring aid only; the runtime schema stays a
|
|
368
|
+
* non-empty string, so a brand-new model the installed SDK does not yet list is flagged but still
|
|
369
|
+
* runs.
|
|
370
|
+
*/
|
|
335
371
|
type KnownModels<M extends string> = M extends string ? (string extends M ? never : M) : never;
|
|
372
|
+
/** One provider's config variant with `options.model` narrowed to that provider's known literals. */
|
|
336
373
|
type AuthoringVariant<Id extends ProviderId, M extends string> = Extract<ProviderConfig, {
|
|
337
374
|
id: Id;
|
|
338
375
|
}> extends infer Variant ? Variant extends {
|
|
@@ -344,6 +381,12 @@ type AuthoringVariant<Id extends ProviderId, M extends string> = Extract<Provide
|
|
|
344
381
|
model: KnownModels<M>;
|
|
345
382
|
};
|
|
346
383
|
} : never : never;
|
|
384
|
+
/**
|
|
385
|
+
* The authoring view of one provider variant, keyed by id. LLM providers with a known SDK model
|
|
386
|
+
* union restrict `options.model` to that provider's literals. DeepL has no model field and
|
|
387
|
+
* openai-compatible's model is whatever the local server exposes (no known-model list to restrict
|
|
388
|
+
* against), so both are carried through unchanged.
|
|
389
|
+
*/
|
|
347
390
|
type AuthoringProviderVariant = {
|
|
348
391
|
anthropic: AuthoringVariant<"anthropic", AnthropicModel>;
|
|
349
392
|
openai: AuthoringVariant<"openai", OpenAiModel>;
|
|
@@ -429,9 +472,19 @@ interface SdkFs {
|
|
|
429
472
|
writeFile(path: string, data: string): Promise<void>;
|
|
430
473
|
/** Write raw bytes atomically (temp file, then rename over the target). Used for the workbook. */
|
|
431
474
|
writeBytes(path: string, data: Uint8Array): Promise<void>;
|
|
475
|
+
/**
|
|
476
|
+
* Atomically create a file only if it does not already exist (O_EXCL semantics), creating any
|
|
477
|
+
* missing parent directory first. Returns true if this call created the file, false if a file
|
|
478
|
+
* was already present (no write performed). The backbone of {@link withLocaleWriteLock}'s
|
|
479
|
+
* exclusive-create lock: the parent-directory creation lives here so that primitive never touches
|
|
480
|
+
* `node:fs` directly and a fake `SdkFs` in tests never needs a real directory to exist.
|
|
481
|
+
*/
|
|
482
|
+
createExclusive(path: string, data: string): Promise<boolean>;
|
|
483
|
+
/** Delete a file if present; a no-op if it is already absent. */
|
|
484
|
+
deleteFile(path: string): Promise<void>;
|
|
432
485
|
}
|
|
433
486
|
|
|
434
|
-
/** Where a config's resolved glossary came from,
|
|
487
|
+
/** Where a config's resolved glossary came from: absent, inline, or a file at the recorded path. */
|
|
435
488
|
type GlossaryProvenance = {
|
|
436
489
|
readonly source: "none";
|
|
437
490
|
} | {
|
|
@@ -533,22 +586,35 @@ declare function loadConfig(options?: LoadConfigOptions): Promise<VerbatraConfig
|
|
|
533
586
|
*
|
|
534
587
|
* - `CONFIG_NOT_FOUND`: no config was found by search, or an explicit `configPath` does not exist
|
|
535
588
|
* (thrown by `loadConfig`).
|
|
536
|
-
* - `CONFIG_INVALID`: a config was found but is unparseable or fails validation
|
|
537
|
-
*
|
|
538
|
-
* - `
|
|
539
|
-
*
|
|
540
|
-
* - `
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
* - `
|
|
544
|
-
*
|
|
589
|
+
* - `CONFIG_INVALID`: a config was found but is unparseable or fails validation, or its glossary
|
|
590
|
+
* file path could not be resolved (thrown by `loadConfig`).
|
|
591
|
+
* - `UNKNOWN_FORMAT`: no adapter is registered for the configured format (thrown by every entry
|
|
592
|
+
* point that selects an adapter, before any file is read).
|
|
593
|
+
* - `UNKNOWN_LOCALE`: a requested locale is not among the configured target locales (thrown via
|
|
594
|
+
* the shared locale selection by `check`, `diff`, `keyIntegrity`, `lockState`, `exportWorkbook`,
|
|
595
|
+
* `keyValue`, `editEntry`, and `retranslateEntry`).
|
|
596
|
+
* - `UNKNOWN_KEY`: a requested key is not among the source resource's own keys (thrown by
|
|
597
|
+
* `keyValue`, `editEntry`, and `retranslateEntry`).
|
|
598
|
+
* - `PROVIDER_CONSTRUCTION_FAILED`: the provider factory threw; wraps the provider's own error,
|
|
599
|
+
* including a missing `*_API_KEY` reported as `MISSING_API_KEY` (thrown by non-dry-run
|
|
600
|
+
* `translate` and by `retranslateEntry`).
|
|
601
|
+
* - `SOURCE_UNREADABLE`: the source locale file is absent (thrown by every entry point that reads
|
|
602
|
+
* the source, and by `watch` at startup).
|
|
603
|
+
* - `SOURCE_INVALID`: the source locale file could not be read or parsed; wraps the adapter read
|
|
604
|
+
* error (thrown by every entry point that reads the source).
|
|
545
605
|
* - `LOCK_FILE_INVALID`: the lock-file is present but corrupt, oversized, or at an unsupported
|
|
546
|
-
* version (thrown
|
|
606
|
+
* version (thrown wherever the lock-file is read or updated: `translate`, `check`, `diff`,
|
|
607
|
+
* `keyIntegrity`, `lockState`, `loadLockFile`, `exportWorkbook`, `importWorkbook`, `editEntry`,
|
|
608
|
+
* and `retranslateEntry`).
|
|
609
|
+
* - `LOCK_CONTENDED`: a locale's write lock (`withLocaleWriteLock`) could not be acquired before
|
|
610
|
+
* its timeout elapsed, because another process is holding it (or an orphaned lock file was left
|
|
611
|
+
* behind by a killed process); the message names the lock file's path (thrown by `translate`,
|
|
612
|
+
* `importWorkbook`, `editEntry`, and `retranslateEntry`).
|
|
547
613
|
* - `LOCALE_FAILED` (NOT thrown): the fallback `code` recorded on a failed `LocaleSummary` when a
|
|
548
|
-
* per-locale failure carries no string code of its own. See the surfaced-not-thrown distinction
|
|
549
|
-
* `translate`.
|
|
614
|
+
* per-locale failure carries no string code of its own. See the surfaced-not-thrown distinction
|
|
615
|
+
* on `translate`.
|
|
550
616
|
*/
|
|
551
|
-
type SdkErrorCode = "CONFIG_NOT_FOUND" | "CONFIG_INVALID" | "UNKNOWN_FORMAT" | "UNKNOWN_LOCALE" | "PROVIDER_CONSTRUCTION_FAILED" | "SOURCE_UNREADABLE" | "SOURCE_INVALID" | "LOCK_FILE_INVALID" | "LOCALE_FAILED";
|
|
617
|
+
type SdkErrorCode = "CONFIG_NOT_FOUND" | "CONFIG_INVALID" | "UNKNOWN_FORMAT" | "UNKNOWN_LOCALE" | "UNKNOWN_KEY" | "PROVIDER_CONSTRUCTION_FAILED" | "SOURCE_UNREADABLE" | "SOURCE_INVALID" | "LOCK_FILE_INVALID" | "LOCK_CONTENDED" | "LOCALE_FAILED";
|
|
552
618
|
/** The single structured error the SDK throws or records. Never carries a secret. */
|
|
553
619
|
declare class SdkError extends Error {
|
|
554
620
|
/** The stable {@link SdkErrorCode} for this failure; branch on this, not the message. */
|
|
@@ -623,6 +689,13 @@ interface ReadResult {
|
|
|
623
689
|
readonly resource: LocaleResource;
|
|
624
690
|
/** Keys whose values are invalid for the format's message syntax. Empty for non-ICU formats. */
|
|
625
691
|
readonly invalidIcuKeys: readonly string[];
|
|
692
|
+
/**
|
|
693
|
+
* Dotted paths of leaves that were present in the source but excluded because they are not
|
|
694
|
+
* strings (a stray number, boolean, or null). These are never translated, hashed, diffed, or
|
|
695
|
+
* checked for placeholder or ICU integrity, and are not written back if the file is later
|
|
696
|
+
* rewritten. Empty for a file with no such leaves.
|
|
697
|
+
*/
|
|
698
|
+
readonly excludedLeafPaths: readonly string[];
|
|
626
699
|
}
|
|
627
700
|
/**
|
|
628
701
|
* The contract every format adapter implements. A new format attaches by implementing this interface
|
|
@@ -740,6 +813,11 @@ declare class AdapterRegistry {
|
|
|
740
813
|
register(adapter: FormatAdapter): this;
|
|
741
814
|
private formats;
|
|
742
815
|
private resolveByFormat;
|
|
816
|
+
/**
|
|
817
|
+
* Resolve by asking every registered adapter's `canHandle`. Several adapters can claim one file
|
|
818
|
+
* (all JSON adapters claim `.json`), so more than one match is reported as `ambiguous` rather
|
|
819
|
+
* than guessed at.
|
|
820
|
+
*/
|
|
743
821
|
private resolveByDetection;
|
|
744
822
|
/**
|
|
745
823
|
* Resolve the adapter for a file, by explicit format when given, otherwise by detection.
|
|
@@ -787,10 +865,11 @@ interface CheckDeps {
|
|
|
787
865
|
readonly fs?: SdkFs;
|
|
788
866
|
}
|
|
789
867
|
/**
|
|
790
|
-
*
|
|
791
|
-
* file, or touching the lock. Each locale is reported as counts only (missing, stale,
|
|
792
|
-
* a locale is `inSync` when nothing is missing or stale, and the top-level `inSync` is
|
|
793
|
-
* every checked locale is. Orphaned keys and integrity are not reported, since they
|
|
868
|
+
* Reports how many keys are missing or stale per target locale, without calling a provider, writing
|
|
869
|
+
* any file, or touching the lock. Each locale is reported as counts only (missing, stale,
|
|
870
|
+
* up-to-date); a locale is `inSync` when nothing is missing or stale, and the top-level `inSync` is
|
|
871
|
+
* true only when every checked locale is. Orphaned keys and integrity are not reported, since they
|
|
872
|
+
* concern a write; use {@link diff} for the key lists.
|
|
794
873
|
*
|
|
795
874
|
* @param input - The validated config and which locales to check.
|
|
796
875
|
* @param deps - Optional composition seams (registry, file system) for tests.
|
|
@@ -836,11 +915,11 @@ interface DiffDeps {
|
|
|
836
915
|
readonly fs?: SdkFs;
|
|
837
916
|
}
|
|
838
917
|
/**
|
|
839
|
-
*
|
|
840
|
-
* without calling a provider, writing any file, or touching the lock.
|
|
841
|
-
* {@link check}. A locale's `hasPendingChanges` is driven by missing or changed only;
|
|
842
|
-
* reported but do not flip it, since a default `translate` run does not prune.
|
|
843
|
-
* `hasPendingChanges` is true when any checked locale has pending changes.
|
|
918
|
+
* Reports the exact pending change per target locale as three key lists (missing, changed,
|
|
919
|
+
* orphaned), without calling a provider, writing any file, or touching the lock. The detailed
|
|
920
|
+
* sibling of {@link check}. A locale's `hasPendingChanges` is driven by missing or changed only;
|
|
921
|
+
* orphaned keys are reported but do not flip it, since a default `translate` run does not prune.
|
|
922
|
+
* The top-level `hasPendingChanges` is true when any checked locale has pending changes.
|
|
844
923
|
*
|
|
845
924
|
* @param input - The validated config and which locales to diff.
|
|
846
925
|
* @param deps - Optional composition seams (registry, file system) for tests.
|
|
@@ -851,6 +930,238 @@ interface DiffDeps {
|
|
|
851
930
|
*/
|
|
852
931
|
declare function diff(input: DiffInput, deps?: DiffDeps): Promise<DiffSummary>;
|
|
853
932
|
|
|
933
|
+
/** Input for {@link editEntry}: the validated config, exactly one target locale/key pair, and the human-typed replacement value. */
|
|
934
|
+
interface EditEntryInput {
|
|
935
|
+
/** The validated configuration (typically from {@link loadConfig}). */
|
|
936
|
+
readonly config: VerbatraConfig;
|
|
937
|
+
/** Directory the file pattern and lock-file resolve against; defaults to cwd. */
|
|
938
|
+
readonly cwd?: string;
|
|
939
|
+
/** The target locale to write the correction into. Must be a configured target locale. */
|
|
940
|
+
readonly locale: string;
|
|
941
|
+
/** The source key being corrected. Must exist in the source resource. */
|
|
942
|
+
readonly key: string;
|
|
943
|
+
/** The human-typed replacement value. */
|
|
944
|
+
readonly value: string;
|
|
945
|
+
}
|
|
946
|
+
/** Composition seam for {@link editEntry}: inject a registry and a file system for tests. No provider builder: this seam never calls a provider. */
|
|
947
|
+
interface EditEntryDeps {
|
|
948
|
+
readonly adapterRegistry?: AdapterRegistry;
|
|
949
|
+
readonly fs?: SdkFs;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* The two-armed result of one edit attempt: acceptance carries the newly written value; a
|
|
953
|
+
* rejection carries the candidate value and which {@link gateCandidateValue} check failed it,
|
|
954
|
+
* with nothing written. Deliberately has no review-reasons field: a human-typed correction never
|
|
955
|
+
* touches a provider, so there is no provider-derived review signal to report.
|
|
956
|
+
*/
|
|
957
|
+
type EditEntryResult = {
|
|
958
|
+
readonly accepted: true;
|
|
959
|
+
readonly value: string;
|
|
960
|
+
} | {
|
|
961
|
+
readonly accepted: false;
|
|
962
|
+
readonly reason: "placeholder" | "icu";
|
|
963
|
+
readonly value: string;
|
|
964
|
+
};
|
|
965
|
+
/**
|
|
966
|
+
* Writes exactly one human-typed correction for exactly one target locale, gated through the
|
|
967
|
+
* shared {@link gateCandidateValue} before anything reaches disk. On acceptance, writes the target
|
|
968
|
+
* locale file (merging just this one key into its current entries, every other key untouched) and
|
|
969
|
+
* updates the lock entry for this key only (see {@link updateLockFileLocale}'s `"merge"` mode); on
|
|
970
|
+
* rejection, writes nothing. Never calls a provider: {@link EditEntryDeps} has no provider seam,
|
|
971
|
+
* so there is no way to construct or call one.
|
|
972
|
+
*
|
|
973
|
+
* `locale` and `key` are resolved fresh on every call, never from a cache: `locale` against
|
|
974
|
+
* `config.targetLocales`, `key` against the source resource read fresh from disk.
|
|
975
|
+
*
|
|
976
|
+
* The target-file write and the lock-file update run inside one held `withLocaleWriteLock`
|
|
977
|
+
* critical section, so no second writer for this locale can observe the target file updated but
|
|
978
|
+
* the lock entry not yet, or vice versa. Reading the source, resolving the key, and selecting the
|
|
979
|
+
* adapter stay outside the lock: they are read-only and need no protection.
|
|
980
|
+
*
|
|
981
|
+
* Never touches `.verbatra-local/run-status.json`: that file is rewritten wholesale by
|
|
982
|
+
* `translate()`/`watch()` outside any locale lock, so patching it here would race that rewrite. A
|
|
983
|
+
* key edited here stays stale in that persisted snapshot until the next real run, which self-heals
|
|
984
|
+
* it since this call already advanced the lock hash.
|
|
985
|
+
*
|
|
986
|
+
* @param input - The validated config, the target locale, the source key, and the replacement value.
|
|
987
|
+
* @param deps - Optional composition seams (registry, file system) for tests.
|
|
988
|
+
* @returns The two-armed {@link EditEntryResult}.
|
|
989
|
+
* @throws {@link SdkError} `UNKNOWN_FORMAT`: no adapter is registered for the configured format.
|
|
990
|
+
* @throws {@link SdkError} `UNKNOWN_LOCALE`: `locale` is not among `config.targetLocales`.
|
|
991
|
+
* @throws {@link SdkError} `SOURCE_UNREADABLE` / `SOURCE_INVALID`: the source locale file is absent
|
|
992
|
+
* or unreadable.
|
|
993
|
+
* @throws {@link SdkError} `UNKNOWN_KEY`: `key` does not exist in the source resource.
|
|
994
|
+
* @throws {@link SdkError} `LOCK_FILE_INVALID`: the lock-file is corrupt, oversized, or at an
|
|
995
|
+
* unsupported version.
|
|
996
|
+
* @throws {@link SdkError} `LOCK_CONTENDED`: this locale's write lock could not be acquired before
|
|
997
|
+
* its timeout, because another process is holding it.
|
|
998
|
+
*/
|
|
999
|
+
declare function editEntry(input: EditEntryInput, deps?: EditEntryDeps): Promise<EditEntryResult>;
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* One key's placeholder and ICU integrity result for one target locale. Only ever computed for a
|
|
1003
|
+
* "changed" key (a current source and a current target value both exist). `hasPlaceholders` is
|
|
1004
|
+
* false when the source value carries no placeholders, in which case `matches` is trivially true
|
|
1005
|
+
* and carries no signal on its own. `icuValid` is computed unconditionally, independent of
|
|
1006
|
+
* `matches`: unlike `gateCandidateValue`, which stops at the first failing check, this is an
|
|
1007
|
+
* information report and stays accurate even when the key already fails on placeholders; it is
|
|
1008
|
+
* always true for a non-ICU format. Carries no source or target string value: only the boolean
|
|
1009
|
+
* results and, on a placeholder mismatch, the specific tokens involved.
|
|
1010
|
+
*/
|
|
1011
|
+
interface KeyIntegrityEntry {
|
|
1012
|
+
readonly key: string;
|
|
1013
|
+
readonly hasPlaceholders: boolean;
|
|
1014
|
+
readonly matches: boolean;
|
|
1015
|
+
readonly missing: readonly string[];
|
|
1016
|
+
readonly extra: readonly string[];
|
|
1017
|
+
readonly icuValid: boolean;
|
|
1018
|
+
}
|
|
1019
|
+
/** One target locale's integrity entries for the keys checked against it. */
|
|
1020
|
+
interface LocaleKeyIntegrity {
|
|
1021
|
+
readonly locale: string;
|
|
1022
|
+
readonly entries: readonly KeyIntegrityEntry[];
|
|
1023
|
+
}
|
|
1024
|
+
/** Input for {@link keyIntegrity}: the validated config, which locales to check, and an optional key filter. */
|
|
1025
|
+
interface KeyIntegrityInput {
|
|
1026
|
+
/** The validated configuration (typically from {@link loadConfig}). */
|
|
1027
|
+
readonly config: VerbatraConfig;
|
|
1028
|
+
/** Directory the file pattern and lock-file resolve against; defaults to cwd. */
|
|
1029
|
+
readonly cwd?: string;
|
|
1030
|
+
/** Subset of target locales to check; defaults to all configured target locales. */
|
|
1031
|
+
readonly locales?: readonly string[];
|
|
1032
|
+
/** Restrict the check to these keys; only the ones that are "changed" for a locale are checked. Defaults to every changed key. */
|
|
1033
|
+
readonly keys?: readonly string[];
|
|
1034
|
+
}
|
|
1035
|
+
/** Composition seam for {@link keyIntegrity}: inject a registry and a file system for tests. */
|
|
1036
|
+
interface KeyIntegrityDeps {
|
|
1037
|
+
readonly adapterRegistry?: AdapterRegistry;
|
|
1038
|
+
readonly fs?: SdkFs;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Runs the placeholder check and the ICU message-validity check for each selected target locale's
|
|
1042
|
+
* "changed" keys (present with a current value on both sides, per core's `diffResources`) and
|
|
1043
|
+
* reports a per-key result. Uses the adapter's own `comparePlaceholders` when present (the
|
|
1044
|
+
* branch-aware ICU path), otherwise core's `checkPlaceholders` over the stored placeholder lists.
|
|
1045
|
+
* `icuValid` is `adapter.validateMessage(target)`, computed unconditionally and independently of
|
|
1046
|
+
* the placeholder result, so a target that is placeholder-valid but syntactically invalid ICU is
|
|
1047
|
+
* still reported accurately. Read-only: it calls no provider, writes no file, and never touches
|
|
1048
|
+
* the lock. Missing and orphaned keys are never checked, since one side's value does not exist for
|
|
1049
|
+
* those.
|
|
1050
|
+
*
|
|
1051
|
+
* The returned entries never carry a source or target string value, only the boolean results and,
|
|
1052
|
+
* on a placeholder mismatch, the specific tokens involved.
|
|
1053
|
+
*
|
|
1054
|
+
* @param input - The validated config, which locales to check, and an optional key filter.
|
|
1055
|
+
* @param deps - Optional composition seams (registry, file system) for tests.
|
|
1056
|
+
* @returns One entry per checked locale, each carrying the integrity result for its changed keys
|
|
1057
|
+
* (narrowed to `input.keys` when supplied).
|
|
1058
|
+
* @throws {@link SdkError} `UNKNOWN_FORMAT`, `SOURCE_UNREADABLE`, `SOURCE_INVALID`,
|
|
1059
|
+
* `LOCK_FILE_INVALID`, or `UNKNOWN_LOCALE`, with the same meanings as in {@link diff}.
|
|
1060
|
+
*/
|
|
1061
|
+
declare function keyIntegrity(input: KeyIntegrityInput, deps?: KeyIntegrityDeps): Promise<readonly LocaleKeyIntegrity[]>;
|
|
1062
|
+
|
|
1063
|
+
/** Input for {@link keyValue}: the validated config and exactly one target locale/key pair. */
|
|
1064
|
+
interface KeyValueInput {
|
|
1065
|
+
/** The validated configuration (typically from {@link loadConfig}). */
|
|
1066
|
+
readonly config: VerbatraConfig;
|
|
1067
|
+
/** Directory the file pattern resolves against; defaults to cwd. */
|
|
1068
|
+
readonly cwd?: string;
|
|
1069
|
+
/** The target locale to read the current translation from. Must be a configured target locale. */
|
|
1070
|
+
readonly locale: string;
|
|
1071
|
+
/** The source key to read. Must exist in the source resource. */
|
|
1072
|
+
readonly key: string;
|
|
1073
|
+
}
|
|
1074
|
+
/** Composition seam for {@link keyValue}: inject a registry and a file system for tests. */
|
|
1075
|
+
interface KeyValueDeps {
|
|
1076
|
+
readonly adapterRegistry?: AdapterRegistry;
|
|
1077
|
+
readonly fs?: SdkFs;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* The current source and target values for one key/locale pair. `target` is absent exactly when
|
|
1081
|
+
* the key does not yet exist in that target locale; a present-but-empty-string target is a real
|
|
1082
|
+
* value and is returned as such.
|
|
1083
|
+
*/
|
|
1084
|
+
interface KeyValueResult {
|
|
1085
|
+
readonly source: string;
|
|
1086
|
+
readonly target?: string;
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Reads a key's current source and target values for exactly one target locale. Read-only: it
|
|
1090
|
+
* calls no provider, writes no file, and mutates nothing. Exposes only the current values on disk,
|
|
1091
|
+
* never a previous one.
|
|
1092
|
+
*
|
|
1093
|
+
* `locale` and `key` are resolved fresh on every call, so a caller feeding an edit dialog with
|
|
1094
|
+
* this result and then submitting through `editEntry` is always working from live data, never a
|
|
1095
|
+
* cached snapshot.
|
|
1096
|
+
*
|
|
1097
|
+
* @param input - The validated config, the target locale, and the source key to read.
|
|
1098
|
+
* @param deps - Optional composition seams (registry, file system) for tests.
|
|
1099
|
+
* @returns The current source value and, when present, the current target value.
|
|
1100
|
+
* @throws {@link SdkError} `UNKNOWN_FORMAT`: no adapter is registered for the configured format.
|
|
1101
|
+
* @throws {@link SdkError} `UNKNOWN_LOCALE`: `locale` is not among `config.targetLocales`.
|
|
1102
|
+
* @throws {@link SdkError} `SOURCE_UNREADABLE` / `SOURCE_INVALID`: the source locale file is absent
|
|
1103
|
+
* or unreadable.
|
|
1104
|
+
* @throws {@link SdkError} `UNKNOWN_KEY`: `key` does not exist in the source resource.
|
|
1105
|
+
*/
|
|
1106
|
+
declare function keyValue(input: KeyValueInput, deps?: KeyValueDeps): Promise<KeyValueResult>;
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* One locale file reduced to a content hash per key, taken at one point in time. Two snapshots of
|
|
1110
|
+
* the same file can be compared with {@link diffLocaleSnapshots} to count what changed between
|
|
1111
|
+
* them.
|
|
1112
|
+
*/
|
|
1113
|
+
interface LocaleFileSnapshot {
|
|
1114
|
+
/** The locale this snapshot was read for (the configured source locale or a target locale). */
|
|
1115
|
+
readonly locale: string;
|
|
1116
|
+
/** Content hash per key, from core's `contentHash`. */
|
|
1117
|
+
readonly hashes: ReadonlyMap<string, string>;
|
|
1118
|
+
}
|
|
1119
|
+
/** Input for {@link readLocaleFileSnapshot}: the validated config and which locale to snapshot. */
|
|
1120
|
+
interface ReadLocaleFileSnapshotInput {
|
|
1121
|
+
/** The validated configuration (typically from {@link loadConfig}). */
|
|
1122
|
+
readonly config: VerbatraConfig;
|
|
1123
|
+
/** The locale to snapshot: the configured source locale or any configured target locale. */
|
|
1124
|
+
readonly locale: string;
|
|
1125
|
+
/** Directory the file pattern resolves against; defaults to cwd. */
|
|
1126
|
+
readonly cwd?: string;
|
|
1127
|
+
}
|
|
1128
|
+
/** Composition seam for {@link readLocaleFileSnapshot}: inject a registry and a file system for tests. */
|
|
1129
|
+
interface ReadLocaleFileSnapshotDeps {
|
|
1130
|
+
readonly adapterRegistry?: AdapterRegistry;
|
|
1131
|
+
readonly fs?: SdkFs;
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Reads one locale file through the configured adapter and reduces it to a content hash per key.
|
|
1135
|
+
* Uses the same tolerant read path as {@link readTarget}: a file that does not exist yet reads as
|
|
1136
|
+
* an empty snapshot rather than throwing, so a caller can establish a baseline before a locale's
|
|
1137
|
+
* first write.
|
|
1138
|
+
*
|
|
1139
|
+
* @param input - The validated config, the locale to snapshot, and the directory to resolve against.
|
|
1140
|
+
* @param deps - Optional composition seams (registry, file system) for tests.
|
|
1141
|
+
* @returns The locale's per-key content hash snapshot.
|
|
1142
|
+
* @throws {@link SdkError} `UNKNOWN_FORMAT` when the configured format has no registered adapter;
|
|
1143
|
+
* rejects with the adapter's own error when the file exists but its content is malformed.
|
|
1144
|
+
*/
|
|
1145
|
+
declare function readLocaleFileSnapshot(input: ReadLocaleFileSnapshotInput, deps?: ReadLocaleFileSnapshotDeps): Promise<LocaleFileSnapshot>;
|
|
1146
|
+
/** A locale file's added, changed, and removed key counts between two of its own snapshots. Counts only, never key names. */
|
|
1147
|
+
interface LocaleSnapshotDelta {
|
|
1148
|
+
readonly added: number;
|
|
1149
|
+
readonly changed: number;
|
|
1150
|
+
readonly removed: number;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Compares two snapshots of the same locale file, taken at different points in time, and counts
|
|
1154
|
+
* how many keys were added, changed, or removed. A direct key-to-hash comparison rather than
|
|
1155
|
+
* core's `diffResources`: that function's source/target/baseline shape exists for cross-resource
|
|
1156
|
+
* comparison, and forcing a same-file, two-points-in-time comparison through it would need a
|
|
1157
|
+
* confusing relabeling (missing becomes removed, orphaned becomes added).
|
|
1158
|
+
*
|
|
1159
|
+
* @param previous - The earlier snapshot of the file.
|
|
1160
|
+
* @param current - The later snapshot of the same file.
|
|
1161
|
+
* @returns The counts of keys added, changed, and removed between the two snapshots.
|
|
1162
|
+
*/
|
|
1163
|
+
declare function diffLocaleSnapshots(previous: LocaleFileSnapshot, current: LocaleFileSnapshot): LocaleSnapshotDelta;
|
|
1164
|
+
|
|
854
1165
|
/** Per-locale key count and drift against the source and target files, from the recorded lock baseline. */
|
|
855
1166
|
interface LockLocaleState {
|
|
856
1167
|
/** The target locale this entry reports on. */
|
|
@@ -906,8 +1217,113 @@ interface LockStateDeps {
|
|
|
906
1217
|
*/
|
|
907
1218
|
declare function lockState(input: LockStateInput, deps?: LockStateDeps): Promise<LockStateResult>;
|
|
908
1219
|
|
|
1220
|
+
/** Builds the provider from its config. Injectable so tests stay offline. */
|
|
1221
|
+
type CreateProvider = (config: ProviderConfig) => TranslationProvider;
|
|
1222
|
+
|
|
1223
|
+
/** Input for {@link retranslateEntry}: the validated config and exactly one target locale/key pair. */
|
|
1224
|
+
interface RetranslateEntryInput {
|
|
1225
|
+
/** The validated configuration (typically from {@link loadConfig}). */
|
|
1226
|
+
readonly config: VerbatraConfig;
|
|
1227
|
+
/** Directory the file pattern and lock-file resolve against; defaults to cwd. */
|
|
1228
|
+
readonly cwd?: string;
|
|
1229
|
+
/** The target locale to retranslate the key into. Must be a configured target locale. */
|
|
1230
|
+
readonly locale: string;
|
|
1231
|
+
/** The source key to retranslate. Must exist in the source resource. */
|
|
1232
|
+
readonly key: string;
|
|
1233
|
+
}
|
|
1234
|
+
/** Composition seam for {@link retranslateEntry}: inject a registry, a provider builder, and a file system for tests. */
|
|
1235
|
+
interface RetranslateEntryDeps {
|
|
1236
|
+
readonly adapterRegistry?: AdapterRegistry;
|
|
1237
|
+
readonly createProvider?: CreateProvider;
|
|
1238
|
+
readonly fs?: SdkFs;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* The two-armed result of one retranslate attempt: `accepted` carries the newly written value and
|
|
1242
|
+
* any derived "needs review" reasons; a rejection carries the candidate value and which
|
|
1243
|
+
* {@link gateCandidateValue} check failed it, without writing anything.
|
|
1244
|
+
*/
|
|
1245
|
+
type RetranslateEntryResult = {
|
|
1246
|
+
readonly accepted: true;
|
|
1247
|
+
readonly value: string;
|
|
1248
|
+
readonly reviewReasons: readonly ReviewReasonCode[];
|
|
1249
|
+
} | {
|
|
1250
|
+
readonly accepted: false;
|
|
1251
|
+
readonly reason: "placeholder" | "icu";
|
|
1252
|
+
readonly value: string;
|
|
1253
|
+
};
|
|
1254
|
+
/**
|
|
1255
|
+
* Retranslate exactly one key for exactly one target locale: a single-entry `translateBatch` call
|
|
1256
|
+
* through the same provider registry `translate()` uses, gated through the shared
|
|
1257
|
+
* {@link gateCandidateValue} before anything reaches disk. On acceptance, writes the target locale
|
|
1258
|
+
* file (merging just this one key into its current entries, every other key untouched) and updates
|
|
1259
|
+
* the lock entry for this key only (see {@link updateLockFileLocale}'s `"merge"` mode); on rejection,
|
|
1260
|
+
* writes nothing. Reuses exactly the building blocks `translate()` already uses
|
|
1261
|
+
* (`selectProvider`/`provider.translateBatch`) and nothing else: no bespoke per-provider plumbing.
|
|
1262
|
+
*
|
|
1263
|
+
* `locale` and `key` are resolved fresh on every call, never cached: `locale` against
|
|
1264
|
+
* `config.targetLocales` (`UNKNOWN_LOCALE`), `key` against the source resource's own keys, read
|
|
1265
|
+
* fresh from disk (`UNKNOWN_KEY`), via a `Map`-backed lookup, never `key in obj` or
|
|
1266
|
+
* `obj[key] !== undefined` against a plain object.
|
|
1267
|
+
*
|
|
1268
|
+
* The target-file write and the lock-file update run inside the same held
|
|
1269
|
+
* `withLocaleWriteLock(cwd, locale, fs, ...)` critical section as the provider call itself, so no
|
|
1270
|
+
* second writer for this locale can ever observe the target file updated but the lock entry not
|
|
1271
|
+
* yet, or vice versa. The same locking also covers `translate()`/`watch()` and `importWorkbook()`.
|
|
1272
|
+
*
|
|
1273
|
+
* Not atomic across the two writes for a single caller's own run: the target locale file is
|
|
1274
|
+
* written before the lock entry is updated. If `updateLockFileLocale` then throws
|
|
1275
|
+
* (`LOCK_FILE_INVALID` for a corrupt lock-file, or `LOCK_CONTENDED` if its own internal lock-file
|
|
1276
|
+
* guard cannot be acquired before its timeout), the target file already carries the new value
|
|
1277
|
+
* while the lock still records the old hash, and the thrown error does not imply nothing was
|
|
1278
|
+
* saved. The same ordering, and the same partial-write shape on that rare path, also exists in
|
|
1279
|
+
* `translate()` and `importWorkbook()`.
|
|
1280
|
+
*
|
|
1281
|
+
* @param input - The validated config, the target locale, and the source key to retranslate.
|
|
1282
|
+
* @param deps - Optional composition seams (registry, provider builder, file system) for tests.
|
|
1283
|
+
* @returns The two-armed {@link RetranslateEntryResult}.
|
|
1284
|
+
* @throws {@link SdkError} `UNKNOWN_FORMAT`: no adapter is registered for the configured format.
|
|
1285
|
+
* @throws {@link SdkError} `UNKNOWN_LOCALE`: `locale` is not among `config.targetLocales`.
|
|
1286
|
+
* @throws {@link SdkError} `SOURCE_UNREADABLE` / `SOURCE_INVALID`: the source locale file is absent
|
|
1287
|
+
* or unreadable.
|
|
1288
|
+
* @throws {@link SdkError} `UNKNOWN_KEY`: `key` does not exist in the source resource.
|
|
1289
|
+
* @throws {@link SdkError} `PROVIDER_CONSTRUCTION_FAILED`: the provider factory threw (wraps a
|
|
1290
|
+
* missing `*_API_KEY` as `MISSING_API_KEY`).
|
|
1291
|
+
* @throws {@link ProviderError} the provider call itself failed, or returned no value at all for
|
|
1292
|
+
* this key (`INVALID_RESPONSE`).
|
|
1293
|
+
* @throws {@link SdkError} `LOCK_FILE_INVALID`: the lock-file is corrupt, oversized, or at an
|
|
1294
|
+
* unsupported version.
|
|
1295
|
+
* @throws {@link SdkError} `LOCK_CONTENDED`: this locale's write lock could not be acquired before
|
|
1296
|
+
* its timeout, because another process (another CLI run, or a second Studio write) is holding it.
|
|
1297
|
+
*/
|
|
1298
|
+
declare function retranslateEntry(input: RetranslateEntryInput, deps?: RetranslateEntryDeps): Promise<RetranslateEntryResult>;
|
|
1299
|
+
|
|
909
1300
|
/** A stable code for an SDK-originated notice (not a provider notice). */
|
|
910
|
-
type SdkNoticeCode = "PLURAL_CATEGORIES_INCOMPLETE" | "SUB_BATCH_FAILED" | "BLANK_ROW_BASELINE_RETAINED";
|
|
1301
|
+
type SdkNoticeCode = "PLURAL_CATEGORIES_INCOMPLETE" | "SUB_BATCH_FAILED" | "BLANK_ROW_BASELINE_RETAINED" | "BUDGET_TOKENS_EXCEEDED";
|
|
1302
|
+
/** Summed token usage across every provider call in a scope (one locale, or the whole run). */
|
|
1303
|
+
interface UsageSummary {
|
|
1304
|
+
readonly inputTokens: number;
|
|
1305
|
+
readonly outputTokens: number;
|
|
1306
|
+
}
|
|
1307
|
+
/** The configured token-budget behavior: warn and continue, or stop making further provider calls. */
|
|
1308
|
+
type BudgetBehavior = "warn" | "stop";
|
|
1309
|
+
/**
|
|
1310
|
+
* The run-wide token-budget outcome, present on {@link RunSummary} only when `maxTokens` is configured.
|
|
1311
|
+
* `supported: false` means the configured provider never reported usage in this run (a token-less
|
|
1312
|
+
* provider such as DeepL, or a dry-run): the guardrail is honestly inert, `tokensUsed` stays `0`, and
|
|
1313
|
+
* `exceeded` stays `false`, never a false trip.
|
|
1314
|
+
*/
|
|
1315
|
+
interface RunBudget {
|
|
1316
|
+
/** The configured ceiling. */
|
|
1317
|
+
readonly maxTokens: number;
|
|
1318
|
+
/** The configured behavior once the ceiling is reached. */
|
|
1319
|
+
readonly behavior: BudgetBehavior;
|
|
1320
|
+
/** Whether the configured provider reported usage at all this run. */
|
|
1321
|
+
readonly supported: boolean;
|
|
1322
|
+
/** Cumulative input plus output tokens across the run so far. */
|
|
1323
|
+
readonly tokensUsed: number;
|
|
1324
|
+
/** Whether `tokensUsed` reached or passed `maxTokens` at any point this run. */
|
|
1325
|
+
readonly exceeded: boolean;
|
|
1326
|
+
}
|
|
911
1327
|
/**
|
|
912
1328
|
* A notice raised by the SDK itself (not a provider), structurally identical to a {@link ProviderNotice}
|
|
913
1329
|
* so both share the {@link LocaleSummary.notices} channel. Carries only a stable code and a static,
|
|
@@ -921,6 +1337,13 @@ interface SdkNotice {
|
|
|
921
1337
|
}
|
|
922
1338
|
/** A notice on a locale summary: either a provider-emitted notice or an SDK-emitted one. */
|
|
923
1339
|
type LocaleNotice = ProviderNotice | SdkNotice;
|
|
1340
|
+
/** One key flagged for human review, and every reason code that applies to it. */
|
|
1341
|
+
interface NeedsReviewEntry {
|
|
1342
|
+
/** The flagged key. */
|
|
1343
|
+
readonly key: string;
|
|
1344
|
+
/** Every {@link ReviewReasonCode} that applies to this key. */
|
|
1345
|
+
readonly reasons: readonly ReviewReasonCode[];
|
|
1346
|
+
}
|
|
924
1347
|
/** Structured outcome for one target locale; surfaced as data on the run, never thrown. */
|
|
925
1348
|
interface LocaleSummary {
|
|
926
1349
|
/** The target locale this summary is for. */
|
|
@@ -928,7 +1351,7 @@ interface LocaleSummary {
|
|
|
928
1351
|
/** Whether this locale's run succeeded or failed (a failure does not abort the run). */
|
|
929
1352
|
readonly status: "succeeded" | "failed";
|
|
930
1353
|
/**
|
|
931
|
-
* Keys translated and written this run. In dry-run, the keys that
|
|
1354
|
+
* Keys translated and written this run. In dry-run, the keys that would be translated
|
|
932
1355
|
* (the provider is not called and nothing is written).
|
|
933
1356
|
*/
|
|
934
1357
|
readonly translated: readonly string[];
|
|
@@ -938,7 +1361,7 @@ interface LocaleSummary {
|
|
|
938
1361
|
readonly orphaned: readonly string[];
|
|
939
1362
|
/**
|
|
940
1363
|
* Orphaned keys actually removed this run because pruning was on. In a dry-run with pruning on, the keys
|
|
941
|
-
* that
|
|
1364
|
+
* that would be removed. Empty when pruning is off (the orphans then survive and are reported in
|
|
942
1365
|
* `orphaned` only). A subset of `orphaned`; never includes a source-present key.
|
|
943
1366
|
*/
|
|
944
1367
|
readonly pruned: readonly string[];
|
|
@@ -966,6 +1389,20 @@ interface LocaleSummary {
|
|
|
966
1389
|
* and acted, and empty in a dry-run.
|
|
967
1390
|
*/
|
|
968
1391
|
readonly generated: readonly string[];
|
|
1392
|
+
/**
|
|
1393
|
+
* Candidate keys never sent to the provider because a configured `maxTokens` budget already tripped
|
|
1394
|
+
* in `"stop"` mode: keys remaining in the current locale once the ceiling was crossed, and every
|
|
1395
|
+
* candidate key of a later, fully-skipped locale. Always present, empty by default, same convention as
|
|
1396
|
+
* {@link LocaleSummary.integrityMismatches} and {@link LocaleSummary.providerFailures}. Each key keeps
|
|
1397
|
+
* its prior lock hash and is picked up again next run, exactly like a `providerFailures` key.
|
|
1398
|
+
*/
|
|
1399
|
+
readonly budgetWithheld: readonly string[];
|
|
1400
|
+
/**
|
|
1401
|
+
* Summed token usage across every provider call for this locale (main translation plus plural
|
|
1402
|
+
* generation). Absent when no call in this locale reported usage: a dry-run, a token-less provider
|
|
1403
|
+
* (DeepL), or a locale with nothing to translate. Never a fabricated `{ inputTokens: 0, outputTokens: 0 }`.
|
|
1404
|
+
*/
|
|
1405
|
+
readonly usage?: UsageSummary;
|
|
969
1406
|
/**
|
|
970
1407
|
* Notices for this locale: provider notices (e.g. DeepL graceful-degradation) and SDK notices
|
|
971
1408
|
* (e.g. a target language needing more CLDR plural categories than the source supplies). Empty when
|
|
@@ -973,7 +1410,16 @@ interface LocaleSummary {
|
|
|
973
1410
|
*/
|
|
974
1411
|
readonly notices: readonly LocaleNotice[];
|
|
975
1412
|
/**
|
|
976
|
-
*
|
|
1413
|
+
* Keys accepted and written this run that the review heuristics flagged for a second look, sorted
|
|
1414
|
+
* by key. Distinct from the placeholder/ICU integrity gate: a review flag is advisory only and never
|
|
1415
|
+
* withholds a key, so this never double-reports a key already surfaced through
|
|
1416
|
+
* {@link LocaleSummary.integrityMismatches} or {@link LocaleSummary.providerFailures}. Empty when
|
|
1417
|
+
* nothing was flagged, always empty for a workbook import (which never calls a provider or recomputes
|
|
1418
|
+
* flags on its own path).
|
|
1419
|
+
*/
|
|
1420
|
+
readonly needsReview: readonly NeedsReviewEntry[];
|
|
1421
|
+
/**
|
|
1422
|
+
* Present only when status is "failed": a structured, secret-free error. `code` is a preserved string
|
|
977
1423
|
* (the underlying provider/adapter error's `code`, or `"LOCALE_FAILED"` as a fallback), intentionally
|
|
978
1424
|
* wider than {@link SdkErrorCode}, so do not treat it as a closed set.
|
|
979
1425
|
*/
|
|
@@ -992,10 +1438,77 @@ interface RunSummary {
|
|
|
992
1438
|
readonly succeeded: readonly string[];
|
|
993
1439
|
/** Locales whose run failed (see each locale's `error`). */
|
|
994
1440
|
readonly failed: readonly string[];
|
|
1441
|
+
/**
|
|
1442
|
+
* Summed token usage across every locale's {@link LocaleSummary.usage}. Absent when no locale in the
|
|
1443
|
+
* run reported usage (a dry-run, or an all-token-less-provider run).
|
|
1444
|
+
*/
|
|
1445
|
+
readonly usage?: UsageSummary;
|
|
1446
|
+
/** The run-wide token-budget outcome; present only when `maxTokens` is configured. */
|
|
1447
|
+
readonly budget?: RunBudget;
|
|
995
1448
|
}
|
|
996
1449
|
|
|
997
|
-
/**
|
|
998
|
-
|
|
1450
|
+
/**
|
|
1451
|
+
* One target locale's snapshot inside the persisted run-status file. `status` is carried over from
|
|
1452
|
+
* `LocaleSummary.status` unchanged, so an empty `needsReview` on a failed locale is never misread as
|
|
1453
|
+
* "this locale ran clean with nothing to review".
|
|
1454
|
+
*/
|
|
1455
|
+
interface RunStatusLocale {
|
|
1456
|
+
readonly locale: string;
|
|
1457
|
+
readonly status: "succeeded" | "failed";
|
|
1458
|
+
/** The full, unmodified list from `LocaleSummary.needsReview`; already bounded and content-free. */
|
|
1459
|
+
readonly needsReview: readonly NeedsReviewEntry[];
|
|
1460
|
+
/** Absent exactly when the source `LocaleSummary.usage` was absent; never a fabricated zero. */
|
|
1461
|
+
readonly usage?: UsageSummary;
|
|
1462
|
+
}
|
|
1463
|
+
/**
|
|
1464
|
+
* The persisted shape of `.verbatra-local/run-status.json`: one whole-run snapshot, overwritten on
|
|
1465
|
+
* every non-dry-run `translate()`/`watch()` run that reaches the end of its per-locale loop. Contains
|
|
1466
|
+
* only the review-flag and token/usage data already computed for `RunSummary`; never a translation
|
|
1467
|
+
* string, a provider error message, or a provider notice message.
|
|
1468
|
+
*/
|
|
1469
|
+
interface RunStatusFile {
|
|
1470
|
+
readonly version: number;
|
|
1471
|
+
/** ISO timestamp of when this snapshot was written. */
|
|
1472
|
+
readonly generatedAt: string;
|
|
1473
|
+
/** Summed run-wide token usage, mirroring `RunSummary.usage`; absent under the same conditions. */
|
|
1474
|
+
readonly usage?: UsageSummary;
|
|
1475
|
+
/** The run-wide token-budget outcome, mirroring `RunSummary.budget`; present only when configured. */
|
|
1476
|
+
readonly budget?: RunBudget;
|
|
1477
|
+
readonly locales: readonly RunStatusLocale[];
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/** Input for {@link runStatus}: the persisted file is self-contained, so no `config` is needed. */
|
|
1481
|
+
interface RunStatusInput {
|
|
1482
|
+
/** Directory the run-status file resolves against; defaults to cwd. */
|
|
1483
|
+
readonly cwd?: string;
|
|
1484
|
+
}
|
|
1485
|
+
/** Composition seam for {@link runStatus}: inject a file system for tests. */
|
|
1486
|
+
interface RunStatusDeps {
|
|
1487
|
+
readonly fs?: SdkFs;
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* The persisted run-status snapshot, or its absence. `available: false` covers a project that has
|
|
1491
|
+
* never run a non-dry-run `translate()`/`watch()`, exactly like every other degrade case
|
|
1492
|
+
* {@link runStatus} folds into it: never a distinct error, so a caller does not need to special-case
|
|
1493
|
+
* "first run" apart from "corrupt file" apart from "unrecognized version".
|
|
1494
|
+
*/
|
|
1495
|
+
type RunStatusResult = {
|
|
1496
|
+
readonly available: false;
|
|
1497
|
+
} | ({
|
|
1498
|
+
readonly available: true;
|
|
1499
|
+
} & RunStatusFile);
|
|
1500
|
+
/**
|
|
1501
|
+
* Read the most recent run's persisted review-flag and token/usage snapshot from
|
|
1502
|
+
* `.verbatra-local/run-status.json`, without calling a provider, writing any file, or mutating the
|
|
1503
|
+
* lock-file. Never throws: a missing file, invalid JSON, a schema mismatch, or an unrecognized
|
|
1504
|
+
* `version` all degrade to `{ available: false }`, since this file is best-effort telemetry, not a
|
|
1505
|
+
* correctness baseline (contrast {@link lockState}, which can throw `LOCK_FILE_INVALID`).
|
|
1506
|
+
*
|
|
1507
|
+
* @param input - Only the directory the file resolves against; no config is needed.
|
|
1508
|
+
* @param deps - Optional composition seam (file system) for tests.
|
|
1509
|
+
* @returns `{ available: false }`, or `{ available: true, ...the persisted fields }`.
|
|
1510
|
+
*/
|
|
1511
|
+
declare function runStatus(input?: RunStatusInput, deps?: RunStatusDeps): Promise<RunStatusResult>;
|
|
999
1512
|
|
|
1000
1513
|
/** Everything the one-shot run needs: the validated config and where/how to run it. */
|
|
1001
1514
|
interface TranslateInput {
|
|
@@ -1036,12 +1549,23 @@ interface TranslateDeps {
|
|
|
1036
1549
|
* locales that succeeded. Dry-run reads + diffs + reports without constructing/calling
|
|
1037
1550
|
* the provider and without writing any file or the lock-file.
|
|
1038
1551
|
*
|
|
1039
|
-
* A per-locale failure does
|
|
1552
|
+
* A per-locale failure does not throw: it is recorded on that locale's {@link LocaleSummary} as
|
|
1040
1553
|
* `status: "failed"` with a secret-free `{ code, message }`, where `code` is a preserved string (the
|
|
1041
1554
|
* underlying provider/adapter code, or `"LOCALE_FAILED"` as a fallback), not necessarily an
|
|
1042
1555
|
* {@link SdkErrorCode}. DeepL notices, integrity mismatches, and invalid-ICU source keys likewise
|
|
1043
1556
|
* surface on each `LocaleSummary`, never as throws.
|
|
1044
1557
|
*
|
|
1558
|
+
* A dry-run reads the lock once, outside any lock, and reuses that one snapshot for every locale:
|
|
1559
|
+
* it never writes, so there is nothing to serialize against. A live run instead re-reads the lock
|
|
1560
|
+
* fresh inside each locale's own write lock (see `runLiveLocale`), so two concurrent live calls
|
|
1561
|
+
* never both diff against the same stale pre-loop snapshot. The token-budget tracker is fresh per
|
|
1562
|
+
* invocation: the `maxTokens` ceiling is per-run, not cumulative across watch cycles.
|
|
1563
|
+
*
|
|
1564
|
+
* On a non-dry-run that reaches the end of the loop, the run's review-flag and token/usage data is
|
|
1565
|
+
* also written to `.verbatra-local/run-status.json` (readable back through {@link runStatus}). This
|
|
1566
|
+
* write is best-effort: any failure is caught and swallowed, never re-thrown and never reflected on
|
|
1567
|
+
* the returned {@link RunSummary}.
|
|
1568
|
+
*
|
|
1045
1569
|
* @param input - The validated config and run options (cwd, dryRun, prune, generatePlurals).
|
|
1046
1570
|
* @param deps - Optional composition seams (registry, provider builder, file system) for tests.
|
|
1047
1571
|
* @returns A {@link RunSummary}: the per-locale {@link LocaleSummary}s and the succeeded/failed locale lists.
|
|
@@ -1147,7 +1671,11 @@ interface ImportWorkbookDeps {
|
|
|
1147
1671
|
* structured {@link SdkError}. A per-sheet failure (a locale not in config, a broken-round-trip key,
|
|
1148
1672
|
* a write failure) is isolated as that locale's `status: "failed"`, not a throw; per-row rejections
|
|
1149
1673
|
* are withheld and reported on the locale. Dry-run validates and reports without writing any locale or
|
|
1150
|
-
* lock file.
|
|
1674
|
+
* lock file, and skips lock acquisition (there is nothing to protect).
|
|
1675
|
+
*
|
|
1676
|
+
* The lock-file is read once, up front, for every sheet's diff baseline. On a non-dry-run, each
|
|
1677
|
+
* sheet's write-and-lock-update step then holds that locale's `withLocaleWriteLock` for its whole
|
|
1678
|
+
* critical section, so a concurrent writer touching the same locale can never interleave with it.
|
|
1151
1679
|
*
|
|
1152
1680
|
* @param input - The validated config, the workbook path, and run options.
|
|
1153
1681
|
* @param deps - Optional composition seams (registry, file system) for tests.
|
|
@@ -1317,4 +1845,4 @@ interface WatchController {
|
|
|
1317
1845
|
*/
|
|
1318
1846
|
declare function watch(input: WatchInput, deps?: WatchDeps): Promise<WatchController>;
|
|
1319
1847
|
|
|
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 };
|
|
1848
|
+
export { type BudgetBehavior, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, type KeyIntegrityDeps, type KeyIntegrityEntry, type KeyIntegrityInput, type KeyValueDeps, type KeyValueInput, type KeyValueResult, LOCK_FILE_NAME, type LoadConfigOptions, type LoadLockFileDeps, type LoadLockFileInput, type LoadedConfig, type LocaleCheckSummary, type LocaleDiff, type LocaleFileSnapshot, type LocaleKeyIntegrity, type LocaleNotice, type LocaleSnapshotDelta, type LocaleSummary, type LockFile, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type NeedsReviewEntry, type ProviderConfig, type ProviderId, type ReadLocaleFileSnapshotDeps, type ReadLocaleFileSnapshotInput, type RetranslateEntryDeps, type RetranslateEntryInput, type RetranslateEntryResult, type ReviewReasonCode, type RunBudget, type RunStatusDeps, type RunStatusFile, type RunStatusInput, type RunStatusLocale, type RunStatusResult, 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 UsageSummary, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
|