@verbatra/sdk 0.8.0 → 0.9.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 +44 -15
- package/dist/config-schema.json +306 -0
- package/dist/index.cjs +725 -290
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +390 -75
- package/dist/index.d.ts +390 -75
- package/dist/index.js +724 -292
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.d.ts
CHANGED
|
@@ -69,10 +69,11 @@ type Tone = "formal" | "informal" | "neutral";
|
|
|
69
69
|
*/
|
|
70
70
|
type PlaceholderExtractor = (value: string) => readonly string[];
|
|
71
71
|
/**
|
|
72
|
-
* Compares a source and translated value
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* ai-providers never derives it itself and never parses
|
|
72
|
+
* Compares a source and translated value directly instead of independently extracting each side's flat
|
|
73
|
+
* placeholder list first, which lets it express checks the flat lists cannot (branch-aware ICU
|
|
74
|
+
* comparison, or a one-directional rule). Supplied by the caller (the SDK, for a format whose adapter
|
|
75
|
+
* defines one) so it matches the entries' format; ai-providers never derives it itself and never parses
|
|
76
|
+
* a specific format's message syntax.
|
|
76
77
|
*/
|
|
77
78
|
type PlaceholderComparator = (source: string, translated: string) => PlaceholderIntegrityResult$1;
|
|
78
79
|
/**
|
|
@@ -94,9 +95,11 @@ interface TranslateRequest {
|
|
|
94
95
|
/** Mandatory placeholder extractor; the output integrity check runs against it. */
|
|
95
96
|
readonly extractPlaceholders: PlaceholderExtractor;
|
|
96
97
|
/**
|
|
97
|
-
* Optional
|
|
98
|
+
* Optional whole-value placeholder comparator. When present, the output integrity check uses it
|
|
98
99
|
* instead of independently extracting each side's placeholders with {@link extractPlaceholders} and
|
|
99
|
-
* diffing the flat lists.
|
|
100
|
+
* diffing the flat lists. Supplied for a format whose adapter defines one, whether because the
|
|
101
|
+
* comparison is branch-aware or because it adds a check the flat lists cannot express. Absent
|
|
102
|
+
* otherwise.
|
|
100
103
|
*/
|
|
101
104
|
readonly comparePlaceholders?: PlaceholderComparator;
|
|
102
105
|
/**
|
|
@@ -310,11 +313,11 @@ type BoundedBytesRead = {
|
|
|
310
313
|
* `deps.fs` option redirects that I/O, which is how the SDK's tests avoid touching disk and how an
|
|
311
314
|
* embedding application can back part of a project with something other than a local disk.
|
|
312
315
|
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
316
|
+
* The seam carries every file the SDK touches: the run-status file, the lock-file, the config
|
|
317
|
+
* glossary, workbook and interchange I/O, and the locale files themselves, which the adapters read
|
|
318
|
+
* and write through a port built from this one. The single exception is a caller-supplied
|
|
319
|
+
* `deps.adapterRegistry`: those adapters were constructed by the caller, so their file access is
|
|
320
|
+
* whatever the caller wired into them, and supplying both means the caller owns that wiring.
|
|
318
321
|
*
|
|
319
322
|
* Reads are size-bounded by contract so that a hostile or accidentally huge file cannot exhaust
|
|
320
323
|
* memory. Writes are expected to be atomic: the default implementation writes to a temporary file
|
|
@@ -435,12 +438,17 @@ type ProviderId = ProviderConfig["id"];
|
|
|
435
438
|
* own tooling.
|
|
436
439
|
*
|
|
437
440
|
* The object is strict, so an unrecognized key is an error rather than being silently ignored: a
|
|
438
|
-
* typo in a config file is reported instead of quietly doing nothing.
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
441
|
+
* typo in a config file is reported instead of quietly doing nothing. The one exception is the
|
|
442
|
+
* optional `$schema` key, accepted so a JSON or YAML config can point an editor at the JSON Schema
|
|
443
|
+
* document the package ships as `@verbatra/sdk/config-schema.json`.
|
|
444
|
+
*
|
|
445
|
+
* Beyond the per-field checks, two whole-config rules are enforced: `targetLocales` must not
|
|
446
|
+
* contain the source locale, and it must not contain two locales that differ only in case (they
|
|
447
|
+
* would collide on a case-insensitive file system). The `{locale}` token requirement is a
|
|
448
|
+
* per-field check on `files.pattern`.
|
|
442
449
|
*/
|
|
443
450
|
declare const verbatraConfigSchema: z.ZodObject<{
|
|
451
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
444
452
|
sourceLocale: z.ZodString;
|
|
445
453
|
targetLocales: z.ZodArray<z.ZodString>;
|
|
446
454
|
format: z.ZodEnum<{
|
|
@@ -682,6 +690,90 @@ type GlossaryProvenance = {
|
|
|
682
690
|
readonly path: string;
|
|
683
691
|
};
|
|
684
692
|
|
|
693
|
+
/** Input for {@link readGlossaryFile} and {@link updateGlossaryTerm}. */
|
|
694
|
+
interface GlossaryFileInput {
|
|
695
|
+
/**
|
|
696
|
+
* Where the loaded config's glossary came from, as reported by {@link loadConfigWithMeta}. Only a
|
|
697
|
+
* `file` provenance is accepted; it also carries the path, which is why neither function takes one.
|
|
698
|
+
*/
|
|
699
|
+
readonly glossary: GlossaryProvenance;
|
|
700
|
+
}
|
|
701
|
+
/** Input for {@link updateGlossaryTerm}. */
|
|
702
|
+
interface UpdateGlossaryTermInput extends GlossaryFileInput {
|
|
703
|
+
/** Directory the write lock is taken under. Defaults to the process working directory. */
|
|
704
|
+
readonly cwd?: string;
|
|
705
|
+
/** The source term to add, replace, or remove. Must not be blank. */
|
|
706
|
+
readonly term: string;
|
|
707
|
+
/** The translation to store for the term, or `null` to remove the term. Must not be blank. */
|
|
708
|
+
readonly translation: string | null;
|
|
709
|
+
}
|
|
710
|
+
/** Injectable dependencies for {@link readGlossaryFile} and {@link updateGlossaryTerm}. */
|
|
711
|
+
interface GlossaryFileDeps {
|
|
712
|
+
/** File-system port. Defaults to the real file system. */
|
|
713
|
+
readonly fs?: SdkFs;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Reads a file-backed glossary fresh from disk, under the same validation {@link loadConfig} applies
|
|
717
|
+
* when it resolves a glossary path. Use it when a long-running tool has to show the glossary as it
|
|
718
|
+
* is now rather than as it was when the config was loaded.
|
|
719
|
+
*
|
|
720
|
+
* The file to read comes from the provenance itself, so there is no way to point this at a file the
|
|
721
|
+
* loaded config does not name.
|
|
722
|
+
*
|
|
723
|
+
* @param input - The glossary provenance from a loaded config.
|
|
724
|
+
* @param deps - Optional file-system override.
|
|
725
|
+
* @returns The glossary as a flat term map.
|
|
726
|
+
*
|
|
727
|
+
* @throws {@link SdkError} `GLOSSARY_NOT_FILE_BACKED`: the config's glossary is inline or absent, so
|
|
728
|
+
* there is no file to read.
|
|
729
|
+
* @throws {@link SdkError} `CONFIG_INVALID`: the glossary file is missing, oversized, not UTF-8, not
|
|
730
|
+
* valid JSON, or not a flat object of string keys to string values.
|
|
731
|
+
*/
|
|
732
|
+
declare function readGlossaryFile(input: GlossaryFileInput, deps?: GlossaryFileDeps): Promise<Readonly<Record<string, string>>>;
|
|
733
|
+
/**
|
|
734
|
+
* Adds, replaces, or removes one term in a file-backed glossary, rewriting the JSON file in place.
|
|
735
|
+
* This is the write path behind a glossary editor: one term changes and the rest of the file keeps
|
|
736
|
+
* its order and its indentation.
|
|
737
|
+
*
|
|
738
|
+
* Only a file-backed glossary can be edited. An inline glossary lives inside the config module,
|
|
739
|
+
* which is executable code this function will not rewrite, and no glossary at all has no file to
|
|
740
|
+
* write to; both are refused rather than converted.
|
|
741
|
+
*
|
|
742
|
+
* The whole read-modify-write runs under a project-wide glossary lock, so two concurrent edits are
|
|
743
|
+
* serialized rather than interleaved, and the file itself is replaced atomically. The result is
|
|
744
|
+
* validated to stay within the same size limit {@link loadConfig} enforces when it reads a glossary
|
|
745
|
+
* file back, so an accepted write is always re-readable.
|
|
746
|
+
*
|
|
747
|
+
* @param input - The glossary provenance, the term, and its new translation or `null` to remove it.
|
|
748
|
+
* @param deps - Optional file-system override.
|
|
749
|
+
* @returns The glossary as it now stands on disk.
|
|
750
|
+
*
|
|
751
|
+
* @throws {@link SdkError} `GLOSSARY_NOT_FILE_BACKED`: the config's glossary is inline or absent, so
|
|
752
|
+
* there is no file to write.
|
|
753
|
+
* @throws {@link SdkError} `CONFIG_INVALID`: the term or its translation is blank, the glossary file
|
|
754
|
+
* on disk is missing, oversized, not UTF-8, not valid JSON, or not a flat object of string keys to
|
|
755
|
+
* string values, or the updated glossary would exceed the maximum glossary file size.
|
|
756
|
+
* @throws {@link SdkError} `LOCK_CONTENDED`: the project's glossary write lock could not be acquired
|
|
757
|
+
* before the timeout elapsed.
|
|
758
|
+
* @throws {@link SdkError} `GLOSSARY_UNWRITABLE`: the glossary file could not be written.
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```ts
|
|
762
|
+
* import { loadConfigWithMeta, updateGlossaryTerm } from "@verbatra/sdk";
|
|
763
|
+
*
|
|
764
|
+
* const loaded = await loadConfigWithMeta({ cwd: process.cwd() });
|
|
765
|
+
* const glossary = await updateGlossaryTerm({
|
|
766
|
+
* glossary: loaded.glossary,
|
|
767
|
+
* cwd: process.cwd(),
|
|
768
|
+
* term: "Verbatra",
|
|
769
|
+
* translation: "Verbatra",
|
|
770
|
+
* });
|
|
771
|
+
*
|
|
772
|
+
* console.log(Object.keys(glossary).length);
|
|
773
|
+
* ```
|
|
774
|
+
*/
|
|
775
|
+
declare function updateGlossaryTerm(input: UpdateGlossaryTermInput, deps?: GlossaryFileDeps): Promise<Readonly<Record<string, string>>>;
|
|
776
|
+
|
|
685
777
|
/** Options for {@link loadConfig} and {@link loadConfigWithMeta}. */
|
|
686
778
|
interface LoadConfigOptions {
|
|
687
779
|
/** Directory to search from, and the base for relative paths. Defaults to the process working directory. */
|
|
@@ -782,18 +874,25 @@ declare function loadConfig(options?: LoadConfigOptions): Promise<VerbatraConfig
|
|
|
782
874
|
* wraps them.
|
|
783
875
|
*
|
|
784
876
|
* - `CONFIG_NOT_FOUND`: no config file was found by search, or an explicit `configPath` does not
|
|
785
|
-
* exist. Thrown by {@link loadConfig} and {@link loadConfigWithMeta}.
|
|
877
|
+
* exist. Thrown by {@link loadConfig} and {@link loadConfigWithMeta}. {@link doctor} narrows it
|
|
878
|
+
* to the explicit-path case: a config that is only absent from the search is reported as a failed
|
|
879
|
+
* check instead, since reporting that is the command's job.
|
|
786
880
|
* - `CONFIG_INVALID`: a config was found but is unparseable or fails validation, or its glossary
|
|
787
881
|
* file could not be resolved or parsed. Thrown by {@link loadConfig} and
|
|
788
|
-
* {@link loadConfigWithMeta}
|
|
789
|
-
*
|
|
790
|
-
*
|
|
882
|
+
* {@link loadConfigWithMeta}, by {@link readGlossaryFile}, and by {@link updateGlossaryTerm},
|
|
883
|
+
* which additionally throws it for a blank term or translation and for an edit whose result would
|
|
884
|
+
* exceed the glossary file size limit. {@link importWorkbook} does not throw it: when a handoff
|
|
885
|
+
* sheet or file names a locale that is not a configured target locale, it records this code on
|
|
886
|
+
* that locale's {@link LocaleSummary} instead.
|
|
791
887
|
* - `UNKNOWN_FORMAT`: no adapter is registered for the configured format. Thrown by every entry
|
|
792
|
-
* point that selects an adapter, before any file is read.
|
|
888
|
+
* point that selects an adapter, before any file is read. {@link doctor} is the exception: it
|
|
889
|
+
* reports an unresolvable format as a failed `format-adapter` check instead, since reporting that
|
|
890
|
+
* is the command's job.
|
|
793
891
|
* - `UNKNOWN_LOCALE`: a requested locale is not among the configured target locales. Thrown through
|
|
794
|
-
* the shared locale selection by {@link
|
|
795
|
-
* {@link
|
|
796
|
-
* {@link retranslateEntry}.
|
|
892
|
+
* the shared locale selection by {@link translate}, {@link watch}, {@link check}, {@link diff},
|
|
893
|
+
* {@link keyIntegrity}, {@link lockState}, {@link exportWorkbook}, {@link keyValue},
|
|
894
|
+
* {@link editEntry}, and {@link retranslateEntry}. {@link translate} throws it before anything is
|
|
895
|
+
* read or spent, and {@link watch} once at startup, before any watching begins.
|
|
797
896
|
* - `UNKNOWN_KEY`: the requested key is not present in the source resource. Thrown by
|
|
798
897
|
* {@link keyValue}, {@link editEntry}, and {@link retranslateEntry}.
|
|
799
898
|
* - `PROVIDER_CONSTRUCTION_FAILED`: the provider factory threw. Wraps the provider's own error,
|
|
@@ -809,15 +908,24 @@ declare function loadConfig(options?: LoadConfigOptions): Promise<VerbatraConfig
|
|
|
809
908
|
* {@link diff}, {@link keyIntegrity}, {@link lockState}, {@link loadLockFile},
|
|
810
909
|
* {@link exportWorkbook}, {@link importWorkbook}, {@link editEntry}, and
|
|
811
910
|
* {@link retranslateEntry}.
|
|
812
|
-
* - `LOCK_CONTENDED`: a
|
|
813
|
-
*
|
|
911
|
+
* - `LOCK_CONTENDED`: a write lock could not be acquired before its timeout elapsed, because
|
|
912
|
+
* another process holds it or a killed process left the lock file behind. The message
|
|
814
913
|
* names the lock file's path. Thrown by {@link editEntry} and {@link retranslateEntry}, which
|
|
815
|
-
* act on one locale
|
|
914
|
+
* act on one locale, and by {@link updateGlossaryTerm}, which takes the project's glossary lock.
|
|
915
|
+
* {@link translate} and {@link importWorkbook} do not throw it: they record it
|
|
816
916
|
* on the contended locale's {@link LocaleSummary} and carry on with the other locales.
|
|
917
|
+
* - `GLOSSARY_NOT_FILE_BACKED`: the loaded config's glossary is written inline or absent, so there
|
|
918
|
+
* is no glossary file to read or rewrite. Thrown by {@link readGlossaryFile} and
|
|
919
|
+
* {@link updateGlossaryTerm}, which work on a file-backed glossary alone and never rewrite the
|
|
920
|
+
* config module itself.
|
|
921
|
+
* - `GLOSSARY_UNWRITABLE`: the glossary file could not be written, because it or its directory is
|
|
922
|
+
* read-only, has been removed, or the disk is out of space. Thrown by
|
|
923
|
+
* {@link updateGlossaryTerm}.
|
|
817
924
|
* - `LOCALE_LAYOUT_INVALID`: the configured `files.pattern` and `files.localeStyle` cannot be
|
|
818
925
|
* combined, or the style has no valid path spelling for a configured locale. Thrown by
|
|
819
926
|
* {@link createLocalePathResolver}, and so by every entry point that maps a locale to a path,
|
|
820
|
-
* before any file is read and before any provider call.
|
|
927
|
+
* before any file is read and before any provider call. {@link doctor} is the exception: it
|
|
928
|
+
* reports the resolver's failure as a failed `source-file` check instead.
|
|
821
929
|
* - `LOCALE_PATH_COLLISION`: two configured locales resolve to the same absolute path, which would
|
|
822
930
|
* make the path-to-locale direction ambiguous and let two locale workers race on one file. Thrown
|
|
823
931
|
* at the same point as `LOCALE_LAYOUT_INVALID`.
|
|
@@ -827,10 +935,16 @@ declare function loadConfig(options?: LoadConfigOptions): Promise<VerbatraConfig
|
|
|
827
935
|
* - `CONCURRENCY_BUDGET_CONFLICT`: a live run requested a `concurrency` above 1 while a token
|
|
828
936
|
* budget is configured. The two are mutually exclusive because concurrency makes the budget's
|
|
829
937
|
* stop guarantee nondeterministic. A dry run is exempt, since it never consults the budget.
|
|
938
|
+
* - `TARGET_UNWRITABLE`: a target locale file could not be written, because its directory is not
|
|
939
|
+
* writable, does not exist, is read-only, or is out of space. The message names the target file
|
|
940
|
+
* relative to `cwd` and the underlying file-system code, never the internal temporary file the
|
|
941
|
+
* atomic write uses. Thrown by {@link editEntry} and {@link retranslateEntry}, which act on one
|
|
942
|
+
* locale. {@link translate} and {@link importWorkbook} do not throw it: they record it on that
|
|
943
|
+
* locale's {@link LocaleSummary} and carry on with the other locales.
|
|
830
944
|
* - `LOCALE_FAILED`: never thrown. It is the fallback code recorded on a failed
|
|
831
945
|
* {@link LocaleSummary} when a per-locale failure carries no code of its own.
|
|
832
946
|
*/
|
|
833
|
-
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_LAYOUT_INVALID" | "LOCALE_PATH_COLLISION" | "CONCURRENCY_INVALID" | "CONCURRENCY_BUDGET_CONFLICT" | "LOCALE_FAILED";
|
|
947
|
+
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" | "GLOSSARY_NOT_FILE_BACKED" | "GLOSSARY_UNWRITABLE" | "LOCALE_LAYOUT_INVALID" | "LOCALE_PATH_COLLISION" | "CONCURRENCY_INVALID" | "CONCURRENCY_BUDGET_CONFLICT" | "TARGET_UNWRITABLE" | "LOCALE_FAILED";
|
|
834
948
|
/**
|
|
835
949
|
* The single structured error the SDK throws. Every whole-run failure surfaces as an `SdkError`
|
|
836
950
|
* carrying a stable {@link SdkErrorCode}; per-locale failures, provider notices, and integrity
|
|
@@ -1016,15 +1130,24 @@ interface FormatAdapter {
|
|
|
1016
1130
|
*/
|
|
1017
1131
|
validateMessage(value: string): boolean;
|
|
1018
1132
|
/**
|
|
1019
|
-
* Optional
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
1133
|
+
* Optional whole-value placeholder comparison, used by callers instead of independently extracting
|
|
1134
|
+
* each side's placeholders with {@link extractPlaceholders} and diffing the flat lists. Two adapter
|
|
1135
|
+
* families define one, for different reasons:
|
|
1136
|
+
*
|
|
1137
|
+
* - The ICU formats (next-intl and ARB) compare branch by branch, because flattening a
|
|
1138
|
+
* plural/select value loses which branch a placeholder came from.
|
|
1139
|
+
* - The double-brace formats (i18next, ngx-translate, and YAML) add a one-directional
|
|
1140
|
+
* single-brace check on top of their flat multiset comparison: a `{name}`-shaped token present
|
|
1141
|
+
* in the translated value but absent from the source is reported as `extra`. Single-brace text
|
|
1142
|
+
* is ordinary literal content under these formats' default delimiters, so it is never required
|
|
1143
|
+
* to survive a translation, but inventing one is never a legitimate translation either.
|
|
1144
|
+
*
|
|
1145
|
+
* Absent for the remaining adapters, which are compared via `extractPlaceholders` plus a flat
|
|
1146
|
+
* multiset check.
|
|
1024
1147
|
*
|
|
1025
1148
|
* @param sourceValue - The source value.
|
|
1026
1149
|
* @param targetValue - The translated value to check against it.
|
|
1027
|
-
* @returns The merged placeholder-integrity result
|
|
1150
|
+
* @returns The merged placeholder-integrity result. Does not throw.
|
|
1028
1151
|
*/
|
|
1029
1152
|
comparePlaceholders?(sourceValue: string, targetValue: string): PlaceholderIntegrityResult;
|
|
1030
1153
|
}
|
|
@@ -1146,9 +1269,10 @@ interface CheckDeps {
|
|
|
1146
1269
|
*
|
|
1147
1270
|
* Use {@link diff} instead when you need the key names rather than the counts.
|
|
1148
1271
|
*
|
|
1149
|
-
* Note that a malformed target locale file surfaces the adapter's own
|
|
1150
|
-
* wrapped {@link SdkError}, because only source reads are wrapped.
|
|
1151
|
-
* should be ready for an unrecognized
|
|
1272
|
+
* Note that a malformed target locale file surfaces the adapter's own error and code rather than a
|
|
1273
|
+
* wrapped {@link SdkError}, because only source reads are wrapped. Its message names the offending
|
|
1274
|
+
* locale and the resolved path. A caller that maps SDK codes should be ready for an unrecognized
|
|
1275
|
+
* error from a target file.
|
|
1152
1276
|
*
|
|
1153
1277
|
* @param input - The config and the optional locale filter.
|
|
1154
1278
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -1217,9 +1341,10 @@ interface DiffDeps {
|
|
|
1217
1341
|
* since the key was last translated rather than merely that the two strings differ. Orphaned keys
|
|
1218
1342
|
* are reported but never removed here; pruning happens only in {@link translate}.
|
|
1219
1343
|
*
|
|
1220
|
-
* Note that a malformed target locale file surfaces the adapter's own
|
|
1221
|
-
* wrapped {@link SdkError}, because only source reads are wrapped.
|
|
1222
|
-
* should be ready for an unrecognized
|
|
1344
|
+
* Note that a malformed target locale file surfaces the adapter's own error and code rather than a
|
|
1345
|
+
* wrapped {@link SdkError}, because only source reads are wrapped. Its message names the offending
|
|
1346
|
+
* locale and the resolved path. A caller that maps SDK codes should be ready for an unrecognized
|
|
1347
|
+
* error from a target file.
|
|
1223
1348
|
*
|
|
1224
1349
|
* @param input - The config and the optional locale filter.
|
|
1225
1350
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -1237,6 +1362,115 @@ interface DiffDeps {
|
|
|
1237
1362
|
*/
|
|
1238
1363
|
declare function diff(input: DiffInput, deps?: DiffDeps): Promise<DiffSummary>;
|
|
1239
1364
|
|
|
1365
|
+
/**
|
|
1366
|
+
* Which project-setup question a {@link DoctorCheck} answers.
|
|
1367
|
+
*
|
|
1368
|
+
* - `config`: a config file was found and passes validation.
|
|
1369
|
+
* - `format-adapter`: the configured `format` resolves to a file adapter.
|
|
1370
|
+
* - `provider`: the configured `provider.id` resolves to a provider factory.
|
|
1371
|
+
* - `api-key`: the environment variable the configured provider reads its key from is set.
|
|
1372
|
+
* - `source-file`: the source locale file exists at its resolved path, is a regular file, and
|
|
1373
|
+
* parses under the configured format.
|
|
1374
|
+
*/
|
|
1375
|
+
type DoctorCheckId = "config" | "format-adapter" | "provider" | "api-key" | "source-file";
|
|
1376
|
+
/**
|
|
1377
|
+
* The verdict on one {@link DoctorCheck}. `skipped` is reported only for the checks that need a
|
|
1378
|
+
* loaded config when the `config` check itself failed, so a skipped check is never a problem of its
|
|
1379
|
+
* own.
|
|
1380
|
+
*/
|
|
1381
|
+
type DoctorCheckStatus = "pass" | "fail" | "skipped";
|
|
1382
|
+
/** One project-setup question and its verdict. */
|
|
1383
|
+
interface DoctorCheck {
|
|
1384
|
+
/** Which question this check answers. */
|
|
1385
|
+
readonly id: DoctorCheckId;
|
|
1386
|
+
/** A short human-readable name for the check, stable across runs. */
|
|
1387
|
+
readonly title: string;
|
|
1388
|
+
/** The verdict. Only `fail` makes {@link DoctorResult.ok} false. */
|
|
1389
|
+
readonly status: DoctorCheckStatus;
|
|
1390
|
+
/**
|
|
1391
|
+
* Why the check reached its verdict. On a failure this carries the underlying error message
|
|
1392
|
+
* verbatim, so a config validation problem reads exactly as {@link loadConfig} words it. It names
|
|
1393
|
+
* environment variables and paths, never an API key value.
|
|
1394
|
+
*/
|
|
1395
|
+
readonly detail: string;
|
|
1396
|
+
}
|
|
1397
|
+
/** The result of {@link doctor}: every check that ran, and one project-wide verdict. */
|
|
1398
|
+
interface DoctorResult {
|
|
1399
|
+
/** True only when no check failed. This is the value a script should branch on. */
|
|
1400
|
+
readonly ok: boolean;
|
|
1401
|
+
/** Every check, always in the same order, one entry per {@link DoctorCheckId}. */
|
|
1402
|
+
readonly checks: readonly DoctorCheck[];
|
|
1403
|
+
}
|
|
1404
|
+
/** Input for {@link doctor}. */
|
|
1405
|
+
interface DoctorInput {
|
|
1406
|
+
/** Directory to search the config from, and the base for locale paths. Defaults to the process working directory. */
|
|
1407
|
+
readonly cwd?: string;
|
|
1408
|
+
/** An explicit config file to validate, bypassing the search. A missing file is an error rather than a failed check. */
|
|
1409
|
+
readonly configPath?: string;
|
|
1410
|
+
}
|
|
1411
|
+
/** Injectable dependencies for {@link doctor}. Every field has a working default. */
|
|
1412
|
+
interface DoctorDeps {
|
|
1413
|
+
/** Format-adapter registry to resolve the configured format against. Defaults to the built-in registry. */
|
|
1414
|
+
readonly adapterRegistry?: AdapterRegistry;
|
|
1415
|
+
/**
|
|
1416
|
+
* File-system port. Threaded into the config loader, so it backs the glossary-file read the
|
|
1417
|
+
* `config` check performs, and used to read and parse the source locale file. Defaults to the
|
|
1418
|
+
* real file system.
|
|
1419
|
+
*/
|
|
1420
|
+
readonly fs?: SdkFs;
|
|
1421
|
+
/** Config loader. Defaults to {@link loadConfigWithMeta}. */
|
|
1422
|
+
readonly loadConfig?: (options: LoadConfigOptions) => Promise<LoadedConfig>;
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Validates a project's setup and spends nothing: no provider is constructed, no network request is
|
|
1426
|
+
* made, and no file is written. Run it before {@link translate} on a fresh project, or when a run
|
|
1427
|
+
* failed and you want the whole list of problems rather than the first one.
|
|
1428
|
+
*
|
|
1429
|
+
* Five checks run: the config loads and validates, the configured format resolves to an adapter,
|
|
1430
|
+
* the configured provider ID resolves to a factory, the environment variable that provider reads
|
|
1431
|
+
* its API key from is set, and the source locale file can be read. Every check runs even when an
|
|
1432
|
+
* earlier one failed, so one call reports every independent problem. The API key is checked by
|
|
1433
|
+
* variable name only: its value is never read, never returned, and never validated against a
|
|
1434
|
+
* provider.
|
|
1435
|
+
*
|
|
1436
|
+
* The source-file check reads and parses the file rather than only probing for its existence, so a
|
|
1437
|
+
* directory standing in for it, an empty file, and malformed content are all reported here rather
|
|
1438
|
+
* than surfacing later as a whole-run failure of {@link check} or {@link translate}. The failure
|
|
1439
|
+
* detail is the same message those entry points raise. When the configured format resolves to no
|
|
1440
|
+
* adapter there is nothing to parse with, so the check falls back to existence alone and says so.
|
|
1441
|
+
*
|
|
1442
|
+
* The `openai-compatible` provider is the one exception on the key check. It falls back to a
|
|
1443
|
+
* placeholder key, so a missing variable passes unless the config names its own variable through
|
|
1444
|
+
* `provider.options.apiKeyEnvVar`, which then has to be set.
|
|
1445
|
+
*
|
|
1446
|
+
* A target locale file is not checked at all: a missing one is not a problem, because
|
|
1447
|
+
* {@link translate} creates it. The source locale file is checked, because every other entry point
|
|
1448
|
+
* fails on it.
|
|
1449
|
+
*
|
|
1450
|
+
* When the config cannot be loaded the four config-dependent checks report `skipped` rather than a
|
|
1451
|
+
* verdict they could not reach, and {@link DoctorResult.ok} is false because the config check
|
|
1452
|
+
* itself failed.
|
|
1453
|
+
*
|
|
1454
|
+
* @param input - The working directory and an optional explicit config path.
|
|
1455
|
+
* @param deps - Optional adapter registry, file-system, and config-loader overrides.
|
|
1456
|
+
* @returns Every check with its verdict, and the project-wide `ok` verdict.
|
|
1457
|
+
*
|
|
1458
|
+
* @throws {@link SdkError} `CONFIG_NOT_FOUND`: an explicit `configPath` was given and no file
|
|
1459
|
+
* exists there. A config that is merely absent from the search is a failed check instead.
|
|
1460
|
+
*
|
|
1461
|
+
* @example
|
|
1462
|
+
* ```ts
|
|
1463
|
+
* import { doctor } from "@verbatra/sdk";
|
|
1464
|
+
*
|
|
1465
|
+
* const report = await doctor();
|
|
1466
|
+
* for (const check of report.checks) {
|
|
1467
|
+
* console.log(`${check.status}: ${check.title} - ${check.detail}`);
|
|
1468
|
+
* }
|
|
1469
|
+
* process.exitCode = report.ok ? 0 : 1;
|
|
1470
|
+
* ```
|
|
1471
|
+
*/
|
|
1472
|
+
declare function doctor(input?: DoctorInput, deps?: DoctorDeps): Promise<DoctorResult>;
|
|
1473
|
+
|
|
1240
1474
|
/**
|
|
1241
1475
|
* Why a candidate translation was refused before it could be written. The same gate guards
|
|
1242
1476
|
* {@link editEntry} and {@link retranslateEntry}, so a hand-typed value and a provider-produced one
|
|
@@ -1246,7 +1480,9 @@ declare function diff(input: DiffInput, deps?: DiffDeps): Promise<DiffSummary>;
|
|
|
1246
1480
|
* previous translation stays intact.
|
|
1247
1481
|
*
|
|
1248
1482
|
* - `placeholder`: the candidate does not carry the same placeholders as the source, so
|
|
1249
|
-
* interpolation would break at runtime.
|
|
1483
|
+
* interpolation would break at runtime. For the double-brace formats (i18next, ngx-translate, and
|
|
1484
|
+
* YAML) this also covers a single-brace `{name}`-shaped token the candidate invented and the
|
|
1485
|
+
* source never had, which is a fabrication whichever interpolation delimiters the project uses.
|
|
1250
1486
|
* - `icu`: the candidate is not a valid ICU message under the configured format's adapter.
|
|
1251
1487
|
* - `degenerate`: the candidate collapsed into runaway output rather than a translation. Two shapes
|
|
1252
1488
|
* are detected: the candidate is at least twelve times the length of a source of meaningful
|
|
@@ -1311,9 +1547,14 @@ type EditEntryResult = {
|
|
|
1311
1547
|
* and feeds the translation memory, which means a later run treats the key as up to date and
|
|
1312
1548
|
* reuses the edited text rather than paying the provider to translate it again.
|
|
1313
1549
|
*
|
|
1314
|
-
* Note that
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
1550
|
+
* Note that the target locale file surfaces the adapter's own error and code rather than a wrapped
|
|
1551
|
+
* {@link SdkError}, on the write as well as on the read, because only the source read is wrapped.
|
|
1552
|
+
* A malformed target file fails the read with a message naming the offending locale and the
|
|
1553
|
+
* resolved path. The write raises the adapter's error too, when the entries cannot be represented
|
|
1554
|
+
* in the configured format or the existing destination file cannot be read back to be updated in
|
|
1555
|
+
* place, and that error is re-thrown unchanged so its own code survives for a caller that maps
|
|
1556
|
+
* adapter codes to its own copy. A caller that maps SDK codes should be ready for an unrecognized
|
|
1557
|
+
* error from a target file on either path.
|
|
1317
1558
|
*
|
|
1318
1559
|
* @param input - The config, locale, key, and new value.
|
|
1319
1560
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -1329,8 +1570,14 @@ type EditEntryResult = {
|
|
|
1329
1570
|
* @throws {@link SdkError} `UNKNOWN_KEY`: the key is not present in the source resource.
|
|
1330
1571
|
* @throws {@link SdkError} `LOCK_CONTENDED`: the locale's write lock could not be acquired before
|
|
1331
1572
|
* the timeout elapsed.
|
|
1573
|
+
* @throws {@link SdkError} `TARGET_UNWRITABLE`: the target locale file could not be written because
|
|
1574
|
+
* of a file-system failure. The message names the target file and the file-system code, never the
|
|
1575
|
+
* internal temporary file.
|
|
1332
1576
|
* @throws {@link SdkError} `LOCK_FILE_INVALID`: the lock-file is corrupt, oversized, or at an
|
|
1333
1577
|
* unsupported version.
|
|
1578
|
+
* @throws `AdapterError`: the adapter itself refused the target locale file, on the read because it
|
|
1579
|
+
* is malformed or on the write because the entries cannot be represented in the configured format.
|
|
1580
|
+
* Its own code is preserved rather than remapped onto an {@link SdkErrorCode}.
|
|
1334
1581
|
*/
|
|
1335
1582
|
declare function editEntry(input: EditEntryInput, deps?: EditEntryDeps): Promise<EditEntryResult>;
|
|
1336
1583
|
|
|
@@ -1387,9 +1634,10 @@ interface KeyIntegrityDeps {
|
|
|
1387
1634
|
* {@link retranslateEntry} enforce at write time, and the data behind a review dashboard's
|
|
1388
1635
|
* per-key integrity indicator.
|
|
1389
1636
|
*
|
|
1390
|
-
* Note that a malformed target locale file surfaces the adapter's own
|
|
1391
|
-
* wrapped {@link SdkError}, because only source reads are wrapped.
|
|
1392
|
-
* should be ready for an unrecognized
|
|
1637
|
+
* Note that a malformed target locale file surfaces the adapter's own error and code rather than a
|
|
1638
|
+
* wrapped {@link SdkError}, because only source reads are wrapped. Its message names the offending
|
|
1639
|
+
* locale and the resolved path. A caller that maps SDK codes should be ready for an unrecognized
|
|
1640
|
+
* error from a target file.
|
|
1393
1641
|
*
|
|
1394
1642
|
* @param input - The config and the optional locale and key filters.
|
|
1395
1643
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -1441,9 +1689,10 @@ interface KeyValueResult {
|
|
|
1441
1689
|
* cannot be edited or retranslated, while a key absent from the target is normal and simply comes
|
|
1442
1690
|
* back with no `target`.
|
|
1443
1691
|
*
|
|
1444
|
-
* Note that a malformed target locale file surfaces the adapter's own
|
|
1445
|
-
* wrapped {@link SdkError}, because only source reads are wrapped.
|
|
1446
|
-
* should be ready for an unrecognized
|
|
1692
|
+
* Note that a malformed target locale file surfaces the adapter's own error and code rather than a
|
|
1693
|
+
* wrapped {@link SdkError}, because only source reads are wrapped. Its message names the offending
|
|
1694
|
+
* locale and the resolved path. A caller that maps SDK codes should be ready for an unrecognized
|
|
1695
|
+
* error from a target file.
|
|
1447
1696
|
*
|
|
1448
1697
|
* @param input - The config, locale, and key to read.
|
|
1449
1698
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -1499,8 +1748,9 @@ interface ReadLocaleFileSnapshotDeps {
|
|
|
1499
1748
|
* watcher can start before the first translation run has written anything.
|
|
1500
1749
|
*
|
|
1501
1750
|
* Note that this entry point does not validate the locale against the configured targets, and that
|
|
1502
|
-
* a malformed target file surfaces the adapter's own
|
|
1503
|
-
* {@link SdkError}, because no source-file contract is being asserted here.
|
|
1751
|
+
* a malformed target file surfaces the adapter's own error and code rather than a wrapped
|
|
1752
|
+
* {@link SdkError}, because no source-file contract is being asserted here. Its message names the
|
|
1753
|
+
* offending locale and the resolved path.
|
|
1504
1754
|
*
|
|
1505
1755
|
* @param input - The config and the locale to snapshot.
|
|
1506
1756
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -1591,9 +1841,10 @@ interface LockStateDeps {
|
|
|
1591
1841
|
* When no lock-file exists the call returns `exists: false` rather than throwing, and does no
|
|
1592
1842
|
* further reading.
|
|
1593
1843
|
*
|
|
1594
|
-
* Note that a malformed target locale file surfaces the adapter's own
|
|
1595
|
-
* wrapped {@link SdkError}, because only source reads are wrapped.
|
|
1596
|
-
* should be ready for an unrecognized
|
|
1844
|
+
* Note that a malformed target locale file surfaces the adapter's own error and code rather than a
|
|
1845
|
+
* wrapped {@link SdkError}, because only source reads are wrapped. Its message names the offending
|
|
1846
|
+
* locale and the resolved path. A caller that maps SDK codes should be ready for an unrecognized
|
|
1847
|
+
* error from a target file.
|
|
1597
1848
|
*
|
|
1598
1849
|
* @param input - The config and the optional locale filter.
|
|
1599
1850
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -1681,9 +1932,14 @@ type RetranslateEntryResult = {
|
|
|
1681
1932
|
* baseline and feeds the translation memory, so a later {@link translate} run sees the key as up
|
|
1682
1933
|
* to date.
|
|
1683
1934
|
*
|
|
1684
|
-
* Note that
|
|
1685
|
-
*
|
|
1686
|
-
*
|
|
1935
|
+
* Note that the target locale file surfaces the adapter's own error and code rather than a wrapped
|
|
1936
|
+
* {@link SdkError}, on the write as well as on the read, because only the source read is wrapped.
|
|
1937
|
+
* A malformed target file fails the read with a message naming the offending locale and the
|
|
1938
|
+
* resolved path. The write raises the adapter's error too, when the entries cannot be represented
|
|
1939
|
+
* in the configured format or the existing destination file cannot be read back to be updated in
|
|
1940
|
+
* place, and that error is re-thrown unchanged so its own code survives for a caller that maps
|
|
1941
|
+
* adapter codes to its own copy. A caller that maps SDK codes should be ready for an unrecognized
|
|
1942
|
+
* error from a target file on either path.
|
|
1687
1943
|
*
|
|
1688
1944
|
* @param input - The config, locale, and key to retranslate.
|
|
1689
1945
|
* @param deps - Optional adapter registry, provider factory, and file-system overrides.
|
|
@@ -1701,8 +1957,14 @@ type RetranslateEntryResult = {
|
|
|
1701
1957
|
* most often because its API key environment variable is unset.
|
|
1702
1958
|
* @throws {@link SdkError} `LOCK_CONTENDED`: the locale's write lock could not be acquired before
|
|
1703
1959
|
* the timeout elapsed.
|
|
1960
|
+
* @throws {@link SdkError} `TARGET_UNWRITABLE`: the target locale file could not be written because
|
|
1961
|
+
* of a file-system failure. The message names the target file and the file-system code, never the
|
|
1962
|
+
* internal temporary file.
|
|
1704
1963
|
* @throws {@link SdkError} `LOCK_FILE_INVALID`: the lock-file is corrupt, oversized, or at an
|
|
1705
1964
|
* unsupported version.
|
|
1965
|
+
* @throws `AdapterError`: the adapter itself refused the target locale file, on the read because it
|
|
1966
|
+
* is malformed or on the write because the entries cannot be represented in the configured format.
|
|
1967
|
+
* Its own code is preserved rather than remapped onto an {@link SdkErrorCode}.
|
|
1706
1968
|
* @throws `ProviderError` `INVALID_RESPONSE`: the provider returned no value for the key. Provider
|
|
1707
1969
|
* transport and rate-limit failures propagate as `ProviderError` too, since a single-key call has
|
|
1708
1970
|
* no per-locale summary to record them on.
|
|
@@ -1884,11 +2146,21 @@ interface LocaleSummary {
|
|
|
1884
2146
|
interface RunSummary {
|
|
1885
2147
|
/** True when the run computed everything but wrote nothing and called no provider. */
|
|
1886
2148
|
readonly dryRun: boolean;
|
|
1887
|
-
/**
|
|
2149
|
+
/**
|
|
2150
|
+
* The full per-locale account. A {@link translate} or {@link watch} run reports them in
|
|
2151
|
+
* configured target order, whatever order a `locales` subset was given in and whatever order
|
|
2152
|
+
* concurrent locales finished in. An {@link importWorkbook} run does not: it reports them in the
|
|
2153
|
+
* order the handoff yielded them, then appends the configured locales the handoff had nothing
|
|
2154
|
+
* for. Match an entry on its `locale` rather than on its position.
|
|
2155
|
+
*/
|
|
1888
2156
|
readonly locales: readonly LocaleSummary[];
|
|
1889
2157
|
/** Names of the locales whose status is `succeeded`. */
|
|
1890
2158
|
readonly succeeded: readonly string[];
|
|
1891
|
-
/**
|
|
2159
|
+
/**
|
|
2160
|
+
* Names of the locales whose status is `partial`: written to disk, but with keys still missing.
|
|
2161
|
+
* Check this alongside `failed` before treating a run as clean. The CLI exits `1` on a partial
|
|
2162
|
+
* locale for that reason.
|
|
2163
|
+
*/
|
|
1892
2164
|
readonly partial: readonly string[];
|
|
1893
2165
|
/** Names of the locales whose status is `failed`. Check this before treating a run as clean. */
|
|
1894
2166
|
readonly failed: readonly string[];
|
|
@@ -1965,8 +2237,9 @@ type RunStatusResult = {
|
|
|
1965
2237
|
* anything.
|
|
1966
2238
|
*
|
|
1967
2239
|
* The read is deliberately total: a missing, oversized, unparseable, schema-invalid, or
|
|
1968
|
-
* wrong-version file all report `available: false` rather than throwing,
|
|
1969
|
-
*
|
|
2240
|
+
* wrong-version file all report `available: false` rather than throwing, and so does an injected
|
|
2241
|
+
* `deps.fs` whose read rejects, because stale or unreachable local status should never break the
|
|
2242
|
+
* tool reading it. This call throws nothing.
|
|
1970
2243
|
*
|
|
1971
2244
|
* @param input - The optional working directory.
|
|
1972
2245
|
* @param deps - Optional file-system override.
|
|
@@ -2075,6 +2348,18 @@ interface TranslateInput {
|
|
|
2075
2348
|
readonly config: VerbatraConfig;
|
|
2076
2349
|
/** Directory the `files.pattern` is resolved against. Defaults to the process working directory. */
|
|
2077
2350
|
readonly cwd?: string;
|
|
2351
|
+
/**
|
|
2352
|
+
* Restrict the run to a subset of the configured target locales, which is how you translate one
|
|
2353
|
+
* locale at a time against a rate-limited provider. Defaults to every configured target. The
|
|
2354
|
+
* result keeps the configured order whatever order the subset is given in, and any locale named
|
|
2355
|
+
* here that is not a configured target fails the whole run with `UNKNOWN_LOCALE` before anything
|
|
2356
|
+
* is read or spent. An explicit empty array selects no locale at all rather than every locale.
|
|
2357
|
+
*
|
|
2358
|
+
* Locales left out are untouched: their files, their lock entries, and their translation-memory
|
|
2359
|
+
* entries all stay as they were. The run-status file always describes the most recent run alone,
|
|
2360
|
+
* so a subset run narrows it to that subset.
|
|
2361
|
+
*/
|
|
2362
|
+
readonly locales?: readonly string[];
|
|
2078
2363
|
/**
|
|
2079
2364
|
* Compute the whole run but write nothing and call no provider. The returned
|
|
2080
2365
|
* {@link RunSummary} reports what would have happened, which makes this safe to run in CI to
|
|
@@ -2122,7 +2407,8 @@ interface TranslateDeps {
|
|
|
2122
2407
|
readonly fs?: SdkFs;
|
|
2123
2408
|
}
|
|
2124
2409
|
/**
|
|
2125
|
-
* Runs the one-shot translation flow over every configured target locale
|
|
2410
|
+
* Runs the one-shot translation flow over every configured target locale, or over the subset named
|
|
2411
|
+
* by `locales`: read the source, diff
|
|
2126
2412
|
* each locale against the lock-file baseline, translate what is missing or stale, verify placeholder
|
|
2127
2413
|
* and ICU integrity, write the locale files, and update the lock-file and translation memory.
|
|
2128
2414
|
*
|
|
@@ -2132,8 +2418,10 @@ interface TranslateDeps {
|
|
|
2132
2418
|
* {@link LocaleSummary} and the other locales continue, so one unreachable provider or one
|
|
2133
2419
|
* unwritable file never discards work that succeeded. A locale whose write lock stays contended is
|
|
2134
2420
|
* one of these per-locale failures: it is recorded with code `LOCK_CONTENDED` on that locale's
|
|
2135
|
-
* summary rather than thrown. Callers should therefore inspect {@link RunSummary.failed}
|
|
2136
|
-
* than relying on a thrown error to detect trouble.
|
|
2421
|
+
* summary rather than thrown. Callers should therefore inspect {@link RunSummary.failed} and
|
|
2422
|
+
* {@link RunSummary.partial} rather than relying on a thrown error to detect trouble. A partial
|
|
2423
|
+
* locale is a locale whose file was written with some keys missing, so treating it as clean would
|
|
2424
|
+
* ship an incomplete translation; the CLI exits `1` for it exactly as it does for a failed one.
|
|
2137
2425
|
*
|
|
2138
2426
|
* A corrupt lock-file is the one exception. On a live run each locale reads the lock-file inside
|
|
2139
2427
|
* its own write lock, so `LOCK_FILE_INVALID` escapes as a thrown error even after earlier locales
|
|
@@ -2150,6 +2438,8 @@ interface TranslateDeps {
|
|
|
2150
2438
|
* @param deps - Optional adapter registry, provider factory, and file-system overrides.
|
|
2151
2439
|
* @returns The per-locale account of the run, including usage and budget.
|
|
2152
2440
|
*
|
|
2441
|
+
* @throws {@link SdkError} `UNKNOWN_LOCALE`: `locales` names a locale that is not a configured
|
|
2442
|
+
* target. Thrown before anything is read, written, or spent.
|
|
2153
2443
|
* @throws {@link SdkError} `UNKNOWN_FORMAT`: no adapter is registered for the configured format.
|
|
2154
2444
|
* @throws {@link SdkError} `CONCURRENCY_INVALID`: `concurrency` is not an integer of at least 1.
|
|
2155
2445
|
* @throws {@link SdkError} `CONCURRENCY_BUDGET_CONFLICT`: a live run combined a `concurrency` above
|
|
@@ -2180,9 +2470,9 @@ interface TranslateDeps {
|
|
|
2180
2470
|
* },
|
|
2181
2471
|
* });
|
|
2182
2472
|
*
|
|
2183
|
-
* if (summary.failed.length > 0) {
|
|
2184
|
-
* for (const locale of summary.locales.filter((entry) => entry.status
|
|
2185
|
-
* console.error(`${locale.locale}: ${locale.error?.message}`);
|
|
2473
|
+
* if (summary.failed.length > 0 || summary.partial.length > 0) {
|
|
2474
|
+
* for (const locale of summary.locales.filter((entry) => entry.status !== "succeeded")) {
|
|
2475
|
+
* console.error(`${locale.locale}: ${locale.status} ${locale.error?.message ?? ""}`);
|
|
2186
2476
|
* }
|
|
2187
2477
|
* process.exitCode = 1;
|
|
2188
2478
|
* }
|
|
@@ -2280,9 +2570,14 @@ interface ExportWorkbookResult {
|
|
|
2280
2570
|
* through the same diff, lock, and integrity checks. It writes only the handoff file and never
|
|
2281
2571
|
* touches the locale files or the lock-file.
|
|
2282
2572
|
*
|
|
2283
|
-
* Note that a malformed target locale file surfaces the adapter's own
|
|
2284
|
-
* wrapped {@link SdkError}, because only source reads are wrapped.
|
|
2285
|
-
* should be ready for an unrecognized
|
|
2573
|
+
* Note that a malformed target locale file surfaces the adapter's own error and code rather than a
|
|
2574
|
+
* wrapped {@link SdkError}, because only source reads are wrapped. Its message names the offending
|
|
2575
|
+
* locale and the resolved path. A caller that maps SDK codes should be ready for an unrecognized
|
|
2576
|
+
* error from a target file.
|
|
2577
|
+
*
|
|
2578
|
+
* Writing the handoff itself is unwrapped in the same way. It is not a locale file, so a failure to
|
|
2579
|
+
* create the output directory or to write the file does not become `TARGET_UNWRITABLE`; the
|
|
2580
|
+
* underlying file-system error propagates as it is.
|
|
2286
2581
|
*
|
|
2287
2582
|
* @param input - The config, output path, locale filter, and handoff format.
|
|
2288
2583
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -2297,6 +2592,10 @@ interface ExportWorkbookResult {
|
|
|
2297
2592
|
* @throws {@link SdkError} `LOCK_FILE_INVALID`: the lock-file is corrupt, oversized, or at an
|
|
2298
2593
|
* unsupported version.
|
|
2299
2594
|
* @throws {@link SdkError} `UNKNOWN_LOCALE`: a requested locale is not a configured target locale.
|
|
2595
|
+
* @throws The underlying file-system error, unwrapped, when the handoff could not be written: `out`
|
|
2596
|
+
* names a directory that does not exist or is not writable, or the device is full. Branch on the
|
|
2597
|
+
* Node `code`, such as `ENOENT`, rather than on the message, which can name the internal temporary
|
|
2598
|
+
* file the atomic write uses.
|
|
2300
2599
|
*/
|
|
2301
2600
|
declare function exportWorkbook(input: ExportWorkbookInput, deps?: ExportWorkbookDeps): Promise<ExportWorkbookResult>;
|
|
2302
2601
|
|
|
@@ -2346,7 +2645,10 @@ interface ImportWorkbookDeps {
|
|
|
2346
2645
|
* that is not configured is contained the same way: that locale fails with `CONFIG_INVALID` on its
|
|
2347
2646
|
* own {@link LocaleSummary}, so nothing is written to an unmanaged path and the configured locales
|
|
2348
2647
|
* still import. Once the handoff has been read, every per-locale failure is isolated this way, so
|
|
2349
|
-
* callers should inspect {@link RunSummary.failed}
|
|
2648
|
+
* callers should inspect {@link RunSummary.failed} and {@link RunSummary.partial} rather than
|
|
2649
|
+
* relying on a thrown error. A corrupt lock-file is the one exception, because it is a single
|
|
2650
|
+
* shared file rather than a per-locale one: it aborts the whole run even when it is discovered
|
|
2651
|
+
* after a locale has been applied, so the locales still to come are not written at all.
|
|
2350
2652
|
*
|
|
2351
2653
|
* @param input - The config and the handoff path, format, and dry-run flag.
|
|
2352
2654
|
* @param deps - Optional adapter registry and file-system overrides.
|
|
@@ -2361,7 +2663,8 @@ interface ImportWorkbookDeps {
|
|
|
2361
2663
|
* cannot be combined, or a configured locale has no valid path spelling under that style.
|
|
2362
2664
|
* @throws {@link SdkError} `LOCALE_PATH_COLLISION`: two configured locales resolve to the same path.
|
|
2363
2665
|
* @throws {@link SdkError} `LOCK_FILE_INVALID`: the lock-file is corrupt, oversized, or at an
|
|
2364
|
-
* unsupported version. Read
|
|
2666
|
+
* unsupported version. Read before any locale is applied, and re-read as each locale's entries are
|
|
2667
|
+
* recorded, so a lock-file that turns corrupt mid-run aborts the run rather than failing one locale.
|
|
2365
2668
|
*/
|
|
2366
2669
|
declare function importWorkbook(input: ImportWorkbookInput, deps?: ImportWorkbookDeps): Promise<RunSummary>;
|
|
2367
2670
|
|
|
@@ -2576,6 +2879,12 @@ interface WatchInput {
|
|
|
2576
2879
|
readonly config: VerbatraConfig;
|
|
2577
2880
|
/** Directory the `files.pattern` is resolved against. Defaults to the process working directory. */
|
|
2578
2881
|
readonly cwd?: string;
|
|
2882
|
+
/**
|
|
2883
|
+
* Restrict every run of the session to a subset of the configured target locales. Validated once
|
|
2884
|
+
* when watching starts, so an unconfigured locale throws `UNKNOWN_LOCALE` before any watching
|
|
2885
|
+
* begins rather than failing on each run. An explicit empty array selects no locale at all.
|
|
2886
|
+
*/
|
|
2887
|
+
readonly locales?: readonly string[];
|
|
2579
2888
|
/** How long to coalesce rapid source changes before running. Defaults to 300 milliseconds. */
|
|
2580
2889
|
readonly debounceMs?: number;
|
|
2581
2890
|
/**
|
|
@@ -2624,7 +2933,8 @@ interface WatchController {
|
|
|
2624
2933
|
* The returned promise resolves as soon as watching is established, not when translation finishes,
|
|
2625
2934
|
* so every run outcome arrives through `onRun`. A run that throws is delivered as a failed
|
|
2626
2935
|
* {@link WatchRunResult} rather than escaping the session, because a watcher that died on the first
|
|
2627
|
-
* bad save would be useless. Only startup problems
|
|
2936
|
+
* bad save would be useless. Only startup problems throw: the inputs validated before any watching
|
|
2937
|
+
* begins, and a failure to construct the watcher itself.
|
|
2628
2938
|
*
|
|
2629
2939
|
* Rapid saves are coalesced by `debounceMs`, and runs never overlap, so an editor writing a file
|
|
2630
2940
|
* several times in a moment produces one run rather than a queue of them.
|
|
@@ -2635,6 +2945,8 @@ interface WatchController {
|
|
|
2635
2945
|
* @param deps - Optional adapter registry, provider factory, file-system, watcher, and runner overrides.
|
|
2636
2946
|
* @returns A controller that stops the session.
|
|
2637
2947
|
*
|
|
2948
|
+
* @throws {@link SdkError} `UNKNOWN_LOCALE`: `locales` names a locale that is not a configured
|
|
2949
|
+
* target. Thrown once at startup, before any watching begins.
|
|
2638
2950
|
* @throws {@link SdkError} `CONCURRENCY_INVALID`: `concurrency` is not an integer of at least 1.
|
|
2639
2951
|
* @throws {@link SdkError} `CONCURRENCY_BUDGET_CONFLICT`: `concurrency` above 1 was combined with a
|
|
2640
2952
|
* configured token budget.
|
|
@@ -2643,6 +2955,9 @@ interface WatchController {
|
|
|
2643
2955
|
* @throws {@link SdkError} `LOCALE_PATH_COLLISION`: two configured locales resolve to the same path.
|
|
2644
2956
|
* @throws {@link SdkError} `SOURCE_UNREADABLE`: the source locale file does not exist when watching
|
|
2645
2957
|
* starts.
|
|
2958
|
+
* @throws Whatever the watcher factory raised, unwrapped, when it could not build a watcher over
|
|
2959
|
+
* the source file. It is not wrapped as an {@link SdkError}. No run has started at that point, so
|
|
2960
|
+
* nothing is watched and `onRun` is never called.
|
|
2646
2961
|
*
|
|
2647
2962
|
* @example
|
|
2648
2963
|
* ```ts
|
|
@@ -2665,4 +2980,4 @@ interface WatchController {
|
|
|
2665
2980
|
*/
|
|
2666
2981
|
declare function watch(input: WatchInput, deps?: WatchDeps): Promise<WatchController>;
|
|
2667
2982
|
|
|
2668
|
-
export { type BudgetBehavior, CACHE_FILE_NAME, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_DELIMITED_PATH, DEFAULT_EXCHANGE_FORMAT, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type DuplicateKeyReport, EXCHANGE_FORMATS, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExchangeFormat, 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 LocalePathResolver, type LocalePathResolverConfig, type LocaleSnapshotDelta, type LocaleStartedEvent, type LocaleStyle, 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, createLocalePathResolver, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate, verbatraConfigSchema, watch };
|
|
2983
|
+
export { type BudgetBehavior, CACHE_FILE_NAME, type CheckDeps, type CheckInput, type CheckSummary, type ConfigSource, type CreateProvider, type CreateWatcher, DEFAULT_DELIMITED_PATH, DEFAULT_EXCHANGE_FORMAT, DEFAULT_WORKBOOK_PATH, type DiffDeps, type DiffInput, type DiffSummary, type DoctorCheck, type DoctorCheckId, type DoctorCheckStatus, type DoctorDeps, type DoctorInput, type DoctorResult, type DuplicateKeyReport, EXCHANGE_FORMATS, type EditEntryDeps, type EditEntryInput, type EditEntryResult, type ExchangeFormat, type ExportWorkbookDeps, type ExportWorkbookInput, type ExportWorkbookResult, type GlossaryFileDeps, type GlossaryFileInput, 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 LocalePathResolver, type LocalePathResolverConfig, type LocaleSnapshotDelta, type LocaleStartedEvent, type LocaleStyle, 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 UpdateGlossaryTermInput, type UsageSummary, type VerbatraConfig, type VerbatraConfigInput, type WatchController, type WatchDeps, type WatchInput, type WatchRunResult, type Watcher, check, createLocalePathResolver, defineConfig, diff, diffLocaleSnapshots, doctor, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readGlossaryFile, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate, updateGlossaryTerm, verbatraConfigSchema, watch };
|