@verbatra/sdk 0.6.2 → 0.6.4
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 +27 -13
- package/dist/index.cjs +127 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -10
- package/dist/index.d.ts +64 -10
- package/dist/index.js +127 -64
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.d.cts
CHANGED
|
@@ -527,6 +527,7 @@ type GlossaryProvenance = {
|
|
|
527
527
|
readonly path: string;
|
|
528
528
|
};
|
|
529
529
|
|
|
530
|
+
/** Where {@link loadConfig} should obtain the configuration. The three sources are alternatives. */
|
|
530
531
|
interface LoadConfigOptions {
|
|
531
532
|
/** Directory to start the search from. Defaults to the current working directory. */
|
|
532
533
|
readonly cwd?: string;
|
|
@@ -972,7 +973,7 @@ interface DiffDeps {
|
|
|
972
973
|
declare function diff(input: DiffInput, deps?: DiffDeps): Promise<DiffSummary>;
|
|
973
974
|
|
|
974
975
|
/** Why {@link gateCandidateValue} rejected a candidate value. */
|
|
975
|
-
type IntegrityGateReason = "placeholder" | "icu" | "degenerate";
|
|
976
|
+
type IntegrityGateReason = "placeholder" | "icu" | "degenerate" | "empty";
|
|
976
977
|
|
|
977
978
|
/** Input for {@link editEntry}: the validated config, exactly one target locale/key pair, and the human-typed replacement value. */
|
|
978
979
|
interface EditEntryInput {
|
|
@@ -1348,7 +1349,7 @@ type RetranslateEntryResult = {
|
|
|
1348
1349
|
declare function retranslateEntry(input: RetranslateEntryInput, deps?: RetranslateEntryDeps): Promise<RetranslateEntryResult>;
|
|
1349
1350
|
|
|
1350
1351
|
/** A stable code for an SDK-originated notice (not a provider notice). */
|
|
1351
|
-
type SdkNoticeCode = "PLURAL_CATEGORIES_INCOMPLETE" | "SUB_BATCH_FAILED" | "BLANK_ROW_BASELINE_RETAINED" | "BUDGET_TOKENS_EXCEEDED";
|
|
1352
|
+
type SdkNoticeCode = "PLURAL_CATEGORIES_INCOMPLETE" | "SUB_BATCH_FAILED" | "BLANK_ROW_BASELINE_RETAINED" | "BUDGET_TOKENS_EXCEEDED" | "CACHE_VERSION_UNRECOGNIZED";
|
|
1352
1353
|
/** Summed token usage across every provider call in a scope (one locale, or the whole run). */
|
|
1353
1354
|
interface UsageSummary {
|
|
1354
1355
|
readonly inputTokens: number;
|
|
@@ -1432,7 +1433,27 @@ interface LocaleSummary {
|
|
|
1432
1433
|
* {@link LocaleSummary.malformedRows}, and {@link LocaleSummary.duplicateKeys}, deliberately do NOT
|
|
1433
1434
|
* feed this status: they are surfaced in their own lists (and the CLI) but are not withholdings of
|
|
1434
1435
|
* an attempted translation, so a locale that dropped a malformed row, collapsed a duplicate key, or
|
|
1435
|
-
* left a
|
|
1436
|
+
* left a row unfilled while accepting its other rows can still be `"succeeded"`.
|
|
1437
|
+
*
|
|
1438
|
+
* This is a settled decision, not an omission. Recording the reasoning here so the question stops
|
|
1439
|
+
* recurring:
|
|
1440
|
+
*
|
|
1441
|
+
* 1. Two questions, two commands. `import`'s exit code answers "did the import apply what the
|
|
1442
|
+
* translator gave me". `check` and `diff` answer "is this project fully translated", and both
|
|
1443
|
+
* already exit non-zero or report pending in every one of these cases. Folding pending human
|
|
1444
|
+
* work into the import exit code duplicates an existing gate rather than adding one.
|
|
1445
|
+
* 2. It would break the staged handoff. With `de` and `fr` configured and only `de` filled in,
|
|
1446
|
+
* failing on unfilled work makes `fr` fail and takes the whole import with it, even though `de`
|
|
1447
|
+
* imported perfectly. Locale-at-a-time handoff is this feature's most common workflow.
|
|
1448
|
+
* 3. {@link LocaleSummary.malformedRows} structurally cannot signal lost work. The Status column is
|
|
1449
|
+
* the only reachable rejection in the workbook reader; every other field is an unconstrained
|
|
1450
|
+
* string or a tolerant parse. So a row is malformed on its Status cell alone, entirely
|
|
1451
|
+
* independently of whether its Translation cell was filled. An untouched sheet with a mangled
|
|
1452
|
+
* Status column (spreadsheet autocorrect, a CAT-tool round trip) is all-malformed with zero lost
|
|
1453
|
+
* work. A bucket that cannot distinguish dropped work from absent work cannot honestly drive a
|
|
1454
|
+
* failure status.
|
|
1455
|
+
* 4. {@link LocaleSummary.duplicateKeys} never could: the first occurrence wins and the intent is
|
|
1456
|
+
* honoured.
|
|
1436
1457
|
*/
|
|
1437
1458
|
readonly status: "succeeded" | "partial" | "failed";
|
|
1438
1459
|
/**
|
|
@@ -1513,11 +1534,16 @@ interface LocaleSummary {
|
|
|
1513
1534
|
*/
|
|
1514
1535
|
readonly needsReview: readonly NeedsReviewEntry[];
|
|
1515
1536
|
/**
|
|
1516
|
-
* Keys the
|
|
1517
|
-
*
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1520
|
-
*
|
|
1537
|
+
* Keys the translator left blank that still need a translation, drifted or not, sorted by key.
|
|
1538
|
+
* Pending work made visible: the row is skipped (nothing is written and the prior lock baseline is
|
|
1539
|
+
* kept), but the key is surfaced so an unfinished import is never silently reported as done. Only a
|
|
1540
|
+
* workbook import populates this; the provider path, which never leaves a candidate key unfilled by
|
|
1541
|
+
* a person, always leaves it empty.
|
|
1542
|
+
*
|
|
1543
|
+
* Membership is decided by the import-time diff, not by the `status` the row was exported with: a
|
|
1544
|
+
* never-translated key exports as `new`, and a first handoff is all-new, which is the most common
|
|
1545
|
+
* unfilled case there is. A row exported as `changed` whose key no longer needs work by import time
|
|
1546
|
+
* is correspondingly excluded.
|
|
1521
1547
|
*/
|
|
1522
1548
|
readonly unfilled: readonly string[];
|
|
1523
1549
|
/**
|
|
@@ -1697,6 +1723,19 @@ interface LocaleFinishedEvent {
|
|
|
1697
1723
|
readonly locale: string;
|
|
1698
1724
|
/** Number of keys accepted (translated) for this locale. In a dry-run, the keys that would be translated. */
|
|
1699
1725
|
readonly translated: number;
|
|
1726
|
+
/**
|
|
1727
|
+
* Zero-based index of this locale in the run's target-locale order, carrying the same value as the
|
|
1728
|
+
* matching {@link LocaleStartedEvent}. This is a correlation key, so a consumer can pair a finish
|
|
1729
|
+
* with its start without matching on the locale name.
|
|
1730
|
+
*
|
|
1731
|
+
* It is deliberately NOT a progress counter. The worker pool claims indices up front, so this is a
|
|
1732
|
+
* claim ordinal rather than a completion ordinal: under concurrency the locales finish in a
|
|
1733
|
+
* different order, and rendering `localeIndex + 1` of `totalLocales` on a finish produces a
|
|
1734
|
+
* counter that jumps around and never reaches its own total in order. Do not reintroduce one.
|
|
1735
|
+
*/
|
|
1736
|
+
readonly localeIndex: number;
|
|
1737
|
+
/** Total number of target locales in this run, mirroring {@link LocaleStartedEvent}. */
|
|
1738
|
+
readonly totalLocales: number;
|
|
1700
1739
|
}
|
|
1701
1740
|
/** The run reached the end of its locale loop: how many locales it processed. */
|
|
1702
1741
|
interface RunFinishedEvent {
|
|
@@ -2060,8 +2099,9 @@ interface WatchInput {
|
|
|
2060
2099
|
readonly lockAcquireTimeoutMs?: number;
|
|
2061
2100
|
/**
|
|
2062
2101
|
* Passed through to every run's {@link TranslateInput.concurrency}: how many locales each cycle may
|
|
2063
|
-
* run at once. Defaults to 1 (strictly serial).
|
|
2064
|
-
*
|
|
2102
|
+
* run at once. Defaults to 1 (strictly serial). Because the per-cycle budget tracker is fresh, a
|
|
2103
|
+
* value greater than 1 cannot be honored alongside a `maxTokens` budget; {@link watch} refuses that
|
|
2104
|
+
* combination at startup rather than letting every cycle fail on it.
|
|
2065
2105
|
*/
|
|
2066
2106
|
readonly concurrency?: number;
|
|
2067
2107
|
/**
|
|
@@ -2095,9 +2135,23 @@ interface WatchController {
|
|
|
2095
2135
|
* `onRun` as a {@link WatchRunResult} and watching continues. Returns a controller whose `stop()`
|
|
2096
2136
|
* closes the watcher and awaits the in-flight run.
|
|
2097
2137
|
*
|
|
2138
|
+
* Both startup refusals are ordered deliberately: `concurrency` is resolved first because it is
|
|
2139
|
+
* decidable from the arguments alone, while the missing-source check is an I/O probe of project
|
|
2140
|
+
* state, so argument validation runs before any I/O and before the watcher exists. The concurrency
|
|
2141
|
+
* resolve passes `dryRun: false` unconditionally, which is exact rather than a simplification: there
|
|
2142
|
+
* is no dry-run watch, so the budget conflict always applies to a session.
|
|
2143
|
+
*
|
|
2144
|
+
* Resolving it here rather than leaving it to each cycle is the point, not an optimization: a watch
|
|
2145
|
+
* session is long-lived, so a per-cycle refusal would fail the initial run and every run after it,
|
|
2146
|
+
* indefinitely, while the session stayed alive and looked healthy.
|
|
2147
|
+
*
|
|
2098
2148
|
* @param input - The config, optional cwd/debounce, and the `onRun` callback that receives each result.
|
|
2099
2149
|
* @param deps - Optional composition seams (watcher, run, registry, provider builder, file system) for tests.
|
|
2100
2150
|
* @returns A {@link WatchController}; call `stop()` to close the watcher and await the in-flight run.
|
|
2151
|
+
* @throws {@link SdkError} `CONCURRENCY_INVALID`: at startup only, when `concurrency` is defined but
|
|
2152
|
+
* not an integer of at least 1.
|
|
2153
|
+
* @throws {@link SdkError} `CONCURRENCY_BUDGET_CONFLICT`: at startup only, when `concurrency` is
|
|
2154
|
+
* greater than 1 while the config sets a `maxTokens` budget.
|
|
2101
2155
|
* @throws {@link SdkError} `SOURCE_UNREADABLE`: at startup only, when the source locale file is absent.
|
|
2102
2156
|
* @example
|
|
2103
2157
|
* ```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -527,6 +527,7 @@ type GlossaryProvenance = {
|
|
|
527
527
|
readonly path: string;
|
|
528
528
|
};
|
|
529
529
|
|
|
530
|
+
/** Where {@link loadConfig} should obtain the configuration. The three sources are alternatives. */
|
|
530
531
|
interface LoadConfigOptions {
|
|
531
532
|
/** Directory to start the search from. Defaults to the current working directory. */
|
|
532
533
|
readonly cwd?: string;
|
|
@@ -972,7 +973,7 @@ interface DiffDeps {
|
|
|
972
973
|
declare function diff(input: DiffInput, deps?: DiffDeps): Promise<DiffSummary>;
|
|
973
974
|
|
|
974
975
|
/** Why {@link gateCandidateValue} rejected a candidate value. */
|
|
975
|
-
type IntegrityGateReason = "placeholder" | "icu" | "degenerate";
|
|
976
|
+
type IntegrityGateReason = "placeholder" | "icu" | "degenerate" | "empty";
|
|
976
977
|
|
|
977
978
|
/** Input for {@link editEntry}: the validated config, exactly one target locale/key pair, and the human-typed replacement value. */
|
|
978
979
|
interface EditEntryInput {
|
|
@@ -1348,7 +1349,7 @@ type RetranslateEntryResult = {
|
|
|
1348
1349
|
declare function retranslateEntry(input: RetranslateEntryInput, deps?: RetranslateEntryDeps): Promise<RetranslateEntryResult>;
|
|
1349
1350
|
|
|
1350
1351
|
/** A stable code for an SDK-originated notice (not a provider notice). */
|
|
1351
|
-
type SdkNoticeCode = "PLURAL_CATEGORIES_INCOMPLETE" | "SUB_BATCH_FAILED" | "BLANK_ROW_BASELINE_RETAINED" | "BUDGET_TOKENS_EXCEEDED";
|
|
1352
|
+
type SdkNoticeCode = "PLURAL_CATEGORIES_INCOMPLETE" | "SUB_BATCH_FAILED" | "BLANK_ROW_BASELINE_RETAINED" | "BUDGET_TOKENS_EXCEEDED" | "CACHE_VERSION_UNRECOGNIZED";
|
|
1352
1353
|
/** Summed token usage across every provider call in a scope (one locale, or the whole run). */
|
|
1353
1354
|
interface UsageSummary {
|
|
1354
1355
|
readonly inputTokens: number;
|
|
@@ -1432,7 +1433,27 @@ interface LocaleSummary {
|
|
|
1432
1433
|
* {@link LocaleSummary.malformedRows}, and {@link LocaleSummary.duplicateKeys}, deliberately do NOT
|
|
1433
1434
|
* feed this status: they are surfaced in their own lists (and the CLI) but are not withholdings of
|
|
1434
1435
|
* an attempted translation, so a locale that dropped a malformed row, collapsed a duplicate key, or
|
|
1435
|
-
* left a
|
|
1436
|
+
* left a row unfilled while accepting its other rows can still be `"succeeded"`.
|
|
1437
|
+
*
|
|
1438
|
+
* This is a settled decision, not an omission. Recording the reasoning here so the question stops
|
|
1439
|
+
* recurring:
|
|
1440
|
+
*
|
|
1441
|
+
* 1. Two questions, two commands. `import`'s exit code answers "did the import apply what the
|
|
1442
|
+
* translator gave me". `check` and `diff` answer "is this project fully translated", and both
|
|
1443
|
+
* already exit non-zero or report pending in every one of these cases. Folding pending human
|
|
1444
|
+
* work into the import exit code duplicates an existing gate rather than adding one.
|
|
1445
|
+
* 2. It would break the staged handoff. With `de` and `fr` configured and only `de` filled in,
|
|
1446
|
+
* failing on unfilled work makes `fr` fail and takes the whole import with it, even though `de`
|
|
1447
|
+
* imported perfectly. Locale-at-a-time handoff is this feature's most common workflow.
|
|
1448
|
+
* 3. {@link LocaleSummary.malformedRows} structurally cannot signal lost work. The Status column is
|
|
1449
|
+
* the only reachable rejection in the workbook reader; every other field is an unconstrained
|
|
1450
|
+
* string or a tolerant parse. So a row is malformed on its Status cell alone, entirely
|
|
1451
|
+
* independently of whether its Translation cell was filled. An untouched sheet with a mangled
|
|
1452
|
+
* Status column (spreadsheet autocorrect, a CAT-tool round trip) is all-malformed with zero lost
|
|
1453
|
+
* work. A bucket that cannot distinguish dropped work from absent work cannot honestly drive a
|
|
1454
|
+
* failure status.
|
|
1455
|
+
* 4. {@link LocaleSummary.duplicateKeys} never could: the first occurrence wins and the intent is
|
|
1456
|
+
* honoured.
|
|
1436
1457
|
*/
|
|
1437
1458
|
readonly status: "succeeded" | "partial" | "failed";
|
|
1438
1459
|
/**
|
|
@@ -1513,11 +1534,16 @@ interface LocaleSummary {
|
|
|
1513
1534
|
*/
|
|
1514
1535
|
readonly needsReview: readonly NeedsReviewEntry[];
|
|
1515
1536
|
/**
|
|
1516
|
-
* Keys the
|
|
1517
|
-
*
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1520
|
-
*
|
|
1537
|
+
* Keys the translator left blank that still need a translation, drifted or not, sorted by key.
|
|
1538
|
+
* Pending work made visible: the row is skipped (nothing is written and the prior lock baseline is
|
|
1539
|
+
* kept), but the key is surfaced so an unfinished import is never silently reported as done. Only a
|
|
1540
|
+
* workbook import populates this; the provider path, which never leaves a candidate key unfilled by
|
|
1541
|
+
* a person, always leaves it empty.
|
|
1542
|
+
*
|
|
1543
|
+
* Membership is decided by the import-time diff, not by the `status` the row was exported with: a
|
|
1544
|
+
* never-translated key exports as `new`, and a first handoff is all-new, which is the most common
|
|
1545
|
+
* unfilled case there is. A row exported as `changed` whose key no longer needs work by import time
|
|
1546
|
+
* is correspondingly excluded.
|
|
1521
1547
|
*/
|
|
1522
1548
|
readonly unfilled: readonly string[];
|
|
1523
1549
|
/**
|
|
@@ -1697,6 +1723,19 @@ interface LocaleFinishedEvent {
|
|
|
1697
1723
|
readonly locale: string;
|
|
1698
1724
|
/** Number of keys accepted (translated) for this locale. In a dry-run, the keys that would be translated. */
|
|
1699
1725
|
readonly translated: number;
|
|
1726
|
+
/**
|
|
1727
|
+
* Zero-based index of this locale in the run's target-locale order, carrying the same value as the
|
|
1728
|
+
* matching {@link LocaleStartedEvent}. This is a correlation key, so a consumer can pair a finish
|
|
1729
|
+
* with its start without matching on the locale name.
|
|
1730
|
+
*
|
|
1731
|
+
* It is deliberately NOT a progress counter. The worker pool claims indices up front, so this is a
|
|
1732
|
+
* claim ordinal rather than a completion ordinal: under concurrency the locales finish in a
|
|
1733
|
+
* different order, and rendering `localeIndex + 1` of `totalLocales` on a finish produces a
|
|
1734
|
+
* counter that jumps around and never reaches its own total in order. Do not reintroduce one.
|
|
1735
|
+
*/
|
|
1736
|
+
readonly localeIndex: number;
|
|
1737
|
+
/** Total number of target locales in this run, mirroring {@link LocaleStartedEvent}. */
|
|
1738
|
+
readonly totalLocales: number;
|
|
1700
1739
|
}
|
|
1701
1740
|
/** The run reached the end of its locale loop: how many locales it processed. */
|
|
1702
1741
|
interface RunFinishedEvent {
|
|
@@ -2060,8 +2099,9 @@ interface WatchInput {
|
|
|
2060
2099
|
readonly lockAcquireTimeoutMs?: number;
|
|
2061
2100
|
/**
|
|
2062
2101
|
* Passed through to every run's {@link TranslateInput.concurrency}: how many locales each cycle may
|
|
2063
|
-
* run at once. Defaults to 1 (strictly serial).
|
|
2064
|
-
*
|
|
2102
|
+
* run at once. Defaults to 1 (strictly serial). Because the per-cycle budget tracker is fresh, a
|
|
2103
|
+
* value greater than 1 cannot be honored alongside a `maxTokens` budget; {@link watch} refuses that
|
|
2104
|
+
* combination at startup rather than letting every cycle fail on it.
|
|
2065
2105
|
*/
|
|
2066
2106
|
readonly concurrency?: number;
|
|
2067
2107
|
/**
|
|
@@ -2095,9 +2135,23 @@ interface WatchController {
|
|
|
2095
2135
|
* `onRun` as a {@link WatchRunResult} and watching continues. Returns a controller whose `stop()`
|
|
2096
2136
|
* closes the watcher and awaits the in-flight run.
|
|
2097
2137
|
*
|
|
2138
|
+
* Both startup refusals are ordered deliberately: `concurrency` is resolved first because it is
|
|
2139
|
+
* decidable from the arguments alone, while the missing-source check is an I/O probe of project
|
|
2140
|
+
* state, so argument validation runs before any I/O and before the watcher exists. The concurrency
|
|
2141
|
+
* resolve passes `dryRun: false` unconditionally, which is exact rather than a simplification: there
|
|
2142
|
+
* is no dry-run watch, so the budget conflict always applies to a session.
|
|
2143
|
+
*
|
|
2144
|
+
* Resolving it here rather than leaving it to each cycle is the point, not an optimization: a watch
|
|
2145
|
+
* session is long-lived, so a per-cycle refusal would fail the initial run and every run after it,
|
|
2146
|
+
* indefinitely, while the session stayed alive and looked healthy.
|
|
2147
|
+
*
|
|
2098
2148
|
* @param input - The config, optional cwd/debounce, and the `onRun` callback that receives each result.
|
|
2099
2149
|
* @param deps - Optional composition seams (watcher, run, registry, provider builder, file system) for tests.
|
|
2100
2150
|
* @returns A {@link WatchController}; call `stop()` to close the watcher and await the in-flight run.
|
|
2151
|
+
* @throws {@link SdkError} `CONCURRENCY_INVALID`: at startup only, when `concurrency` is defined but
|
|
2152
|
+
* not an integer of at least 1.
|
|
2153
|
+
* @throws {@link SdkError} `CONCURRENCY_BUDGET_CONFLICT`: at startup only, when `concurrency` is
|
|
2154
|
+
* greater than 1 while the config sets a `maxTokens` budget.
|
|
2101
2155
|
* @throws {@link SdkError} `SOURCE_UNREADABLE`: at startup only, when the source locale file is absent.
|
|
2102
2156
|
* @example
|
|
2103
2157
|
* ```ts
|