@verbatra/sdk 0.3.0 → 0.4.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 +28 -2
- package/dist/index.cjs +714 -238
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +206 -146
- package/dist/index.d.ts +206 -146
- package/dist/index.js +712 -240
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
package/dist/index.cjs
CHANGED
|
@@ -7,16 +7,20 @@ var cosmiconfigTypescriptLoader = require('cosmiconfig-typescript-loader');
|
|
|
7
7
|
var zod = require('zod');
|
|
8
8
|
var Anthropic = require('@anthropic-ai/sdk');
|
|
9
9
|
var deepl = require('deepl-node');
|
|
10
|
+
var module$1 = require('module');
|
|
10
11
|
var log = require('loglevel');
|
|
11
12
|
var genai = require('@google/genai');
|
|
12
13
|
var OpenAI = require('openai');
|
|
13
14
|
var crypto = require('crypto');
|
|
14
15
|
var promises = require('fs/promises');
|
|
15
16
|
var icuMessageformatParser = require('@formatjs/icu-messageformat-parser');
|
|
17
|
+
var xmldom = require('@xmldom/xmldom');
|
|
18
|
+
var yaml = require('yaml');
|
|
16
19
|
var ExcelJS = require('exceljs');
|
|
17
20
|
var JSZip = require('jszip');
|
|
18
21
|
var chokidar = require('chokidar');
|
|
19
22
|
|
|
23
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
20
24
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
25
|
|
|
22
26
|
function _interopNamespace(e) {
|
|
@@ -129,7 +133,10 @@ var SUPPORTED_FORMATS = [
|
|
|
129
133
|
"i18next-json",
|
|
130
134
|
"vue-i18n-json",
|
|
131
135
|
"next-intl-json",
|
|
132
|
-
"ngx-translate-json"
|
|
136
|
+
"ngx-translate-json",
|
|
137
|
+
"xliff",
|
|
138
|
+
"yaml",
|
|
139
|
+
"arb"
|
|
133
140
|
];
|
|
134
141
|
var supportedFormatSchema = zod.z.enum(SUPPORTED_FORMATS);
|
|
135
142
|
var translationEntrySchema = zod.z.object({
|
|
@@ -184,6 +191,26 @@ var LOCALE_TOKEN = "{locale}";
|
|
|
184
191
|
function localeFilePath(cwd, pattern, locale) {
|
|
185
192
|
return path.resolve(cwd, pattern.replaceAll(LOCALE_TOKEN, locale));
|
|
186
193
|
}
|
|
194
|
+
var REDACTED = "[REDACTED]";
|
|
195
|
+
var KEY_PATTERNS = [
|
|
196
|
+
// The `\b` anchors `sk-` to a word start so hyphenated words like "risk-" or "task-" pass through.
|
|
197
|
+
/\bsk-[A-Za-z0-9_-]{8,}/g,
|
|
198
|
+
/AIza[0-9A-Za-z_-]{35}/g,
|
|
199
|
+
/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(?::fx)?/g
|
|
200
|
+
];
|
|
201
|
+
function escapeForRegExp(value) {
|
|
202
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
203
|
+
}
|
|
204
|
+
function redact(text, secret = process.env.ANTHROPIC_API_KEY) {
|
|
205
|
+
let out = text;
|
|
206
|
+
for (const pattern of KEY_PATTERNS) {
|
|
207
|
+
out = out.replace(pattern, REDACTED);
|
|
208
|
+
}
|
|
209
|
+
if (secret !== void 0 && secret.length > 0) {
|
|
210
|
+
out = out.replace(new RegExp(escapeForRegExp(secret), "g"), REDACTED);
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
187
214
|
var ProviderError = class extends Error {
|
|
188
215
|
/** The stable {@link ProviderErrorCode} for this failure; branch on this, not the message. */
|
|
189
216
|
code;
|
|
@@ -192,7 +219,7 @@ var ProviderError = class extends Error {
|
|
|
192
219
|
* @param message - A fixed, safe message; callers must never pass key, SDK, or request-derived text.
|
|
193
220
|
*/
|
|
194
221
|
constructor(code, message) {
|
|
195
|
-
super(message);
|
|
222
|
+
super(redact(message, ""));
|
|
196
223
|
this.name = "ProviderError";
|
|
197
224
|
this.code = code;
|
|
198
225
|
}
|
|
@@ -319,6 +346,12 @@ function assertNotTruncated(truncated) {
|
|
|
319
346
|
throw new ProviderError("OUTPUT_TRUNCATED", OUTPUT_TRUNCATED_MESSAGE);
|
|
320
347
|
}
|
|
321
348
|
}
|
|
349
|
+
var PROVIDER_ENV = {
|
|
350
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
351
|
+
openai: "OPENAI_API_KEY",
|
|
352
|
+
gemini: "GEMINI_API_KEY",
|
|
353
|
+
deepl: "DEEPL_API_KEY"
|
|
354
|
+
};
|
|
322
355
|
function readRequiredEnv(name) {
|
|
323
356
|
const value = process.env[name];
|
|
324
357
|
if (value === void 0 || value.length === 0) {
|
|
@@ -327,16 +360,16 @@ function readRequiredEnv(name) {
|
|
|
327
360
|
return value;
|
|
328
361
|
}
|
|
329
362
|
function requireAnthropicKey() {
|
|
330
|
-
return readRequiredEnv(
|
|
363
|
+
return readRequiredEnv(PROVIDER_ENV.anthropic);
|
|
331
364
|
}
|
|
332
365
|
function requireOpenAiKey() {
|
|
333
|
-
return readRequiredEnv(
|
|
366
|
+
return readRequiredEnv(PROVIDER_ENV.openai);
|
|
334
367
|
}
|
|
335
368
|
function requireGeminiKey() {
|
|
336
|
-
return readRequiredEnv(
|
|
369
|
+
return readRequiredEnv(PROVIDER_ENV.gemini);
|
|
337
370
|
}
|
|
338
371
|
function requireDeepLKey() {
|
|
339
|
-
return readRequiredEnv(
|
|
372
|
+
return readRequiredEnv(PROVIDER_ENV.deepl);
|
|
340
373
|
}
|
|
341
374
|
function createDefaultClient() {
|
|
342
375
|
const sdk = new Anthropic__default.default({ apiKey: requireAnthropicKey(), logLevel: "off" });
|
|
@@ -437,8 +470,22 @@ function toUsage(usage) {
|
|
|
437
470
|
var deepLConfigSchema = zod.z.object({
|
|
438
471
|
glossaryId: zod.z.string().min(1).optional()
|
|
439
472
|
});
|
|
473
|
+
var DEEPL_LOGGER = "deepl";
|
|
474
|
+
function resolveDeeplLoglevel(requireFn = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
475
|
+
try {
|
|
476
|
+
const entry = requireFn.resolve("deepl-node");
|
|
477
|
+
return module$1.createRequire(entry)("loglevel");
|
|
478
|
+
} catch {
|
|
479
|
+
return void 0;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function silenceDeeplLogger(instances) {
|
|
483
|
+
for (const instance of instances) {
|
|
484
|
+
instance?.getLogger(DEEPL_LOGGER).setLevel("silent");
|
|
485
|
+
}
|
|
486
|
+
}
|
|
440
487
|
function silenceSdkLogging() {
|
|
441
|
-
log__default.default
|
|
488
|
+
silenceDeeplLogger([log__default.default, resolveDeeplLoglevel()]);
|
|
442
489
|
}
|
|
443
490
|
function createDefaultClient2() {
|
|
444
491
|
silenceSdkLogging();
|
|
@@ -799,6 +846,11 @@ function createMechanism3(client, config) {
|
|
|
799
846
|
function callClient4(client, body) {
|
|
800
847
|
return guardProviderCall(() => client.chat.completions.create(body));
|
|
801
848
|
}
|
|
849
|
+
var SCAFFOLD_MODELS = {
|
|
850
|
+
anthropic: "claude-sonnet-4-6",
|
|
851
|
+
openai: "gpt-5.4-mini",
|
|
852
|
+
gemini: "gemini-2.5-flash"
|
|
853
|
+
};
|
|
802
854
|
var providerConfigSchema = zod.z.discriminatedUnion("id", [
|
|
803
855
|
zod.z.object({ id: zod.z.literal("anthropic"), options: anthropicConfigSchema.strict() }),
|
|
804
856
|
zod.z.object({ id: zod.z.literal("openai"), options: openAiConfigSchema.strict() }),
|
|
@@ -829,27 +881,25 @@ var verbatraConfigSchema = zod.z.strictObject({
|
|
|
829
881
|
glossary: zod.z.record(zod.z.string(), zod.z.string()).optional(),
|
|
830
882
|
tone: zod.z.enum(["formal", "informal", "neutral"]).optional(),
|
|
831
883
|
/**
|
|
832
|
-
* Opt-in orphan pruning, off by default
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
* is non-secret, consistent with the config's no-secret invariant.
|
|
884
|
+
* Opt-in orphan pruning, off by default. When true, keys present in a target file but absent from
|
|
885
|
+
* the source are removed from the written file and the lock. The per-run `prune` option on
|
|
886
|
+
* `translate` (the CLI `--prune` flag) overrides this.
|
|
836
887
|
*/
|
|
837
888
|
prune: zod.z.boolean().optional(),
|
|
838
889
|
/**
|
|
839
|
-
* Opt-in plural-category generation, off by default
|
|
840
|
-
*
|
|
841
|
-
*
|
|
890
|
+
* Opt-in plural-category generation, off by default. When true, and only for an i18next-JSON project
|
|
891
|
+
* translated by an LLM provider, verbatra synthesizes the CLDR plural forms a target language
|
|
892
|
+
* requires but the source does not supply (for example Polish few/many). The per-run
|
|
842
893
|
* `generatePlurals` option on `translate` overrides this. Unsupported cases (DeepL, non-i18next, an
|
|
843
894
|
* unknown language) fall back to the per-locale plural warning.
|
|
844
895
|
*/
|
|
845
896
|
generatePlurals: zod.z.boolean().optional(),
|
|
846
897
|
/**
|
|
847
|
-
* Optional maximum number of entries sent in a single provider request. A locale's missing
|
|
848
|
-
* entries are split into sequential sub-batches no larger than this so one oversized request
|
|
849
|
-
* the whole locale; a failed sub-batch is withheld
|
|
850
|
-
*
|
|
851
|
-
*
|
|
852
|
-
* well inside provider context windows for typical short i18n strings while keeping request counts low.
|
|
898
|
+
* Optional maximum number of entries sent in a single provider request. A locale's missing and
|
|
899
|
+
* changed entries are split into sequential sub-batches no larger than this, so one oversized request
|
|
900
|
+
* cannot sink the whole locale; a failed sub-batch is withheld while the others make progress. Must
|
|
901
|
+
* be a positive integer (zero, negative, or non-integer is rejected, never coerced). When absent,
|
|
902
|
+
* {@link DEFAULT_MAX_BATCH_SIZE} applies.
|
|
853
903
|
*/
|
|
854
904
|
maxBatchSize: zod.z.number().int().positive().optional()
|
|
855
905
|
}).refine((config) => !config.targetLocales.includes(config.sourceLocale), {
|
|
@@ -1081,6 +1131,85 @@ async function writeLockFile(path, lock, fs) {
|
|
|
1081
1131
|
await fs.writeFile(path, `${JSON.stringify(ordered, null, 2)}
|
|
1082
1132
|
`);
|
|
1083
1133
|
}
|
|
1134
|
+
var VALID_EMPTY = { placeholders: [], isPlural: false, valid: true };
|
|
1135
|
+
var INVALID = { placeholders: [], isPlural: false, valid: false };
|
|
1136
|
+
function tokenOf(element) {
|
|
1137
|
+
switch (element.type) {
|
|
1138
|
+
case icuMessageformatParser.TYPE.argument:
|
|
1139
|
+
case icuMessageformatParser.TYPE.number:
|
|
1140
|
+
case icuMessageformatParser.TYPE.date:
|
|
1141
|
+
case icuMessageformatParser.TYPE.time:
|
|
1142
|
+
case icuMessageformatParser.TYPE.select:
|
|
1143
|
+
case icuMessageformatParser.TYPE.plural:
|
|
1144
|
+
return `{${element.value}}`;
|
|
1145
|
+
case icuMessageformatParser.TYPE.tag:
|
|
1146
|
+
return `<${element.value}>`;
|
|
1147
|
+
default:
|
|
1148
|
+
return void 0;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
function childMessages(element) {
|
|
1152
|
+
if (element.type === icuMessageformatParser.TYPE.plural || element.type === icuMessageformatParser.TYPE.select) {
|
|
1153
|
+
return Object.values(element.options).map((option) => option.value);
|
|
1154
|
+
}
|
|
1155
|
+
if (element.type === icuMessageformatParser.TYPE.tag) {
|
|
1156
|
+
return [element.children];
|
|
1157
|
+
}
|
|
1158
|
+
return [];
|
|
1159
|
+
}
|
|
1160
|
+
function collect(elements, add, state) {
|
|
1161
|
+
for (const element of elements) {
|
|
1162
|
+
const token = tokenOf(element);
|
|
1163
|
+
if (token !== void 0) {
|
|
1164
|
+
add(token);
|
|
1165
|
+
}
|
|
1166
|
+
if (element.type === icuMessageformatParser.TYPE.plural) {
|
|
1167
|
+
state.isPlural = true;
|
|
1168
|
+
}
|
|
1169
|
+
for (const child of childMessages(element)) {
|
|
1170
|
+
collect(child, add, state);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
function analyzeIcuValue(value) {
|
|
1175
|
+
if (!value.includes("{") && !value.includes("<")) {
|
|
1176
|
+
return VALID_EMPTY;
|
|
1177
|
+
}
|
|
1178
|
+
try {
|
|
1179
|
+
const ast = icuMessageformatParser.parse(value);
|
|
1180
|
+
const placeholders = [];
|
|
1181
|
+
const state = { isPlural: false };
|
|
1182
|
+
collect(
|
|
1183
|
+
ast,
|
|
1184
|
+
(token) => {
|
|
1185
|
+
placeholders.push(token);
|
|
1186
|
+
},
|
|
1187
|
+
state
|
|
1188
|
+
);
|
|
1189
|
+
return { placeholders, isPlural: state.isPlural, valid: true };
|
|
1190
|
+
} catch {
|
|
1191
|
+
return INVALID;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function icuPlaceholders(value) {
|
|
1195
|
+
return analyzeIcuValue(value).placeholders;
|
|
1196
|
+
}
|
|
1197
|
+
function icuIsValid(value) {
|
|
1198
|
+
return analyzeIcuValue(value).valid;
|
|
1199
|
+
}
|
|
1200
|
+
function icuDeriveEntry(_key, value) {
|
|
1201
|
+
const analysis = analyzeIcuValue(value);
|
|
1202
|
+
return { placeholders: analysis.placeholders, isPlural: analysis.isPlural };
|
|
1203
|
+
}
|
|
1204
|
+
function icuInvalidKeys(entries) {
|
|
1205
|
+
const invalid = [];
|
|
1206
|
+
for (const [key, entry] of entries) {
|
|
1207
|
+
if (!icuIsValid(entry.value)) {
|
|
1208
|
+
invalid.push(key);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
return invalid;
|
|
1212
|
+
}
|
|
1084
1213
|
var AdapterError = class extends Error {
|
|
1085
1214
|
code;
|
|
1086
1215
|
constructor(code, message) {
|
|
@@ -1089,6 +1218,82 @@ var AdapterError = class extends Error {
|
|
|
1089
1218
|
this.code = code;
|
|
1090
1219
|
}
|
|
1091
1220
|
};
|
|
1221
|
+
var MAX_DEPTH = 100;
|
|
1222
|
+
var MAX_INPUT_BYTES = 16 * 1024 * 1024;
|
|
1223
|
+
var jsonTreeSchema = zod.z.lazy(
|
|
1224
|
+
() => zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), jsonTreeSchema)])
|
|
1225
|
+
);
|
|
1226
|
+
var rootSchema = zod.z.record(zod.z.string(), jsonTreeSchema);
|
|
1227
|
+
function assertWithinDepth(value, max) {
|
|
1228
|
+
const stack = [{ node: value, depth: 1 }];
|
|
1229
|
+
while (stack.length > 0) {
|
|
1230
|
+
const top = stack.pop();
|
|
1231
|
+
if (top === void 0) {
|
|
1232
|
+
break;
|
|
1233
|
+
}
|
|
1234
|
+
const { node, depth } = top;
|
|
1235
|
+
if (typeof node !== "object" || node === null) {
|
|
1236
|
+
continue;
|
|
1237
|
+
}
|
|
1238
|
+
if (depth > max) {
|
|
1239
|
+
throw new AdapterError("MAX_DEPTH_EXCEEDED", "The file nests objects too deeply.");
|
|
1240
|
+
}
|
|
1241
|
+
for (const child of Object.values(node)) {
|
|
1242
|
+
stack.push({ node: child, depth: depth + 1 });
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function assertJsonRecord(value) {
|
|
1247
|
+
assertWithinDepth(value, MAX_DEPTH);
|
|
1248
|
+
const result = rootSchema.safeParse(value);
|
|
1249
|
+
if (!result.success) {
|
|
1250
|
+
throw new AdapterError(
|
|
1251
|
+
"INVALID_STRUCTURE",
|
|
1252
|
+
"The file is not a valid object (expected nested objects of string values)."
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
return result.data;
|
|
1256
|
+
}
|
|
1257
|
+
function parseJsonObject(content) {
|
|
1258
|
+
let parsed;
|
|
1259
|
+
try {
|
|
1260
|
+
parsed = JSON.parse(content);
|
|
1261
|
+
} catch {
|
|
1262
|
+
throw new AdapterError("INVALID_JSON", "The file is not valid JSON.");
|
|
1263
|
+
}
|
|
1264
|
+
return assertJsonRecord(parsed);
|
|
1265
|
+
}
|
|
1266
|
+
function serializeJsonTree(tree) {
|
|
1267
|
+
return `${JSON.stringify(tree, null, 2)}
|
|
1268
|
+
`;
|
|
1269
|
+
}
|
|
1270
|
+
function namespaceOf(filePath) {
|
|
1271
|
+
return path.basename(filePath, path.extname(filePath));
|
|
1272
|
+
}
|
|
1273
|
+
function rethrowStructured(error, message) {
|
|
1274
|
+
if (error instanceof AdapterError) {
|
|
1275
|
+
throw error;
|
|
1276
|
+
}
|
|
1277
|
+
throw new AdapterError("INVALID_STRUCTURE", message);
|
|
1278
|
+
}
|
|
1279
|
+
function computeIcu(entries, compute) {
|
|
1280
|
+
if (!compute) {
|
|
1281
|
+
return [];
|
|
1282
|
+
}
|
|
1283
|
+
try {
|
|
1284
|
+
return compute(entries);
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
rethrowStructured(error, "The file could not be analyzed for message validity.");
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function buildCanHandle(extensions, sniff) {
|
|
1290
|
+
return (filePath, sample) => {
|
|
1291
|
+
if (!extensions.includes(path.extname(filePath).toLowerCase())) {
|
|
1292
|
+
return false;
|
|
1293
|
+
}
|
|
1294
|
+
return sample === void 0 || sniff === void 0 || sniff(sample);
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1092
1297
|
var nodeOps = {
|
|
1093
1298
|
writeFile: (path, data) => promises.writeFile(path, data, "utf8"),
|
|
1094
1299
|
rename: (from, to) => promises.rename(from, to),
|
|
@@ -1113,8 +1318,6 @@ async function atomicWriteFile(path, data, ops = nodeOps) {
|
|
|
1113
1318
|
throw error;
|
|
1114
1319
|
}
|
|
1115
1320
|
}
|
|
1116
|
-
var MAX_DEPTH = 100;
|
|
1117
|
-
var MAX_INPUT_BYTES = 16 * 1024 * 1024;
|
|
1118
1321
|
async function readBoundedUtf82(handle, size) {
|
|
1119
1322
|
const buffer = Buffer.allocUnsafe(size);
|
|
1120
1323
|
let offset = 0;
|
|
@@ -1142,6 +1345,16 @@ async function readBounded2(filePath) {
|
|
|
1142
1345
|
await handle.close();
|
|
1143
1346
|
}
|
|
1144
1347
|
}
|
|
1348
|
+
async function readFileContent(filePath) {
|
|
1349
|
+
const outcome = await readBounded2(filePath);
|
|
1350
|
+
if (outcome.kind === "not-a-file") {
|
|
1351
|
+
throw new AdapterError("INVALID_STRUCTURE", "The path is not a regular file.");
|
|
1352
|
+
}
|
|
1353
|
+
if (outcome.kind === "too-large") {
|
|
1354
|
+
throw new AdapterError("INPUT_TOO_LARGE", "The file exceeds the maximum allowed size.");
|
|
1355
|
+
}
|
|
1356
|
+
return outcome.content;
|
|
1357
|
+
}
|
|
1145
1358
|
var BACKSLASH = "\\";
|
|
1146
1359
|
var DOT = ".";
|
|
1147
1360
|
var ESCAPED_BACKSLASH = "\\\\";
|
|
@@ -1255,46 +1468,6 @@ function flattenTree(tree, namespace, derive, keyMode = "literal-leaf") {
|
|
|
1255
1468
|
addEntries({ namespace, derive, out, claimed: /* @__PURE__ */ new Map() }, [], tree);
|
|
1256
1469
|
return out;
|
|
1257
1470
|
}
|
|
1258
|
-
var jsonTreeSchema = zod.z.lazy(
|
|
1259
|
-
() => zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), jsonTreeSchema)])
|
|
1260
|
-
);
|
|
1261
|
-
var rootSchema = zod.z.record(zod.z.string(), jsonTreeSchema);
|
|
1262
|
-
function assertWithinDepth(value, max) {
|
|
1263
|
-
const stack = [{ node: value, depth: 1 }];
|
|
1264
|
-
while (stack.length > 0) {
|
|
1265
|
-
const top = stack.pop();
|
|
1266
|
-
if (top === void 0) {
|
|
1267
|
-
break;
|
|
1268
|
-
}
|
|
1269
|
-
const { node, depth } = top;
|
|
1270
|
-
if (typeof node !== "object" || node === null) {
|
|
1271
|
-
continue;
|
|
1272
|
-
}
|
|
1273
|
-
if (depth > max) {
|
|
1274
|
-
throw new AdapterError("MAX_DEPTH_EXCEEDED", "The file nests objects too deeply.");
|
|
1275
|
-
}
|
|
1276
|
-
for (const child of Object.values(node)) {
|
|
1277
|
-
stack.push({ node: child, depth: depth + 1 });
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
function parseJsonObject(content) {
|
|
1282
|
-
let parsed;
|
|
1283
|
-
try {
|
|
1284
|
-
parsed = JSON.parse(content);
|
|
1285
|
-
} catch {
|
|
1286
|
-
throw new AdapterError("INVALID_JSON", "The file is not valid JSON.");
|
|
1287
|
-
}
|
|
1288
|
-
assertWithinDepth(parsed, MAX_DEPTH);
|
|
1289
|
-
const result = rootSchema.safeParse(parsed);
|
|
1290
|
-
if (!result.success) {
|
|
1291
|
-
throw new AdapterError(
|
|
1292
|
-
"INVALID_STRUCTURE",
|
|
1293
|
-
"The file is not a valid JSON object (expected nested objects of string values)."
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1296
|
-
return result.data;
|
|
1297
|
-
}
|
|
1298
1471
|
function emptyNode() {
|
|
1299
1472
|
return /* @__PURE__ */ Object.create(null);
|
|
1300
1473
|
}
|
|
@@ -1331,77 +1504,146 @@ function unflattenEntries(entries) {
|
|
|
1331
1504
|
}
|
|
1332
1505
|
return root;
|
|
1333
1506
|
}
|
|
1334
|
-
function
|
|
1335
|
-
return path.basename(filePath, path.extname(filePath));
|
|
1336
|
-
}
|
|
1337
|
-
function canHandle(filePath, sample) {
|
|
1338
|
-
if (path.extname(filePath).toLowerCase() !== ".json") {
|
|
1339
|
-
return false;
|
|
1340
|
-
}
|
|
1341
|
-
return sample === void 0 || sample.trimStart().startsWith("{");
|
|
1342
|
-
}
|
|
1343
|
-
function rethrowStructured(error, message) {
|
|
1344
|
-
if (error instanceof AdapterError) {
|
|
1345
|
-
throw error;
|
|
1346
|
-
}
|
|
1347
|
-
throw new AdapterError("INVALID_STRUCTURE", message);
|
|
1348
|
-
}
|
|
1349
|
-
function toEntries(content, namespace, deriveEntry, keyMode, validateTree) {
|
|
1507
|
+
function toEntries(content, namespace, parse2, deriveEntry, keyMode, validateTree) {
|
|
1350
1508
|
try {
|
|
1351
|
-
const tree =
|
|
1509
|
+
const tree = parse2(content);
|
|
1352
1510
|
validateTree?.(tree);
|
|
1353
1511
|
return flattenTree(tree, namespace, deriveEntry, keyMode);
|
|
1354
1512
|
} catch (error) {
|
|
1355
|
-
rethrowStructured(error, "The file could not be
|
|
1513
|
+
rethrowStructured(error, "The file could not be parsed.");
|
|
1356
1514
|
}
|
|
1357
1515
|
}
|
|
1358
|
-
function
|
|
1359
|
-
if (!compute) {
|
|
1360
|
-
return [];
|
|
1361
|
-
}
|
|
1362
|
-
try {
|
|
1363
|
-
return compute(entries);
|
|
1364
|
-
} catch (error) {
|
|
1365
|
-
rethrowStructured(error, "The file could not be analyzed for message validity.");
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
function createJsonFileAdapter(options) {
|
|
1516
|
+
function createTreeFileAdapter(options) {
|
|
1369
1517
|
const {
|
|
1370
1518
|
format,
|
|
1519
|
+
extensions,
|
|
1520
|
+
sniff,
|
|
1521
|
+
parse: parse2,
|
|
1522
|
+
serialize,
|
|
1371
1523
|
deriveEntry,
|
|
1372
|
-
extractPlaceholders
|
|
1373
|
-
computeInvalidIcuKeys
|
|
1374
|
-
validateMessage
|
|
1524
|
+
extractPlaceholders,
|
|
1525
|
+
computeInvalidIcuKeys,
|
|
1526
|
+
validateMessage,
|
|
1375
1527
|
validateTree,
|
|
1376
1528
|
buildWriteTree,
|
|
1377
1529
|
keyMode = "literal-leaf"
|
|
1378
1530
|
} = options;
|
|
1379
1531
|
return {
|
|
1380
1532
|
format,
|
|
1381
|
-
canHandle,
|
|
1382
|
-
extractPlaceholders
|
|
1383
|
-
|
|
1384
|
-
validateMessage: validateMessage2 ?? (() => true),
|
|
1533
|
+
canHandle: buildCanHandle(extensions, sniff),
|
|
1534
|
+
extractPlaceholders,
|
|
1535
|
+
validateMessage: validateMessage ?? (() => true),
|
|
1385
1536
|
async read(filePath, locale) {
|
|
1386
|
-
const
|
|
1387
|
-
if (outcome.kind === "not-a-file") {
|
|
1388
|
-
throw new AdapterError("INVALID_STRUCTURE", "The path is not a regular file.");
|
|
1389
|
-
}
|
|
1390
|
-
if (outcome.kind === "too-large") {
|
|
1391
|
-
throw new AdapterError("INPUT_TOO_LARGE", "The file exceeds the maximum allowed size.");
|
|
1392
|
-
}
|
|
1537
|
+
const content = await readFileContent(filePath);
|
|
1393
1538
|
const namespace = namespaceOf(filePath);
|
|
1394
|
-
const entries = toEntries(
|
|
1539
|
+
const entries = toEntries(content, namespace, parse2, deriveEntry, keyMode, validateTree);
|
|
1395
1540
|
const resource = { locale, namespace, format, entries };
|
|
1396
|
-
const invalidIcuKeys = computeIcu(entries,
|
|
1541
|
+
const invalidIcuKeys = computeIcu(entries, computeInvalidIcuKeys);
|
|
1397
1542
|
return { resource, invalidIcuKeys };
|
|
1398
1543
|
},
|
|
1399
1544
|
async write(resource, filePath) {
|
|
1400
1545
|
const tree = buildWriteTree ? await buildWriteTree(resource.entries, filePath) : unflattenEntries(resource.entries);
|
|
1401
|
-
await atomicWriteFile(filePath,
|
|
1402
|
-
|
|
1546
|
+
await atomicWriteFile(filePath, serialize(tree));
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
function isMetadataKey(key) {
|
|
1551
|
+
return key.startsWith("@");
|
|
1552
|
+
}
|
|
1553
|
+
function parseArbObject(content) {
|
|
1554
|
+
let parsed;
|
|
1555
|
+
try {
|
|
1556
|
+
parsed = JSON.parse(content);
|
|
1557
|
+
} catch {
|
|
1558
|
+
throw new AdapterError("INVALID_JSON", "The file is not valid JSON.");
|
|
1559
|
+
}
|
|
1560
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1561
|
+
throw new AdapterError(
|
|
1562
|
+
"INVALID_STRUCTURE",
|
|
1563
|
+
"The file is not a valid object (expected nested objects of string values)."
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
return parsed;
|
|
1567
|
+
}
|
|
1568
|
+
function stripArbMetadata(tree) {
|
|
1569
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1570
|
+
for (const [key, value] of Object.entries(tree)) {
|
|
1571
|
+
if (!isMetadataKey(key)) {
|
|
1572
|
+
out[key] = value;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
return out;
|
|
1576
|
+
}
|
|
1577
|
+
function originalKey(encoded) {
|
|
1578
|
+
return decodeKeyToSegments(encoded).join(".");
|
|
1579
|
+
}
|
|
1580
|
+
function messagesFromEntries(entries) {
|
|
1581
|
+
const out = /* @__PURE__ */ new Map();
|
|
1582
|
+
for (const [key, entry] of entries) {
|
|
1583
|
+
out.set(originalKey(key), entry.value);
|
|
1584
|
+
}
|
|
1585
|
+
return out;
|
|
1586
|
+
}
|
|
1587
|
+
async function readDestinationPairs(filePath) {
|
|
1588
|
+
let parsed;
|
|
1589
|
+
try {
|
|
1590
|
+
const outcome = await readBounded2(filePath);
|
|
1591
|
+
if (outcome.kind !== "ok") {
|
|
1592
|
+
return null;
|
|
1593
|
+
}
|
|
1594
|
+
parsed = JSON.parse(outcome.content);
|
|
1595
|
+
} catch {
|
|
1596
|
+
return null;
|
|
1597
|
+
}
|
|
1598
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1599
|
+
return null;
|
|
1600
|
+
}
|
|
1601
|
+
return Object.entries(parsed);
|
|
1602
|
+
}
|
|
1603
|
+
async function buildArbWriteTree(entries, filePath) {
|
|
1604
|
+
const messages = messagesFromEntries(entries);
|
|
1605
|
+
const pairs = await readDestinationPairs(filePath);
|
|
1606
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1607
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
1608
|
+
for (const [key, value] of pairs ?? []) {
|
|
1609
|
+
const translated = isMetadataKey(key) ? void 0 : messages.get(key);
|
|
1610
|
+
if (translated !== void 0) {
|
|
1611
|
+
consumed.add(key);
|
|
1403
1612
|
}
|
|
1404
|
-
|
|
1613
|
+
out[key] = translated ?? value;
|
|
1614
|
+
}
|
|
1615
|
+
for (const [key, value] of messages) {
|
|
1616
|
+
if (!consumed.has(key)) {
|
|
1617
|
+
out[key] = value;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return out;
|
|
1621
|
+
}
|
|
1622
|
+
function parseArb(content) {
|
|
1623
|
+
return assertJsonRecord(stripArbMetadata(parseArbObject(content)));
|
|
1624
|
+
}
|
|
1625
|
+
function createArbAdapter() {
|
|
1626
|
+
return createTreeFileAdapter({
|
|
1627
|
+
format: "arb",
|
|
1628
|
+
extensions: [".arb"],
|
|
1629
|
+
sniff: (sample) => sample.trimStart().startsWith("{"),
|
|
1630
|
+
parse: parseArb,
|
|
1631
|
+
serialize: serializeJsonTree,
|
|
1632
|
+
extractPlaceholders: icuPlaceholders,
|
|
1633
|
+
deriveEntry: icuDeriveEntry,
|
|
1634
|
+
computeInvalidIcuKeys: icuInvalidKeys,
|
|
1635
|
+
validateMessage: icuIsValid,
|
|
1636
|
+
buildWriteTree: buildArbWriteTree
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
function createJsonFileAdapter(options) {
|
|
1640
|
+
return createTreeFileAdapter({
|
|
1641
|
+
...options,
|
|
1642
|
+
extensions: [".json"],
|
|
1643
|
+
sniff: (sample) => sample.trimStart().startsWith("{"),
|
|
1644
|
+
parse: parseJsonObject,
|
|
1645
|
+
serialize: serializeJsonTree
|
|
1646
|
+
});
|
|
1405
1647
|
}
|
|
1406
1648
|
var DOUBLE_BRACE_PATTERN = /\{\{[^{}]*\}\}/g;
|
|
1407
1649
|
var I18NEXT_PATTERN = /\{\{[^{}]*\}\}|\$t\([^()]*\)/g;
|
|
@@ -1448,91 +1690,13 @@ function createI18nextJsonAdapter() {
|
|
|
1448
1690
|
})
|
|
1449
1691
|
});
|
|
1450
1692
|
}
|
|
1451
|
-
var VALID_EMPTY = { placeholders: [], isPlural: false, valid: true };
|
|
1452
|
-
var INVALID = { placeholders: [], isPlural: false, valid: false };
|
|
1453
|
-
function tokenOf(element) {
|
|
1454
|
-
switch (element.type) {
|
|
1455
|
-
case icuMessageformatParser.TYPE.argument:
|
|
1456
|
-
case icuMessageformatParser.TYPE.number:
|
|
1457
|
-
case icuMessageformatParser.TYPE.date:
|
|
1458
|
-
case icuMessageformatParser.TYPE.time:
|
|
1459
|
-
case icuMessageformatParser.TYPE.select:
|
|
1460
|
-
case icuMessageformatParser.TYPE.plural:
|
|
1461
|
-
return `{${element.value}}`;
|
|
1462
|
-
case icuMessageformatParser.TYPE.tag:
|
|
1463
|
-
return `<${element.value}>`;
|
|
1464
|
-
default:
|
|
1465
|
-
return void 0;
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
function childMessages(element) {
|
|
1469
|
-
if (element.type === icuMessageformatParser.TYPE.plural || element.type === icuMessageformatParser.TYPE.select) {
|
|
1470
|
-
return Object.values(element.options).map((option) => option.value);
|
|
1471
|
-
}
|
|
1472
|
-
if (element.type === icuMessageformatParser.TYPE.tag) {
|
|
1473
|
-
return [element.children];
|
|
1474
|
-
}
|
|
1475
|
-
return [];
|
|
1476
|
-
}
|
|
1477
|
-
function collect(elements, add, state) {
|
|
1478
|
-
for (const element of elements) {
|
|
1479
|
-
const token = tokenOf(element);
|
|
1480
|
-
if (token !== void 0) {
|
|
1481
|
-
add(token);
|
|
1482
|
-
}
|
|
1483
|
-
if (element.type === icuMessageformatParser.TYPE.plural) {
|
|
1484
|
-
state.isPlural = true;
|
|
1485
|
-
}
|
|
1486
|
-
for (const child of childMessages(element)) {
|
|
1487
|
-
collect(child, add, state);
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
function analyzeIcuValue(value) {
|
|
1492
|
-
if (!value.includes("{") && !value.includes("<")) {
|
|
1493
|
-
return VALID_EMPTY;
|
|
1494
|
-
}
|
|
1495
|
-
try {
|
|
1496
|
-
const ast = icuMessageformatParser.parse(value);
|
|
1497
|
-
const placeholders = [];
|
|
1498
|
-
const state = { isPlural: false };
|
|
1499
|
-
collect(
|
|
1500
|
-
ast,
|
|
1501
|
-
(token) => {
|
|
1502
|
-
placeholders.push(token);
|
|
1503
|
-
},
|
|
1504
|
-
state
|
|
1505
|
-
);
|
|
1506
|
-
return { placeholders, isPlural: state.isPlural, valid: true };
|
|
1507
|
-
} catch {
|
|
1508
|
-
return INVALID;
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
function extractPlaceholders(value) {
|
|
1512
|
-
return analyzeIcuValue(value).placeholders;
|
|
1513
|
-
}
|
|
1514
|
-
function validateMessage(value) {
|
|
1515
|
-
return analyzeIcuValue(value).valid;
|
|
1516
|
-
}
|
|
1517
|
-
function computeInvalidIcuKeys(entries) {
|
|
1518
|
-
const invalid = [];
|
|
1519
|
-
for (const [key, entry] of entries) {
|
|
1520
|
-
if (!validateMessage(entry.value)) {
|
|
1521
|
-
invalid.push(key);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
return invalid;
|
|
1525
|
-
}
|
|
1526
1693
|
function createNextIntlJsonAdapter() {
|
|
1527
1694
|
return createJsonFileAdapter({
|
|
1528
1695
|
format: "next-intl-json",
|
|
1529
|
-
extractPlaceholders,
|
|
1530
|
-
deriveEntry:
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
},
|
|
1534
|
-
computeInvalidIcuKeys,
|
|
1535
|
-
validateMessage
|
|
1696
|
+
extractPlaceholders: icuPlaceholders,
|
|
1697
|
+
deriveEntry: icuDeriveEntry,
|
|
1698
|
+
computeInvalidIcuKeys: icuInvalidKeys,
|
|
1699
|
+
validateMessage: icuIsValid
|
|
1536
1700
|
});
|
|
1537
1701
|
}
|
|
1538
1702
|
function assertNotMixed(tree) {
|
|
@@ -1594,8 +1758,7 @@ function createNgxTranslateJsonAdapter() {
|
|
|
1594
1758
|
}),
|
|
1595
1759
|
validateTree: assertNotMixed,
|
|
1596
1760
|
buildWriteTree: buildNgxWriteTree,
|
|
1597
|
-
// ngx-translate flat style uses dotted keys as path notation, not literal leaves
|
|
1598
|
-
// keep the legacy non-encoding flatten so its flat/nested round-trip is unchanged.
|
|
1761
|
+
// ngx-translate flat style uses dotted keys as path notation, not literal leaves.
|
|
1599
1762
|
keyMode: "path-notation"
|
|
1600
1763
|
});
|
|
1601
1764
|
}
|
|
@@ -1671,8 +1834,245 @@ function createVueI18nJsonAdapter() {
|
|
|
1671
1834
|
})
|
|
1672
1835
|
});
|
|
1673
1836
|
}
|
|
1837
|
+
function toEntries2(content, namespace, parseEntries) {
|
|
1838
|
+
try {
|
|
1839
|
+
return parseEntries(content, namespace);
|
|
1840
|
+
} catch (error) {
|
|
1841
|
+
rethrowStructured(error, "The file could not be parsed.");
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
function createFlatFileAdapter(options) {
|
|
1845
|
+
const {
|
|
1846
|
+
format,
|
|
1847
|
+
extensions,
|
|
1848
|
+
sniff,
|
|
1849
|
+
parseEntries,
|
|
1850
|
+
serializeEntries,
|
|
1851
|
+
extractPlaceholders,
|
|
1852
|
+
validateMessage,
|
|
1853
|
+
computeInvalidIcuKeys
|
|
1854
|
+
} = options;
|
|
1855
|
+
return {
|
|
1856
|
+
format,
|
|
1857
|
+
canHandle: buildCanHandle(extensions, sniff),
|
|
1858
|
+
extractPlaceholders,
|
|
1859
|
+
validateMessage: validateMessage ?? (() => true),
|
|
1860
|
+
async read(filePath, locale) {
|
|
1861
|
+
const content = await readFileContent(filePath);
|
|
1862
|
+
const namespace = namespaceOf(filePath);
|
|
1863
|
+
const entries = toEntries2(content, namespace, parseEntries);
|
|
1864
|
+
const resource = { locale, namespace, format, entries };
|
|
1865
|
+
const invalidIcuKeys = computeIcu(entries, computeInvalidIcuKeys);
|
|
1866
|
+
return { resource, invalidIcuKeys };
|
|
1867
|
+
},
|
|
1868
|
+
async write(resource, filePath) {
|
|
1869
|
+
const data = await serializeEntries(resource.entries, filePath);
|
|
1870
|
+
await atomicWriteFile(filePath, data);
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
var XLIFF_PATTERN = /<(?:x|g|bx|ex|ph|it|mrk)\b[^>]*>|\{[^{}]+\}/g;
|
|
1875
|
+
function extractXliffPlaceholders(value) {
|
|
1876
|
+
const result = [];
|
|
1877
|
+
for (const match of value.matchAll(XLIFF_PATTERN)) {
|
|
1878
|
+
const token = match[0];
|
|
1879
|
+
if (token !== void 0) {
|
|
1880
|
+
result.push(token);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
return result;
|
|
1884
|
+
}
|
|
1885
|
+
var ELEMENT_NODE = 1;
|
|
1886
|
+
function isElement(node) {
|
|
1887
|
+
return node.nodeType === ELEMENT_NODE;
|
|
1888
|
+
}
|
|
1889
|
+
function elementChildren(parent) {
|
|
1890
|
+
return Array.from(parent.childNodes).filter(isElement);
|
|
1891
|
+
}
|
|
1892
|
+
function childByName(parent, name) {
|
|
1893
|
+
return elementChildren(parent).find((el) => el.localName === name) ?? null;
|
|
1894
|
+
}
|
|
1895
|
+
function collectByTag(root, name) {
|
|
1896
|
+
return Array.from(root.getElementsByTagName(name));
|
|
1897
|
+
}
|
|
1898
|
+
function unitKey(element, index) {
|
|
1899
|
+
return element.getAttribute("id") ?? element.getAttribute("resname") ?? `unit-${index}`;
|
|
1900
|
+
}
|
|
1901
|
+
function onFatal(level) {
|
|
1902
|
+
if (level === "fatalError") {
|
|
1903
|
+
throw new Error("malformed XML");
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
function assertNoDoctype(content) {
|
|
1907
|
+
if (/<!DOCTYPE/i.test(content) || /<!ENTITY/i.test(content)) {
|
|
1908
|
+
throw new AdapterError("INVALID_XML", "XLIFF with a DTD or entity declaration is rejected.");
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
function parseXml(content) {
|
|
1912
|
+
assertNoDoctype(content);
|
|
1913
|
+
let doc;
|
|
1914
|
+
try {
|
|
1915
|
+
doc = new xmldom.DOMParser({ onError: onFatal }).parseFromString(content, "text/xml");
|
|
1916
|
+
} catch {
|
|
1917
|
+
throw new AdapterError("INVALID_XML", "The file is not valid XML.");
|
|
1918
|
+
}
|
|
1919
|
+
const root = doc.documentElement;
|
|
1920
|
+
if (root === null || root.localName !== "xliff") {
|
|
1921
|
+
throw new AdapterError("INVALID_STRUCTURE", "The file is not an XLIFF document.");
|
|
1922
|
+
}
|
|
1923
|
+
return { doc, root };
|
|
1924
|
+
}
|
|
1925
|
+
function walkXliff12(root) {
|
|
1926
|
+
const units = [];
|
|
1927
|
+
collectByTag(root, "trans-unit").forEach((tu, index) => {
|
|
1928
|
+
const source = childByName(tu, "source");
|
|
1929
|
+
if (source !== null) {
|
|
1930
|
+
units.push({
|
|
1931
|
+
key: unitKey(tu, index),
|
|
1932
|
+
source,
|
|
1933
|
+
target: childByName(tu, "target"),
|
|
1934
|
+
container: tu
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
return units;
|
|
1939
|
+
}
|
|
1940
|
+
function walkXliff20(root) {
|
|
1941
|
+
const units = [];
|
|
1942
|
+
collectByTag(root, "unit").forEach((unit, index) => {
|
|
1943
|
+
const baseKey = unitKey(unit, index);
|
|
1944
|
+
const segments = elementChildren(unit).filter((el) => el.localName === "segment");
|
|
1945
|
+
segments.forEach((segment, segIndex) => {
|
|
1946
|
+
const source = childByName(segment, "source");
|
|
1947
|
+
if (source !== null) {
|
|
1948
|
+
const key = segments.length > 1 ? `${baseKey}#${segIndex}` : baseKey;
|
|
1949
|
+
units.push({ key, source, target: childByName(segment, "target"), container: segment });
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1952
|
+
});
|
|
1953
|
+
return units;
|
|
1954
|
+
}
|
|
1955
|
+
function walkUnits(root) {
|
|
1956
|
+
const version = root.getAttribute("version") ?? "1.2";
|
|
1957
|
+
return version.startsWith("2") ? walkXliff20(root) : walkXliff12(root);
|
|
1958
|
+
}
|
|
1959
|
+
function innerXml(serializer, element) {
|
|
1960
|
+
return Array.from(element.childNodes).map((node) => serializer.serializeToString(node)).join("");
|
|
1961
|
+
}
|
|
1962
|
+
function unitValue(serializer, unit) {
|
|
1963
|
+
if (unit.target !== null) {
|
|
1964
|
+
const targetXml = innerXml(serializer, unit.target);
|
|
1965
|
+
if (targetXml.trim() !== "") {
|
|
1966
|
+
return targetXml;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return innerXml(serializer, unit.source);
|
|
1970
|
+
}
|
|
1971
|
+
function parseXliffEntries(content, namespace) {
|
|
1972
|
+
const { root } = parseXml(content);
|
|
1973
|
+
const serializer = new xmldom.XMLSerializer();
|
|
1974
|
+
const out = /* @__PURE__ */ new Map();
|
|
1975
|
+
for (const unit of walkUnits(root)) {
|
|
1976
|
+
const value = unitValue(serializer, unit);
|
|
1977
|
+
out.set(unit.key, {
|
|
1978
|
+
key: unit.key,
|
|
1979
|
+
namespace,
|
|
1980
|
+
value,
|
|
1981
|
+
placeholders: extractXliffPlaceholders(value),
|
|
1982
|
+
isPlural: false
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
return out;
|
|
1986
|
+
}
|
|
1987
|
+
async function readDestination(filePath) {
|
|
1988
|
+
let outcome;
|
|
1989
|
+
try {
|
|
1990
|
+
outcome = await readBounded2(filePath);
|
|
1991
|
+
} catch {
|
|
1992
|
+
throw new AdapterError("INVALID_STRUCTURE", "The destination XLIFF file does not exist.");
|
|
1993
|
+
}
|
|
1994
|
+
if (outcome.kind === "not-a-file") {
|
|
1995
|
+
throw new AdapterError("INVALID_STRUCTURE", "The destination path is not a regular file.");
|
|
1996
|
+
}
|
|
1997
|
+
if (outcome.kind === "too-large") {
|
|
1998
|
+
throw new AdapterError("INPUT_TOO_LARGE", "The file exceeds the maximum allowed size.");
|
|
1999
|
+
}
|
|
2000
|
+
return outcome.content;
|
|
2001
|
+
}
|
|
2002
|
+
function fragmentNodes(parser, value) {
|
|
2003
|
+
try {
|
|
2004
|
+
const root = parser.parseFromString(`<wrapper>${value}</wrapper>`, "text/xml").documentElement;
|
|
2005
|
+
return root === null ? null : Array.from(root.childNodes);
|
|
2006
|
+
} catch {
|
|
2007
|
+
return null;
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
function setTargetValue(doc, parser, element, value) {
|
|
2011
|
+
while (element.firstChild !== null) {
|
|
2012
|
+
element.removeChild(element.firstChild);
|
|
2013
|
+
}
|
|
2014
|
+
const nodes = fragmentNodes(parser, value);
|
|
2015
|
+
if (nodes === null) {
|
|
2016
|
+
element.textContent = value;
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
for (const node of nodes) {
|
|
2020
|
+
element.appendChild(doc.importNode(node, true));
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
async function serializeXliffEntries(entries, filePath) {
|
|
2024
|
+
const { doc, root } = parseXml(await readDestination(filePath));
|
|
2025
|
+
const parser = new xmldom.DOMParser({ onError: onFatal });
|
|
2026
|
+
for (const unit of walkUnits(root)) {
|
|
2027
|
+
const entry = entries.get(unit.key);
|
|
2028
|
+
if (entry !== void 0) {
|
|
2029
|
+
const target = unit.target ?? doc.createElement("target");
|
|
2030
|
+
if (unit.target === null) {
|
|
2031
|
+
unit.container.appendChild(target);
|
|
2032
|
+
}
|
|
2033
|
+
setTargetValue(doc, parser, target, entry.value);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
return new xmldom.XMLSerializer().serializeToString(doc);
|
|
2037
|
+
}
|
|
2038
|
+
function sniffXliff(sample) {
|
|
2039
|
+
const head = sample.trimStart();
|
|
2040
|
+
return head.startsWith("<xliff") || head.startsWith("<?xml");
|
|
2041
|
+
}
|
|
2042
|
+
function createXliffAdapter() {
|
|
2043
|
+
return createFlatFileAdapter({
|
|
2044
|
+
format: "xliff",
|
|
2045
|
+
extensions: [".xlf", ".xliff"],
|
|
2046
|
+
sniff: sniffXliff,
|
|
2047
|
+
parseEntries: parseXliffEntries,
|
|
2048
|
+
serializeEntries: serializeXliffEntries,
|
|
2049
|
+
extractPlaceholders: extractXliffPlaceholders
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
function parseYamlObject(content) {
|
|
2053
|
+
let parsed;
|
|
2054
|
+
try {
|
|
2055
|
+
parsed = yaml.parse(content, { maxAliasCount: 100 });
|
|
2056
|
+
} catch {
|
|
2057
|
+
throw new AdapterError("INVALID_YAML", "The file is not valid YAML.");
|
|
2058
|
+
}
|
|
2059
|
+
return assertJsonRecord(parsed);
|
|
2060
|
+
}
|
|
2061
|
+
function createYamlAdapter() {
|
|
2062
|
+
return createTreeFileAdapter({
|
|
2063
|
+
format: "yaml",
|
|
2064
|
+
extensions: [".yml", ".yaml"],
|
|
2065
|
+
parse: parseYamlObject,
|
|
2066
|
+
serialize: (tree) => yaml.stringify(tree),
|
|
2067
|
+
extractPlaceholders: extractDoubleBracePlaceholders,
|
|
2068
|
+
deriveEntry: (_key, value) => ({
|
|
2069
|
+
placeholders: extractDoubleBracePlaceholders(value),
|
|
2070
|
+
isPlural: false
|
|
2071
|
+
})
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
1674
2074
|
function createDefaultRegistry() {
|
|
1675
|
-
return new AdapterRegistry().register(createI18nextJsonAdapter()).register(createVueI18nJsonAdapter()).register(createNextIntlJsonAdapter()).register(createNgxTranslateJsonAdapter());
|
|
2075
|
+
return new AdapterRegistry().register(createI18nextJsonAdapter()).register(createVueI18nJsonAdapter()).register(createNextIntlJsonAdapter()).register(createNgxTranslateJsonAdapter()).register(createXliffAdapter()).register(createYamlAdapter()).register(createArbAdapter());
|
|
1676
2076
|
}
|
|
1677
2077
|
|
|
1678
2078
|
// src/selection/select-adapter.ts
|
|
@@ -1687,6 +2087,89 @@ function selectAdapter(format, registry = createDefaultRegistry()) {
|
|
|
1687
2087
|
);
|
|
1688
2088
|
}
|
|
1689
2089
|
|
|
2090
|
+
// src/flow/source.ts
|
|
2091
|
+
async function readSource(config, cwd, fs, adapter) {
|
|
2092
|
+
const sourcePath = localeFilePath(cwd, config.files.pattern, config.sourceLocale);
|
|
2093
|
+
if (!await fs.fileExists(sourcePath)) {
|
|
2094
|
+
throw new SdkError(
|
|
2095
|
+
"SOURCE_UNREADABLE",
|
|
2096
|
+
`The source locale file was not found at ${sourcePath}.`
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
try {
|
|
2100
|
+
return await adapter.read(sourcePath, config.sourceLocale);
|
|
2101
|
+
} catch (error) {
|
|
2102
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2103
|
+
throw new SdkError(
|
|
2104
|
+
"SOURCE_INVALID",
|
|
2105
|
+
`The source locale file at ${sourcePath} could not be read: ${detail}`
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// src/flow/diff-locales.ts
|
|
2111
|
+
async function readTarget(cwd, config, adapter, fs, locale) {
|
|
2112
|
+
const path = localeFilePath(cwd, config.files.pattern, locale);
|
|
2113
|
+
if (!await fs.fileExists(path)) {
|
|
2114
|
+
return { locale, namespace: "", format: config.format, entries: /* @__PURE__ */ new Map() };
|
|
2115
|
+
}
|
|
2116
|
+
return (await adapter.read(path, locale)).resource;
|
|
2117
|
+
}
|
|
2118
|
+
function selectedLocales(config, requested) {
|
|
2119
|
+
if (requested === void 0) {
|
|
2120
|
+
return config.targetLocales;
|
|
2121
|
+
}
|
|
2122
|
+
const wanted = new Set(requested);
|
|
2123
|
+
return config.targetLocales.filter((locale) => wanted.has(locale));
|
|
2124
|
+
}
|
|
2125
|
+
async function diffLocales(input, deps = {}) {
|
|
2126
|
+
const config = input.config;
|
|
2127
|
+
const cwd = input.cwd ?? process.cwd();
|
|
2128
|
+
const fs = deps.fs ?? defaultFs;
|
|
2129
|
+
const adapter = selectAdapter(config.format, deps.adapterRegistry);
|
|
2130
|
+
const source = await readSource(config, cwd, fs, adapter);
|
|
2131
|
+
const lock = await readLockFile(lockFilePath(cwd), fs);
|
|
2132
|
+
return Promise.all(
|
|
2133
|
+
selectedLocales(config, input.locales).map(async (locale) => {
|
|
2134
|
+
const target = await readTarget(cwd, config, adapter, fs, locale);
|
|
2135
|
+
const diff2 = diffResources(source.resource, target, { baseline: baselineFor(lock, locale) });
|
|
2136
|
+
return { locale, diff: diff2 };
|
|
2137
|
+
})
|
|
2138
|
+
);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
// src/flow/check.ts
|
|
2142
|
+
function toCheckSummary(locale, diff2) {
|
|
2143
|
+
return {
|
|
2144
|
+
locale,
|
|
2145
|
+
missing: diff2.missing.length,
|
|
2146
|
+
stale: diff2.changed.length,
|
|
2147
|
+
upToDate: diff2.unchanged.length,
|
|
2148
|
+
inSync: diff2.missing.length === 0 && diff2.changed.length === 0
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
async function check(input, deps = {}) {
|
|
2152
|
+
const results = await diffLocales(input, deps);
|
|
2153
|
+
const locales = results.map(({ locale, diff: diff2 }) => toCheckSummary(locale, diff2));
|
|
2154
|
+
return { inSync: locales.every((entry) => entry.inSync), locales };
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// src/flow/diff.ts
|
|
2158
|
+
function toLocaleDiff(locale, diff2) {
|
|
2159
|
+
return {
|
|
2160
|
+
locale,
|
|
2161
|
+
missing: diff2.missing,
|
|
2162
|
+
changed: diff2.changed,
|
|
2163
|
+
orphaned: diff2.orphaned,
|
|
2164
|
+
hasPendingChanges: diff2.missing.length > 0 || diff2.changed.length > 0
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
async function diff(input, deps = {}) {
|
|
2168
|
+
const results = await diffLocales(input, deps);
|
|
2169
|
+
const locales = results.map(({ locale, diff: result }) => toLocaleDiff(locale, result));
|
|
2170
|
+
return { hasPendingChanges: locales.some((entry) => entry.hasPendingChanges), locales };
|
|
2171
|
+
}
|
|
2172
|
+
|
|
1690
2173
|
// src/selection/select-provider.ts
|
|
1691
2174
|
function selectProvider(config, createProvider = buildProvider) {
|
|
1692
2175
|
try {
|
|
@@ -1937,7 +2420,7 @@ async function generatePluralForms(context) {
|
|
|
1937
2420
|
function emptyResource(locale, format) {
|
|
1938
2421
|
return { locale, namespace: "", format, entries: /* @__PURE__ */ new Map() };
|
|
1939
2422
|
}
|
|
1940
|
-
async function
|
|
2423
|
+
async function readTarget2(params) {
|
|
1941
2424
|
const path = localeFilePath(params.cwd, params.filesPattern, params.targetLocale);
|
|
1942
2425
|
if (!await params.fs.fileExists(path)) {
|
|
1943
2426
|
return emptyResource(params.targetLocale, params.format);
|
|
@@ -1955,12 +2438,12 @@ function buildRequest3(params, entries) {
|
|
|
1955
2438
|
};
|
|
1956
2439
|
}
|
|
1957
2440
|
async function runLocale(params) {
|
|
1958
|
-
const target = await
|
|
1959
|
-
const
|
|
1960
|
-
const orphaned = params.generatePlurals ?
|
|
2441
|
+
const target = await readTarget2(params);
|
|
2442
|
+
const diff2 = diffResources(params.source, target, { baseline: params.baseline });
|
|
2443
|
+
const orphaned = params.generatePlurals ? diff2.orphaned.filter((key) => !isGeneratedPluralKey(key, sourcePluralBaseKeys(params.source))) : diff2.orphaned;
|
|
1961
2444
|
const pruned = params.prune ? orphaned : [];
|
|
1962
2445
|
const invalidIcu = new Set(params.sourceInvalidIcuKeys);
|
|
1963
|
-
const candidates = [...
|
|
2446
|
+
const candidates = [...diff2.missing, ...diff2.changed];
|
|
1964
2447
|
const toTranslate = candidates.filter((key) => !invalidIcu.has(key));
|
|
1965
2448
|
const invalidIcuSource = candidates.filter((key) => invalidIcu.has(key));
|
|
1966
2449
|
const pluralNotice = detectMissingPluralCategories(
|
|
@@ -1974,7 +2457,7 @@ async function runLocale(params) {
|
|
|
1974
2457
|
return {
|
|
1975
2458
|
summary: baseSummary({
|
|
1976
2459
|
locale: params.targetLocale,
|
|
1977
|
-
unchanged:
|
|
2460
|
+
unchanged: diff2.unchanged,
|
|
1978
2461
|
orphaned,
|
|
1979
2462
|
invalidIcuSource,
|
|
1980
2463
|
translated: toTranslate,
|
|
@@ -2023,12 +2506,12 @@ async function runLocale(params) {
|
|
|
2023
2506
|
return {
|
|
2024
2507
|
summary: baseSummary({
|
|
2025
2508
|
locale: params.targetLocale,
|
|
2026
|
-
unchanged:
|
|
2509
|
+
unchanged: diff2.unchanged,
|
|
2027
2510
|
orphaned,
|
|
2028
2511
|
invalidIcuSource,
|
|
2029
2512
|
translated: [...accepted.keys()],
|
|
2030
2513
|
generated: generation.accepted.map((form) => form.targetKey).sort(),
|
|
2031
|
-
// Withheld generated forms surface alongside withheld translations
|
|
2514
|
+
// Withheld generated forms surface alongside withheld translations: both failed integrity.
|
|
2032
2515
|
integrityMismatches: [...integrityMismatches, ...generation.withheld].sort(),
|
|
2033
2516
|
pruned,
|
|
2034
2517
|
notices
|
|
@@ -2152,26 +2635,6 @@ function carryGeneratedLock(lockEntries, baseline, key, sourceBaseKeys) {
|
|
|
2152
2635
|
}
|
|
2153
2636
|
}
|
|
2154
2637
|
|
|
2155
|
-
// src/flow/source.ts
|
|
2156
|
-
async function readSource(config, cwd, fs, adapter) {
|
|
2157
|
-
const sourcePath = localeFilePath(cwd, config.files.pattern, config.sourceLocale);
|
|
2158
|
-
if (!await fs.fileExists(sourcePath)) {
|
|
2159
|
-
throw new SdkError(
|
|
2160
|
-
"SOURCE_UNREADABLE",
|
|
2161
|
-
`The source locale file was not found at ${sourcePath}.`
|
|
2162
|
-
);
|
|
2163
|
-
}
|
|
2164
|
-
try {
|
|
2165
|
-
return await adapter.read(sourcePath, config.sourceLocale);
|
|
2166
|
-
} catch (error) {
|
|
2167
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
2168
|
-
throw new SdkError(
|
|
2169
|
-
"SOURCE_INVALID",
|
|
2170
|
-
`The source locale file at ${sourcePath} could not be read: ${detail}`
|
|
2171
|
-
);
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
2638
|
// src/flow/translate-project.ts
|
|
2176
2639
|
async function translate2(input, deps = {}) {
|
|
2177
2640
|
const config = input.config;
|
|
@@ -2380,7 +2843,7 @@ var DEFAULT_WORKBOOK_LIMITS = {
|
|
|
2380
2843
|
maxRowsPerSheet: 1e5,
|
|
2381
2844
|
maxCellsPerRow: 64
|
|
2382
2845
|
};
|
|
2383
|
-
function
|
|
2846
|
+
function assertNoDoctype2(name, xml) {
|
|
2384
2847
|
if (/<!DOCTYPE/i.test(xml) || /<!ENTITY/i.test(xml)) {
|
|
2385
2848
|
throw new ExchangeError(
|
|
2386
2849
|
"WORKBOOK_INVALID",
|
|
@@ -2435,7 +2898,7 @@ async function guardWorkbookBytes(bytes, limits) {
|
|
|
2435
2898
|
`The workbook decompresses to more than the maximum of ${limits.maxDecompressedBytes} bytes.`
|
|
2436
2899
|
);
|
|
2437
2900
|
}
|
|
2438
|
-
|
|
2901
|
+
assertNoDoctype2(file.name, content);
|
|
2439
2902
|
}
|
|
2440
2903
|
}
|
|
2441
2904
|
var rowSchema = zod.z.object({
|
|
@@ -2544,7 +3007,7 @@ async function readWorkbook(bytes, options = {}) {
|
|
|
2544
3007
|
|
|
2545
3008
|
// src/flow/workbook/export-workbook.ts
|
|
2546
3009
|
var DEFAULT_WORKBOOK_PATH = "verbatra-translations.xlsx";
|
|
2547
|
-
async function
|
|
3010
|
+
async function readTarget3(cwd, config, adapter, fs, locale) {
|
|
2548
3011
|
const path = localeFilePath(cwd, config.files.pattern, locale);
|
|
2549
3012
|
if (!await fs.fileExists(path)) {
|
|
2550
3013
|
return { locale, namespace: "", format: config.format, entries: /* @__PURE__ */ new Map() };
|
|
@@ -2552,7 +3015,7 @@ async function readTarget2(cwd, config, adapter, fs, locale) {
|
|
|
2552
3015
|
return (await adapter.read(path, locale)).resource;
|
|
2553
3016
|
}
|
|
2554
3017
|
function buildRows(source, target, baseline, includeUnchanged) {
|
|
2555
|
-
const
|
|
3018
|
+
const diff2 = diffResources(source, target, { baseline });
|
|
2556
3019
|
const rows = [];
|
|
2557
3020
|
const add = (keys, status) => {
|
|
2558
3021
|
for (const key of keys) {
|
|
@@ -2570,14 +3033,14 @@ function buildRows(source, target, baseline, includeUnchanged) {
|
|
|
2570
3033
|
});
|
|
2571
3034
|
}
|
|
2572
3035
|
};
|
|
2573
|
-
add(
|
|
2574
|
-
add(
|
|
3036
|
+
add(diff2.missing, "new");
|
|
3037
|
+
add(diff2.changed, "changed");
|
|
2575
3038
|
if (includeUnchanged) {
|
|
2576
|
-
add(
|
|
3039
|
+
add(diff2.unchanged, "changed");
|
|
2577
3040
|
}
|
|
2578
3041
|
return [...rows].sort((a, b) => a.key < b.key ? -1 : 1);
|
|
2579
3042
|
}
|
|
2580
|
-
function
|
|
3043
|
+
function selectedLocales2(config, requested) {
|
|
2581
3044
|
if (requested === void 0) {
|
|
2582
3045
|
return config.targetLocales;
|
|
2583
3046
|
}
|
|
@@ -2591,10 +3054,10 @@ async function exportWorkbook(input, deps = {}) {
|
|
|
2591
3054
|
const adapter = selectAdapter(config.format, deps.adapterRegistry);
|
|
2592
3055
|
const source = await readSource(config, cwd, fs, adapter);
|
|
2593
3056
|
const lock = await readLockFile(lockFilePath(cwd), fs);
|
|
2594
|
-
const locales =
|
|
3057
|
+
const locales = selectedLocales2(config, input.locales);
|
|
2595
3058
|
const sheets = await Promise.all(
|
|
2596
3059
|
locales.map(async (locale) => {
|
|
2597
|
-
const target = await
|
|
3060
|
+
const target = await readTarget3(cwd, config, adapter, fs, locale);
|
|
2598
3061
|
const rows = buildRows(
|
|
2599
3062
|
source.resource,
|
|
2600
3063
|
target,
|
|
@@ -2664,7 +3127,7 @@ function classifyRows(params, buckets) {
|
|
|
2664
3127
|
}
|
|
2665
3128
|
}
|
|
2666
3129
|
function importLocale(params) {
|
|
2667
|
-
const
|
|
3130
|
+
const diff2 = diffResources(params.source, params.target, { baseline: params.baseline });
|
|
2668
3131
|
const buckets = { accepted: /* @__PURE__ */ new Map(), mismatches: [], withheld: /* @__PURE__ */ new Set() };
|
|
2669
3132
|
classifyRows(params, buckets);
|
|
2670
3133
|
const rowKeys = new Set(params.sheet.rows.map((row) => row.key));
|
|
@@ -2673,8 +3136,8 @@ function importLocale(params) {
|
|
|
2673
3136
|
locale: params.sheet.locale,
|
|
2674
3137
|
status: "succeeded",
|
|
2675
3138
|
translated: [...buckets.accepted.keys()].sort(),
|
|
2676
|
-
unchanged:
|
|
2677
|
-
orphaned:
|
|
3139
|
+
unchanged: diff2.unchanged,
|
|
3140
|
+
orphaned: diff2.orphaned,
|
|
2678
3141
|
// Import never prunes: orphans are reported but never removed here (pruning is a translate-flow concern).
|
|
2679
3142
|
pruned: [],
|
|
2680
3143
|
invalidIcuSource,
|
|
@@ -2701,7 +3164,7 @@ async function readWorkbookBytes(path, fs) {
|
|
|
2701
3164
|
}
|
|
2702
3165
|
return read.bytes;
|
|
2703
3166
|
}
|
|
2704
|
-
async function
|
|
3167
|
+
async function readTarget4(cwd, config, adapter, fs, locale) {
|
|
2705
3168
|
const path = localeFilePath(cwd, config.files.pattern, locale);
|
|
2706
3169
|
if (!await fs.fileExists(path)) {
|
|
2707
3170
|
return { locale, namespace: "", format: config.format, entries: /* @__PURE__ */ new Map() };
|
|
@@ -2740,7 +3203,7 @@ async function runSheet(ctx, sheet, lock) {
|
|
|
2740
3203
|
`The workbook has a sheet for locale "${sheet.locale}", which is not a configured target locale.`
|
|
2741
3204
|
);
|
|
2742
3205
|
}
|
|
2743
|
-
const target = await
|
|
3206
|
+
const target = await readTarget4(ctx.cwd, ctx.config, ctx.adapter, ctx.fs, sheet.locale);
|
|
2744
3207
|
const baseline = baselineFor(lock, sheet.locale);
|
|
2745
3208
|
const { summary, accepted, withheld } = importLocale({
|
|
2746
3209
|
sheet,
|
|
@@ -2810,6 +3273,16 @@ async function importWorkbook(input, deps = {}) {
|
|
|
2810
3273
|
const { succeeded, failed } = partition(summaries);
|
|
2811
3274
|
return { dryRun, locales: summaries, succeeded, failed };
|
|
2812
3275
|
}
|
|
3276
|
+
|
|
3277
|
+
// src/scaffolding.ts
|
|
3278
|
+
var scaffoldingMetadata = {
|
|
3279
|
+
/** Provider id -> the environment variable its API key is read from. Owned by ai-providers. */
|
|
3280
|
+
providerEnv: PROVIDER_ENV,
|
|
3281
|
+
/** LLM provider id -> a cosmetic default scaffold model. Owned by ai-providers. DeepL has none. */
|
|
3282
|
+
scaffoldModels: SCAFFOLD_MODELS,
|
|
3283
|
+
/** The closed set of source format ids. Owned by core. */
|
|
3284
|
+
supportedFormats: SUPPORTED_FORMATS
|
|
3285
|
+
};
|
|
2813
3286
|
var defaultCreateWatcher = (paths) => {
|
|
2814
3287
|
const fsWatcher = chokidar.watch([...paths], { persistent: true, ignoreInitial: true });
|
|
2815
3288
|
return {
|
|
@@ -2918,10 +3391,13 @@ async function watch(input, deps = {}) {
|
|
|
2918
3391
|
|
|
2919
3392
|
exports.DEFAULT_WORKBOOK_PATH = DEFAULT_WORKBOOK_PATH;
|
|
2920
3393
|
exports.SdkError = SdkError;
|
|
3394
|
+
exports.check = check;
|
|
2921
3395
|
exports.defineConfig = defineConfig;
|
|
3396
|
+
exports.diff = diff;
|
|
2922
3397
|
exports.exportWorkbook = exportWorkbook;
|
|
2923
3398
|
exports.importWorkbook = importWorkbook;
|
|
2924
3399
|
exports.loadConfig = loadConfig;
|
|
3400
|
+
exports.scaffoldingMetadata = scaffoldingMetadata;
|
|
2925
3401
|
exports.translate = translate2;
|
|
2926
3402
|
exports.verbatraConfigSchema = verbatraConfigSchema;
|
|
2927
3403
|
exports.watch = watch;
|