@verbatra/sdk 0.6.1 → 0.6.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/README.md +1 -1
- package/dist/index.cjs +895 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +262 -57
- package/dist/index.d.ts +262 -57
- package/dist/index.js +895 -99
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -231,10 +231,81 @@ declare const supportedFormatSchema$1: z.ZodEnum<{
|
|
|
231
231
|
xliff: "xliff";
|
|
232
232
|
yaml: "yaml";
|
|
233
233
|
arb: "arb";
|
|
234
|
+
properties: "properties";
|
|
234
235
|
}>;
|
|
235
236
|
/** One of the supported source formats; a member of {@link SUPPORTED_FORMATS}. */
|
|
236
237
|
type SupportedFormat$1 = z.infer<typeof supportedFormatSchema$1>;
|
|
237
238
|
|
|
239
|
+
/** Outcome of a bounded read: the content, or why it could not be read in bounds. */
|
|
240
|
+
type BoundedFileRead = {
|
|
241
|
+
readonly kind: "ok";
|
|
242
|
+
readonly content: string;
|
|
243
|
+
} | {
|
|
244
|
+
readonly kind: "missing";
|
|
245
|
+
} | {
|
|
246
|
+
readonly kind: "too-large";
|
|
247
|
+
};
|
|
248
|
+
/** Outcome of a bounded binary read: the bytes, or why they could not be read in bounds. */
|
|
249
|
+
type BoundedBytesRead = {
|
|
250
|
+
readonly kind: "ok";
|
|
251
|
+
readonly bytes: Uint8Array;
|
|
252
|
+
} | {
|
|
253
|
+
readonly kind: "missing";
|
|
254
|
+
} | {
|
|
255
|
+
readonly kind: "too-large";
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* The minimal file-system surface the SDK needs. Reads are bounded and writes are atomic. Injectable so
|
|
259
|
+
* tests stay deterministic; the format adapters do their own file IO and bypass this seam.
|
|
260
|
+
*/
|
|
261
|
+
interface SdkFs {
|
|
262
|
+
/** Whether a readable file exists at the path. */
|
|
263
|
+
fileExists(path: string): Promise<boolean>;
|
|
264
|
+
/**
|
|
265
|
+
* Read a file as UTF-8 through a single handle, bounded to maxBytes. TOCTOU-safe: the handle is
|
|
266
|
+
* fstat'd and the read never advances past the sized length, so swapping in a larger file cannot
|
|
267
|
+
* bypass the cap. A missing or unreadable path is "missing"; a file over the cap is "too-large".
|
|
268
|
+
*/
|
|
269
|
+
readFileBounded(path: string, maxBytes: number): Promise<BoundedFileRead>;
|
|
270
|
+
/** Read a file as raw bytes with the same TOCTOU-safe, bounded discipline as {@link readFileBounded}. */
|
|
271
|
+
readBytesBounded(path: string, maxBytes: number): Promise<BoundedBytesRead>;
|
|
272
|
+
/** Write atomically: a temp file in the same directory, then rename over the target. */
|
|
273
|
+
writeFile(path: string, data: string): Promise<void>;
|
|
274
|
+
/** Write raw bytes atomically (temp file, then rename over the target). Used for the workbook. */
|
|
275
|
+
writeBytes(path: string, data: Uint8Array): Promise<void>;
|
|
276
|
+
/**
|
|
277
|
+
* Atomically create a file only if it does not already exist (O_EXCL semantics), creating any
|
|
278
|
+
* missing parent directory first. Returns true if this call created the file, false if a file
|
|
279
|
+
* was already present (no write performed). The backbone of {@link withLocaleWriteLock}'s
|
|
280
|
+
* exclusive-create lock: the parent-directory creation lives here so that primitive never touches
|
|
281
|
+
* `node:fs` directly and a fake `SdkFs` in tests never needs a real directory to exist.
|
|
282
|
+
*/
|
|
283
|
+
createExclusive(path: string, data: string): Promise<boolean>;
|
|
284
|
+
/** Delete a file if present; a no-op if it is already absent. */
|
|
285
|
+
deleteFile(path: string): Promise<void>;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The content-addressed translation-memory (TM) cache: a local, gitignored, regenerable sidecar to
|
|
290
|
+
* the lock file (`verbatra.cache.json`). It lets a translation whose source content is unchanged be
|
|
291
|
+
* reused for free, even under a renamed key or across two keys with byte-identical source text,
|
|
292
|
+
* instead of being re-sent to the provider.
|
|
293
|
+
*
|
|
294
|
+
* Nesting is `entries[fingerprint][targetLocale][contentHash] = value`. The fingerprint scopes every
|
|
295
|
+
* entry to one translation context (provider, model, tone, glossary), so a tone or glossary change
|
|
296
|
+
* never serves a stale value. Unlike the lock file, a corrupt or wrong-version cache degrades to an
|
|
297
|
+
* empty cache and never fails a run: it is a cost optimization, not a correctness record.
|
|
298
|
+
*/
|
|
299
|
+
interface TranslationMemory {
|
|
300
|
+
/** The cache schema version; a value this build does not recognize degrades the whole file to empty. */
|
|
301
|
+
readonly version: number;
|
|
302
|
+
/** Cached values, nested fingerprint to target locale to source content hash to translated value. */
|
|
303
|
+
readonly entries: Readonly<Record<string, Readonly<Record<string, Readonly<Record<string, string>>>>>>;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** The cache file's name, a sibling of the lock file and obviously JSON. */
|
|
307
|
+
declare const CACHE_FILE_NAME = "verbatra.cache.json";
|
|
308
|
+
|
|
238
309
|
/**
|
|
239
310
|
* The provider section of the config: a discriminated union over the provider id, reusing each
|
|
240
311
|
* provider's own config schema. There is no key field anywhere in this union except
|
|
@@ -299,6 +370,7 @@ declare const verbatraConfigSchema: z.ZodObject<{
|
|
|
299
370
|
xliff: "xliff";
|
|
300
371
|
yaml: "yaml";
|
|
301
372
|
arb: "arb";
|
|
373
|
+
properties: "properties";
|
|
302
374
|
}>;
|
|
303
375
|
files: z.ZodObject<{
|
|
304
376
|
pattern: z.ZodString;
|
|
@@ -445,55 +517,6 @@ declare function defineConfig(config: AuthoringConfigFor<"gemini">): VerbatraCon
|
|
|
445
517
|
declare function defineConfig(config: AuthoringConfigFor<"deepl">): VerbatraConfigInput;
|
|
446
518
|
declare function defineConfig(config: AuthoringConfig): VerbatraConfigInput;
|
|
447
519
|
|
|
448
|
-
/** Outcome of a bounded read: the content, or why it could not be read in bounds. */
|
|
449
|
-
type BoundedFileRead = {
|
|
450
|
-
readonly kind: "ok";
|
|
451
|
-
readonly content: string;
|
|
452
|
-
} | {
|
|
453
|
-
readonly kind: "missing";
|
|
454
|
-
} | {
|
|
455
|
-
readonly kind: "too-large";
|
|
456
|
-
};
|
|
457
|
-
/** Outcome of a bounded binary read: the bytes, or why they could not be read in bounds. */
|
|
458
|
-
type BoundedBytesRead = {
|
|
459
|
-
readonly kind: "ok";
|
|
460
|
-
readonly bytes: Uint8Array;
|
|
461
|
-
} | {
|
|
462
|
-
readonly kind: "missing";
|
|
463
|
-
} | {
|
|
464
|
-
readonly kind: "too-large";
|
|
465
|
-
};
|
|
466
|
-
/**
|
|
467
|
-
* The minimal file-system surface the SDK needs. Reads are bounded and writes are atomic. Injectable so
|
|
468
|
-
* tests stay deterministic; the format adapters do their own file IO and bypass this seam.
|
|
469
|
-
*/
|
|
470
|
-
interface SdkFs {
|
|
471
|
-
/** Whether a readable file exists at the path. */
|
|
472
|
-
fileExists(path: string): Promise<boolean>;
|
|
473
|
-
/**
|
|
474
|
-
* Read a file as UTF-8 through a single handle, bounded to maxBytes. TOCTOU-safe: the handle is
|
|
475
|
-
* fstat'd and the read never advances past the sized length, so swapping in a larger file cannot
|
|
476
|
-
* bypass the cap. A missing or unreadable path is "missing"; a file over the cap is "too-large".
|
|
477
|
-
*/
|
|
478
|
-
readFileBounded(path: string, maxBytes: number): Promise<BoundedFileRead>;
|
|
479
|
-
/** Read a file as raw bytes with the same TOCTOU-safe, bounded discipline as {@link readFileBounded}. */
|
|
480
|
-
readBytesBounded(path: string, maxBytes: number): Promise<BoundedBytesRead>;
|
|
481
|
-
/** Write atomically: a temp file in the same directory, then rename over the target. */
|
|
482
|
-
writeFile(path: string, data: string): Promise<void>;
|
|
483
|
-
/** Write raw bytes atomically (temp file, then rename over the target). Used for the workbook. */
|
|
484
|
-
writeBytes(path: string, data: Uint8Array): Promise<void>;
|
|
485
|
-
/**
|
|
486
|
-
* Atomically create a file only if it does not already exist (O_EXCL semantics), creating any
|
|
487
|
-
* missing parent directory first. Returns true if this call created the file, false if a file
|
|
488
|
-
* was already present (no write performed). The backbone of {@link withLocaleWriteLock}'s
|
|
489
|
-
* exclusive-create lock: the parent-directory creation lives here so that primitive never touches
|
|
490
|
-
* `node:fs` directly and a fake `SdkFs` in tests never needs a real directory to exist.
|
|
491
|
-
*/
|
|
492
|
-
createExclusive(path: string, data: string): Promise<boolean>;
|
|
493
|
-
/** Delete a file if present; a no-op if it is already absent. */
|
|
494
|
-
deleteFile(path: string): Promise<void>;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
520
|
/** Where a config's resolved glossary came from: absent, inline, or a file at the recorded path. */
|
|
498
521
|
type GlossaryProvenance = {
|
|
499
522
|
readonly source: "none";
|
|
@@ -620,11 +643,18 @@ declare function loadConfig(options?: LoadConfigOptions): Promise<VerbatraConfig
|
|
|
620
643
|
* its timeout elapsed, because another process is holding it (or an orphaned lock file was left
|
|
621
644
|
* behind by a killed process); the message names the lock file's path (thrown by `translate`,
|
|
622
645
|
* `importWorkbook`, `editEntry`, and `retranslateEntry`).
|
|
646
|
+
* - `CONCURRENCY_INVALID`: the `concurrency` input is not an integer of at least 1 (thrown by
|
|
647
|
+
* `translate` and, per run, by `watch`, before any locale runs).
|
|
648
|
+
* - `CONCURRENCY_BUDGET_CONFLICT`: a live run requested `concurrency` greater than 1 while a token
|
|
649
|
+
* budget (`maxTokens`) is configured; the two are mutually exclusive because concurrency makes the
|
|
650
|
+
* budget's stop guarantee nondeterministic (thrown by `translate` before any provider call; a dry
|
|
651
|
+
* run is exempt, since it never folds usage into or consults the budget tracker, so the conflict
|
|
652
|
+
* cannot arise).
|
|
623
653
|
* - `LOCALE_FAILED` (NOT thrown): the fallback `code` recorded on a failed `LocaleSummary` when a
|
|
624
654
|
* per-locale failure carries no string code of its own. See the surfaced-not-thrown distinction
|
|
625
655
|
* on `translate`.
|
|
626
656
|
*/
|
|
627
|
-
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";
|
|
657
|
+
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" | "CONCURRENCY_INVALID" | "CONCURRENCY_BUDGET_CONFLICT" | "LOCALE_FAILED";
|
|
628
658
|
/** The single structured error the SDK throws or records. Never carries a secret. */
|
|
629
659
|
declare class SdkError extends Error {
|
|
630
660
|
/** The stable {@link SdkErrorCode} for this failure; branch on this, not the message. */
|
|
@@ -645,6 +675,7 @@ declare const supportedFormatSchema: z.ZodEnum<{
|
|
|
645
675
|
xliff: "xliff";
|
|
646
676
|
yaml: "yaml";
|
|
647
677
|
arb: "arb";
|
|
678
|
+
properties: "properties";
|
|
648
679
|
}>;
|
|
649
680
|
/** One of the supported source formats; a member of {@link SUPPORTED_FORMATS}. */
|
|
650
681
|
type SupportedFormat = z.infer<typeof supportedFormatSchema>;
|
|
@@ -981,7 +1012,10 @@ type EditEntryResult = {
|
|
|
981
1012
|
* locale file (merging just this one key into its current entries, every other key untouched) and
|
|
982
1013
|
* updates the lock entry for this key only (see {@link updateLockFileLocale}'s `"merge"` mode); on
|
|
983
1014
|
* rejection, writes nothing. Never calls a provider: {@link EditEntryDeps} has no provider seam,
|
|
984
|
-
* so there is no way to construct or call one.
|
|
1015
|
+
* so there is no way to construct or call one. On acceptance it also feeds the accepted value into
|
|
1016
|
+
* the local translation-memory cache (best-effort, keyed by the source content hash and the config
|
|
1017
|
+
* fingerprint), so a later run can reuse it for free; a cache failure is swallowed and never fails
|
|
1018
|
+
* the edit.
|
|
985
1019
|
*
|
|
986
1020
|
* `locale` and `key` are resolved fresh on every call, never from a cache: `locale` against
|
|
987
1021
|
* `config.targetLocales`, `key` against the source resource read fresh from disk.
|
|
@@ -1272,6 +1306,9 @@ type RetranslateEntryResult = {
|
|
|
1272
1306
|
* the lock entry for this key only (see {@link updateLockFileLocale}'s `"merge"` mode); on rejection,
|
|
1273
1307
|
* writes nothing. Reuses exactly the building blocks `translate()` already uses
|
|
1274
1308
|
* (`selectProvider`/`provider.translateBatch`) and nothing else: no bespoke per-provider plumbing.
|
|
1309
|
+
* Always calls the provider (it never consults the cache for a hit, since its whole purpose is a
|
|
1310
|
+
* fresh translation), then feeds the accepted value into the translation-memory cache best-effort so
|
|
1311
|
+
* a later run can reuse it; a cache failure is swallowed and never fails the retranslate.
|
|
1275
1312
|
*
|
|
1276
1313
|
* `locale` and `key` are resolved fresh on every call, never cached: `locale` against
|
|
1277
1314
|
* `config.targetLocales` (`UNKNOWN_LOCALE`), `key` against the source resource's own keys, read
|
|
@@ -1357,6 +1394,26 @@ interface NeedsReviewEntry {
|
|
|
1357
1394
|
/** Every {@link ReviewReasonCode} that applies to this key. */
|
|
1358
1395
|
readonly reasons: readonly ReviewReasonCode[];
|
|
1359
1396
|
}
|
|
1397
|
+
/**
|
|
1398
|
+
* One row a workbook import could not read: the 1-based worksheet row number and the header label of
|
|
1399
|
+
* the offending column. Carries no cell content, so untrusted workbook text never reaches the summary.
|
|
1400
|
+
*/
|
|
1401
|
+
interface MalformedRowReport {
|
|
1402
|
+
/** The 1-based worksheet row number of the malformed row. */
|
|
1403
|
+
readonly row: number;
|
|
1404
|
+
/** The header label of the column the row was rejected on (for example "Status"). */
|
|
1405
|
+
readonly column: string;
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* One duplicate-key conflict a workbook import found in a locale's sheet: the duplicated key and the
|
|
1409
|
+
* 1-based worksheet row number of the later (losing) occurrence. The first occurrence is the winner.
|
|
1410
|
+
*/
|
|
1411
|
+
interface DuplicateKeyReport {
|
|
1412
|
+
/** The key that appeared more than once in the locale's sheet. */
|
|
1413
|
+
readonly key: string;
|
|
1414
|
+
/** The 1-based worksheet row number of the later occurrence that lost to the first. */
|
|
1415
|
+
readonly row: number;
|
|
1416
|
+
}
|
|
1360
1417
|
/** Structured outcome for one target locale; surfaced as data on the run, never thrown. */
|
|
1361
1418
|
interface LocaleSummary {
|
|
1362
1419
|
/** The target locale this summary is for. */
|
|
@@ -1370,6 +1427,12 @@ interface LocaleSummary {
|
|
|
1370
1427
|
* {@link LocaleSummary.budgetWithheld} non-empty). `"failed"` when the locale had candidate keys
|
|
1371
1428
|
* but accepted nothing at all (neither translated nor generated) while withholding at least one.
|
|
1372
1429
|
* Withheld keys keep their prior lock hash and retry next run.
|
|
1430
|
+
*
|
|
1431
|
+
* A workbook import's structural findings, {@link LocaleSummary.unfilled},
|
|
1432
|
+
* {@link LocaleSummary.malformedRows}, and {@link LocaleSummary.duplicateKeys}, deliberately do NOT
|
|
1433
|
+
* feed this status: they are surfaced in their own lists (and the CLI) but are not withholdings of
|
|
1434
|
+
* an attempted translation, so a locale that dropped a malformed row, collapsed a duplicate key, or
|
|
1435
|
+
* left a `changed` row unfilled while accepting its other rows can still be `"succeeded"`.
|
|
1373
1436
|
*/
|
|
1374
1437
|
readonly status: "succeeded" | "partial" | "failed";
|
|
1375
1438
|
/**
|
|
@@ -1389,6 +1452,15 @@ interface LocaleSummary {
|
|
|
1389
1452
|
readonly pruned: readonly string[];
|
|
1390
1453
|
/** Source keys flagged invalid-ICU that were skipped for translation this run. */
|
|
1391
1454
|
readonly invalidIcuSource: readonly string[];
|
|
1455
|
+
/**
|
|
1456
|
+
* Keys served from the local translation-memory cache instead of the provider: their source content
|
|
1457
|
+
* was unchanged (possibly under a renamed key or shared with another key) and the cached value passed
|
|
1458
|
+
* the integrity gate, so no provider request was made for them. They are written and lock-advanced
|
|
1459
|
+
* exactly like a translated key, but kept distinct from {@link LocaleSummary.translated} because they
|
|
1460
|
+
* cost no provider usage. Always present, empty by default; empty in a dry-run and when the cache is
|
|
1461
|
+
* bypassed. Sorted by key.
|
|
1462
|
+
*/
|
|
1463
|
+
readonly cacheHits: readonly string[];
|
|
1392
1464
|
/**
|
|
1393
1465
|
* Translated keys that failed the placeholder-integrity check and were withheld. Never includes a
|
|
1394
1466
|
* key withheld because the provider call itself failed; see {@link LocaleSummary.providerFailures}
|
|
@@ -1440,6 +1512,27 @@ interface LocaleSummary {
|
|
|
1440
1512
|
* flags on its own path).
|
|
1441
1513
|
*/
|
|
1442
1514
|
readonly needsReview: readonly NeedsReviewEntry[];
|
|
1515
|
+
/**
|
|
1516
|
+
* Keys the workbook exported as `changed` (needing an updated translation) that the translator left
|
|
1517
|
+
* blank, drifted or not, sorted by key. Pending work made visible: the row is skipped (nothing is
|
|
1518
|
+
* written and the prior lock baseline is kept), but the key is surfaced so an unfinished import is
|
|
1519
|
+
* never silently reported as done. Only a workbook import populates this; the provider path, which
|
|
1520
|
+
* never leaves a candidate key unfilled by a person, always leaves it empty.
|
|
1521
|
+
*/
|
|
1522
|
+
readonly unfilled: readonly string[];
|
|
1523
|
+
/**
|
|
1524
|
+
* Rows a workbook import could not read for this locale, each by worksheet row number and offending
|
|
1525
|
+
* column, in row order. The sheet's other rows still import; a malformed row is reported, not
|
|
1526
|
+
* written. Only a workbook import populates this; the provider path always leaves it empty.
|
|
1527
|
+
*/
|
|
1528
|
+
readonly malformedRows: readonly MalformedRowReport[];
|
|
1529
|
+
/**
|
|
1530
|
+
* Duplicate-key conflicts a workbook import found in this locale's sheet, in row order. The rule is
|
|
1531
|
+
* first occurrence wins: the first row for a key is judged and its later occurrences are reported
|
|
1532
|
+
* here and otherwise ignored. Only a workbook import populates this; the provider path always leaves
|
|
1533
|
+
* it empty.
|
|
1534
|
+
*/
|
|
1535
|
+
readonly duplicateKeys: readonly DuplicateKeyReport[];
|
|
1443
1536
|
/**
|
|
1444
1537
|
* A structured, secret-free error for a locale that threw (an adapter/lock/provider-construction
|
|
1445
1538
|
* failure isolated as data): present only on a "failed" locale, and only that throw path sets it.
|
|
@@ -1564,6 +1657,63 @@ interface LockWaitEvent {
|
|
|
1564
1657
|
/** Called while an acquire is blocked, to surface wait progress; the lock module itself writes no output. */
|
|
1565
1658
|
type LockWaitListener = (event: LockWaitEvent) => void;
|
|
1566
1659
|
|
|
1660
|
+
/**
|
|
1661
|
+
* One structured progress notification emitted while a run advances through its locales and their
|
|
1662
|
+
* provider sub-batches. Emitted to a caller-supplied {@link ProgressListener}; the SDK itself writes
|
|
1663
|
+
* nothing to any stream, so a caller (the CLI) is free to render these to stderr and keep stdout a
|
|
1664
|
+
* clean summary or NDJSON stream. Discriminated on `type`, mirroring the lock-wait event contract.
|
|
1665
|
+
*/
|
|
1666
|
+
type ProgressEvent = LocaleStartedEvent | SubBatchProgressEvent | LocaleFinishedEvent | RunFinishedEvent;
|
|
1667
|
+
/** A locale is about to be processed: its position in the run's target-locale order. */
|
|
1668
|
+
interface LocaleStartedEvent {
|
|
1669
|
+
readonly type: "locale-started";
|
|
1670
|
+
/** The target locale about to run. */
|
|
1671
|
+
readonly locale: string;
|
|
1672
|
+
/** Zero-based index of this locale in the run's target-locale order. */
|
|
1673
|
+
readonly localeIndex: number;
|
|
1674
|
+
/** Total number of target locales in this run. */
|
|
1675
|
+
readonly totalLocales: number;
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* One sub-batch of a locale's main translation loop has been reached. Emitted once per loop
|
|
1679
|
+
* iteration, in order, so a caller can render a "batch N of M" line. Fired at the top of the
|
|
1680
|
+
* iteration regardless of whether the batch then makes a provider call: a budget-withheld batch
|
|
1681
|
+
* (the run's token budget already tripped) still emits this event, which keeps `batchIndex` 1-based
|
|
1682
|
+
* and contiguous against the total. A dry-run never reaches this loop and so never emits it.
|
|
1683
|
+
*/
|
|
1684
|
+
interface SubBatchProgressEvent {
|
|
1685
|
+
readonly type: "sub-batch";
|
|
1686
|
+
/** The locale whose entries this sub-batch carries. */
|
|
1687
|
+
readonly locale: string;
|
|
1688
|
+
/** One-based index of this sub-batch within the locale. */
|
|
1689
|
+
readonly batchIndex: number;
|
|
1690
|
+
/** Total number of sub-batches this locale's main translation loop runs. */
|
|
1691
|
+
readonly totalBatches: number;
|
|
1692
|
+
}
|
|
1693
|
+
/** A locale finished processing: how many keys it accepted (translated). */
|
|
1694
|
+
interface LocaleFinishedEvent {
|
|
1695
|
+
readonly type: "locale-finished";
|
|
1696
|
+
/** The target locale that finished. */
|
|
1697
|
+
readonly locale: string;
|
|
1698
|
+
/** Number of keys accepted (translated) for this locale. In a dry-run, the keys that would be translated. */
|
|
1699
|
+
readonly translated: number;
|
|
1700
|
+
}
|
|
1701
|
+
/** The run reached the end of its locale loop: how many locales it processed. */
|
|
1702
|
+
interface RunFinishedEvent {
|
|
1703
|
+
readonly type: "run-finished";
|
|
1704
|
+
/**
|
|
1705
|
+
* Number of locales the run processed (including failed locales: a per-locale failure is isolated
|
|
1706
|
+
* as a summary rather than thrown, so it is still counted here).
|
|
1707
|
+
*/
|
|
1708
|
+
readonly localesCompleted: number;
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Called with each {@link ProgressEvent} as a run advances; the SDK emits no output of its own. A
|
|
1712
|
+
* caller (the CLI) uses this to render progress, always to stderr, so stdout stays byte-identical to
|
|
1713
|
+
* a run with no listener attached.
|
|
1714
|
+
*/
|
|
1715
|
+
type ProgressListener = (event: ProgressEvent) => void;
|
|
1716
|
+
|
|
1567
1717
|
/** Everything the one-shot run needs: the validated config and where/how to run it. */
|
|
1568
1718
|
interface TranslateInput {
|
|
1569
1719
|
/** The validated configuration (typically from {@link loadConfig}). */
|
|
@@ -1592,12 +1742,43 @@ interface TranslateInput {
|
|
|
1592
1742
|
* is the only wait-progress signal (the CLI renders its "still waiting" line from it).
|
|
1593
1743
|
*/
|
|
1594
1744
|
readonly onLockWait?: LockWaitListener;
|
|
1745
|
+
/**
|
|
1746
|
+
* Called as the run advances: once per locale before it starts and once after it finishes, once
|
|
1747
|
+
* per provider sub-batch within a locale, and once when the whole locale loop ends. Never fires a
|
|
1748
|
+
* sub-batch event on a dry-run (no provider call is made). The SDK writes no output; this is the
|
|
1749
|
+
* only progress signal (the CLI renders it to stderr, keeping stdout byte-identical).
|
|
1750
|
+
*
|
|
1751
|
+
* Pairing is not guaranteed when the run throws: a whole-run failure (for example a corrupt
|
|
1752
|
+
* lock-file surfacing as `LOCK_FILE_INVALID`, which re-throws instead of being isolated per locale)
|
|
1753
|
+
* can emit a `locale-started` event with no matching `locale-finished`, and no `run-finished` at
|
|
1754
|
+
* all. A per-locale failure that is isolated (not re-thrown) still emits both, and is counted in
|
|
1755
|
+
* `run-finished`.
|
|
1756
|
+
*/
|
|
1757
|
+
readonly onProgress?: ProgressListener;
|
|
1595
1758
|
/**
|
|
1596
1759
|
* Override how long a locale's write lock keeps retrying before failing with `LOCK_CONTENDED`, in
|
|
1597
1760
|
* milliseconds. Defaults to the lock's own 10-minute default when unset (surfaced to the CLI as
|
|
1598
1761
|
* `--lock-timeout`).
|
|
1599
1762
|
*/
|
|
1600
1763
|
readonly lockAcquireTimeoutMs?: number;
|
|
1764
|
+
/**
|
|
1765
|
+
* How many target locales may run at once, a positive integer (surfaced to the CLI as
|
|
1766
|
+
* `--concurrency`). Defaults to 1, which runs locales strictly in sequence and is byte-identical
|
|
1767
|
+
* to a run with this option unset. A value below 1 or a non-integer is rejected with a whole-run
|
|
1768
|
+
* `CONCURRENCY_INVALID` error. On a live run, a value greater than 1 is rejected with a whole-run
|
|
1769
|
+
* `CONCURRENCY_BUDGET_CONFLICT` error when the config sets a `maxTokens` budget, because
|
|
1770
|
+
* concurrency would make the budget's stop guarantee nondeterministic; a dry run is exempt. The
|
|
1771
|
+
* per-locale write locks already isolate concurrent locales on disk, so no extra locking is added.
|
|
1772
|
+
*/
|
|
1773
|
+
readonly concurrency?: number;
|
|
1774
|
+
/**
|
|
1775
|
+
* Whether to use the local content-addressed translation-memory cache (`verbatra.cache.json`). On by
|
|
1776
|
+
* default; the CLI exposes it as `--no-cache`. When false, both the cache read and the end-of-run
|
|
1777
|
+
* cache write are skipped: the run makes exactly the provider calls it would with no cache present,
|
|
1778
|
+
* and leaves any existing cache file untouched. Ignored on a dry-run, which never reads or writes the
|
|
1779
|
+
* cache regardless. See the cache module for the fingerprint and degrade-to-empty contract.
|
|
1780
|
+
*/
|
|
1781
|
+
readonly cache?: boolean;
|
|
1601
1782
|
}
|
|
1602
1783
|
/** Composition seam: inject a registry, a provider builder, and a file system for tests. */
|
|
1603
1784
|
interface TranslateDeps {
|
|
@@ -1644,6 +1825,11 @@ interface TranslateDeps {
|
|
|
1644
1825
|
* @throws {@link SdkError} `SOURCE_INVALID`: the source locale file could not be read or parsed (wraps the
|
|
1645
1826
|
* adapter read error).
|
|
1646
1827
|
* @throws {@link SdkError} `LOCK_FILE_INVALID`: the lock-file is present but corrupt or oversized.
|
|
1828
|
+
* @throws {@link SdkError} `CONCURRENCY_INVALID`: `concurrency` is defined but not an integer of at
|
|
1829
|
+
* least 1; raised before any locale runs.
|
|
1830
|
+
* @throws {@link SdkError} `CONCURRENCY_BUDGET_CONFLICT`: a live run set `concurrency` greater than 1
|
|
1831
|
+
* while the config configures a `maxTokens` budget; raised before any provider call (a dry run is
|
|
1832
|
+
* exempt).
|
|
1647
1833
|
* @example
|
|
1648
1834
|
* ```ts
|
|
1649
1835
|
* import { loadConfig, translate } from "@verbatra/sdk";
|
|
@@ -1735,10 +1921,13 @@ interface ImportWorkbookDeps {
|
|
|
1735
1921
|
* identical to `translate`'s.
|
|
1736
1922
|
*
|
|
1737
1923
|
* Whole-run failures (unknown format, unreadable/invalid/oversized workbook, corrupt lock) throw a
|
|
1738
|
-
* structured {@link SdkError}. A per-sheet failure (a locale not in config, a
|
|
1739
|
-
* a write failure) is isolated as that locale's `status: "failed"`, not a
|
|
1740
|
-
* are withheld and reported on the locale.
|
|
1741
|
-
*
|
|
1924
|
+
* structured {@link SdkError}. A per-sheet failure (a sheet named for a locale not in config, a
|
|
1925
|
+
* broken-round-trip key, a write failure) is isolated as that locale's `status: "failed"`, not a
|
|
1926
|
+
* throw; per-row rejections are withheld and reported on the locale. A configured target locale with
|
|
1927
|
+
* no sheet at all (a deleted, renamed, or reordered tab) is reconciled after the sheet loop and
|
|
1928
|
+
* reported as that locale's `status: "failed"` (`WORKBOOK_SHEET_MISSING`) rather than silently
|
|
1929
|
+
* dropped. Dry-run validates and reports without writing any locale or lock file, and skips lock
|
|
1930
|
+
* acquisition (there is nothing to protect).
|
|
1742
1931
|
*
|
|
1743
1932
|
* The lock-file is read once, up front, for every sheet's diff baseline. On a non-dry-run, each
|
|
1744
1933
|
* sheet's write-and-lock-update step then holds that locale's `withLocaleWriteLock` for its whole
|
|
@@ -1817,7 +2006,7 @@ declare const scaffoldingMetadata: {
|
|
|
1817
2006
|
readonly gemini: "gemini-2.5-flash";
|
|
1818
2007
|
};
|
|
1819
2008
|
/** The closed set of source format ids. Owned by core. */
|
|
1820
|
-
readonly supportedFormats: readonly ["i18next-json", "vue-i18n-json", "next-intl-json", "ngx-translate-json", "xliff", "yaml", "arb"];
|
|
2009
|
+
readonly supportedFormats: readonly ["i18next-json", "vue-i18n-json", "next-intl-json", "ngx-translate-json", "xliff", "yaml", "arb", "properties"];
|
|
1821
2010
|
};
|
|
1822
2011
|
|
|
1823
2012
|
/** A minimal source-change event source. Production wraps chokidar; tests inject a stub. */
|
|
@@ -1862,8 +2051,24 @@ interface WatchInput {
|
|
|
1862
2051
|
* one-shot run does.
|
|
1863
2052
|
*/
|
|
1864
2053
|
readonly onLockWait?: LockWaitListener;
|
|
2054
|
+
/**
|
|
2055
|
+
* Passed through to every run's {@link TranslateInput.onProgress}: fires per locale and per provider
|
|
2056
|
+
* sub-batch as each run advances, so a watch caller can surface the same progress a one-shot run does.
|
|
2057
|
+
*/
|
|
2058
|
+
readonly onProgress?: ProgressListener;
|
|
1865
2059
|
/** Passed through to every run's {@link TranslateInput.lockAcquireTimeoutMs}; the lock's 10-minute default when unset. */
|
|
1866
2060
|
readonly lockAcquireTimeoutMs?: number;
|
|
2061
|
+
/**
|
|
2062
|
+
* Passed through to every run's {@link TranslateInput.concurrency}: how many locales each cycle may
|
|
2063
|
+
* run at once. Defaults to 1 (strictly serial). The per-cycle tracker is fresh, so a budgeted watch
|
|
2064
|
+
* with concurrency greater than 1 still fails each run with `CONCURRENCY_BUDGET_CONFLICT`.
|
|
2065
|
+
*/
|
|
2066
|
+
readonly concurrency?: number;
|
|
2067
|
+
/**
|
|
2068
|
+
* Passed through to every run's {@link TranslateInput.cache}: when false, each run bypasses the
|
|
2069
|
+
* translation-memory cache (both read and write). On by default.
|
|
2070
|
+
*/
|
|
2071
|
+
readonly cache?: boolean;
|
|
1867
2072
|
}
|
|
1868
2073
|
/** Composition seam: inject the watcher and the run for deterministic, offline tests. */
|
|
1869
2074
|
interface WatchDeps {
|
|
@@ -1920,4 +2125,4 @@ interface WatchController {
|
|
|
1920
2125
|
*/
|
|
1921
2126
|
declare function watch(input: WatchInput, deps?: WatchDeps): Promise<WatchController>;
|
|
1922
2127
|
|
|
1923
|
-
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 IntegrityGateReason, 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 LockHolder, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type LockWaitEvent, type LockWaitListener, 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 };
|
|
2128
|
+
export { type BudgetBehavior, CACHE_FILE_NAME, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type DuplicateKeyReport, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryProvenance, type ImportWorkbookDeps, type ImportWorkbookInput, type IntegrityGateReason, 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 LocaleFinishedEvent, type LocaleKeyIntegrity, type LocaleNotice, type LocaleSnapshotDelta, type LocaleStartedEvent, type LocaleSummary, type LockFile, type LockHolder, type LockLocaleState, type LockStateDeps, type LockStateInput, type LockStateResult, type LockWaitEvent, type LockWaitListener, type MalformedRowReport, type NeedsReviewEntry, type ProgressEvent, type ProgressListener, type ProviderConfig, type ProviderId, type ReadLocaleFileSnapshotDeps, type ReadLocaleFileSnapshotInput, type RetranslateEntryDeps, type RetranslateEntryInput, type RetranslateEntryResult, type ReviewReasonCode, type RunBudget, type RunFinishedEvent, 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 SubBatchProgressEvent, type SupportedFormat$1 as SupportedFormat, type TranslateDeps, type TranslateInput, type TranslationMemory, 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 };
|