lingo.dev 0.111.3 → 0.111.5

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/build/cli.mjs CHANGED
@@ -216,6 +216,7 @@ var docLinks = {
216
216
  };
217
217
  var CLIError = class extends Error {
218
218
  docUrl;
219
+ errorType = "cli_error";
219
220
  constructor({ message, docUrl }) {
220
221
  super(message);
221
222
  this.docUrl = docLinks[docUrl];
@@ -223,6 +224,137 @@ var CLIError = class extends Error {
223
224
  visit: ${this.docUrl}`;
224
225
  }
225
226
  };
227
+ var ConfigError = class extends CLIError {
228
+ errorType = "config_error";
229
+ constructor({ message, docUrl }) {
230
+ super({ message, docUrl });
231
+ this.name = "ConfigError";
232
+ }
233
+ };
234
+ var AuthenticationError = class extends CLIError {
235
+ errorType = "auth_error";
236
+ constructor({ message, docUrl }) {
237
+ super({ message, docUrl });
238
+ this.name = "AuthenticationError";
239
+ }
240
+ };
241
+ var ValidationError = class extends CLIError {
242
+ errorType = "validation_error";
243
+ constructor({ message, docUrl }) {
244
+ super({ message, docUrl });
245
+ this.name = "ValidationError";
246
+ }
247
+ };
248
+ var LocalizationError = class extends Error {
249
+ errorType = "locale_error";
250
+ bucket;
251
+ sourceLocale;
252
+ targetLocale;
253
+ pathPattern;
254
+ constructor(message, context) {
255
+ super(message);
256
+ this.name = "LocalizationError";
257
+ this.bucket = context?.bucket;
258
+ this.sourceLocale = context?.sourceLocale;
259
+ this.targetLocale = context?.targetLocale;
260
+ this.pathPattern = context?.pathPattern;
261
+ }
262
+ };
263
+ var BucketProcessingError = class extends Error {
264
+ errorType = "bucket_error";
265
+ bucket;
266
+ constructor(message, bucket) {
267
+ super(message);
268
+ this.name = "BucketProcessingError";
269
+ this.bucket = bucket;
270
+ }
271
+ };
272
+ function isConfigError(error) {
273
+ return error instanceof ConfigError || error.errorType === "config_error";
274
+ }
275
+ function isAuthenticationError(error) {
276
+ return error instanceof AuthenticationError || error.errorType === "auth_error";
277
+ }
278
+ function isValidationError(error) {
279
+ return error instanceof ValidationError || error.errorType === "validation_error";
280
+ }
281
+ function isLocalizationError(error) {
282
+ return error instanceof LocalizationError || error.errorType === "locale_error";
283
+ }
284
+ function isBucketProcessingError(error) {
285
+ return error instanceof BucketProcessingError || error.errorType === "bucket_error";
286
+ }
287
+ function getCLIErrorType(error) {
288
+ if (isConfigError(error)) return "config_error";
289
+ if (isAuthenticationError(error)) return "auth_error";
290
+ if (isValidationError(error)) return "validation_error";
291
+ if (isLocalizationError(error)) return "locale_error";
292
+ if (isBucketProcessingError(error)) return "bucket_error";
293
+ if (error instanceof CLIError) return "cli_error";
294
+ return "unknown_error";
295
+ }
296
+ function createPreviousErrorContext(errorDetails) {
297
+ if (errorDetails.length === 0) return void 0;
298
+ return {
299
+ count: errorDetails.length,
300
+ types: [...new Set(errorDetails.map((e) => e.type))],
301
+ buckets: [...new Set(errorDetails.map((e) => e.bucket).filter(Boolean))]
302
+ };
303
+ }
304
+ function aggregateErrorAnalytics(errorDetails, buckets, targetLocales, i18nConfig) {
305
+ if (errorDetails.length === 0) {
306
+ return {
307
+ errorCount: 0,
308
+ errorTypes: [],
309
+ errorsByBucket: {},
310
+ errorsByType: {},
311
+ firstError: void 0,
312
+ bucketCount: buckets.length,
313
+ localeCount: targetLocales.length,
314
+ i18nConfig: {
315
+ sourceLocale: i18nConfig.locale.source,
316
+ targetLocales: i18nConfig.locale.targets,
317
+ bucketTypes: Object.keys(i18nConfig.buckets)
318
+ }
319
+ };
320
+ }
321
+ const errorsByBucket = errorDetails.reduce(
322
+ (acc, error) => {
323
+ if (error.bucket) {
324
+ acc[error.bucket] = (acc[error.bucket] || 0) + 1;
325
+ }
326
+ return acc;
327
+ },
328
+ {}
329
+ );
330
+ const errorsByType = errorDetails.reduce(
331
+ (acc, error) => {
332
+ acc[error.type] = (acc[error.type] || 0) + 1;
333
+ return acc;
334
+ },
335
+ {}
336
+ );
337
+ return {
338
+ errorCount: errorDetails.length,
339
+ errorTypes: [...new Set(errorDetails.map((e) => e.type))],
340
+ errorsByBucket,
341
+ errorsByType,
342
+ firstError: {
343
+ type: errorDetails[0].type,
344
+ bucket: errorDetails[0].bucket,
345
+ locale: errorDetails[0].locale,
346
+ pathPattern: errorDetails[0].pathPattern,
347
+ message: errorDetails[0].message
348
+ },
349
+ bucketCount: buckets.length,
350
+ localeCount: targetLocales.length,
351
+ i18nConfig: {
352
+ sourceLocale: i18nConfig.locale.source,
353
+ targetLocales: i18nConfig.locale.targets,
354
+ bucketTypes: Object.keys(i18nConfig.buckets)
355
+ }
356
+ };
357
+ }
226
358
 
227
359
  // src/cli/utils/cloudflare-status.ts
228
360
  async function checkCloudflareStatus() {
@@ -926,9 +1058,9 @@ function makeGitlabInitializer(spinner) {
926
1058
 
927
1059
  // src/cli/cmd/init.ts
928
1060
  import open2 from "open";
929
- var openUrl = (path17) => {
1061
+ var openUrl = (path18) => {
930
1062
  const settings = getSettings(void 0);
931
- open2(`${settings.auth.webUrl}${path17}`, { wait: false });
1063
+ open2(`${settings.auth.webUrl}${path18}`, { wait: false });
932
1064
  };
933
1065
  var throwHelpError = (option, value) => {
934
1066
  if (value === "help") {
@@ -1348,8 +1480,8 @@ var files_default = new Command6().command("files").description("Print out the l
1348
1480
  } else if (type.target) {
1349
1481
  result.push(...targetPaths);
1350
1482
  }
1351
- result.forEach((path17) => {
1352
- console.log(path17);
1483
+ result.forEach((path18) => {
1484
+ console.log(path18);
1353
1485
  });
1354
1486
  }
1355
1487
  }
@@ -1667,8 +1799,8 @@ function extractCommentsFromJsonc(jsoncString) {
1667
1799
  const keyMatch = line.match(/^\s*["']?([^"':,\s]+)["']?\s*:/);
1668
1800
  if (keyMatch) {
1669
1801
  const key = keyMatch[1];
1670
- const path17 = contextStack.map((ctx) => ctx.key).filter(Boolean);
1671
- keyInfo = { key, path: path17 };
1802
+ const path18 = contextStack.map((ctx) => ctx.key).filter(Boolean);
1803
+ keyInfo = { key, path: path18 };
1672
1804
  }
1673
1805
  } else {
1674
1806
  keyInfo = findAssociatedKey(lines, commentData.lineIndex, contextStack);
@@ -1752,8 +1884,8 @@ function findAssociatedKey(lines, commentLineIndex, contextStack) {
1752
1884
  const keyMatch = line.match(/^\s*["']?([^"':,\s]+)["']?\s*:/);
1753
1885
  if (keyMatch) {
1754
1886
  const key = keyMatch[1];
1755
- const path17 = contextStack.map((ctx) => ctx.key).filter(Boolean);
1756
- return { key, path: path17 };
1887
+ const path18 = contextStack.map((ctx) => ctx.key).filter(Boolean);
1888
+ return { key, path: path18 };
1757
1889
  }
1758
1890
  }
1759
1891
  return { key: null, path: [] };
@@ -1772,9 +1904,9 @@ function updateContext(contextStack, line, parsedJson) {
1772
1904
  }
1773
1905
  }
1774
1906
  }
1775
- function setCommentAtPath(comments, path17, key, hint) {
1907
+ function setCommentAtPath(comments, path18, key, hint) {
1776
1908
  let current = comments;
1777
- for (const pathKey of path17) {
1909
+ for (const pathKey of path18) {
1778
1910
  if (!current[pathKey]) {
1779
1911
  current[pathKey] = {};
1780
1912
  }
@@ -2435,9 +2567,9 @@ function createHtmlLoader() {
2435
2567
  const bDepth = b.split("/").length;
2436
2568
  return aDepth - bDepth;
2437
2569
  });
2438
- paths.forEach((path17) => {
2439
- const value = data[path17];
2440
- const [nodePath, attribute] = path17.split("#");
2570
+ paths.forEach((path18) => {
2571
+ const value = data[path18];
2572
+ const [nodePath, attribute] = path18.split("#");
2441
2573
  const [rootTag, ...indices] = nodePath.split("/");
2442
2574
  let parent = rootTag === "head" ? document.head : document.body;
2443
2575
  let current = parent;
@@ -4080,24 +4212,24 @@ function createRawDatoValue(parsedDatoValue, originalRawDatoValue, isClean = fal
4080
4212
  }
4081
4213
  function serializeStructuredText(rawStructuredText) {
4082
4214
  return serializeStructuredTextNode(rawStructuredText);
4083
- function serializeStructuredTextNode(node, path17 = [], acc = {}) {
4215
+ function serializeStructuredTextNode(node, path18 = [], acc = {}) {
4084
4216
  if ("document" in node) {
4085
4217
  return serializeStructuredTextNode(
4086
4218
  node.document,
4087
- [...path17, "document"],
4219
+ [...path18, "document"],
4088
4220
  acc
4089
4221
  );
4090
4222
  }
4091
4223
  if (!_18.isNil(node.value)) {
4092
- acc[[...path17, "value"].join(".")] = node.value;
4224
+ acc[[...path18, "value"].join(".")] = node.value;
4093
4225
  } else if (_18.get(node, "type") === "block") {
4094
- acc[[...path17, "item"].join(".")] = serializeBlock(node.item);
4226
+ acc[[...path18, "item"].join(".")] = serializeBlock(node.item);
4095
4227
  }
4096
4228
  if (node.children) {
4097
4229
  for (let i = 0; i < node.children.length; i++) {
4098
4230
  serializeStructuredTextNode(
4099
4231
  node.children[i],
4100
- [...path17, i.toString()],
4232
+ [...path18, i.toString()],
4101
4233
  acc
4102
4234
  );
4103
4235
  }
@@ -4164,8 +4296,8 @@ function deserializeBlockList(parsedBlockList, originalRawBlockList, isClean = f
4164
4296
  }
4165
4297
  function deserializeStructuredText(parsedStructuredText, originalRawStructuredText) {
4166
4298
  const result = _18.cloneDeep(originalRawStructuredText);
4167
- for (const [path17, value] of _18.entries(parsedStructuredText)) {
4168
- const realPath = _18.chain(path17.split(".")).flatMap((s) => !_18.isNaN(_18.toNumber(s)) ? ["children", s] : s).value();
4299
+ for (const [path18, value] of _18.entries(parsedStructuredText)) {
4300
+ const realPath = _18.chain(path18.split(".")).flatMap((s) => !_18.isNaN(_18.toNumber(s)) ? ["children", s] : s).value();
4169
4301
  const deserializedValue = createRawDatoValue(
4170
4302
  value,
4171
4303
  _18.get(originalRawStructuredText, realPath),
@@ -4591,15 +4723,15 @@ function parseTypeScript(input2) {
4591
4723
  function extractStringsFromDefaultExport(ast) {
4592
4724
  let extracted = {};
4593
4725
  traverse(ast, {
4594
- ExportDefaultDeclaration(path17) {
4595
- const { declaration } = path17.node;
4726
+ ExportDefaultDeclaration(path18) {
4727
+ const { declaration } = path18.node;
4596
4728
  const decl = unwrapTSAsExpression(declaration);
4597
4729
  if (t.isObjectExpression(decl)) {
4598
4730
  extracted = objectExpressionToObject(decl);
4599
4731
  } else if (t.isArrayExpression(decl)) {
4600
4732
  extracted = arrayExpressionToArray(decl);
4601
4733
  } else if (t.isIdentifier(decl)) {
4602
- const binding = path17.scope.bindings[decl.name];
4734
+ const binding = path18.scope.bindings[decl.name];
4603
4735
  if (binding && t.isVariableDeclarator(binding.path.node) && binding.path.node.init) {
4604
4736
  const initRaw = binding.path.node.init;
4605
4737
  const init = initRaw ? unwrapTSAsExpression(initRaw) : initRaw;
@@ -4664,8 +4796,8 @@ function arrayExpressionToArray(arrayExpression) {
4664
4796
  function updateStringsInDefaultExport(ast, data) {
4665
4797
  let modified = false;
4666
4798
  traverse(ast, {
4667
- ExportDefaultDeclaration(path17) {
4668
- const { declaration } = path17.node;
4799
+ ExportDefaultDeclaration(path18) {
4800
+ const { declaration } = path18.node;
4669
4801
  const decl = unwrapTSAsExpression(declaration);
4670
4802
  if (t.isObjectExpression(decl)) {
4671
4803
  modified = updateStringsInObjectExpression(decl, data) || modified;
@@ -4674,7 +4806,7 @@ function updateStringsInDefaultExport(ast, data) {
4674
4806
  modified = updateStringsInArrayExpression(decl, data) || modified;
4675
4807
  }
4676
4808
  } else if (t.isIdentifier(decl)) {
4677
- modified = updateStringsInExportedIdentifier(path17, data) || modified;
4809
+ modified = updateStringsInExportedIdentifier(path18, data) || modified;
4678
4810
  }
4679
4811
  }
4680
4812
  });
@@ -4745,9 +4877,9 @@ function updateStringsInArrayExpression(arrayExpression, incoming) {
4745
4877
  });
4746
4878
  return modified;
4747
4879
  }
4748
- function updateStringsInExportedIdentifier(path17, data) {
4749
- const exportName = path17.node.declaration.name;
4750
- const binding = path17.scope.bindings[exportName];
4880
+ function updateStringsInExportedIdentifier(path18, data) {
4881
+ const exportName = path18.node.declaration.name;
4882
+ const binding = path18.scope.bindings[exportName];
4751
4883
  if (!binding || !binding.path.node) return false;
4752
4884
  if (t.isVariableDeclarator(binding.path.node) && binding.path.node.init) {
4753
4885
  const initRaw = binding.path.node.init;
@@ -6349,11 +6481,11 @@ function _getAllKeys(obj, prefix = "") {
6349
6481
  let keys = [];
6350
6482
  for (const key in obj) {
6351
6483
  if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
6352
- const path17 = prefix ? `${prefix}.${key}` : key;
6484
+ const path18 = prefix ? `${prefix}.${key}` : key;
6353
6485
  if (typeof obj[key] === "object" && obj[key] !== null && !Array.isArray(obj[key])) {
6354
- keys = keys.concat(_getAllKeys(obj[key], path17));
6486
+ keys = keys.concat(_getAllKeys(obj[key], path18));
6355
6487
  } else {
6356
- keys.push(path17);
6488
+ keys.push(path18);
6357
6489
  }
6358
6490
  }
6359
6491
  return keys;
@@ -6866,19 +6998,19 @@ function createJsonDictionaryLoader() {
6866
6998
  );
6867
6999
  return input2;
6868
7000
  }
6869
- function walk(obj, dataNode, path17 = []) {
7001
+ function walk(obj, dataNode, path18 = []) {
6870
7002
  if (Array.isArray(obj) && Array.isArray(dataNode)) {
6871
7003
  obj.forEach(
6872
- (item, idx) => walk(item, dataNode[idx], [...path17, String(idx)])
7004
+ (item, idx) => walk(item, dataNode[idx], [...path18, String(idx)])
6873
7005
  );
6874
7006
  } else if (obj && typeof obj === "object" && dataNode && typeof dataNode === "object" && !Array.isArray(dataNode)) {
6875
7007
  for (const key of Object.keys(obj)) {
6876
7008
  if (dataNode.hasOwnProperty(key)) {
6877
- walk(obj[key], dataNode[key], [...path17, key]);
7009
+ walk(obj[key], dataNode[key], [...path18, key]);
6878
7010
  }
6879
7011
  }
6880
7012
  } else if (obj && typeof obj === "object" && !Array.isArray(obj) && typeof dataNode === "string") {
6881
- setNestedLocale(input2, path17, locale, dataNode, originalLocale);
7013
+ setNestedLocale(input2, path18, locale, dataNode, originalLocale);
6882
7014
  }
6883
7015
  }
6884
7016
  walk(input2, data);
@@ -6906,14 +7038,14 @@ function extractTranslatables(obj, locale) {
6906
7038
  function isTranslatableObject(obj, locale) {
6907
7039
  return obj && typeof obj === "object" && !Array.isArray(obj) && Object.prototype.hasOwnProperty.call(obj, locale);
6908
7040
  }
6909
- function setNestedLocale(obj, path17, locale, value, originalLocale) {
7041
+ function setNestedLocale(obj, path18, locale, value, originalLocale) {
6910
7042
  let curr = obj;
6911
- for (let i = 0; i < path17.length - 1; i++) {
6912
- const key = path17[i];
7043
+ for (let i = 0; i < path18.length - 1; i++) {
7044
+ const key = path18[i];
6913
7045
  if (!(key in curr)) curr[key] = {};
6914
7046
  curr = curr[key];
6915
7047
  }
6916
- const last = path17[path17.length - 1];
7048
+ const last = path18[path18.length - 1];
6917
7049
  if (curr[last] && typeof curr[last] === "object") {
6918
7050
  curr[last][locale] = value;
6919
7051
  if (originalLocale && curr[last][originalLocale]) {
@@ -7483,7 +7615,11 @@ function trackEvent(distinctId, event, properties) {
7483
7615
  properties: {
7484
7616
  ...properties,
7485
7617
  $lib: "lingo.dev-cli",
7486
- $lib_version: process.env.npm_package_version || "unknown"
7618
+ $lib_version: process.env.npm_package_version || "unknown",
7619
+ // Essential debugging context only
7620
+ node_version: process.version,
7621
+ is_ci: !!process.env.CI,
7622
+ debug_enabled: process.env.DEBUG === "true"
7487
7623
  },
7488
7624
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
7489
7625
  };
@@ -7683,7 +7819,21 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
7683
7819
  ).action(async function(options) {
7684
7820
  updateGitignore();
7685
7821
  const ora = Ora7();
7686
- const flags = parseFlags(options);
7822
+ let flags;
7823
+ try {
7824
+ flags = parseFlags(options);
7825
+ } catch (parseError) {
7826
+ await trackEvent("unknown", "cmd.i18n.error", {
7827
+ errorType: "validation_error",
7828
+ errorName: parseError.name || "ValidationError",
7829
+ errorMessage: parseError.message || "Invalid command line options",
7830
+ errorStack: parseError.stack,
7831
+ fatal: true,
7832
+ errorCount: 1,
7833
+ stage: "flag_validation"
7834
+ });
7835
+ throw parseError;
7836
+ }
7687
7837
  if (flags.debug) {
7688
7838
  const { debug } = await inquirer2.prompt([
7689
7839
  {
@@ -7695,6 +7845,7 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
7695
7845
  }
7696
7846
  let hasErrors = false;
7697
7847
  let authId = null;
7848
+ const errorDetails = [];
7698
7849
  try {
7699
7850
  ora.start("Loading configuration...");
7700
7851
  const i18nConfig = getConfig();
@@ -7727,7 +7878,7 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
7727
7878
  if (flags.file?.length) {
7728
7879
  buckets = buckets.map((bucket) => {
7729
7880
  const paths = bucket.paths.filter(
7730
- (path17) => flags.file.find((file) => path17.pathPattern?.includes(file))
7881
+ (path18) => flags.file.find((file) => path18.pathPattern?.includes(file))
7731
7882
  );
7732
7883
  return { ...bucket, paths };
7733
7884
  }).filter((bucket) => bucket.paths.length > 0);
@@ -7742,8 +7893,8 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
7742
7893
  ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
7743
7894
  buckets.map((bucket) => {
7744
7895
  ora.info(` ${bucket.type}:`);
7745
- bucket.paths.forEach((path17) => {
7746
- ora.info(` - ${path17.pathPattern}`);
7896
+ bucket.paths.forEach((path18) => {
7897
+ ora.info(` - ${path18.pathPattern}`);
7747
7898
  });
7748
7899
  });
7749
7900
  }
@@ -7996,9 +8147,23 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
7996
8147
  );
7997
8148
  }
7998
8149
  } catch (_error) {
7999
- const error = new Error(
8000
- `[${sourceLocale} -> ${targetLocale}] Localization failed: ${_error.message}`
8150
+ const error = new LocalizationError(
8151
+ `[${sourceLocale} -> ${targetLocale}] Localization failed: ${_error.message}`,
8152
+ {
8153
+ bucket: bucket.type,
8154
+ sourceLocale,
8155
+ targetLocale,
8156
+ pathPattern: bucketPath.pathPattern
8157
+ }
8001
8158
  );
8159
+ errorDetails.push({
8160
+ type: "locale_error",
8161
+ bucket: bucket.type,
8162
+ locale: `${sourceLocale} -> ${targetLocale}`,
8163
+ pathPattern: bucketPath.pathPattern,
8164
+ message: _error.message,
8165
+ stack: _error.stack
8166
+ });
8002
8167
  if (flags.strict) {
8003
8168
  throw error;
8004
8169
  } else {
@@ -8014,9 +8179,16 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
8014
8179
  }
8015
8180
  }
8016
8181
  } catch (_error) {
8017
- const error = new Error(
8018
- `Failed to process bucket ${bucket.type}: ${_error.message}`
8182
+ const error = new BucketProcessingError(
8183
+ `Failed to process bucket ${bucket.type}: ${_error.message}`,
8184
+ bucket.type
8019
8185
  );
8186
+ errorDetails.push({
8187
+ type: "bucket_error",
8188
+ bucket: bucket.type,
8189
+ message: _error.message,
8190
+ stack: _error.stack
8191
+ });
8020
8192
  if (flags.strict) {
8021
8193
  throw error;
8022
8194
  } else {
@@ -8029,20 +8201,54 @@ var i18n_default = new Command12().command("i18n").description("Run Localization
8029
8201
  if (!hasErrors) {
8030
8202
  ora.succeed("Localization completed.");
8031
8203
  await trackEvent(authId, "cmd.i18n.success", {
8032
- i18nConfig,
8033
- flags
8204
+ i18nConfig: {
8205
+ sourceLocale: i18nConfig.locale.source,
8206
+ targetLocales: i18nConfig.locale.targets,
8207
+ bucketTypes: Object.keys(i18nConfig.buckets)
8208
+ },
8209
+ flags,
8210
+ bucketCount: buckets.length,
8211
+ localeCount: targetLocales.length,
8212
+ processedSuccessfully: true
8034
8213
  });
8035
8214
  } else {
8036
8215
  ora.warn("Localization completed with errors.");
8037
8216
  await trackEvent(authId || "unknown", "cmd.i18n.error", {
8038
- flags
8217
+ flags,
8218
+ ...aggregateErrorAnalytics(
8219
+ errorDetails,
8220
+ buckets,
8221
+ targetLocales,
8222
+ i18nConfig
8223
+ )
8039
8224
  });
8040
8225
  }
8041
8226
  } catch (error) {
8042
8227
  ora.fail(error.message);
8228
+ const errorType = getCLIErrorType(error);
8229
+ let errorContext = {};
8230
+ if (isLocalizationError(error)) {
8231
+ errorContext = {
8232
+ bucket: error.bucket,
8233
+ sourceLocale: error.sourceLocale,
8234
+ targetLocale: error.targetLocale,
8235
+ pathPattern: error.pathPattern
8236
+ };
8237
+ } else if (isBucketProcessingError(error)) {
8238
+ errorContext = {
8239
+ bucket: error.bucket
8240
+ };
8241
+ }
8043
8242
  await trackEvent(authId || "unknown", "cmd.i18n.error", {
8044
8243
  flags,
8045
- error
8244
+ errorType,
8245
+ errorName: error.name || "Error",
8246
+ errorMessage: error.message,
8247
+ errorStack: error.stack,
8248
+ errorContext,
8249
+ fatal: true,
8250
+ errorCount: errorDetails.length + 1,
8251
+ previousErrors: createPreviousErrorContext(errorDetails)
8046
8252
  });
8047
8253
  }
8048
8254
  });
@@ -8063,7 +8269,7 @@ function parseFlags(options) {
8063
8269
  }
8064
8270
  async function validateAuth(settings) {
8065
8271
  if (!settings.auth.apiKey) {
8066
- throw new CLIError({
8272
+ throw new AuthenticationError({
8067
8273
  message: "Not authenticated. Please run `lingo.dev login` to authenticate.",
8068
8274
  docUrl: "authError"
8069
8275
  });
@@ -8074,7 +8280,7 @@ async function validateAuth(settings) {
8074
8280
  });
8075
8281
  const user = await authenticator.whoami();
8076
8282
  if (!user) {
8077
- throw new CLIError({
8283
+ throw new AuthenticationError({
8078
8284
  message: "Invalid API key. Please run `lingo.dev login` to authenticate.",
8079
8285
  docUrl: "authError"
8080
8286
  });
@@ -8083,24 +8289,24 @@ async function validateAuth(settings) {
8083
8289
  }
8084
8290
  function validateParams(i18nConfig, flags) {
8085
8291
  if (!i18nConfig) {
8086
- throw new CLIError({
8292
+ throw new ConfigError({
8087
8293
  message: "i18n.json not found. Please run `lingo.dev init` to initialize the project.",
8088
8294
  docUrl: "i18nNotFound"
8089
8295
  });
8090
8296
  } else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {
8091
- throw new CLIError({
8297
+ throw new ConfigError({
8092
8298
  message: "No buckets found in i18n.json. Please add at least one bucket containing i18n content.",
8093
8299
  docUrl: "bucketNotFound"
8094
8300
  });
8095
8301
  } else if (flags.locale?.some((locale) => !i18nConfig.locale.targets.includes(locale))) {
8096
- throw new CLIError({
8302
+ throw new ValidationError({
8097
8303
  message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
8098
8304
  docUrl: "localeTargetNotFound"
8099
8305
  });
8100
8306
  } else if (flags.bucket?.some(
8101
8307
  (bucket) => !i18nConfig.buckets[bucket]
8102
8308
  )) {
8103
- throw new CLIError({
8309
+ throw new ValidationError({
8104
8310
  message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
8105
8311
  docUrl: "bucketNotFound"
8106
8312
  });
@@ -8533,7 +8739,7 @@ import { execSync as execSync2 } from "child_process";
8533
8739
 
8534
8740
  // src/cli/cmd/ci/flows/in-branch.ts
8535
8741
  import { execSync } from "child_process";
8536
- import path16 from "path";
8742
+ import path17 from "path";
8537
8743
 
8538
8744
  // src/cli/cmd/ci/flows/_base.ts
8539
8745
  var IntegrationFlow = class {
@@ -8553,6 +8759,10 @@ function escapeShellArg(arg) {
8553
8759
 
8554
8760
  // src/cli/cmd/run/index.ts
8555
8761
  import { Command as Command16 } from "interactive-commander";
8762
+ import { exec } from "child_process";
8763
+ import path16 from "path";
8764
+ import { fileURLToPath } from "url";
8765
+ import os2 from "os";
8556
8766
 
8557
8767
  // src/cli/cmd/run/setup.ts
8558
8768
  import chalk10 from "chalk";
@@ -9296,14 +9506,14 @@ async function watch2(ctx) {
9296
9506
  pollInterval: 100
9297
9507
  }
9298
9508
  });
9299
- watcher.on("change", (path17) => {
9300
- handleFileChange(path17, state, ctx);
9509
+ watcher.on("change", (path18) => {
9510
+ handleFileChange(path18, state, ctx);
9301
9511
  });
9302
- watcher.on("add", (path17) => {
9303
- handleFileChange(path17, state, ctx);
9512
+ watcher.on("add", (path18) => {
9513
+ handleFileChange(path18, state, ctx);
9304
9514
  });
9305
- watcher.on("unlink", (path17) => {
9306
- handleFileChange(path17, state, ctx);
9515
+ watcher.on("unlink", (path18) => {
9516
+ handleFileChange(path18, state, ctx);
9307
9517
  });
9308
9518
  watcher.on("error", (error) => {
9309
9519
  console.error(
@@ -9414,8 +9624,9 @@ var flagsSchema2 = z2.object({
9414
9624
  sourceLocale: z2.string().optional(),
9415
9625
  targetLocale: z2.array(z2.string()).optional(),
9416
9626
  watch: z2.boolean().default(false),
9417
- debounce: z2.number().positive().default(5e3)
9627
+ debounce: z2.number().positive().default(5e3),
9418
9628
  // 5 seconds default
9629
+ sound: z2.boolean().optional()
9419
9630
  });
9420
9631
 
9421
9632
  // src/cli/cmd/run/_utils.ts
@@ -9434,6 +9645,32 @@ async function determineAuthId(ctx) {
9434
9645
  }
9435
9646
 
9436
9647
  // src/cli/cmd/run/index.ts
9648
+ var __dirname = path16.dirname(fileURLToPath(import.meta.url));
9649
+ function playSound(type) {
9650
+ const platform = os2.platform();
9651
+ return new Promise((resolve) => {
9652
+ const assetDir = path16.join(__dirname, "../assets");
9653
+ const soundFiles = [path16.join(assetDir, `${type}.mp3`)];
9654
+ let command = "";
9655
+ if (platform === "linux") {
9656
+ command = soundFiles.map(
9657
+ (file) => `mpg123 -q "${file}" 2>/dev/null || aplay "${file}" 2>/dev/null`
9658
+ ).join(" || ");
9659
+ } else if (platform === "darwin") {
9660
+ command = soundFiles.map((file) => `afplay "${file}"`).join(" || ");
9661
+ } else if (platform === "win32") {
9662
+ command = `powershell -c "try { (New-Object Media.SoundPlayer '${soundFiles[1]}').PlaySync() } catch { Start-Process -FilePath '${soundFiles[0]}' -WindowStyle Hidden -Wait }"`;
9663
+ } else {
9664
+ command = soundFiles.map(
9665
+ (file) => `aplay "${file}" 2>/dev/null || afplay "${file}" 2>/dev/null`
9666
+ ).join(" || ");
9667
+ }
9668
+ exec(command, () => {
9669
+ resolve();
9670
+ });
9671
+ setTimeout(resolve, 3e3);
9672
+ });
9673
+ }
9437
9674
  var run_default = new Command16().command("run").description("Run Lingo.dev localization engine").helpOption("-h, --help", "Show help").option(
9438
9675
  "--source-locale <source-locale>",
9439
9676
  "Locale to use as source locale. Defaults to i18n.json locale.source"
@@ -9473,6 +9710,9 @@ var run_default = new Command16().command("run").description("Run Lingo.dev loca
9473
9710
  "--debounce <milliseconds>",
9474
9711
  "Debounce delay in milliseconds for watch mode (default: 5000ms)",
9475
9712
  (val) => parseInt(val)
9713
+ ).option(
9714
+ "--sound",
9715
+ "Play sound on completion, partially completion and failed of the task"
9476
9716
  ).action(async (args) => {
9477
9717
  let authId = null;
9478
9718
  try {
@@ -9502,6 +9742,9 @@ var run_default = new Command16().command("run").description("Run Lingo.dev loca
9502
9742
  await renderSpacer();
9503
9743
  await renderSummary(ctx.results);
9504
9744
  await renderSpacer();
9745
+ if (ctx.flags.sound) {
9746
+ await playSound("success");
9747
+ }
9505
9748
  if (ctx.flags.watch) {
9506
9749
  await watch2(ctx);
9507
9750
  }
@@ -9511,6 +9754,9 @@ var run_default = new Command16().command("run").description("Run Lingo.dev loca
9511
9754
  });
9512
9755
  } catch (error) {
9513
9756
  await trackEvent(authId || "unknown", "cmd.run.error", {});
9757
+ if (args.sound) {
9758
+ await playSound("failure");
9759
+ }
9514
9760
  throw error;
9515
9761
  }
9516
9762
  });
@@ -9601,7 +9847,7 @@ var InBranchFlow = class extends IntegrationFlow {
9601
9847
  return false;
9602
9848
  }
9603
9849
  }
9604
- const workingDir = path16.resolve(
9850
+ const workingDir = path17.resolve(
9605
9851
  process.cwd(),
9606
9852
  this.platformKit.config.workingDir
9607
9853
  );
@@ -10340,8 +10586,8 @@ var status_default = new Command18().command("status").description("Show the sta
10340
10586
  if (flags.file?.length) {
10341
10587
  buckets = buckets.map((bucket) => {
10342
10588
  const paths = bucket.paths.filter(
10343
- (path17) => flags.file.find(
10344
- (file) => path17.pathPattern?.includes(file) || path17.pathPattern?.match(file) || minimatch(path17.pathPattern, file)
10589
+ (path18) => flags.file.find(
10590
+ (file) => path18.pathPattern?.includes(file) || path18.pathPattern?.match(file) || minimatch(path18.pathPattern, file)
10345
10591
  )
10346
10592
  );
10347
10593
  return { ...bucket, paths };
@@ -10355,8 +10601,8 @@ var status_default = new Command18().command("status").description("Show the sta
10355
10601
  ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
10356
10602
  buckets.map((bucket) => {
10357
10603
  ora.info(` ${bucket.type}:`);
10358
- bucket.paths.forEach((path17) => {
10359
- ora.info(` - ${path17.pathPattern}`);
10604
+ bucket.paths.forEach((path18) => {
10605
+ ora.info(` - ${path18.pathPattern}`);
10360
10606
  });
10361
10607
  });
10362
10608
  }
@@ -10640,10 +10886,10 @@ var status_default = new Command18().command("status").description("Show the sta
10640
10886
  if (flags.confirm && Object.keys(fileStats).length > 0) {
10641
10887
  console.log(chalk14.bold(`
10642
10888
  \u{1F4D1} BREAKDOWN BY FILE:`));
10643
- Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path17, stats]) => {
10889
+ Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path18, stats]) => {
10644
10890
  if (stats.sourceKeys === 0) return;
10645
10891
  console.log(chalk14.bold(`
10646
- \u2022 ${path17}:`));
10892
+ \u2022 ${path18}:`));
10647
10893
  console.log(
10648
10894
  ` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`
10649
10895
  );
@@ -10863,7 +11109,7 @@ async function renderHero2() {
10863
11109
  // package.json
10864
11110
  var package_default = {
10865
11111
  name: "lingo.dev",
10866
- version: "0.111.3",
11112
+ version: "0.111.5",
10867
11113
  description: "Lingo.dev CLI",
10868
11114
  private: false,
10869
11115
  publishConfig: {
@@ -10961,7 +11207,8 @@ var package_default = {
10961
11207
  },
10962
11208
  files: [
10963
11209
  "bin",
10964
- "build"
11210
+ "build",
11211
+ "assets"
10965
11212
  ],
10966
11213
  scripts: {
10967
11214
  "lingo.dev": "node --inspect=9229 ./bin/cli.mjs",