@ls-stack/server-i18n 0.0.0-stage → 0.6.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/dist/cli.mjs ADDED
@@ -0,0 +1,2327 @@
1
+ import { n as __require, t as __commonJSMin } from "./chunk-CQZEQomT.mjs";
2
+ import { _ as string, m as object, o as array, p as number } from "./schemas-BfUoDuyI.mjs";
3
+ import fs from "node:fs";
4
+ import path, { join, relative, resolve, sep } from "node:path";
5
+ import { readFileSync, writeFileSync } from "fs";
6
+ import path$1 from "path";
7
+ import ts from "@typescript/typescript6";
8
+ import { lstat, readdir, realpath, stat } from "node:fs/promises";
9
+ import { Readable } from "node:stream";
10
+ import { consoleFmt } from "@ls-stack/utils/consoleFmt";
11
+ import { typeFlag } from "type-flag";
12
+
13
+ //#region ../core/src/cli/ai-logger.ts
14
+ const MAX_LOG_FILES = 10;
15
+ function getAiLogsFolder() {
16
+ return process.env["I18N_AI_LOGS_FOLDER"] || null;
17
+ }
18
+ function cleanupOldLogs(folder) {
19
+ const files = fs.readdirSync(folder).filter((f) => f.startsWith("ai-log-") && f.endsWith(".json")).sort();
20
+ while (files.length >= MAX_LOG_FILES) {
21
+ const oldest = files.shift();
22
+ if (oldest) fs.unlinkSync(path.join(folder, oldest));
23
+ }
24
+ }
25
+ function logAiGeneration(data) {
26
+ const folder = getAiLogsFolder();
27
+ if (!folder) return null;
28
+ fs.mkdirSync(folder, { recursive: true });
29
+ cleanupOldLogs(folder);
30
+ const filename = `ai-log-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`;
31
+ const filepath = path.join(folder, filename);
32
+ fs.writeFileSync(filepath, JSON.stringify(data, null, 2));
33
+ console.info(`AI log saved to: ${filepath}`);
34
+ return filepath;
35
+ }
36
+
37
+ //#endregion
38
+ //#region ../core/src/cli/ai-translator.ts
39
+ function buildTranslationsSchema() {
40
+ return object({
41
+ strings: array(object({
42
+ key: string(),
43
+ translation: string()
44
+ })),
45
+ plurals: array(object({
46
+ key: string(),
47
+ translation: object({
48
+ zero: string().nullable(),
49
+ one: string().nullable(),
50
+ plus2: string(),
51
+ many: string().nullable(),
52
+ manyLimit: number().nullable()
53
+ })
54
+ }))
55
+ });
56
+ }
57
+ function buildPrompt(contexts) {
58
+ const lines = [
59
+ "<role>",
60
+ "You are a professional translator specializing in i18n localization.",
61
+ "</role>",
62
+ "",
63
+ "<instructions>",
64
+ "- Translate the provided keys to the specified target locale",
65
+ "- Keep placeholders like {1}, {2}, # exactly as they appear",
66
+ "- For plural translations, provide an object with one and plus2 forms",
67
+ "- Use similar existing translations as style/terminology reference",
68
+ "- Respond with a JSON object containing two arrays: \"strings\" and \"plurals\"",
69
+ "- Each item in \"strings\" has \"key\" (the original source key) and \"translation\" (the translated string)",
70
+ "- Each item in \"plurals\" has \"key\" (the original source key) and \"translation\" (an object with plural forms)",
71
+ "</instructions>",
72
+ "",
73
+ "<plural-format>",
74
+ "Plural translations must be objects with these keys:",
75
+ " one: text when count is 1",
76
+ " plus2: text when count is 2 or more (use # as the number placeholder)",
77
+ "",
78
+ "English example: { \"one\": \"1 item\", \"plus2\": \"# items\" }",
79
+ "Portuguese example: { \"one\": \"1 item\", \"plus2\": \"# itens\" }",
80
+ "</plural-format>",
81
+ "",
82
+ "<keys>"
83
+ ];
84
+ for (const ctx of contexts) {
85
+ lines.push("");
86
+ lines.push(`Key: "${ctx.sourceKey}"`);
87
+ lines.push(`Locale: ${ctx.targetLocale}`);
88
+ lines.push(`Type: ${ctx.isPlural ? "plural" : "string"}`);
89
+ if (ctx.similarTranslations.length > 0) {
90
+ lines.push("Similar translations:");
91
+ for (const match of ctx.similarTranslations.slice(0, 3)) {
92
+ const translationValue = typeof match.translation === "string" ? match.translation : JSON.stringify(match.translation);
93
+ lines.push(` "${match.key}" → ${translationValue}`);
94
+ }
95
+ }
96
+ }
97
+ lines.push("</keys>");
98
+ return lines.join("\n");
99
+ }
100
+ function parseGeneratedObject(obj, contexts) {
101
+ const results = /* @__PURE__ */ new Map();
102
+ const validKeys = new Set(contexts.map((ctx) => ctx.sourceKey));
103
+ const pluralKeys = new Set(contexts.filter((ctx) => ctx.isPlural).map((ctx) => ctx.sourceKey));
104
+ for (const item of obj.strings) {
105
+ if (!validKeys.has(item.key) || pluralKeys.has(item.key)) continue;
106
+ results.set(item.key, {
107
+ type: "string",
108
+ value: item.translation
109
+ });
110
+ }
111
+ for (const item of obj.plurals) {
112
+ if (!validKeys.has(item.key) || !pluralKeys.has(item.key)) continue;
113
+ results.set(item.key, {
114
+ type: "plural",
115
+ value: {
116
+ zero: item.translation.zero ?? void 0,
117
+ one: item.translation.one ?? void 0,
118
+ "+2": item.translation.plus2,
119
+ many: item.translation.many ?? void 0,
120
+ manyLimit: item.translation.manyLimit ?? void 0
121
+ }
122
+ });
123
+ }
124
+ return results;
125
+ }
126
+ function createAITranslator(provider, model) {
127
+ return { async translateBatch(contexts) {
128
+ if (contexts.length === 0) return { translations: /* @__PURE__ */ new Map() };
129
+ const ai = await import("./dist-BCX2gdnz.mjs");
130
+ let languageModel;
131
+ if (model) languageModel = model;
132
+ else if (provider === "google") {
133
+ const { google } = await import("./dist-CrzKI7n8.mjs");
134
+ languageModel = google("gemini-2.5-flash");
135
+ } else {
136
+ const { openai } = await import("./dist-Dtrz_N2T.mjs");
137
+ languageModel = openai("gpt-5-mini");
138
+ }
139
+ const prompt = buildPrompt(contexts);
140
+ const schema = buildTranslationsSchema();
141
+ const startTime = Date.now();
142
+ try {
143
+ const result = await ai.generateText({
144
+ model: languageModel,
145
+ output: ai.Output.object({ schema }),
146
+ prompt
147
+ });
148
+ const output = result.output;
149
+ if (!output) {
150
+ logAiGeneration({
151
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
152
+ provider,
153
+ model: result.response.modelId,
154
+ prompt,
155
+ contexts,
156
+ results: [],
157
+ usage: void 0,
158
+ durationMs: Date.now() - startTime,
159
+ error: void 0
160
+ });
161
+ return { translations: /* @__PURE__ */ new Map() };
162
+ }
163
+ const { inputTokens, outputTokens, totalTokens } = result.usage;
164
+ const usage = inputTokens !== void 0 && outputTokens !== void 0 && totalTokens !== void 0 ? {
165
+ inputTokens,
166
+ outputTokens,
167
+ totalTokens
168
+ } : void 0;
169
+ const translations = parseGeneratedObject(output, contexts);
170
+ logAiGeneration({
171
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
172
+ provider,
173
+ model: result.response.modelId,
174
+ prompt,
175
+ contexts,
176
+ results: contexts.map((ctx) => ({
177
+ sourceKey: ctx.sourceKey,
178
+ result: translations.get(ctx.sourceKey)
179
+ })),
180
+ usage,
181
+ durationMs: Date.now() - startTime,
182
+ error: void 0
183
+ });
184
+ return {
185
+ translations,
186
+ model: result.response.modelId,
187
+ usage
188
+ };
189
+ } catch (err) {
190
+ logAiGeneration({
191
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
192
+ provider,
193
+ model: void 0,
194
+ prompt,
195
+ contexts,
196
+ results: [],
197
+ usage: void 0,
198
+ durationMs: Date.now() - startTime,
199
+ error: err instanceof Error ? err.message : String(err)
200
+ });
201
+ throw err;
202
+ }
203
+ } };
204
+ }
205
+
206
+ //#endregion
207
+ //#region ../node_modules/.pnpm/dotenv@17.2.3/node_modules/dotenv/package.json
208
+ var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
209
+ module.exports = {
210
+ "name": "dotenv",
211
+ "version": "17.2.3",
212
+ "description": "Loads environment variables from .env file",
213
+ "main": "lib/main.js",
214
+ "types": "lib/main.d.ts",
215
+ "exports": {
216
+ ".": {
217
+ "types": "./lib/main.d.ts",
218
+ "require": "./lib/main.js",
219
+ "default": "./lib/main.js"
220
+ },
221
+ "./config": "./config.js",
222
+ "./config.js": "./config.js",
223
+ "./lib/env-options": "./lib/env-options.js",
224
+ "./lib/env-options.js": "./lib/env-options.js",
225
+ "./lib/cli-options": "./lib/cli-options.js",
226
+ "./lib/cli-options.js": "./lib/cli-options.js",
227
+ "./package.json": "./package.json"
228
+ },
229
+ "scripts": {
230
+ "dts-check": "tsc --project tests/types/tsconfig.json",
231
+ "lint": "standard",
232
+ "pretest": "npm run lint && npm run dts-check",
233
+ "test": "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
234
+ "test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
235
+ "prerelease": "npm test",
236
+ "release": "standard-version"
237
+ },
238
+ "repository": {
239
+ "type": "git",
240
+ "url": "git://github.com/motdotla/dotenv.git"
241
+ },
242
+ "homepage": "https://github.com/motdotla/dotenv#readme",
243
+ "funding": "https://dotenvx.com",
244
+ "keywords": [
245
+ "dotenv",
246
+ "env",
247
+ ".env",
248
+ "environment",
249
+ "variables",
250
+ "config",
251
+ "settings"
252
+ ],
253
+ "readmeFilename": "README.md",
254
+ "license": "BSD-2-Clause",
255
+ "devDependencies": {
256
+ "@types/node": "^18.11.3",
257
+ "decache": "^4.6.2",
258
+ "sinon": "^14.0.1",
259
+ "standard": "^17.0.0",
260
+ "standard-version": "^9.5.0",
261
+ "tap": "^19.2.0",
262
+ "typescript": "^4.8.4"
263
+ },
264
+ "engines": { "node": ">=12" },
265
+ "browser": { "fs": false }
266
+ };
267
+ }));
268
+
269
+ //#endregion
270
+ //#region ../node_modules/.pnpm/dotenv@17.2.3/node_modules/dotenv/lib/main.js
271
+ var require_main = /* @__PURE__ */ __commonJSMin(((exports, module) => {
272
+ const fs$1 = __require("fs");
273
+ const path$2 = __require("path");
274
+ const os = __require("os");
275
+ const crypto = __require("crypto");
276
+ const version = require_package().version;
277
+ const TIPS = [
278
+ "🔐 encrypt with Dotenvx: https://dotenvx.com",
279
+ "🔐 prevent committing .env to code: https://dotenvx.com/precommit",
280
+ "🔐 prevent building .env in docker: https://dotenvx.com/prebuild",
281
+ "📡 add observability to secrets: https://dotenvx.com/ops",
282
+ "👥 sync secrets across teammates & machines: https://dotenvx.com/ops",
283
+ "🗂️ backup and recover secrets: https://dotenvx.com/ops",
284
+ "✅ audit secrets and track compliance: https://dotenvx.com/ops",
285
+ "🔄 add secrets lifecycle management: https://dotenvx.com/ops",
286
+ "🔑 add access controls to secrets: https://dotenvx.com/ops",
287
+ "🛠️ run anywhere with `dotenvx run -- yourcommand`",
288
+ "⚙️ specify custom .env file path with { path: '/custom/path/.env' }",
289
+ "⚙️ enable debug logging with { debug: true }",
290
+ "⚙️ override existing env vars with { override: true }",
291
+ "⚙️ suppress all logs with { quiet: true }",
292
+ "⚙️ write to custom object with { processEnv: myObject }",
293
+ "⚙️ load multiple .env files with { path: ['.env.local', '.env'] }"
294
+ ];
295
+ function _getRandomTip() {
296
+ return TIPS[Math.floor(Math.random() * TIPS.length)];
297
+ }
298
+ function parseBoolean(value) {
299
+ if (typeof value === "string") return ![
300
+ "false",
301
+ "0",
302
+ "no",
303
+ "off",
304
+ ""
305
+ ].includes(value.toLowerCase());
306
+ return Boolean(value);
307
+ }
308
+ function supportsAnsi() {
309
+ return process.stdout.isTTY;
310
+ }
311
+ function dim(text) {
312
+ return supportsAnsi() ? `\x1b[2m${text}\x1b[0m` : text;
313
+ }
314
+ const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
315
+ function parse(src) {
316
+ const obj = {};
317
+ let lines = src.toString();
318
+ lines = lines.replace(/\r\n?/gm, "\n");
319
+ let match;
320
+ while ((match = LINE.exec(lines)) != null) {
321
+ const key = match[1];
322
+ let value = match[2] || "";
323
+ value = value.trim();
324
+ const maybeQuote = value[0];
325
+ value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
326
+ if (maybeQuote === "\"") {
327
+ value = value.replace(/\\n/g, "\n");
328
+ value = value.replace(/\\r/g, "\r");
329
+ }
330
+ obj[key] = value;
331
+ }
332
+ return obj;
333
+ }
334
+ function _parseVault(options) {
335
+ options = options || {};
336
+ const vaultPath = _vaultPath(options);
337
+ options.path = vaultPath;
338
+ const result = DotenvModule.configDotenv(options);
339
+ if (!result.parsed) {
340
+ const err = /* @__PURE__ */ new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
341
+ err.code = "MISSING_DATA";
342
+ throw err;
343
+ }
344
+ const keys = _dotenvKey(options).split(",");
345
+ const length = keys.length;
346
+ let decrypted;
347
+ for (let i = 0; i < length; i++) try {
348
+ const attrs = _instructions(result, keys[i].trim());
349
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
350
+ break;
351
+ } catch (error) {
352
+ if (i + 1 >= length) throw error;
353
+ }
354
+ return DotenvModule.parse(decrypted);
355
+ }
356
+ function _warn(message) {
357
+ console.error(`[dotenv@${version}][WARN] ${message}`);
358
+ }
359
+ function _debug(message) {
360
+ console.log(`[dotenv@${version}][DEBUG] ${message}`);
361
+ }
362
+ function _log(message) {
363
+ console.log(`[dotenv@${version}] ${message}`);
364
+ }
365
+ function _dotenvKey(options) {
366
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) return options.DOTENV_KEY;
367
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) return process.env.DOTENV_KEY;
368
+ return "";
369
+ }
370
+ function _instructions(result, dotenvKey) {
371
+ let uri;
372
+ try {
373
+ uri = new URL(dotenvKey);
374
+ } catch (error) {
375
+ if (error.code === "ERR_INVALID_URL") {
376
+ const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
377
+ err.code = "INVALID_DOTENV_KEY";
378
+ throw err;
379
+ }
380
+ throw error;
381
+ }
382
+ const key = uri.password;
383
+ if (!key) {
384
+ const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing key part");
385
+ err.code = "INVALID_DOTENV_KEY";
386
+ throw err;
387
+ }
388
+ const environment = uri.searchParams.get("environment");
389
+ if (!environment) {
390
+ const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing environment part");
391
+ err.code = "INVALID_DOTENV_KEY";
392
+ throw err;
393
+ }
394
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
395
+ const ciphertext = result.parsed[environmentKey];
396
+ if (!ciphertext) {
397
+ const err = /* @__PURE__ */ new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
398
+ err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
399
+ throw err;
400
+ }
401
+ return {
402
+ ciphertext,
403
+ key
404
+ };
405
+ }
406
+ function _vaultPath(options) {
407
+ let possibleVaultPath = null;
408
+ if (options && options.path && options.path.length > 0) if (Array.isArray(options.path)) {
409
+ for (const filepath of options.path) if (fs$1.existsSync(filepath)) possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
410
+ } else possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
411
+ else possibleVaultPath = path$2.resolve(process.cwd(), ".env.vault");
412
+ if (fs$1.existsSync(possibleVaultPath)) return possibleVaultPath;
413
+ return null;
414
+ }
415
+ function _resolveHome(envPath) {
416
+ return envPath[0] === "~" ? path$2.join(os.homedir(), envPath.slice(1)) : envPath;
417
+ }
418
+ function _configVault(options) {
419
+ const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
420
+ const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
421
+ if (debug || !quiet) _log("Loading env from encrypted .env.vault");
422
+ const parsed = DotenvModule._parseVault(options);
423
+ let processEnv = process.env;
424
+ if (options && options.processEnv != null) processEnv = options.processEnv;
425
+ DotenvModule.populate(processEnv, parsed, options);
426
+ return { parsed };
427
+ }
428
+ function configDotenv(options) {
429
+ const dotenvPath = path$2.resolve(process.cwd(), ".env");
430
+ let encoding = "utf8";
431
+ let processEnv = process.env;
432
+ if (options && options.processEnv != null) processEnv = options.processEnv;
433
+ let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
434
+ let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
435
+ if (options && options.encoding) encoding = options.encoding;
436
+ else if (debug) _debug("No encoding is specified. UTF-8 is used by default");
437
+ let optionPaths = [dotenvPath];
438
+ if (options && options.path) if (!Array.isArray(options.path)) optionPaths = [_resolveHome(options.path)];
439
+ else {
440
+ optionPaths = [];
441
+ for (const filepath of options.path) optionPaths.push(_resolveHome(filepath));
442
+ }
443
+ let lastError;
444
+ const parsedAll = {};
445
+ for (const path of optionPaths) try {
446
+ const parsed = DotenvModule.parse(fs$1.readFileSync(path, { encoding }));
447
+ DotenvModule.populate(parsedAll, parsed, options);
448
+ } catch (e) {
449
+ if (debug) _debug(`Failed to load ${path} ${e.message}`);
450
+ lastError = e;
451
+ }
452
+ const populated = DotenvModule.populate(processEnv, parsedAll, options);
453
+ debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
454
+ quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
455
+ if (debug || !quiet) {
456
+ const keysCount = Object.keys(populated).length;
457
+ const shortPaths = [];
458
+ for (const filePath of optionPaths) try {
459
+ const relative = path$2.relative(process.cwd(), filePath);
460
+ shortPaths.push(relative);
461
+ } catch (e) {
462
+ if (debug) _debug(`Failed to load ${filePath} ${e.message}`);
463
+ lastError = e;
464
+ }
465
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
466
+ }
467
+ if (lastError) return {
468
+ parsed: parsedAll,
469
+ error: lastError
470
+ };
471
+ else return { parsed: parsedAll };
472
+ }
473
+ function config(options) {
474
+ if (_dotenvKey(options).length === 0) return DotenvModule.configDotenv(options);
475
+ const vaultPath = _vaultPath(options);
476
+ if (!vaultPath) {
477
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
478
+ return DotenvModule.configDotenv(options);
479
+ }
480
+ return DotenvModule._configVault(options);
481
+ }
482
+ function decrypt(encrypted, keyStr) {
483
+ const key = Buffer.from(keyStr.slice(-64), "hex");
484
+ let ciphertext = Buffer.from(encrypted, "base64");
485
+ const nonce = ciphertext.subarray(0, 12);
486
+ const authTag = ciphertext.subarray(-16);
487
+ ciphertext = ciphertext.subarray(12, -16);
488
+ try {
489
+ const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
490
+ aesgcm.setAuthTag(authTag);
491
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
492
+ } catch (error) {
493
+ const isRange = error instanceof RangeError;
494
+ const invalidKeyLength = error.message === "Invalid key length";
495
+ const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
496
+ if (isRange || invalidKeyLength) {
497
+ const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
498
+ err.code = "INVALID_DOTENV_KEY";
499
+ throw err;
500
+ } else if (decryptionFailed) {
501
+ const err = /* @__PURE__ */ new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
502
+ err.code = "DECRYPTION_FAILED";
503
+ throw err;
504
+ } else throw error;
505
+ }
506
+ }
507
+ function populate(processEnv, parsed, options = {}) {
508
+ const debug = Boolean(options && options.debug);
509
+ const override = Boolean(options && options.override);
510
+ const populated = {};
511
+ if (typeof parsed !== "object") {
512
+ const err = /* @__PURE__ */ new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
513
+ err.code = "OBJECT_REQUIRED";
514
+ throw err;
515
+ }
516
+ for (const key of Object.keys(parsed)) if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
517
+ if (override === true) {
518
+ processEnv[key] = parsed[key];
519
+ populated[key] = parsed[key];
520
+ }
521
+ if (debug) if (override === true) _debug(`"${key}" is already defined and WAS overwritten`);
522
+ else _debug(`"${key}" is already defined and was NOT overwritten`);
523
+ } else {
524
+ processEnv[key] = parsed[key];
525
+ populated[key] = parsed[key];
526
+ }
527
+ return populated;
528
+ }
529
+ const DotenvModule = {
530
+ configDotenv,
531
+ _configVault,
532
+ _parseVault,
533
+ config,
534
+ decrypt,
535
+ parse,
536
+ populate
537
+ };
538
+ module.exports.configDotenv = DotenvModule.configDotenv;
539
+ module.exports._configVault = DotenvModule._configVault;
540
+ module.exports._parseVault = DotenvModule._parseVault;
541
+ module.exports.config = DotenvModule.config;
542
+ module.exports.decrypt = DotenvModule.decrypt;
543
+ module.exports.parse = DotenvModule.parse;
544
+ module.exports.populate = DotenvModule.populate;
545
+ module.exports = DotenvModule;
546
+ }));
547
+
548
+ //#endregion
549
+ //#region ../core/src/cli/loadEnvFiles.ts
550
+ var import_main = require_main();
551
+ function loadEnvFiles() {
552
+ (0, import_main.config)({
553
+ path: [".env.local", ".env"],
554
+ quiet: true
555
+ });
556
+ }
557
+
558
+ //#endregion
559
+ //#region ../core/src/cli/findMissingTranslations.ts
560
+ function getHashFromTemplate(template) {
561
+ if (ts.isNoSubstitutionTemplateLiteral(template)) return template.text;
562
+ if (ts.isTemplateExpression(template)) {
563
+ let hash = "";
564
+ hash += template.head.text;
565
+ template.templateSpans.forEach((span, i) => {
566
+ hash += `{${i + 1}}${span.literal.text}`;
567
+ });
568
+ return hash;
569
+ }
570
+ return null;
571
+ }
572
+ function isPrimitiveInterpolation(node) {
573
+ if (ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return true;
574
+ if (ts.isIdentifier(node)) return true;
575
+ if (ts.isPropertyAccessExpression(node)) return true;
576
+ if (ts.isCallExpression(node)) return true;
577
+ if (ts.isConditionalExpression(node)) return isPrimitiveInterpolation(node.whenTrue) && isPrimitiveInterpolation(node.whenFalse);
578
+ if (ts.isBinaryExpression(node)) return isPrimitiveInterpolation(node.left) && isPrimitiveInterpolation(node.right);
579
+ if (ts.isParenthesizedExpression(node)) return isPrimitiveInterpolation(node.expression);
580
+ if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) return false;
581
+ if (ts.isJsxFragment(node)) return false;
582
+ return true;
583
+ }
584
+ function hasOnlyPrimitiveInterpolations(template) {
585
+ if (ts.isNoSubstitutionTemplateLiteral(template)) return true;
586
+ if (ts.isTemplateExpression(template)) {
587
+ for (const span of template.templateSpans) if (!isPrimitiveInterpolation(span.expression)) return false;
588
+ return true;
589
+ }
590
+ return true;
591
+ }
592
+ function getI18nUsagesInCode(fileName, code) {
593
+ if (!(code.includes("i18n") || code.includes("__`") || code.includes("__p(") || code.includes("__jsx`") || code.includes("__pjsx("))) return {
594
+ pluralTranslations: [],
595
+ stringTranslations: [],
596
+ jsxStringTranslations: /* @__PURE__ */ new Set(),
597
+ jsxPluralTranslations: /* @__PURE__ */ new Set(),
598
+ primitiveOnlyJsx: /* @__PURE__ */ new Set(),
599
+ usageMap: /* @__PURE__ */ new Map()
600
+ };
601
+ const sourceFile = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, void 0, ts.ScriptKind.TSX);
602
+ const pluralTranslations = /* @__PURE__ */ new Set();
603
+ const stringTranslations = /* @__PURE__ */ new Set();
604
+ const jsxStringTranslations = /* @__PURE__ */ new Set();
605
+ const jsxPluralTranslations = /* @__PURE__ */ new Set();
606
+ const primitiveOnlyJsx = /* @__PURE__ */ new Set();
607
+ const usageMap = /* @__PURE__ */ new Map();
608
+ function addUsage(hash, isJsx, isPlural, onlyPrimitives, node) {
609
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
610
+ const location = {
611
+ file: fileName,
612
+ line: line + 1,
613
+ column: character + 1
614
+ };
615
+ const existing = usageMap.get(hash);
616
+ if (existing) {
617
+ existing.locations.push(location);
618
+ if (!onlyPrimitives) existing.hasOnlyPrimitiveInterpolations = false;
619
+ } else usageMap.set(hash, {
620
+ hash,
621
+ isJsx,
622
+ isPlural,
623
+ hasOnlyPrimitiveInterpolations: onlyPrimitives,
624
+ locations: [location]
625
+ });
626
+ }
627
+ function checkNode(node) {
628
+ if (ts.isTaggedTemplateExpression(node)) {
629
+ const tteNode = node;
630
+ const isTranslationFn = (id) => {
631
+ return ts.idText(id) === "__";
632
+ };
633
+ const isJsxTranslationFn = (id) => {
634
+ return ts.idText(id) === "__jsx";
635
+ };
636
+ const isPluralTranslationFn = (id) => {
637
+ return ts.idText(id) === "__p";
638
+ };
639
+ const isJsxPluralTranslationFn = (id) => {
640
+ return ts.idText(id) === "__pjsx";
641
+ };
642
+ if (ts.isIdentifier(tteNode.tag) && isTranslationFn(tteNode.tag) || ts.isPropertyAccessExpression(tteNode.tag) && isTranslationFn(tteNode.tag.name)) {
643
+ const template = tteNode.template;
644
+ const hash = getHashFromTemplate(template);
645
+ if (hash) {
646
+ stringTranslations.add(hash);
647
+ addUsage(hash, false, false, true, node);
648
+ }
649
+ } else if (ts.isIdentifier(tteNode.tag) && isJsxTranslationFn(tteNode.tag) || ts.isPropertyAccessExpression(tteNode.tag) && isJsxTranslationFn(tteNode.tag.name)) {
650
+ const template = tteNode.template;
651
+ const hash = getHashFromTemplate(template);
652
+ if (hash) {
653
+ stringTranslations.add(hash);
654
+ jsxStringTranslations.add(hash);
655
+ const onlyPrimitives = hasOnlyPrimitiveInterpolations(template);
656
+ if (onlyPrimitives) primitiveOnlyJsx.add(hash);
657
+ addUsage(hash, true, false, onlyPrimitives, node);
658
+ }
659
+ } else if (ts.isCallExpression(tteNode.tag)) {
660
+ if (ts.isIdentifier(tteNode.tag.expression) && isPluralTranslationFn(tteNode.tag.expression) || ts.isPropertyAccessExpression(tteNode.tag.expression) && isPluralTranslationFn(tteNode.tag.expression.name)) {
661
+ const hash = getHashFromTemplate(tteNode.template);
662
+ if (hash) {
663
+ pluralTranslations.add(hash);
664
+ addUsage(hash, false, true, true, node);
665
+ }
666
+ } else if (ts.isIdentifier(tteNode.tag.expression) && isJsxPluralTranslationFn(tteNode.tag.expression) || ts.isPropertyAccessExpression(tteNode.tag.expression) && isJsxPluralTranslationFn(tteNode.tag.expression.name)) {
667
+ const hash = getHashFromTemplate(tteNode.template);
668
+ if (hash) {
669
+ pluralTranslations.add(hash);
670
+ jsxPluralTranslations.add(hash);
671
+ const onlyPrimitives = hasOnlyPrimitiveInterpolations(tteNode.template);
672
+ if (onlyPrimitives) primitiveOnlyJsx.add(hash);
673
+ addUsage(hash, true, true, onlyPrimitives, node);
674
+ }
675
+ }
676
+ }
677
+ }
678
+ ts.forEachChild(node, checkNode);
679
+ }
680
+ checkNode(sourceFile);
681
+ return {
682
+ pluralTranslations: Array.from(pluralTranslations),
683
+ stringTranslations: Array.from(stringTranslations),
684
+ jsxStringTranslations,
685
+ jsxPluralTranslations,
686
+ primitiveOnlyJsx,
687
+ usageMap
688
+ };
689
+ }
690
+
691
+ //#endregion
692
+ //#region ../core/src/cli/similarity.ts
693
+ const NUM_TOKEN = "__num__";
694
+ const NUM_TOKEN_PLACEHOLDER = "numtoken";
695
+ function normalizePlaceholders(text) {
696
+ let normalized = text.replace(/\{[^}]*\}/g, ` ${NUM_TOKEN} `);
697
+ normalized = normalized.replace(/#/g, ` ${NUM_TOKEN} `);
698
+ normalized = normalized.replace(/\d+/g, ` ${NUM_TOKEN} `);
699
+ return normalized;
700
+ }
701
+ function splitCamelCase(text) {
702
+ return text.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
703
+ }
704
+ function tokenizeWords(text) {
705
+ let normalized = normalizePlaceholders(text);
706
+ normalized = splitCamelCase(normalized);
707
+ normalized = normalized.toLowerCase();
708
+ normalized = normalized.replaceAll(NUM_TOKEN, ` ${NUM_TOKEN_PLACEHOLDER} `);
709
+ normalized = normalized.replace(/[^a-z0-9]+/g, " ");
710
+ const tokens = /* @__PURE__ */ new Set();
711
+ for (const rawToken of normalized.trim().split(/\s+/)) {
712
+ if (!rawToken) continue;
713
+ const token = rawToken === NUM_TOKEN_PLACEHOLDER ? NUM_TOKEN : rawToken;
714
+ if (token.length >= 2 || token === NUM_TOKEN) tokens.add(token);
715
+ }
716
+ return tokens;
717
+ }
718
+ function normalizeForGrams(text) {
719
+ let normalized = normalizePlaceholders(text);
720
+ normalized = splitCamelCase(normalized);
721
+ normalized = normalized.toLowerCase();
722
+ normalized = normalized.replaceAll(NUM_TOKEN, NUM_TOKEN_PLACEHOLDER);
723
+ normalized = normalized.replace(/[^a-z0-9]+/g, "");
724
+ return normalized;
725
+ }
726
+ function tokenizeGrams(text) {
727
+ const normalized = normalizeForGrams(text);
728
+ const tokens = /* @__PURE__ */ new Set();
729
+ if (normalized.length >= 3) for (let i = 0; i <= normalized.length - 3; i++) tokens.add(`ng:${normalized.slice(i, i + 3)}`);
730
+ else if (normalized.length === 2) tokens.add(`ng2:${normalized}`);
731
+ else if (normalized.length === 1) tokens.add(`ng1:${normalized}`);
732
+ return tokens;
733
+ }
734
+ function jaccardSimilarity(set1, set2) {
735
+ if (set1.size === 0 && set2.size === 0) return 0;
736
+ let intersection = 0;
737
+ for (const item of set1) if (set2.has(item)) intersection++;
738
+ const union = set1.size + set2.size - intersection;
739
+ return union === 0 ? 0 : intersection / union;
740
+ }
741
+ function commonPrefixScoreNormalized(a, b) {
742
+ let prefixLen = 0;
743
+ const minLen = Math.min(a.length, b.length);
744
+ for (let i = 0; i < minLen; i++) if (a[i] === b[i]) prefixLen++;
745
+ else break;
746
+ const maxLen = Math.max(a.length, b.length);
747
+ return maxLen === 0 ? 0 : prefixLen / maxLen;
748
+ }
749
+ function getTokenWeight(token, idf, maxIdf) {
750
+ return idf.get(token) ?? maxIdf;
751
+ }
752
+ function sumTokenWeights(tokens, idf, maxIdf) {
753
+ let sum = 0;
754
+ for (const token of tokens) sum += getTokenWeight(token, idf, maxIdf);
755
+ return sum;
756
+ }
757
+ function weightedJaccard(set1, set2, idf, maxIdf, set1WeightSum, set2WeightSum) {
758
+ if (set1.size === 0 && set2.size === 0) return 0;
759
+ const weightSum1 = set1WeightSum ?? sumTokenWeights(set1, idf, maxIdf);
760
+ const weightSum2 = set2WeightSum ?? sumTokenWeights(set2, idf, maxIdf);
761
+ let intersectionWeight = 0;
762
+ if (set1.size <= set2.size) {
763
+ for (const token of set1) if (set2.has(token)) intersectionWeight += getTokenWeight(token, idf, maxIdf);
764
+ } else for (const token of set2) if (set1.has(token)) intersectionWeight += getTokenWeight(token, idf, maxIdf);
765
+ const union = weightSum1 + weightSum2 - intersectionWeight;
766
+ return union === 0 ? 0 : intersectionWeight / union;
767
+ }
768
+ function toTranslationText(translation) {
769
+ if (typeof translation === "string") return translation;
770
+ const parts = [];
771
+ if (translation.zero) parts.push(translation.zero);
772
+ if (translation.one) parts.push(translation.one);
773
+ if (translation["+2"]) parts.push(translation["+2"]);
774
+ if (translation.many) parts.push(translation.many);
775
+ return parts.join(" ");
776
+ }
777
+ function createSimilarityIndex(existingTranslations) {
778
+ const entries = [];
779
+ const tokenToEntries = /* @__PURE__ */ new Map();
780
+ const documentFrequency = /* @__PURE__ */ new Map();
781
+ for (const [key, translation] of existingTranslations) {
782
+ const wordTokens = tokenizeWords(key);
783
+ const gramTokens = tokenizeGrams(key);
784
+ const normalizedKey = normalizeForGrams(key);
785
+ const translationTokens = tokenizeWords(toTranslationText(translation));
786
+ const entryIndex = entries.length;
787
+ for (const token of wordTokens) {
788
+ documentFrequency.set(token, (documentFrequency.get(token) ?? 0) + 1);
789
+ const existing = tokenToEntries.get(token);
790
+ if (existing) existing.push(entryIndex);
791
+ else tokenToEntries.set(token, [entryIndex]);
792
+ }
793
+ entries.push({
794
+ key,
795
+ translation,
796
+ wordTokens,
797
+ gramTokens,
798
+ normalizedKey,
799
+ wordWeightSum: 0,
800
+ translationTokens
801
+ });
802
+ }
803
+ const totalDocuments = entries.length;
804
+ const idf = /* @__PURE__ */ new Map();
805
+ for (const [token, count] of documentFrequency) idf.set(token, Math.log((totalDocuments + 1) / (count + 1)) + 1);
806
+ const maxIdf = Math.log((totalDocuments + 1) / 1) + 1;
807
+ for (const entry of entries) entry.wordWeightSum = sumTokenWeights(entry.wordTokens, idf, maxIdf);
808
+ return {
809
+ entries,
810
+ tokenToEntries,
811
+ idf,
812
+ maxIdf
813
+ };
814
+ }
815
+ function findSimilarFromIndex(index, sourceKey, maxResults = 5) {
816
+ if (index.entries.length === 0) return [];
817
+ const sourceTokens = tokenizeWords(sourceKey);
818
+ const sourceGrams = tokenizeGrams(sourceKey);
819
+ const sourceNormalized = normalizeForGrams(sourceKey);
820
+ const sourceWeightSum = sumTokenWeights(sourceTokens, index.idf, index.maxIdf);
821
+ let candidateIndexes = [];
822
+ if (sourceTokens.size > 0) {
823
+ const candidateSet = /* @__PURE__ */ new Set();
824
+ for (const token of sourceTokens) {
825
+ const matches = index.tokenToEntries.get(token);
826
+ if (!matches) continue;
827
+ for (const entryIndex of matches) candidateSet.add(entryIndex);
828
+ }
829
+ candidateIndexes = candidateSet.size === 0 ? index.entries.map((_entry, idx) => idx) : Array.from(candidateSet);
830
+ } else candidateIndexes = index.entries.map((_entry, idx) => idx);
831
+ const scored = [];
832
+ for (const entryIndex of candidateIndexes) {
833
+ const entry = index.entries[entryIndex];
834
+ if (!entry) continue;
835
+ const wordScore = weightedJaccard(sourceTokens, entry.wordTokens, index.idf, index.maxIdf, sourceWeightSum, entry.wordWeightSum);
836
+ const gramScore = jaccardSimilarity(sourceGrams, entry.gramTokens);
837
+ const prefixScore = commonPrefixScoreNormalized(sourceNormalized, entry.normalizedKey);
838
+ const keyScore = wordScore * .65 + gramScore * .25 + prefixScore * .1;
839
+ if (keyScore >= .12) scored.push({
840
+ index: entryIndex,
841
+ keyScore
842
+ });
843
+ }
844
+ if (scored.length === 0) return [];
845
+ scored.sort((a, b) => b.keyScore - a.keyScore);
846
+ const topCount = Math.min(20, scored.length);
847
+ const topEntry = scored[0];
848
+ if (!topEntry) return [];
849
+ const referenceTokens = index.entries[topEntry.index]?.translationTokens ?? /* @__PURE__ */ new Set();
850
+ const finalCandidates = [];
851
+ for (let i = 0; i < topCount; i++) {
852
+ const scoredEntry = scored[i];
853
+ if (!scoredEntry) continue;
854
+ const entry = index.entries[scoredEntry.index];
855
+ if (!entry) continue;
856
+ let translationScore = 0;
857
+ if (referenceTokens.size > 0 && entry.translationTokens.size > 0) translationScore = jaccardSimilarity(referenceTokens, entry.translationTokens);
858
+ const finalScore = scoredEntry.keyScore * .85 + translationScore * .15;
859
+ finalCandidates.push({
860
+ index: scoredEntry.index,
861
+ finalScore,
862
+ keyScore: scoredEntry.keyScore
863
+ });
864
+ }
865
+ for (let i = topCount; i < scored.length; i++) {
866
+ const scoredEntry = scored[i];
867
+ if (!scoredEntry) continue;
868
+ finalCandidates.push({
869
+ index: scoredEntry.index,
870
+ finalScore: scoredEntry.keyScore,
871
+ keyScore: scoredEntry.keyScore
872
+ });
873
+ }
874
+ finalCandidates.sort((a, b) => {
875
+ if (b.finalScore !== a.finalScore) return b.finalScore - a.finalScore;
876
+ if (b.keyScore !== a.keyScore) return b.keyScore - a.keyScore;
877
+ const aKey = index.entries[a.index]?.key ?? "";
878
+ const bKey = index.entries[b.index]?.key ?? "";
879
+ return aKey.localeCompare(bKey);
880
+ });
881
+ const results = [];
882
+ for (const candidate of finalCandidates.slice(0, maxResults)) {
883
+ const entry = index.entries[candidate.index];
884
+ if (!entry) continue;
885
+ results.push({
886
+ key: entry.key,
887
+ translation: entry.translation,
888
+ score: candidate.finalScore
889
+ });
890
+ }
891
+ return results;
892
+ }
893
+
894
+ //#endregion
895
+ //#region ../node_modules/.pnpm/readdirp@5.0.0/node_modules/readdirp/index.js
896
+ const EntryTypes = {
897
+ FILE_TYPE: "files",
898
+ DIR_TYPE: "directories",
899
+ FILE_DIR_TYPE: "files_directories",
900
+ EVERYTHING_TYPE: "all"
901
+ };
902
+ const defaultOptions = {
903
+ root: ".",
904
+ fileFilter: (_entryInfo) => true,
905
+ directoryFilter: (_entryInfo) => true,
906
+ type: EntryTypes.FILE_TYPE,
907
+ lstat: false,
908
+ depth: 2147483648,
909
+ alwaysStat: false,
910
+ highWaterMark: 4096
911
+ };
912
+ Object.freeze(defaultOptions);
913
+ const RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR";
914
+ const NORMAL_FLOW_ERRORS = new Set([
915
+ "ENOENT",
916
+ "EPERM",
917
+ "EACCES",
918
+ "ELOOP",
919
+ RECURSIVE_ERROR_CODE
920
+ ]);
921
+ const ALL_TYPES = [
922
+ EntryTypes.DIR_TYPE,
923
+ EntryTypes.EVERYTHING_TYPE,
924
+ EntryTypes.FILE_DIR_TYPE,
925
+ EntryTypes.FILE_TYPE
926
+ ];
927
+ const DIR_TYPES = new Set([
928
+ EntryTypes.DIR_TYPE,
929
+ EntryTypes.EVERYTHING_TYPE,
930
+ EntryTypes.FILE_DIR_TYPE
931
+ ]);
932
+ const FILE_TYPES = new Set([
933
+ EntryTypes.EVERYTHING_TYPE,
934
+ EntryTypes.FILE_DIR_TYPE,
935
+ EntryTypes.FILE_TYPE
936
+ ]);
937
+ const isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
938
+ const wantBigintFsStats = process.platform === "win32";
939
+ const emptyFn = (_entryInfo) => true;
940
+ const normalizeFilter = (filter) => {
941
+ if (filter === void 0) return emptyFn;
942
+ if (typeof filter === "function") return filter;
943
+ if (typeof filter === "string") {
944
+ const fl = filter.trim();
945
+ return (entry) => entry.basename === fl;
946
+ }
947
+ if (Array.isArray(filter)) {
948
+ const trItems = filter.map((item) => item.trim());
949
+ return (entry) => trItems.some((f) => entry.basename === f);
950
+ }
951
+ return emptyFn;
952
+ };
953
+ /** Readable readdir stream, emitting new files as they're being listed. */
954
+ var ReaddirpStream = class extends Readable {
955
+ parents;
956
+ reading;
957
+ parent;
958
+ _stat;
959
+ _maxDepth;
960
+ _wantsDir;
961
+ _wantsFile;
962
+ _wantsEverything;
963
+ _root;
964
+ _isDirent;
965
+ _statsProp;
966
+ _rdOptions;
967
+ _fileFilter;
968
+ _directoryFilter;
969
+ constructor(options = {}) {
970
+ super({
971
+ objectMode: true,
972
+ autoDestroy: true,
973
+ highWaterMark: options.highWaterMark
974
+ });
975
+ const opts = {
976
+ ...defaultOptions,
977
+ ...options
978
+ };
979
+ const { root, type } = opts;
980
+ this._fileFilter = normalizeFilter(opts.fileFilter);
981
+ this._directoryFilter = normalizeFilter(opts.directoryFilter);
982
+ const statMethod = opts.lstat ? lstat : stat;
983
+ if (wantBigintFsStats) this._stat = (path) => statMethod(path, { bigint: true });
984
+ else this._stat = statMethod;
985
+ this._maxDepth = opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
986
+ this._wantsDir = type ? DIR_TYPES.has(type) : false;
987
+ this._wantsFile = type ? FILE_TYPES.has(type) : false;
988
+ this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
989
+ this._root = resolve(root);
990
+ this._isDirent = !opts.alwaysStat;
991
+ this._statsProp = this._isDirent ? "dirent" : "stats";
992
+ this._rdOptions = {
993
+ encoding: "utf8",
994
+ withFileTypes: this._isDirent
995
+ };
996
+ this.parents = [this._exploreDir(root, 1)];
997
+ this.reading = false;
998
+ this.parent = void 0;
999
+ }
1000
+ async _read(batch) {
1001
+ if (this.reading) return;
1002
+ this.reading = true;
1003
+ try {
1004
+ while (!this.destroyed && batch > 0) {
1005
+ const par = this.parent;
1006
+ const fil = par && par.files;
1007
+ if (fil && fil.length > 0) {
1008
+ const { path, depth } = par;
1009
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));
1010
+ const awaited = await Promise.all(slice);
1011
+ for (const entry of awaited) {
1012
+ if (!entry) continue;
1013
+ if (this.destroyed) return;
1014
+ const entryType = await this._getEntryType(entry);
1015
+ if (entryType === "directory" && this._directoryFilter(entry)) {
1016
+ if (depth <= this._maxDepth) this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
1017
+ if (this._wantsDir) {
1018
+ this.push(entry);
1019
+ batch--;
1020
+ }
1021
+ } else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) {
1022
+ if (this._wantsFile) {
1023
+ this.push(entry);
1024
+ batch--;
1025
+ }
1026
+ }
1027
+ }
1028
+ } else {
1029
+ const parent = this.parents.pop();
1030
+ if (!parent) {
1031
+ this.push(null);
1032
+ break;
1033
+ }
1034
+ this.parent = await parent;
1035
+ if (this.destroyed) return;
1036
+ }
1037
+ }
1038
+ } catch (error) {
1039
+ this.destroy(error);
1040
+ } finally {
1041
+ this.reading = false;
1042
+ }
1043
+ }
1044
+ async _exploreDir(path, depth) {
1045
+ let files;
1046
+ try {
1047
+ files = await readdir(path, this._rdOptions);
1048
+ } catch (error) {
1049
+ this._onError(error);
1050
+ }
1051
+ return {
1052
+ files,
1053
+ depth,
1054
+ path
1055
+ };
1056
+ }
1057
+ async _formatEntry(dirent, path) {
1058
+ let entry;
1059
+ const basename = this._isDirent ? dirent.name : dirent;
1060
+ try {
1061
+ const fullPath = resolve(join(path, basename));
1062
+ entry = {
1063
+ path: relative(this._root, fullPath),
1064
+ fullPath,
1065
+ basename
1066
+ };
1067
+ entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
1068
+ } catch (err) {
1069
+ this._onError(err);
1070
+ return;
1071
+ }
1072
+ return entry;
1073
+ }
1074
+ _onError(err) {
1075
+ if (isNormalFlowError(err) && !this.destroyed) this.emit("warn", err);
1076
+ else this.destroy(err);
1077
+ }
1078
+ async _getEntryType(entry) {
1079
+ if (!entry && this._statsProp in entry) return "";
1080
+ const stats = entry[this._statsProp];
1081
+ if (stats.isFile()) return "file";
1082
+ if (stats.isDirectory()) return "directory";
1083
+ if (stats && stats.isSymbolicLink()) {
1084
+ const full = entry.fullPath;
1085
+ try {
1086
+ const entryRealPath = await realpath(full);
1087
+ const entryRealPathStats = await lstat(entryRealPath);
1088
+ if (entryRealPathStats.isFile()) return "file";
1089
+ if (entryRealPathStats.isDirectory()) {
1090
+ const len = entryRealPath.length;
1091
+ if (full.startsWith(entryRealPath) && full.substr(len, 1) === sep) {
1092
+ const recursiveError = /* @__PURE__ */ new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
1093
+ recursiveError.code = RECURSIVE_ERROR_CODE;
1094
+ return this._onError(recursiveError);
1095
+ }
1096
+ return "directory";
1097
+ }
1098
+ } catch (error) {
1099
+ this._onError(error);
1100
+ return "";
1101
+ }
1102
+ }
1103
+ }
1104
+ _includeAsFile(entry) {
1105
+ const stats = entry && entry[this._statsProp];
1106
+ return stats && this._wantsEverything && !stats.isDirectory();
1107
+ }
1108
+ };
1109
+ /**
1110
+ * Streaming version: Reads all files and directories in given root recursively.
1111
+ * Consumes ~constant small amount of RAM.
1112
+ * @param root Root directory
1113
+ * @param options Options to specify root (start directory), filters and recursion depth
1114
+ */
1115
+ function readdirp(root, options = {}) {
1116
+ let type = options.entryType || options.type;
1117
+ if (type === "both") type = EntryTypes.FILE_DIR_TYPE;
1118
+ if (type) options.type = type;
1119
+ if (!root) throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)");
1120
+ else if (typeof root !== "string") throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)");
1121
+ else if (type && !ALL_TYPES.includes(type)) throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`);
1122
+ options.root = root;
1123
+ return new ReaddirpStream(options);
1124
+ }
1125
+
1126
+ //#endregion
1127
+ //#region ../node_modules/.pnpm/runcheck@1.15.0/node_modules/runcheck/dist/chunk-JPCZKLXK.js
1128
+ function b(e) {
1129
+ if (S(e)) return e;
1130
+ if (w(e)) {
1131
+ let r = {};
1132
+ for (let [n, t] of Object.entries(e)) r[n] = b(t);
1133
+ return x(r);
1134
+ } else if (Array.isArray(e)) {
1135
+ let [r, n] = e;
1136
+ switch (r) {
1137
+ case "optional": return b(n).optional();
1138
+ case "nullish_or": return b(n).orNullish();
1139
+ case "null_or": return b(n).orNull();
1140
+ case "array_of": return P(b(n));
1141
+ case "loose_array_of": return E(b(n));
1142
+ case "optional_array_of": return P(b(n)).optional();
1143
+ case "optional_loose_array_of": return E(b(n)).optional();
1144
+ case "null_or_array_of": return P(b(n)).orNull();
1145
+ case "null_or_loose_array_of": return E(b(n)).orNull();
1146
+ case "nullish_or_array_of": return P(b(n)).orNullish();
1147
+ case "nullish_or_loose_array_of": return E(b(n)).orNullish();
1148
+ default: return r;
1149
+ }
1150
+ }
1151
+ throw new Error(`invalid schema: ${e}`);
1152
+ }
1153
+ function x(e, { normalizeKeysFrom: r, extends: n } = {}) {
1154
+ let t = {};
1155
+ for (let [o, a] of Object.entries(e)) t[o] = b(a);
1156
+ let s = Object.entries(t).map(([o, a]) => ({
1157
+ key: o,
1158
+ type: a
1159
+ }));
1160
+ return {
1161
+ ...T,
1162
+ t,
1163
+ e: n ? "extends_object" : "object",
1164
+ T: "",
1165
+ o: !0,
1166
+ _: !!n,
1167
+ r(o, a) {
1168
+ return f(this, o, a, () => {
1169
+ if (!w(o)) {
1170
+ if (a.a = -1, a.s && !this.T) return !1;
1171
+ if (!this.T) {
1172
+ let l = `${this.e}{ `, y = 0;
1173
+ for (let { key: _, type: k } of s) {
1174
+ if (l.length > 100) {
1175
+ l += ", ...";
1176
+ break;
1177
+ }
1178
+ y !== 0 && (l += ", "), l += `${_}: ${k.e}`, y++;
1179
+ }
1180
+ l += " }", this.T = l;
1181
+ }
1182
+ return {
1183
+ data: void 0,
1184
+ errors: [p(a, `Type '${j(o, !1)}' is not assignable to '${this.T}'`)]
1185
+ };
1186
+ }
1187
+ let i = this.e === "strict_obj" || a.i ? new Set(Object.keys(o)) : void 0;
1188
+ if (i && i.size > s.length) {
1189
+ a.a = -1;
1190
+ let l = [];
1191
+ if (a.s) return {
1192
+ data: void 0,
1193
+ errors: [p(a, `Expected strict object with ${s.length} keys but got ${i.size}`)]
1194
+ };
1195
+ for (let { key: y } of s) i.has(y) ? i.delete(y) : l.push(p(a, `Key '${y}' is missing`));
1196
+ for (let y of i) l.push(p(a, `Key '${y}' is not defined in the object shape`));
1197
+ return {
1198
+ data: void 0,
1199
+ errors: l
1200
+ };
1201
+ }
1202
+ let c = {}, h = [], g = a.n, m = -1;
1203
+ for (let l of s) {
1204
+ let y = l.key, _ = l.type, k = y;
1205
+ m += 1;
1206
+ let R = `${g}${y === "" || y.includes(" ") ? `['${y}']` : `.${y}`}`;
1207
+ a.n = R;
1208
+ let O = o[y], D = y;
1209
+ if (O === void 0 && _.u && (O = o[_.u], D = _.u), O === void 0 && r === "snake_case") {
1210
+ let N = V(y);
1211
+ O = o[N], D = N;
1212
+ }
1213
+ i?.delete(D);
1214
+ let B = _.r(O, a);
1215
+ if (B.ok) c[k] = B.data;
1216
+ else {
1217
+ for (let N of B.errors) a.n = R, h.push(N);
1218
+ if (a.s) {
1219
+ a.a = m;
1220
+ break;
1221
+ }
1222
+ }
1223
+ }
1224
+ if (i && i.size > 0) for (let l of i) a.n = g, h.push(p(a, `Key '${l}' is not defined in the object shape`));
1225
+ return h.length > 0 ? {
1226
+ errors: h,
1227
+ data: void 0
1228
+ } : (a.n = g, this._ ? {
1229
+ errors: !1,
1230
+ data: {
1231
+ ...o,
1232
+ ...c
1233
+ }
1234
+ } : {
1235
+ errors: !1,
1236
+ data: c
1237
+ });
1238
+ });
1239
+ }
1240
+ };
1241
+ }
1242
+ var ie = () => {};
1243
+ function ue(e) {
1244
+ return {
1245
+ ...this,
1246
+ p: e === void 0 ? ie : e
1247
+ };
1248
+ }
1249
+ function ce(e) {
1250
+ return be(this, e);
1251
+ }
1252
+ function le(e) {
1253
+ return ge(this, e);
1254
+ }
1255
+ function ye(e) {
1256
+ return _e(this, e);
1257
+ }
1258
+ function fe(e, r) {
1259
+ return $(e, this, r);
1260
+ }
1261
+ function Te(e, r) {
1262
+ return je(e, this, r);
1263
+ }
1264
+ function p(e, r) {
1265
+ return `${e.n ? `$${e.n}: ` : ""}${r}`;
1266
+ }
1267
+ function C(e, r) {
1268
+ e.b.push(r.startsWith("$") ? r : p(e, r));
1269
+ }
1270
+ function J(e, r) {
1271
+ r.forEach((n) => C(e, n));
1272
+ }
1273
+ function f(e, r, n, t) {
1274
+ if (e.c && r === void 0) return {
1275
+ ok: !0,
1276
+ data: r,
1277
+ errors: void 0
1278
+ };
1279
+ if (e.l && r == null) return {
1280
+ ok: !0,
1281
+ data: r,
1282
+ errors: void 0
1283
+ };
1284
+ if (e.f && r === null) return {
1285
+ ok: !0,
1286
+ data: r,
1287
+ errors: void 0
1288
+ };
1289
+ let s = t();
1290
+ if (s && (s === !0 || !s.errors)) return {
1291
+ ok: !0,
1292
+ data: s === !0 ? r : s.data,
1293
+ errors: void 0
1294
+ };
1295
+ if (!n.y) {
1296
+ let o = e.p;
1297
+ if (o !== void 0) return C(n, `Fallback used, errors -> ${L(s, n, e, r)}`), {
1298
+ ok: !0,
1299
+ data: U(o) ? o(r) : o,
1300
+ errors: void 0
1301
+ };
1302
+ if (e.g && e.d) {
1303
+ let a = e.d(r);
1304
+ if (a) return a.safeFix || C(n, `Autofixed from error -> ${L(s, n, e, r)}`), a.errors ? {
1305
+ ok: !1,
1306
+ data: void 0,
1307
+ errors: a.errors.map((u) => p(n, u))
1308
+ } : {
1309
+ ok: !0,
1310
+ data: a.fixed,
1311
+ errors: void 0
1312
+ };
1313
+ }
1314
+ }
1315
+ return {
1316
+ ok: !1,
1317
+ data: void 0,
1318
+ errors: s ? s.errors : [p(n, Q(e, r))]
1319
+ };
1320
+ }
1321
+ function L(e, r, n, t) {
1322
+ return e ? e.errors.map((s) => s.replace(r.n, "")).join("; ") : Q(n, t);
1323
+ }
1324
+ function de(e) {
1325
+ return {
1326
+ ...this,
1327
+ g: !0,
1328
+ d: e
1329
+ };
1330
+ }
1331
+ function pe(e) {
1332
+ return {
1333
+ ...this,
1334
+ r: (r, n) => f(this, r, n, () => {
1335
+ let t = this.r(r, n);
1336
+ if (!t.ok) return {
1337
+ errors: t.errors,
1338
+ data: void 0
1339
+ };
1340
+ let s = e(t.data);
1341
+ return s !== !0 ? {
1342
+ errors: [p(n, `Predicate failed${s === !1 ? ` for type '${this.e}'` : `: ${s.error}`}`)],
1343
+ data: void 0
1344
+ } : {
1345
+ errors: !1,
1346
+ data: t.data
1347
+ };
1348
+ })
1349
+ };
1350
+ }
1351
+ function M() {
1352
+ return {
1353
+ ...this,
1354
+ c: !0,
1355
+ e: `undefined | ${this.e}`
1356
+ };
1357
+ }
1358
+ function Q(e, r) {
1359
+ return `Type '${j(r, !!e.k)}' is not assignable to '${e.e}'`;
1360
+ }
1361
+ function Re() {
1362
+ return {
1363
+ ...this,
1364
+ f: !0,
1365
+ e: `null | ${this.e}`
1366
+ };
1367
+ }
1368
+ function he() {
1369
+ return {
1370
+ ...this,
1371
+ l: !0,
1372
+ e: `null | undefined | ${this.e}`
1373
+ };
1374
+ }
1375
+ var T = {
1376
+ __rc_type: void 0,
1377
+ withFallback: ue,
1378
+ where: pe,
1379
+ r: void 0,
1380
+ e: void 0,
1381
+ optional: M,
1382
+ optionalKey: M,
1383
+ orNullish: he,
1384
+ withAutofix: de,
1385
+ orNull: Re,
1386
+ default: ce,
1387
+ nullishDefault: le,
1388
+ or: ye,
1389
+ parse: fe,
1390
+ parseJson: Te,
1391
+ R: void 0,
1392
+ p: void 0,
1393
+ m: void 0,
1394
+ c: !1,
1395
+ f: !1,
1396
+ l: !1,
1397
+ g: !1,
1398
+ k: !1,
1399
+ u: void 0,
1400
+ d: void 0,
1401
+ t: void 0,
1402
+ T: void 0,
1403
+ o: !1,
1404
+ _: !1
1405
+ }, ze = {
1406
+ ...T,
1407
+ r(e, r) {
1408
+ return f(this, e, r, () => e === void 0);
1409
+ },
1410
+ e: "undefined"
1411
+ }, Je = {
1412
+ ...T,
1413
+ r(e, r) {
1414
+ return f(this, e, r, () => e === null);
1415
+ },
1416
+ e: "null"
1417
+ }, Qe = {
1418
+ ...T,
1419
+ r(e) {
1420
+ return {
1421
+ ok: !0,
1422
+ data: e,
1423
+ errors: void 0
1424
+ };
1425
+ },
1426
+ e: "any"
1427
+ }, Ze = {
1428
+ ...T,
1429
+ r(e) {
1430
+ return {
1431
+ ok: !0,
1432
+ data: e,
1433
+ errors: void 0
1434
+ };
1435
+ },
1436
+ e: "unknown"
1437
+ }, Ge = {
1438
+ ...T,
1439
+ r(e, r) {
1440
+ return f(this, e, r, () => typeof e == "boolean");
1441
+ },
1442
+ e: "boolean"
1443
+ }, He = {
1444
+ ...T,
1445
+ r(e, r) {
1446
+ return f(this, e, r, () => typeof e == "string");
1447
+ },
1448
+ e: "string"
1449
+ }, Xe = {
1450
+ ...T,
1451
+ r(e, r) {
1452
+ return f(this, e, r, () => typeof e == "number" && !Number.isNaN(e));
1453
+ },
1454
+ e: "number"
1455
+ }, Ye = {
1456
+ ...T,
1457
+ r(e, r) {
1458
+ return f(this, e, r, () => typeof e == "object" && e instanceof Date && !Number.isNaN(e.getTime()));
1459
+ },
1460
+ e: "date"
1461
+ };
1462
+ var z = 1;
1463
+ function _e(...e) {
1464
+ if (e.length === 0) throw new Error("Unions should have at least one type");
1465
+ let r = "", n = !1;
1466
+ for (let t of e) r && (r += " | "), r += t.e, !n && t.o && (n = !0);
1467
+ return {
1468
+ ...T,
1469
+ e: r,
1470
+ o: n,
1471
+ r(t, s) {
1472
+ return f(this, t, s, () => {
1473
+ let o = s.n, a = [], u = 0, i = !1, c = [], h = 0;
1474
+ for (let g of e) {
1475
+ h += 1, g.o && (s.n = `${o}|union ${h}|`);
1476
+ let m = s.s;
1477
+ s.s = !0, s.a = 0;
1478
+ let l = g.r(t, s), y = s.a;
1479
+ if (s.s = m, s.a = 0, l.ok) return {
1480
+ data: l.data,
1481
+ errors: !1
1482
+ };
1483
+ g.o && y !== -1 ? y > 0 ? c.push(...l.errors) : (u < z && a.push(...l.errors), u += 1) : i = !0;
1484
+ }
1485
+ return s.n = o, c.length > 0 || a.length > 0 ? ((u > z || i) && a.push(p(s, "not matches any other union member")), {
1486
+ errors: [...c, ...a],
1487
+ data: void 0
1488
+ }) : !1;
1489
+ });
1490
+ }
1491
+ };
1492
+ }
1493
+ function be(e, r) {
1494
+ return {
1495
+ ...e,
1496
+ c: !1,
1497
+ l: !1,
1498
+ f: !1,
1499
+ r(t, s) {
1500
+ return f(this, t, s, () => {
1501
+ if (t === void 0) return n();
1502
+ let o = e.r(t, s);
1503
+ return o.ok ? o.data === void 0 ? n() : {
1504
+ data: o.data,
1505
+ errors: !1
1506
+ } : {
1507
+ data: void 0,
1508
+ errors: o.errors
1509
+ };
1510
+ });
1511
+ },
1512
+ e: `${e.e}_default`
1513
+ };
1514
+ function n() {
1515
+ return {
1516
+ data: U(r) ? r() : r,
1517
+ errors: !1
1518
+ };
1519
+ }
1520
+ }
1521
+ function ge(e, r) {
1522
+ return {
1523
+ ...e,
1524
+ c: !1,
1525
+ l: !1,
1526
+ f: !1,
1527
+ r(t, s) {
1528
+ return f(this, t, s, () => {
1529
+ if (t == null) return n();
1530
+ let o = e.r(t, s);
1531
+ return o.ok ? o.data === null || o.data === void 0 ? n() : {
1532
+ data: o.data,
1533
+ errors: !1
1534
+ } : {
1535
+ data: void 0,
1536
+ errors: o.errors
1537
+ };
1538
+ });
1539
+ },
1540
+ e: `${e.e}_nullish_default`
1541
+ };
1542
+ function n() {
1543
+ return {
1544
+ data: U(r) ? r() : r,
1545
+ errors: !1
1546
+ };
1547
+ }
1548
+ }
1549
+ function ke(e, { checkKey: r, looseCheck: n } = {}) {
1550
+ return {
1551
+ ...T,
1552
+ e: `record<string, ${e.e}>`,
1553
+ o: !0,
1554
+ r(t, s) {
1555
+ return f(this, t, s, () => {
1556
+ if (!w(t)) return !1;
1557
+ let o = {}, a = [], u = s.n;
1558
+ for (let [i, c] of Object.entries(t)) {
1559
+ if (s.n = `${u}${i === "" || i.includes(" ") ? `['${i}']` : `.${i}`}`, r && !r(i)) {
1560
+ a.push(p(s, `Key '${i}' is not allowed`));
1561
+ continue;
1562
+ }
1563
+ let m = t[i], l = e.r(c, s);
1564
+ if (l.ok) o[i] = m;
1565
+ else {
1566
+ let y = l.errors;
1567
+ for (let _ of y) a.push(_);
1568
+ if (s.s) break;
1569
+ }
1570
+ }
1571
+ if (a.length > 0) if (n) J(s, a);
1572
+ else return {
1573
+ errors: a,
1574
+ data: void 0
1575
+ };
1576
+ return s.n = u, {
1577
+ errors: !1,
1578
+ data: o
1579
+ };
1580
+ });
1581
+ }
1582
+ };
1583
+ }
1584
+ function A(e, r, n, t = !1, s) {
1585
+ let o = t && !n.y && !n.h, a = s?.unique, u = [], i = [], c = a ? /* @__PURE__ */ new Set() : void 0, h = n.n, g = Array.isArray(r), m = -1;
1586
+ for (let l of e) {
1587
+ m++;
1588
+ let y = g ? r[m] : r, _ = `[${m}]`, k = `${h}${_}`;
1589
+ if (n.n = k, s?.filter) {
1590
+ let R = s.filter(l);
1591
+ if (typeof R == "boolean") {
1592
+ if (!R) continue;
1593
+ } else if ("errors" in R) if (o) {
1594
+ u.push([R.errors, k]);
1595
+ continue;
1596
+ } else return {
1597
+ errors: R.errors,
1598
+ data: void 0
1599
+ };
1600
+ n.n = k;
1601
+ }
1602
+ let d = y.r(l, n);
1603
+ if (n.n = k, d.ok && c) {
1604
+ let R = d.data, O = typeof a == "string";
1605
+ O ? R = d.data[a] : typeof a == "function" && (R = a(d.data)), c.has(R) ? (O && (n.n = `${h}${_}.${a}`), d = {
1606
+ ok: !1,
1607
+ data: void 0,
1608
+ errors: [p(n, O ? `Type '${y.t?.[a]?.e}' with value "${R}" is not unique` : typeof a == "function" ? `Type '${y.e}' unique fn return with value "${R}" is not unique` : `${y.e} value is not unique`)]
1609
+ }) : c.add(R);
1610
+ }
1611
+ if (d.ok) i.push(d.data);
1612
+ else if (o) {
1613
+ u.push([d.errors, k]);
1614
+ continue;
1615
+ } else return {
1616
+ errors: d.errors,
1617
+ data: void 0
1618
+ };
1619
+ }
1620
+ if (u.length > 0) {
1621
+ let l = [];
1622
+ for (let [y, _] of u) {
1623
+ for (let k of y) {
1624
+ let d = k.slice(_.length + 1);
1625
+ d.startsWith(": ") && (d = d.slice(2)), (d.startsWith(".") || d.startsWith("[")) && (d = `#${d}`);
1626
+ let R = `$${_}: Rejected, error -> ${d}`;
1627
+ l.push(R);
1628
+ }
1629
+ J(n, l);
1630
+ }
1631
+ }
1632
+ return {
1633
+ errors: !1,
1634
+ data: i
1635
+ };
1636
+ }
1637
+ function P(e, r) {
1638
+ return {
1639
+ ...T,
1640
+ e: `${e.e}[]`,
1641
+ R: e,
1642
+ r(n, t) {
1643
+ return f(this, n, t, () => Array.isArray(n) ? n.length === 0 ? !0 : A.call(this, n, e, t, !1, r) : !1);
1644
+ }
1645
+ };
1646
+ }
1647
+ function E(e, r) {
1648
+ return {
1649
+ ...T,
1650
+ R: e,
1651
+ e: `${e.e}[]`,
1652
+ r(n, t) {
1653
+ return f(this, n, t, () => Array.isArray(n) ? n.length === 0 ? !0 : A.call(this, n, e, t, !0, r) : !1);
1654
+ }
1655
+ };
1656
+ }
1657
+ function q(e) {
1658
+ e.warnings && e.warnings.length > 0 && console.warn(`Unwrap warnings: ${e.warnings.join(`
1659
+ `)}`);
1660
+ }
1661
+ function v() {
1662
+ if (this.errors) throw new I(this.errors);
1663
+ return q(this), this.value;
1664
+ }
1665
+ function K(e) {
1666
+ return this.errors ? e : (q(this), this.value);
1667
+ }
1668
+ function W() {
1669
+ return this.errors ? null : (q(this), this.value);
1670
+ }
1671
+ function $(e, r, { noWarnings: n = !1 } = {}) {
1672
+ let t = {
1673
+ b: [],
1674
+ n: "",
1675
+ s: !1,
1676
+ a: 0,
1677
+ y: n,
1678
+ i: !1,
1679
+ h: !1
1680
+ }, s = r.r(e, t);
1681
+ return s.ok ? {
1682
+ error: !1,
1683
+ errors: !1,
1684
+ ok: !0,
1685
+ data: s.data,
1686
+ value: s.data,
1687
+ warnings: t.b.length > 0 ? t.b : !1,
1688
+ unwrap: v,
1689
+ unwrapOr: K,
1690
+ unwrapOrNull: W
1691
+ } : {
1692
+ ok: !1,
1693
+ error: !0,
1694
+ errors: s.errors,
1695
+ unwrap: v,
1696
+ unwrapOr: K,
1697
+ unwrapOrNull: W
1698
+ };
1699
+ }
1700
+ var I = class extends Error {
1701
+ constructor(n) {
1702
+ super(n.join(`
1703
+ `));
1704
+ this.errors = n;
1705
+ }
1706
+ name = "RcValidationError";
1707
+ };
1708
+ function j(e, r) {
1709
+ let n = typeof e, t = (() => {
1710
+ if (n === "object") {
1711
+ if (Array.isArray(e)) return "array";
1712
+ if (!e) return "null";
1713
+ }
1714
+ return typeof e == "number" && Number.isNaN(e) ? "NaN" : n;
1715
+ })();
1716
+ return r && (t === "string" || t === "number" || t === "boolean") ? `${t}(${e})` : t;
1717
+ }
1718
+ function w(e) {
1719
+ return typeof e == "object" && e !== null && !Array.isArray(e);
1720
+ }
1721
+ function V(e) {
1722
+ return e.replace(/\W+/g, " ").split(/ |\B(?=[A-Z])/).map((r) => r.toLowerCase()).join("_");
1723
+ }
1724
+ function je(e, r, n) {
1725
+ try {
1726
+ if (typeof e != "string") return {
1727
+ ok: !1,
1728
+ error: !0,
1729
+ errors: [`expected a json string, got ${j(e, !0)}`],
1730
+ unwrap: v,
1731
+ unwrapOr: K,
1732
+ unwrapOrNull: W
1733
+ };
1734
+ return $(JSON.parse(e), r, n);
1735
+ } catch (t) {
1736
+ return {
1737
+ ok: !1,
1738
+ error: !0,
1739
+ errors: [`json parsing error: ${w(t) ? t.message : ""}`],
1740
+ unwrap: v,
1741
+ unwrapOr: K,
1742
+ unwrapOrNull: W
1743
+ };
1744
+ }
1745
+ }
1746
+ function U(e) {
1747
+ return typeof e == "function";
1748
+ }
1749
+ function S(e) {
1750
+ return w(e) && "__rc_type" in e;
1751
+ }
1752
+
1753
+ //#endregion
1754
+ //#region ../core/src/cli/validation.ts
1755
+ const translationFileSchema = ke(_e(He, Je, x({
1756
+ manyLimit: Xe.optional(),
1757
+ zero: He.optional(),
1758
+ one: He.optional(),
1759
+ "+2": _e(He, Je),
1760
+ many: He.optional()
1761
+ })));
1762
+ function isObject(value) {
1763
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1764
+ }
1765
+ const MISSING_START_MARKER = "👇 missing start 👇";
1766
+ const MISSING_END_MARKER = "👆 missing end 👆";
1767
+ const MISSING_MARKER_VALUE = "🛑 delete this line 🛑";
1768
+ function isMarkerKey(key) {
1769
+ return key === MISSING_START_MARKER || key === MISSING_END_MARKER;
1770
+ }
1771
+ function findMarkerLine(fileContent) {
1772
+ const lines = fileContent.split("\n");
1773
+ for (let i = 0; i < lines.length; i++) if (lines[i].includes(MISSING_START_MARKER)) return i + 1;
1774
+ return null;
1775
+ }
1776
+ function calculateInsertPosition(totalKeys) {
1777
+ if (totalKeys === 0) return 0;
1778
+ return Math.floor(Math.random() * totalKeys);
1779
+ }
1780
+ function buildOrderedTranslations(existing, missing, insertPosition, addMarkers = true, aiTranslated) {
1781
+ const result = {};
1782
+ const existingKeys = Object.keys(existing).filter((k) => k !== "" && !isMarkerKey(k));
1783
+ const allNewKeys = /* @__PURE__ */ new Map();
1784
+ if (aiTranslated) for (const [key, value] of aiTranslated) allNewKeys.set(key, value);
1785
+ for (const [key, value] of missing) allNewKeys.set(key, value);
1786
+ let inserted = false;
1787
+ for (let i = 0; i < existingKeys.length; i++) {
1788
+ if (i === insertPosition && !inserted && allNewKeys.size > 0) {
1789
+ if (aiTranslated) for (const [key, value] of aiTranslated) result[key] = value;
1790
+ if (missing.size > 0) {
1791
+ if (addMarkers) result[MISSING_START_MARKER] = MISSING_MARKER_VALUE;
1792
+ for (const [key, value] of missing) result[key] = value;
1793
+ if (addMarkers) result[MISSING_END_MARKER] = MISSING_MARKER_VALUE;
1794
+ }
1795
+ inserted = true;
1796
+ }
1797
+ const key = existingKeys[i];
1798
+ if (key !== void 0) result[key] = existing[key];
1799
+ }
1800
+ if (!inserted && allNewKeys.size > 0) {
1801
+ if (aiTranslated) for (const [key, value] of aiTranslated) result[key] = value;
1802
+ if (missing.size > 0) {
1803
+ if (addMarkers) result[MISSING_START_MARKER] = MISSING_MARKER_VALUE;
1804
+ for (const [key, value] of missing) result[key] = value;
1805
+ if (addMarkers) result[MISSING_END_MARKER] = MISSING_MARKER_VALUE;
1806
+ }
1807
+ }
1808
+ result[""] = "";
1809
+ return result;
1810
+ }
1811
+ const defaultFs = {
1812
+ readFileSync: (filePath, encoding) => readFileSync(filePath, encoding),
1813
+ writeFileSync: (filePath, content) => writeFileSync(filePath, content),
1814
+ async *scanDir(dirPath, options) {
1815
+ for await (const entry_ of readdirp(dirPath, {
1816
+ fileFilter: options.fileFilter,
1817
+ directoryFilter: (entry) => !entry.path.includes("node_modules") && !entry.path.includes(".git")
1818
+ })) {
1819
+ const entry = entry_;
1820
+ yield {
1821
+ fullPath: entry.fullPath,
1822
+ basename: entry.basename
1823
+ };
1824
+ }
1825
+ }
1826
+ };
1827
+ function hasInterpolationPlaceholder(hash) {
1828
+ return /\{[0-9]+\}/.test(hash);
1829
+ }
1830
+ function getInterpolationPrefix(value) {
1831
+ const match = value.match(/^(.*?)\{[0-9]+\}/);
1832
+ return match ? match[1] ?? "" : "";
1833
+ }
1834
+ function getInterpolationSuffix(value) {
1835
+ const match = value.match(/\{[0-9]+\}([^{]*)$/);
1836
+ return match ? match[1] ?? "" : "";
1837
+ }
1838
+ function isIgnoredAffix(value) {
1839
+ return /^[\s?!.()""'']*$/.test(value);
1840
+ }
1841
+ function isPluralOnlyPlus2(value) {
1842
+ return value.zero === void 0 && value.one === void 0 && value.many === void 0;
1843
+ }
1844
+ function getStringValue(value) {
1845
+ if (value === void 0 || value === null) return null;
1846
+ if (typeof value === "string") return value;
1847
+ return value["+2"];
1848
+ }
1849
+ function formatIssue(issue, severity, srcPath) {
1850
+ const icon = severity === "error" ? "❌" : "⚠️";
1851
+ const location = issue.locations[0];
1852
+ if (location) return `${icon} ${path$1.relative(path$1.dirname(srcPath), path$1.join(srcPath, location.file))}:${location.line}:${location.column} ${issue.message}`;
1853
+ return `${icon} ${issue.message}`;
1854
+ }
1855
+ async function validateTranslations(options) {
1856
+ const { configDir, srcDir, defaultLocale, fix = false, noColor = false, colorFn = (_, text) => text, fs = defaultFs, log = console, aiTranslator, rules = {}, maxTranslationIdSize = 80 } = options;
1857
+ const getRuleSeverity = (rule) => {
1858
+ return rules[rule] ?? "error";
1859
+ };
1860
+ const allStringTranslationHashs = /* @__PURE__ */ new Set();
1861
+ const allPluralTranslationHashs = /* @__PURE__ */ new Set();
1862
+ let hasError = false;
1863
+ const srcPath = path$1.isAbsolute(srcDir) ? srcDir : path$1.join(process.cwd(), srcDir);
1864
+ const globalUsageMap = /* @__PURE__ */ new Map();
1865
+ const globalJsxStringTranslations = /* @__PURE__ */ new Set();
1866
+ const globalJsxPluralTranslations = /* @__PURE__ */ new Set();
1867
+ const globalPrimitiveOnlyJsx = /* @__PURE__ */ new Set();
1868
+ for await (const entry of fs.scanDir(srcPath, { fileFilter: (entry) => entry.path.endsWith(".ts") || entry.path.endsWith(".tsx") })) {
1869
+ const { fullPath } = entry;
1870
+ const fileTextContent = fs.readFileSync(fullPath, "utf-8");
1871
+ const i18nUsages = getI18nUsagesInCode(path$1.relative(srcPath, fullPath), fileTextContent);
1872
+ for (const hash of i18nUsages.stringTranslations) allStringTranslationHashs.add(hash);
1873
+ for (const hash of i18nUsages.pluralTranslations) allPluralTranslationHashs.add(hash);
1874
+ for (const hash of i18nUsages.jsxStringTranslations) globalJsxStringTranslations.add(hash);
1875
+ for (const hash of i18nUsages.jsxPluralTranslations) globalJsxPluralTranslations.add(hash);
1876
+ for (const hash of i18nUsages.primitiveOnlyJsx) globalPrimitiveOnlyJsx.add(hash);
1877
+ for (const [hash, usage] of i18nUsages.usageMap) {
1878
+ const existing = globalUsageMap.get(hash);
1879
+ if (existing) {
1880
+ existing.locations.push(...usage.locations);
1881
+ if (!usage.hasOnlyPrimitiveInterpolations) existing.hasOnlyPrimitiveInterpolations = false;
1882
+ } else globalUsageMap.set(hash, { ...usage });
1883
+ }
1884
+ }
1885
+ if (allStringTranslationHashs.size === 0 && allPluralTranslationHashs.size === 0) {
1886
+ log.error("❌ No translations found in dir: ", srcDir);
1887
+ return { hasError: true };
1888
+ }
1889
+ const configPath = path$1.isAbsolute(configDir) ? configDir : path$1.join(process.cwd(), configDir);
1890
+ const allLocaleTranslations = /* @__PURE__ */ new Map();
1891
+ const localeFiles = [];
1892
+ for await (const entry of fs.scanDir(configPath, { fileFilter: (entry) => entry.path.endsWith(".json") })) localeFiles.push(entry);
1893
+ for (const { fullPath, basename } of localeFiles) {
1894
+ const fileParseResult = $(JSON.parse(fs.readFileSync(fullPath, "utf-8")), translationFileSchema);
1895
+ if (!fileParseResult.ok) {
1896
+ log.error(`❌ ${basename} has invalid format:`, fileParseResult.errors);
1897
+ hasError = true;
1898
+ continue;
1899
+ }
1900
+ const localeId = basename.replace(".json", "");
1901
+ allLocaleTranslations.set(localeId, fileParseResult.value);
1902
+ }
1903
+ const validationIssues = [];
1904
+ const allHashes = new Set([...allStringTranslationHashs, ...allPluralTranslationHashs]);
1905
+ for (const hash of allHashes) {
1906
+ if (hash.startsWith("$") || hash.includes("~~")) continue;
1907
+ const locations = globalUsageMap.get(hash)?.locations ?? [];
1908
+ if (getRuleSeverity("max-translation-id-size") !== "off" && hash.length > maxTranslationIdSize) {
1909
+ const truncated = hash.length > 40 ? `${hash.slice(0, 37)}...` : hash;
1910
+ validationIssues.push({
1911
+ rule: "max-translation-id-size",
1912
+ hash,
1913
+ message: `translation ID exceeds ${maxTranslationIdSize} chars (${hash.length}): "${truncated}". Use a $shortId instead, e.g.: __\`$myKey\``,
1914
+ locations
1915
+ });
1916
+ }
1917
+ if (getRuleSeverity("constant-translation") !== "off") {
1918
+ const values = [];
1919
+ for (const [localeId, translations] of allLocaleTranslations) {
1920
+ const value = translations[hash];
1921
+ const strValue = getStringValue(value);
1922
+ const isDefaultLocaleEntry = defaultLocale !== void 0 && localeId === defaultLocale;
1923
+ if (strValue !== null) values.push(strValue);
1924
+ else if (isDefaultLocaleEntry) values.push(hash);
1925
+ }
1926
+ if (values.length >= 2 && new Set(values).size === 1) validationIssues.push({
1927
+ rule: "constant-translation",
1928
+ hash,
1929
+ message: `constant translation "${hash}" has the same value in all locales. Either translate it differently per locale, or use plain strings if it doesn't need translation`,
1930
+ locations
1931
+ });
1932
+ }
1933
+ if (getRuleSeverity("unnecessary-plural") !== "off" && allPluralTranslationHashs.has(hash)) {
1934
+ let allOnlyPlus2 = true;
1935
+ let hasAnyPluralTranslation = false;
1936
+ for (const [, translations] of allLocaleTranslations) {
1937
+ const value = translations[hash];
1938
+ if (value !== null && value !== void 0 && isObject(value)) {
1939
+ hasAnyPluralTranslation = true;
1940
+ if (!isPluralOnlyPlus2(value)) {
1941
+ allOnlyPlus2 = false;
1942
+ break;
1943
+ }
1944
+ }
1945
+ }
1946
+ if (hasAnyPluralTranslation && allOnlyPlus2) validationIssues.push({
1947
+ rule: "unnecessary-plural",
1948
+ hash,
1949
+ message: `unnecessary plural "${hash}" only uses the +2 form in all locales. Use __\`# items\` with interpolation instead of __p(count)\`# items\`, or add zero/one/many forms if needed`,
1950
+ locations
1951
+ });
1952
+ }
1953
+ if (getRuleSeverity("jsx-without-interpolation") !== "off") {
1954
+ if ((globalJsxStringTranslations.has(hash) || globalJsxPluralTranslations.has(hash)) && !hasInterpolationPlaceholder(hash)) validationIssues.push({
1955
+ rule: "jsx-without-interpolation",
1956
+ hash,
1957
+ message: `__jsx used without interpolations for "${hash}". Use __\`${hash}\` instead since there are no JSX elements to interpolate`,
1958
+ locations
1959
+ });
1960
+ }
1961
+ if (getRuleSeverity("jsx-without-jsx-nodes") !== "off") {
1962
+ const isJsx = globalJsxStringTranslations.has(hash) || globalJsxPluralTranslations.has(hash);
1963
+ const isPrimitiveOnly = globalPrimitiveOnlyJsx.has(hash);
1964
+ if (isJsx && isPrimitiveOnly && hasInterpolationPlaceholder(hash)) validationIssues.push({
1965
+ rule: "jsx-without-jsx-nodes",
1966
+ hash,
1967
+ message: `__jsx used but all interpolations are primitives (strings/numbers) for "${hash}". Use __\`...\` instead of __jsx\`...\` when not interpolating JSX elements`,
1968
+ locations
1969
+ });
1970
+ }
1971
+ if (getRuleSeverity("unnecessary-interpolated-affix") !== "off" && hasInterpolationPlaceholder(hash)) {
1972
+ const prefixes = [];
1973
+ const suffixes = [];
1974
+ for (const [localeId, translations] of allLocaleTranslations) {
1975
+ const value = translations[hash];
1976
+ const strValue = getStringValue(value);
1977
+ const effectiveValue = strValue !== null ? strValue : defaultLocale !== void 0 && localeId === defaultLocale ? hash : null;
1978
+ if (effectiveValue !== null) {
1979
+ prefixes.push(getInterpolationPrefix(effectiveValue));
1980
+ suffixes.push(getInterpolationSuffix(effectiveValue));
1981
+ }
1982
+ }
1983
+ if (prefixes.length >= 2) {
1984
+ const uniquePrefixes = new Set(prefixes);
1985
+ const prefix = prefixes[0] ?? "";
1986
+ if (uniquePrefixes.size === 1 && prefix !== "" && !isIgnoredAffix(prefix)) validationIssues.push({
1987
+ rule: "unnecessary-interpolated-affix",
1988
+ hash,
1989
+ message: `prefix "${prefix}" before interpolation in "${hash}" is identical in all locales. Move it outside the translation: "${prefix}" + __\`...\``,
1990
+ locations
1991
+ });
1992
+ }
1993
+ if (suffixes.length >= 2) {
1994
+ const uniqueSuffixes = new Set(suffixes);
1995
+ const suffix = suffixes[0] ?? "";
1996
+ if (uniqueSuffixes.size === 1 && suffix !== "" && !isIgnoredAffix(suffix)) validationIssues.push({
1997
+ rule: "unnecessary-interpolated-affix",
1998
+ hash,
1999
+ message: `suffix "${suffix}" after interpolation in "${hash}" is identical in all locales. Move it outside the translation: __\`...\` + "${suffix}"`,
2000
+ locations
2001
+ });
2002
+ }
2003
+ }
2004
+ }
2005
+ for (const issue of validationIssues) {
2006
+ const severity = getRuleSeverity(issue.rule);
2007
+ if (severity === "off") continue;
2008
+ const message = formatIssue(issue, severity, srcPath);
2009
+ if (severity === "error") {
2010
+ hasError = true;
2011
+ log.error(message);
2012
+ } else log.info(message);
2013
+ }
2014
+ const aiTranslationTasks = [];
2015
+ for (const { fullPath, basename } of localeFiles) {
2016
+ const invalidPluralTranslations = [];
2017
+ const invalidSpecialTranslations = [];
2018
+ const incompletePluralTranslations = [];
2019
+ const localeId = basename.replace(".json", "");
2020
+ const localeTranslations = allLocaleTranslations.get(localeId);
2021
+ if (!localeTranslations) continue;
2022
+ const isDefaultLocale = basename === `${defaultLocale}.json`;
2023
+ const localeFileHashs = Object.keys(localeTranslations);
2024
+ const extraHashs = new Set(localeFileHashs);
2025
+ const missingHashs = new Set([...isDefaultLocale ? [] : [...allStringTranslationHashs], ...allPluralTranslationHashs]);
2026
+ extraHashs.delete("");
2027
+ for (const hash of localeFileHashs) if (allStringTranslationHashs.has(hash)) {
2028
+ const translationValue = localeTranslations[hash];
2029
+ const isNullTranslation = translationValue === null;
2030
+ const isUnneededDefaultHash = isDefaultLocale && (isNullTranslation || translationValue === hash);
2031
+ const isIncompleteNonDefaultTranslation = defaultLocale !== void 0 && !isDefaultLocale && isNullTranslation;
2032
+ if (isUnneededDefaultHash) missingHashs.delete(hash);
2033
+ else if (!isIncompleteNonDefaultTranslation) {
2034
+ missingHashs.delete(hash);
2035
+ extraHashs.delete(hash);
2036
+ }
2037
+ if ((hash.includes("~~") || hash.startsWith("$")) && translationValue === hash) invalidSpecialTranslations.push(hash);
2038
+ } else if (allPluralTranslationHashs.has(hash)) {
2039
+ missingHashs.delete(hash);
2040
+ extraHashs.delete(hash);
2041
+ const pluralValue = localeTranslations[hash];
2042
+ if (pluralValue !== void 0 && !isObject(pluralValue)) {
2043
+ invalidPluralTranslations.push(hash);
2044
+ delete localeTranslations[hash];
2045
+ if (fix) missingHashs.add(hash);
2046
+ } else if (!isDefaultLocale && isObject(pluralValue) && pluralValue["+2"] === null) incompletePluralTranslations.push(hash);
2047
+ } else missingHashs.delete(hash);
2048
+ if (missingHashs.size > 0 || extraHashs.size > 0 || invalidPluralTranslations.length > 0 || invalidSpecialTranslations.length > 0 || incompletePluralTranslations.length > 0) if (!fix) {
2049
+ hasError = true;
2050
+ if (invalidPluralTranslations.length > 0) log.error(`❌ ${basename} has invalid plural translations: `, invalidPluralTranslations);
2051
+ if (incompletePluralTranslations.length > 0) log.error(`❌ ${basename} has incomplete plural translations ('+2' is null, which is only allowed in the default locale): `, incompletePluralTranslations);
2052
+ if (invalidSpecialTranslations.length > 0) log.error(`❌ ${basename} has invalid special translations (value equals key): `, invalidSpecialTranslations);
2053
+ if (missingHashs.size > 0 || extraHashs.size > 0) {
2054
+ const parts = [];
2055
+ if (missingHashs.size) {
2056
+ const countStr = noColor ? String(missingHashs.size) : colorFn("red", String(missingHashs.size));
2057
+ parts.push(`missing ${countStr}`);
2058
+ }
2059
+ if (extraHashs.size) {
2060
+ const countStr = noColor ? String(extraHashs.size) : colorFn("red", String(extraHashs.size));
2061
+ parts.push(`extra ${countStr}`);
2062
+ }
2063
+ const allExtrasAreMarkers = extraHashs.size > 0 && [...extraHashs].every((k) => isMarkerKey(k));
2064
+ let lineInfo = "";
2065
+ if (allExtrasAreMarkers) {
2066
+ const markerLine = findMarkerLine(fs.readFileSync(fullPath, "utf-8"));
2067
+ lineInfo = markerLine ? `:${markerLine}` : "";
2068
+ }
2069
+ log.error(`❌ ${basename}${lineInfo} has invalid translations: ${parts.join(", ")}`);
2070
+ }
2071
+ } else if (invalidSpecialTranslations.length > 0) log.error(`❌ ${basename} has invalid special translations (value equals key): `, invalidSpecialTranslations);
2072
+ else if (missingHashs.size === 0 && extraHashs.size > 0 && [...extraHashs].every((k) => isMarkerKey(k))) {
2073
+ const markerLine = findMarkerLine(fs.readFileSync(fullPath, "utf-8"));
2074
+ const lineInfo = markerLine ? `:${markerLine}` : "";
2075
+ log.error(`❌ ${basename}${lineInfo} has missing translations`);
2076
+ } else {
2077
+ const cleanedExisting = {};
2078
+ for (const key of Object.keys(localeTranslations)) {
2079
+ if (key === "" || isMarkerKey(key) || extraHashs.has(key)) continue;
2080
+ cleanedExisting[key] = localeTranslations[key];
2081
+ }
2082
+ const missingSpecialTranslations = [];
2083
+ for (const hash of missingHashs) if (hash.startsWith("$") || hash.includes("~~")) missingSpecialTranslations.push(hash);
2084
+ const regularMissingHashs = new Set([...missingHashs].filter((h) => !h.startsWith("$") && !h.includes("~~")));
2085
+ if (aiTranslator && missingSpecialTranslations.length > 0) {
2086
+ hasError = true;
2087
+ log.error(`❌ ${basename} has missing $ or ~~ translations that require manual translation:`, missingSpecialTranslations);
2088
+ }
2089
+ if (aiTranslator && regularMissingHashs.size > 0) {
2090
+ const targetLocale = basename.replace(".json", "");
2091
+ const existingTranslationsMap = /* @__PURE__ */ new Map();
2092
+ for (const [key, value] of Object.entries(cleanedExisting)) if (typeof value === "string" || typeof value === "object" && value !== null) existingTranslationsMap.set(key, value);
2093
+ for (const extraKey of extraHashs) {
2094
+ if (isMarkerKey(extraKey) || existingTranslationsMap.has(extraKey)) continue;
2095
+ const value = localeTranslations[extraKey];
2096
+ if (typeof value === "string" || typeof value === "object" && value !== null) existingTranslationsMap.set(extraKey, value);
2097
+ }
2098
+ const contexts = [];
2099
+ const similarityIndex = createSimilarityIndex(existingTranslationsMap);
2100
+ for (const hash of regularMissingHashs) {
2101
+ const isPlural = allPluralTranslationHashs.has(hash);
2102
+ const similarTranslations = findSimilarFromIndex(similarityIndex, hash);
2103
+ contexts.push({
2104
+ sourceKey: hash,
2105
+ targetLocale,
2106
+ isPlural,
2107
+ similarTranslations
2108
+ });
2109
+ }
2110
+ aiTranslationTasks.push({
2111
+ fullPath,
2112
+ basename,
2113
+ cleanedExisting,
2114
+ regularMissingHashs,
2115
+ missingSpecialTranslations,
2116
+ contexts,
2117
+ extraHashs,
2118
+ missingHashs
2119
+ });
2120
+ } else {
2121
+ const missingMap = /* @__PURE__ */ new Map();
2122
+ for (const hash of missingHashs) {
2123
+ const value = allPluralTranslationHashs.has(hash) ? {
2124
+ zero: "No x",
2125
+ one: "1 x",
2126
+ "+2": "# x",
2127
+ many: void 0,
2128
+ manyLimit: void 0
2129
+ } : null;
2130
+ missingMap.set(hash, value);
2131
+ }
2132
+ const existingKeysCount = Object.keys(cleanedExisting).length;
2133
+ const orderedTranslations = buildOrderedTranslations(cleanedExisting, missingMap, calculateInsertPosition(existingKeysCount), missingMap.size > 0);
2134
+ const writtenContent = JSON.stringify(orderedTranslations, null, 2);
2135
+ if (missingHashs.size > 0) {
2136
+ hasError = true;
2137
+ const markerLine = findMarkerLine(writtenContent);
2138
+ const lineInfo = markerLine ? `:${markerLine}` : "";
2139
+ log.info(`🟠 ${basename}${lineInfo} translations keys were added`);
2140
+ } else log.info(`✅ ${basename} translations fixed`);
2141
+ fs.writeFileSync(fullPath, writtenContent);
2142
+ }
2143
+ }
2144
+ else log.info(`✅ ${basename} translations are up to date`);
2145
+ }
2146
+ if (aiTranslationTasks.length > 0) {
2147
+ const aiResults = await Promise.all(aiTranslationTasks.map(async (task) => {
2148
+ try {
2149
+ return {
2150
+ task,
2151
+ result: await aiTranslator.translateBatch(task.contexts),
2152
+ error: null
2153
+ };
2154
+ } catch (error) {
2155
+ return {
2156
+ task,
2157
+ result: null,
2158
+ error
2159
+ };
2160
+ }
2161
+ }));
2162
+ for (const { task, result, error } of aiResults) {
2163
+ const { fullPath, basename, cleanedExisting, regularMissingHashs, missingSpecialTranslations, missingHashs } = task;
2164
+ const missingMap = /* @__PURE__ */ new Map();
2165
+ const aiTranslatedMap = /* @__PURE__ */ new Map();
2166
+ if (error) {
2167
+ log.error(`❌ AI translation failed for ${basename}:`, error instanceof Error ? error.message : error);
2168
+ for (const hash of regularMissingHashs) {
2169
+ const value = allPluralTranslationHashs.has(hash) ? {
2170
+ zero: "No x",
2171
+ one: "1 x",
2172
+ "+2": "# x",
2173
+ many: void 0,
2174
+ manyLimit: void 0
2175
+ } : null;
2176
+ missingMap.set(hash, value);
2177
+ }
2178
+ } else if (result) {
2179
+ const { translations: aiTranslations, model, usage } = result;
2180
+ for (const hash of regularMissingHashs) {
2181
+ const aiResult = aiTranslations.get(hash);
2182
+ if (aiResult) aiTranslatedMap.set(hash, aiResult.value);
2183
+ else {
2184
+ const fallbackValue = allPluralTranslationHashs.has(hash) ? {
2185
+ zero: "No x",
2186
+ one: "1 x",
2187
+ "+2": "# x",
2188
+ many: void 0,
2189
+ manyLimit: void 0
2190
+ } : null;
2191
+ missingMap.set(hash, fallbackValue);
2192
+ }
2193
+ }
2194
+ if (model || usage) {
2195
+ const parts = [];
2196
+ if (model) parts.push(`Model: ${model}`);
2197
+ if (usage) parts.push(`Tokens: ${usage.inputTokens} in / ${usage.outputTokens} out`);
2198
+ log.info(` ${basename}: ${parts.join(" | ")}`);
2199
+ }
2200
+ }
2201
+ for (const hash of missingSpecialTranslations) {
2202
+ const value = allPluralTranslationHashs.has(hash) ? {
2203
+ zero: "No x",
2204
+ one: "1 x",
2205
+ "+2": "# x",
2206
+ many: void 0,
2207
+ manyLimit: void 0
2208
+ } : null;
2209
+ missingMap.set(hash, value);
2210
+ }
2211
+ const existingKeysCount = Object.keys(cleanedExisting).length;
2212
+ const orderedTranslations = buildOrderedTranslations(cleanedExisting, missingMap, calculateInsertPosition(existingKeysCount), missingMap.size > 0, aiTranslatedMap.size > 0 ? aiTranslatedMap : void 0);
2213
+ const writtenContent = JSON.stringify(orderedTranslations, null, 2);
2214
+ if (missingHashs.size > 0) if (missingMap.size === 0) log.info(`✅ ${basename} translations were AI-generated`);
2215
+ else {
2216
+ hasError = true;
2217
+ const markerLine = findMarkerLine(writtenContent);
2218
+ const lineInfo = markerLine ? `:${markerLine}` : "";
2219
+ log.info(`🟠 ${basename}${lineInfo} translations keys were added`);
2220
+ }
2221
+ else log.info(`✅ ${basename} translations fixed`);
2222
+ fs.writeFileSync(fullPath, writtenContent);
2223
+ }
2224
+ }
2225
+ return { hasError };
2226
+ }
2227
+
2228
+ //#endregion
2229
+ //#region src/cli.ts
2230
+ loadEnvFiles();
2231
+ const parsed = typeFlag({
2232
+ "config-dir": {
2233
+ type: String,
2234
+ alias: "c"
2235
+ },
2236
+ "src-dir": {
2237
+ type: String,
2238
+ alias: "r"
2239
+ },
2240
+ default: {
2241
+ type: String,
2242
+ alias: "d"
2243
+ },
2244
+ fix: {
2245
+ type: Boolean,
2246
+ alias: "f",
2247
+ default: false
2248
+ },
2249
+ "no-color": {
2250
+ type: Boolean,
2251
+ default: false
2252
+ },
2253
+ ai: { type: String },
2254
+ "max-id-size": {
2255
+ type: Number,
2256
+ default: 80
2257
+ },
2258
+ "disable-rule": {
2259
+ type: [String],
2260
+ default: []
2261
+ },
2262
+ "warn-rule": {
2263
+ type: [String],
2264
+ default: []
2265
+ }
2266
+ });
2267
+ const srcDir = parsed.flags["src-dir"];
2268
+ if (!srcDir) {
2269
+ console.error("--src-dir is required");
2270
+ process.exit(1);
2271
+ }
2272
+ const configDir = parsed.flags["config-dir"];
2273
+ if (!configDir) {
2274
+ console.error("--config-dir is required");
2275
+ process.exit(1);
2276
+ }
2277
+ let aiTranslator;
2278
+ const aiProviderInput = parsed.flags.ai ?? process.env["I18N_AI_AUTO_TRANSLATE"];
2279
+ if (aiProviderInput) {
2280
+ if (aiProviderInput !== "google" && aiProviderInput !== "openai") {
2281
+ console.error(`Invalid AI provider: ${aiProviderInput}. Must be 'google' or 'openai'.`);
2282
+ process.exit(1);
2283
+ }
2284
+ const provider = aiProviderInput;
2285
+ const apiKeyEnvVar = provider === "google" ? "GOOGLE_GENERATIVE_AI_API_KEY" : "OPENAI_API_KEY";
2286
+ if (!Boolean(process.env[apiKeyEnvVar])) console.warn(`Warning: ${apiKeyEnvVar} not set. AI translation will be skipped.`);
2287
+ else aiTranslator = createAITranslator(provider);
2288
+ }
2289
+ const validRuleNames = [
2290
+ "constant-translation",
2291
+ "unnecessary-plural",
2292
+ "jsx-without-interpolation",
2293
+ "jsx-without-jsx-nodes",
2294
+ "unnecessary-interpolated-affix",
2295
+ "max-translation-id-size"
2296
+ ];
2297
+ const rules = {};
2298
+ for (const rule of parsed.flags["disable-rule"]) {
2299
+ if (!validRuleNames.includes(rule)) {
2300
+ console.error(`Unknown rule: ${rule}`);
2301
+ process.exit(1);
2302
+ }
2303
+ rules[rule] = "off";
2304
+ }
2305
+ for (const rule of parsed.flags["warn-rule"]) {
2306
+ if (!validRuleNames.includes(rule)) {
2307
+ console.error(`Unknown rule: ${rule}`);
2308
+ process.exit(1);
2309
+ }
2310
+ rules[rule] = "warning";
2311
+ }
2312
+ const { hasError } = await validateTranslations({
2313
+ srcDir,
2314
+ configDir,
2315
+ defaultLocale: parsed.flags.default,
2316
+ fix: parsed.flags.fix,
2317
+ noColor: parsed.flags["no-color"],
2318
+ colorFn: (color, text) => consoleFmt.color(color, text),
2319
+ aiTranslator,
2320
+ rules,
2321
+ maxTranslationIdSize: parsed.flags["max-id-size"]
2322
+ });
2323
+ if (hasError) process.exit(1);
2324
+
2325
+ //#endregion
2326
+ export { };
2327
+ //# sourceMappingURL=cli.mjs.map