lingo.dev 0.93.5 → 0.93.6

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.cjs CHANGED
@@ -5400,2166 +5400,2207 @@ function escapeShellArg(arg) {
5400
5400
  return `'${arg.replace(/'/g, "'\\''")}'`;
5401
5401
  }
5402
5402
 
5403
- // src/cli/cmd/ci/flows/in-branch.ts
5404
- var InBranchFlow = class extends IntegrationFlow {
5405
- async preRun() {
5406
- this.ora.start("Configuring git");
5407
- const canContinue = this.configureGit();
5408
- this.ora.succeed("Git configured");
5409
- return canContinue;
5410
- }
5411
- async run(forcePush = false) {
5412
- this.ora.start("Running Lingo.dev");
5413
- await this.runLingoDotDev();
5414
- this.ora.succeed("Done running Lingo.dev");
5415
- _child_process.execSync.call(void 0, `rm -f i18n.cache`, { stdio: "inherit" });
5416
- this.ora.start("Checking for changes");
5417
- const hasChanges = this.checkCommitableChanges();
5418
- this.ora.succeed(hasChanges ? "Changes detected" : "No changes detected");
5419
- if (hasChanges) {
5420
- this.ora.start("Committing changes");
5421
- _child_process.execSync.call(void 0, `git add .`, { stdio: "inherit" });
5422
- _child_process.execSync.call(void 0, `git status --porcelain`, { stdio: "inherit" });
5423
- _child_process.execSync.call(void 0,
5424
- `git commit -m ${escapeShellArg(this.platformKit.config.commitMessage)} --no-verify`,
5425
- {
5426
- stdio: "inherit"
5427
- }
5428
- );
5429
- this.ora.succeed("Changes committed");
5430
- this.ora.start("Pushing changes to remote");
5431
- const currentBranch = _nullishCoalesce(this.i18nBranchName, () => ( this.platformKit.platformConfig.baseBranchName));
5432
- _child_process.execSync.call(void 0,
5433
- `git push origin ${currentBranch} ${forcePush ? "--force" : ""}`,
5434
- {
5435
- stdio: "inherit"
5436
- }
5437
- );
5438
- this.ora.succeed("Changes pushed to remote");
5439
- }
5440
- return hasChanges;
5441
- }
5442
- checkCommitableChanges() {
5443
- return _child_process.execSync.call(void 0, 'git status --porcelain || echo "has_changes"', {
5444
- encoding: "utf8"
5445
- }).length > 0;
5446
- }
5447
- async runLingoDotDev() {
5448
- try {
5449
- await i18n_default.exitOverride().parseAsync(["--api-key", this.platformKit.config.replexicaApiKey], {
5450
- from: "user"
5451
- });
5452
- } catch (err) {
5453
- if (err.code === "commander.helpDisplayed") return;
5454
- throw err;
5455
- }
5456
- }
5457
- configureGit() {
5458
- const { processOwnCommits } = this.platformKit.config;
5459
- const { baseBranchName } = this.platformKit.platformConfig;
5460
- this.ora.info(`Current working directory:`);
5461
- _child_process.execSync.call(void 0, `pwd`, { stdio: "inherit" });
5462
- _child_process.execSync.call(void 0, `ls -la`, { stdio: "inherit" });
5463
- _child_process.execSync.call(void 0, `git config --global safe.directory ${process.cwd()}`);
5464
- _child_process.execSync.call(void 0, `git config user.name "${gitConfig.userName}"`);
5465
- _child_process.execSync.call(void 0, `git config user.email "${gitConfig.userEmail}"`);
5466
- _optionalChain([this, 'access', _186 => _186.platformKit, 'optionalAccess', _187 => _187.gitConfig, 'call', _188 => _188()]);
5467
- _child_process.execSync.call(void 0, `git fetch origin ${baseBranchName}`, { stdio: "inherit" });
5468
- _child_process.execSync.call(void 0, `git checkout ${baseBranchName} --`, { stdio: "inherit" });
5469
- if (!processOwnCommits) {
5470
- const currentAuthor = `${gitConfig.userName} <${gitConfig.userEmail}>`;
5471
- const authorOfLastCommit = _child_process.execSync.call(void 0,
5472
- `git log -1 --pretty=format:'%an <%ae>'`
5473
- ).toString();
5474
- if (authorOfLastCommit === currentAuthor) {
5475
- this.ora.warn(
5476
- `The last commit was already made by ${currentAuthor}, so this run will be skipped, as running again would have no effect. See docs: https://docs.lingo.dev/ci-action/overview`
5477
- );
5478
- return false;
5479
- }
5480
- }
5481
- const workingDir = path12.default.resolve(
5482
- process.cwd(),
5483
- this.platformKit.config.workingDir
5484
- );
5485
- if (workingDir !== process.cwd()) {
5486
- this.ora.info(
5487
- `Changing to working directory: ${this.platformKit.config.workingDir}`
5488
- );
5489
- process.chdir(workingDir);
5490
- }
5491
- return true;
5403
+ // src/cli/cmd/run/index.ts
5404
+
5405
+
5406
+ // src/cli/cmd/run/setup.ts
5407
+
5408
+ var _listr2 = require('listr2');
5409
+
5410
+ // src/cli/cmd/run/_const.ts
5411
+
5412
+
5413
+ var commonTaskRendererOptions = {
5414
+ color: {
5415
+ [_listr2.ListrDefaultRendererLogLevels.COMPLETED]: (msg) => msg ? _chalk2.default.hex(colors.green)(msg) : _chalk2.default.hex(colors.green)("")
5416
+ },
5417
+ icon: {
5418
+ [_listr2.ListrDefaultRendererLogLevels.COMPLETED]: _chalk2.default.hex(colors.green)("\u2713")
5492
5419
  }
5493
5420
  };
5494
5421
 
5495
- // src/cli/cmd/ci/flows/pull-request.ts
5496
- var PullRequestFlow = class extends InBranchFlow {
5497
- async preRun() {
5498
- const canContinue = await _optionalChain([super.preRun.bind(this), 'optionalCall', _189 => _189()]);
5499
- if (!canContinue) {
5500
- return false;
5501
- }
5502
- this.ora.start("Calculating automated branch name");
5503
- this.i18nBranchName = this.calculatePrBranchName();
5504
- this.ora.succeed(
5505
- `Automated branch name calculated: ${this.i18nBranchName}`
5422
+ // src/cli/localizer/lingodotdev.ts
5423
+
5424
+
5425
+
5426
+ function createLingoDotDevLocalizer(explicitApiKey) {
5427
+ const { auth } = getSettings(explicitApiKey);
5428
+ if (!auth) {
5429
+ throw new Error(
5430
+ _dedent2.default`
5431
+ You're trying to use ${_chalk2.default.hex(colors.green)("Lingo.dev")} provider, however, you are not authenticated.
5432
+
5433
+ To fix this issue:
5434
+ 1. Run ${_chalk2.default.dim("lingo.dev login")} to authenticate, or
5435
+ 2. Use the ${_chalk2.default.dim("--api-key")} flag to provide an API key.
5436
+ 3. Set ${_chalk2.default.dim("LINGODOTDEV_API_KEY")} environment variable.
5437
+ `
5506
5438
  );
5507
- this.ora.start("Checking if branch exists");
5508
- const branchExists = await this.checkBranchExistance(this.i18nBranchName);
5509
- this.ora.succeed(branchExists ? "Branch exists" : "Branch does not exist");
5510
- if (branchExists) {
5511
- this.ora.start(`Checking out branch ${this.i18nBranchName}`);
5512
- this.checkoutI18nBranch(this.i18nBranchName);
5513
- this.ora.succeed(`Checked out branch ${this.i18nBranchName}`);
5514
- this.ora.start(
5515
- `Syncing with ${this.platformKit.platformConfig.baseBranchName}`
5439
+ }
5440
+ const engine = new (0, __sdk.LingoDotDevEngine)({
5441
+ apiKey: auth.apiKey,
5442
+ apiUrl: auth.apiUrl
5443
+ });
5444
+ return {
5445
+ id: "Lingo.dev",
5446
+ checkAuth: async () => {
5447
+ try {
5448
+ const response = await engine.whoami();
5449
+ return {
5450
+ authenticated: !!response,
5451
+ username: _optionalChain([response, 'optionalAccess', _186 => _186.email])
5452
+ };
5453
+ } catch (e2) {
5454
+ return { authenticated: false };
5455
+ }
5456
+ },
5457
+ localize: async (input2, onProgress) => {
5458
+ if (!Object.keys(input2.processableData).length) {
5459
+ return input2;
5460
+ }
5461
+ const processedData = await engine.localizeObject(
5462
+ input2.processableData,
5463
+ {
5464
+ sourceLocale: input2.sourceLocale,
5465
+ targetLocale: input2.targetLocale,
5466
+ reference: {
5467
+ [input2.sourceLocale]: input2.sourceData,
5468
+ [input2.targetLocale]: input2.targetData
5469
+ }
5470
+ },
5471
+ onProgress
5516
5472
  );
5517
- this.syncI18nBranch();
5518
- this.ora.succeed(`Checked out and synced branch ${this.i18nBranchName}`);
5519
- } else {
5520
- this.ora.start(`Creating branch ${this.i18nBranchName}`);
5521
- this.createI18nBranch(this.i18nBranchName);
5522
- this.ora.succeed(`Created branch ${this.i18nBranchName}`);
5473
+ return processedData;
5523
5474
  }
5524
- return true;
5525
- }
5526
- async run() {
5527
- return super.run(true);
5528
- }
5529
- async postRun() {
5530
- if (!this.i18nBranchName) {
5475
+ };
5476
+ }
5477
+
5478
+ // src/cli/localizer/explicit.ts
5479
+
5480
+
5481
+
5482
+
5483
+
5484
+
5485
+ function createExplicitLocalizer(provider) {
5486
+ switch (provider.id) {
5487
+ default:
5531
5488
  throw new Error(
5532
- "i18nBranchName is not set. Did you forget to call preRun?"
5489
+ _dedent2.default`
5490
+ You're trying to use unsupported provider: ${_chalk2.default.dim(provider.id)}.
5491
+
5492
+ To fix this issue:
5493
+ 1. Switch to one of the supported providers, or
5494
+ 2. Remove the ${_chalk2.default.italic("provider")} node from your i18n.json configuration to switch to ${_chalk2.default.hex(colors.green)("Lingo.dev")}
5495
+
5496
+ ${_chalk2.default.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
5497
+ `
5533
5498
  );
5534
- }
5535
- this.ora.start("Checking if PR already exists");
5536
- const pullRequestNumber = await this.ensureFreshPr(this.i18nBranchName);
5537
- this.ora.succeed(
5538
- `Pull request ready: ${this.platformKit.buildPullRequestUrl(pullRequestNumber)}`
5539
- );
5540
- }
5541
- calculatePrBranchName() {
5542
- return `lingo.dev/${this.platformKit.platformConfig.baseBranchName}`;
5543
- }
5544
- async checkBranchExistance(prBranchName) {
5545
- return this.platformKit.branchExists({
5546
- branch: prBranchName
5547
- });
5548
- }
5549
- async ensureFreshPr(i18nBranchName) {
5550
- this.ora.start(
5551
- `Checking for existing PR with head ${i18nBranchName} and base ${this.platformKit.platformConfig.baseBranchName}`
5552
- );
5553
- let prNumber = await this.platformKit.getOpenPullRequestNumber({
5554
- branch: i18nBranchName
5555
- });
5556
- if (prNumber) {
5557
- this.ora.succeed(`Existing PR found: #${prNumber}`);
5558
- } else {
5559
- this.ora.start(`Creating new PR`);
5560
- prNumber = await this.platformKit.createPullRequest({
5561
- head: i18nBranchName,
5562
- title: this.platformKit.config.pullRequestTitle,
5563
- body: this.getPrBodyContent()
5499
+ case "openai":
5500
+ return createAiSdkLocalizer({
5501
+ factory: (params) => _openai.createOpenAI.call(void 0, params).languageModel(provider.model),
5502
+ id: provider.id,
5503
+ prompt: provider.prompt,
5504
+ apiKeyName: "OPENAI_API_KEY",
5505
+ baseUrl: provider.baseUrl
5506
+ });
5507
+ case "anthropic":
5508
+ return createAiSdkLocalizer({
5509
+ factory: (params) => _anthropic.createAnthropic.call(void 0, params).languageModel(provider.model),
5510
+ id: provider.id,
5511
+ prompt: provider.prompt,
5512
+ apiKeyName: "ANTHROPIC_API_KEY",
5513
+ baseUrl: provider.baseUrl
5564
5514
  });
5565
- this.ora.succeed(`Created new PR: #${prNumber}`);
5566
- }
5567
- return prNumber;
5568
- }
5569
- checkoutI18nBranch(i18nBranchName) {
5570
- _child_process.execSync.call(void 0, `git fetch origin ${i18nBranchName}`, { stdio: "inherit" });
5571
- _child_process.execSync.call(void 0, `git checkout -b ${i18nBranchName}`, {
5572
- stdio: "inherit"
5573
- });
5574
5515
  }
5575
- createI18nBranch(i18nBranchName) {
5576
- try {
5577
- _child_process.execSync.call(void 0,
5578
- `git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,
5579
- { stdio: "inherit" }
5580
- );
5581
- _child_process.execSync.call(void 0,
5582
- `git checkout -b ${i18nBranchName} origin/${this.platformKit.platformConfig.baseBranchName}`,
5583
- {
5584
- stdio: "inherit"
5585
- }
5586
- );
5587
- } catch (error) {
5588
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
5589
- this.ora.fail(`Failed to create branch: ${errorMessage}`);
5590
- this.ora.info(`
5591
- Troubleshooting tips:
5592
- 1. Make sure you have permission to create branches
5593
- 2. Check if the branch already exists locally (try 'git branch -a')
5594
- 3. Verify connectivity to remote repository
5595
- `);
5596
- throw new Error(`Branch creation failed: ${errorMessage}`);
5597
- }
5598
- }
5599
- syncI18nBranch() {
5600
- if (!this.i18nBranchName) {
5601
- throw new Error("i18nBranchName is not set");
5602
- }
5603
- this.ora.start(
5604
- `Fetching latest changes from ${this.platformKit.platformConfig.baseBranchName}`
5605
- );
5606
- _child_process.execSync.call(void 0,
5607
- `git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,
5608
- { stdio: "inherit" }
5609
- );
5610
- this.ora.succeed(
5611
- `Fetched latest changes from ${this.platformKit.platformConfig.baseBranchName}`
5612
- );
5613
- try {
5614
- this.ora.start("Attempting to rebase branch");
5615
- _child_process.execSync.call(void 0,
5616
- `git rebase origin/${this.platformKit.platformConfig.baseBranchName}`,
5617
- { stdio: "inherit" }
5618
- );
5619
- this.ora.succeed("Successfully rebased branch");
5620
- } catch (error) {
5621
- this.ora.warn("Rebase failed, falling back to alternative sync method");
5622
- this.ora.start("Aborting failed rebase");
5623
- _child_process.execSync.call(void 0, "git rebase --abort", { stdio: "inherit" });
5624
- this.ora.succeed("Aborted failed rebase");
5625
- this.ora.start(
5626
- `Resetting to ${this.platformKit.platformConfig.baseBranchName}`
5627
- );
5628
- _child_process.execSync.call(void 0,
5629
- `git reset --hard origin/${this.platformKit.platformConfig.baseBranchName}`,
5630
- { stdio: "inherit" }
5631
- );
5632
- this.ora.succeed(
5633
- `Reset to ${this.platformKit.platformConfig.baseBranchName}`
5634
- );
5635
- this.ora.start("Restoring target files");
5636
- const targetFiles = ["i18n.lock"];
5637
- const targetFileNames = _child_process.execSync.call(void 0,
5638
- `npx lingo.dev@latest show files --target ${this.platformKit.platformConfig.baseBranchName}`,
5639
- { encoding: "utf8" }
5640
- ).split("\n").filter(Boolean);
5641
- targetFiles.push(...targetFileNames);
5642
- _child_process.execSync.call(void 0, `git fetch origin ${this.i18nBranchName}`, { stdio: "inherit" });
5643
- for (const file of targetFiles) {
5644
- try {
5645
- _child_process.execSync.call(void 0, `git checkout FETCH_HEAD -- ${file}`, { stdio: "inherit" });
5646
- } catch (error2) {
5647
- this.ora.warn(`Skipping non-existent file: ${file}`);
5648
- continue;
5649
- }
5650
- }
5651
- this.ora.succeed("Restored target files");
5652
- }
5653
- this.ora.start("Checking for changes to commit");
5654
- const hasChanges = this.checkCommitableChanges();
5655
- if (hasChanges) {
5656
- _child_process.execSync.call(void 0, "git add .", { stdio: "inherit" });
5657
- _child_process.execSync.call(void 0,
5658
- `git commit -m "chore: sync with ${this.platformKit.platformConfig.baseBranchName}" --no-verify`,
5659
- {
5660
- stdio: "inherit"
5661
- }
5662
- );
5663
- this.ora.succeed("Committed additional changes");
5664
- } else {
5665
- this.ora.succeed("No changes to commit");
5666
- }
5667
- }
5668
- getPrBodyContent() {
5669
- return `
5670
- Hey team,
5671
-
5672
- [**Lingo.dev**](https://lingo.dev) here with fresh translations!
5673
-
5674
- ### In this update
5675
-
5676
- - Added missing translations
5677
- - Performed brand voice, context and glossary checks
5678
- - Enhanced translations using Lingo.dev Localization Engine
5516
+ }
5517
+ function createAiSdkLocalizer(params) {
5518
+ const apiKey = process.env[params.apiKeyName];
5519
+ if (!apiKey) {
5520
+ throw new Error(
5521
+ _dedent2.default`
5522
+ You're trying to use raw ${_chalk2.default.dim(params.id)} API for translation, however, ${_chalk2.default.dim(params.apiKeyName)} environment variable is not set.
5679
5523
 
5680
- ### Next Steps
5524
+ To fix this issue:
5525
+ 1. Set ${_chalk2.default.dim(params.apiKeyName)} in your environment variables, or
5526
+ 2. Remove the ${_chalk2.default.italic("provider")} node from your i18n.json configuration to switch to ${_chalk2.default.hex(colors.green)("Lingo.dev")}
5681
5527
 
5682
- - [ ] Review the changes
5683
- - [ ] Merge when ready
5684
- `.trim();
5528
+ ${_chalk2.default.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
5529
+ `
5530
+ );
5685
5531
  }
5686
- };
5687
-
5688
- // src/cli/cmd/ci/platforms/bitbucket.ts
5689
-
5690
- var _bitbucket = require('bitbucket'); var _bitbucket2 = _interopRequireDefault(_bitbucket);
5691
-
5692
-
5693
- // src/cli/cmd/ci/platforms/_base.ts
5694
-
5695
-
5696
- var defaultMessage = "feat: update translations via @lingodotdev";
5697
- var PlatformKit = class {
5698
- gitConfig(token, repoUrl) {
5699
- if (token && repoUrl) {
5700
- _child_process.execSync.call(void 0, `git remote set-url origin ${repoUrl}`, {
5701
- stdio: "inherit"
5532
+ const model = params.factory({
5533
+ apiKey,
5534
+ baseUrl: params.baseUrl
5535
+ });
5536
+ return {
5537
+ id: params.id,
5538
+ checkAuth: async () => {
5539
+ try {
5540
+ await _ai.generateText.call(void 0, {
5541
+ model,
5542
+ messages: [
5543
+ { role: "system", content: "You are an echo server" },
5544
+ { role: "user", content: "OK" },
5545
+ { role: "assistant", content: "OK" },
5546
+ { role: "user", content: "OK" }
5547
+ ]
5548
+ });
5549
+ return { authenticated: true, username: "anonymous" };
5550
+ } catch (error) {
5551
+ return { authenticated: false };
5552
+ }
5553
+ },
5554
+ localize: async (input2) => {
5555
+ const systemPrompt = params.prompt.replaceAll("{source}", input2.sourceLocale).replaceAll("{target}", input2.targetLocale);
5556
+ const shots = [
5557
+ [
5558
+ {
5559
+ sourceLocale: "en",
5560
+ targetLocale: "es",
5561
+ data: {
5562
+ message: "Hello, world!"
5563
+ }
5564
+ },
5565
+ {
5566
+ sourceLocale: "en",
5567
+ targetLocale: "es",
5568
+ data: {
5569
+ message: "Hola, mundo!"
5570
+ }
5571
+ }
5572
+ ]
5573
+ ];
5574
+ const payload = {
5575
+ sourceLocale: input2.sourceLocale,
5576
+ targetLocale: input2.targetLocale,
5577
+ data: input2.processableData
5578
+ };
5579
+ const response = await _ai.generateText.call(void 0, {
5580
+ model,
5581
+ messages: [
5582
+ { role: "system", content: systemPrompt },
5583
+ { role: "user", content: "OK" },
5584
+ ...shots.flatMap(
5585
+ ([userShot, assistantShot]) => [
5586
+ { role: "user", content: JSON.stringify(userShot) },
5587
+ { role: "assistant", content: JSON.stringify(assistantShot) }
5588
+ ]
5589
+ ),
5590
+ { role: "user", content: JSON.stringify(payload) }
5591
+ ]
5702
5592
  });
5593
+ const result = JSON.parse(response.text);
5594
+ const index = result.data.indexOf("{");
5595
+ const lastIndex = result.data.lastIndexOf("}");
5596
+ const trimmed = result.data.slice(index, lastIndex + 1);
5597
+ const repaired = _jsonrepair.jsonrepair.call(void 0, trimmed);
5598
+ const finalResult = JSON.parse(repaired);
5599
+ return finalResult.data;
5703
5600
  }
5704
- }
5705
- get config() {
5706
- const env = _zod2.default.object({
5707
- LINGODOTDEV_API_KEY: _zod2.default.string(),
5708
- LINGODOTDEV_PULL_REQUEST: _zod2.default.preprocess((val) => val === "true" || val === true, _zod2.default.boolean()),
5709
- LINGODOTDEV_COMMIT_MESSAGE: _zod2.default.string().optional(),
5710
- LINGODOTDEV_PULL_REQUEST_TITLE: _zod2.default.string().optional(),
5711
- LINGODOTDEV_WORKING_DIRECTORY: _zod2.default.string().optional(),
5712
- LINGODOTDEV_PROCESS_OWN_COMMITS: _zod2.default.preprocess((val) => val === "true" || val === true, _zod2.default.boolean()).optional()
5713
- }).parse(process.env);
5714
- return {
5715
- replexicaApiKey: env.LINGODOTDEV_API_KEY,
5716
- isPullRequestMode: env.LINGODOTDEV_PULL_REQUEST,
5717
- commitMessage: env.LINGODOTDEV_COMMIT_MESSAGE || defaultMessage,
5718
- pullRequestTitle: env.LINGODOTDEV_PULL_REQUEST_TITLE || defaultMessage,
5719
- workingDir: env.LINGODOTDEV_WORKING_DIRECTORY || ".",
5720
- processOwnCommits: env.LINGODOTDEV_PROCESS_OWN_COMMITS || false
5721
- };
5722
- }
5723
- };
5601
+ };
5602
+ }
5724
5603
 
5725
- // src/cli/cmd/ci/platforms/bitbucket.ts
5726
- var { Bitbucket } = _bitbucket2.default;
5727
- var BitbucketPlatformKit = class extends PlatformKit {
5728
-
5729
- get bb() {
5730
- if (!this._bb) {
5731
- this._bb = new Bitbucket({
5732
- auth: { token: this.platformConfig.bbToken || "" }
5733
- });
5734
- }
5735
- return this._bb;
5736
- }
5737
- async branchExists({ branch }) {
5738
- return await this.bb.repositories.getBranch({
5739
- workspace: this.platformConfig.repositoryOwner,
5740
- repo_slug: this.platformConfig.repositoryName,
5741
- name: branch
5742
- }).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
5743
- }
5744
- async getOpenPullRequestNumber({ branch }) {
5745
- return await this.bb.repositories.listPullRequests({
5746
- workspace: this.platformConfig.repositoryOwner,
5747
- repo_slug: this.platformConfig.repositoryName,
5748
- state: "OPEN"
5749
- }).then(({ data: { values } }) => {
5750
- return _optionalChain([values, 'optionalAccess', _190 => _190.find, 'call', _191 => _191(
5751
- ({ source, destination }) => _optionalChain([source, 'optionalAccess', _192 => _192.branch, 'optionalAccess', _193 => _193.name]) === branch && _optionalChain([destination, 'optionalAccess', _194 => _194.branch, 'optionalAccess', _195 => _195.name]) === this.platformConfig.baseBranchName
5752
- )]);
5753
- }).then((pr) => _optionalChain([pr, 'optionalAccess', _196 => _196.id]));
5604
+ // src/cli/localizer/index.ts
5605
+ function createLocalizer(provider, apiKey) {
5606
+ if (!provider) {
5607
+ return createLingoDotDevLocalizer(apiKey);
5608
+ } else {
5609
+ return createExplicitLocalizer(provider);
5754
5610
  }
5755
- async closePullRequest({ pullRequestNumber }) {
5756
- await this.bb.repositories.declinePullRequest({
5757
- workspace: this.platformConfig.repositoryOwner,
5758
- repo_slug: this.platformConfig.repositoryName,
5759
- pull_request_id: pullRequestNumber
5760
- });
5761
- }
5762
- async createPullRequest({
5763
- title,
5764
- body,
5765
- head
5766
- }) {
5767
- return await this.bb.repositories.createPullRequest({
5768
- workspace: this.platformConfig.repositoryOwner,
5769
- repo_slug: this.platformConfig.repositoryName,
5770
- _body: {
5771
- title,
5772
- description: body,
5773
- source: { branch: { name: head } },
5774
- destination: { branch: { name: this.platformConfig.baseBranchName } }
5775
- }
5776
- }).then(({ data }) => _nullishCoalesce(data.id, () => ( 0)));
5777
- }
5778
- async commentOnPullRequest({
5779
- pullRequestNumber,
5780
- body
5781
- }) {
5782
- await this.bb.repositories.createPullRequestComment({
5783
- workspace: this.platformConfig.repositoryOwner,
5784
- repo_slug: this.platformConfig.repositoryName,
5785
- pull_request_id: pullRequestNumber,
5786
- _body: {
5787
- content: {
5788
- raw: body
5611
+ }
5612
+
5613
+ // src/cli/cmd/run/setup.ts
5614
+ async function setup(input2) {
5615
+ console.log(_chalk2.default.hex(colors.orange)("[Setup]"));
5616
+ return new (0, _listr2.Listr)(
5617
+ [
5618
+ {
5619
+ title: "Setting up the environment",
5620
+ task: async (ctx, task) => {
5621
+ task.title = `Environment setup completed`;
5789
5622
  }
5790
- }
5791
- });
5792
- }
5793
- async gitConfig() {
5794
- _child_process.execSync.call(void 0, "git config --unset http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy", {
5795
- stdio: "inherit"
5796
- });
5797
- _child_process.execSync.call(void 0,
5798
- "git config http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy http://host.docker.internal:29418/",
5623
+ },
5799
5624
  {
5800
- stdio: "inherit"
5625
+ title: "Loading i18n configuration",
5626
+ task: async (ctx, task) => {
5627
+ ctx.config = getConfig(true);
5628
+ if (!ctx.config) {
5629
+ throw new Error(
5630
+ "i18n.json not found. Please run `lingo.dev init` to initialize the project."
5631
+ );
5632
+ } else if (!ctx.config.buckets || !Object.keys(ctx.config.buckets).length) {
5633
+ throw new Error(
5634
+ "No buckets found in i18n.json. Please add at least one bucket containing i18n content."
5635
+ );
5636
+ } else if (_optionalChain([ctx, 'access', _187 => _187.flags, 'access', _188 => _188.bucket, 'optionalAccess', _189 => _189.some, 'call', _190 => _190(
5637
+ (bucket) => !_optionalChain([ctx, 'access', _191 => _191.config, 'optionalAccess', _192 => _192.buckets, 'access', _193 => _193[bucket]])
5638
+ )])) {
5639
+ throw new Error(
5640
+ `One or more specified buckets do not exist in i18n.json. Please add them to the list first and try again.`
5641
+ );
5642
+ }
5643
+ task.title = `Loaded i18n configuration`;
5644
+ }
5645
+ },
5646
+ {
5647
+ title: "Selecting localization provider",
5648
+ task: async (ctx, task) => {
5649
+ ctx.localizer = createLocalizer(
5650
+ _optionalChain([ctx, 'access', _194 => _194.config, 'optionalAccess', _195 => _195.provider]),
5651
+ ctx.flags.apiKey
5652
+ );
5653
+ if (!ctx.localizer) {
5654
+ throw new Error(
5655
+ "Could not create localization provider. Please check your i18n.json configuration."
5656
+ );
5657
+ }
5658
+ task.title = ctx.localizer.id === "Lingo.dev" ? `Using ${_chalk2.default.hex(colors.green)(ctx.localizer.id)} provider` : `Using raw ${_chalk2.default.hex(colors.yellow)(ctx.localizer.id)} API`;
5659
+ }
5660
+ },
5661
+ {
5662
+ title: "Checking authentication",
5663
+ task: async (ctx, task) => {
5664
+ const authStatus = await ctx.localizer.checkAuth();
5665
+ if (!authStatus.authenticated) {
5666
+ throw new Error(
5667
+ `Failed to authenticate with ${_chalk2.default.hex(colors.yellow)(ctx.localizer.id)} provider. Please check your API key and try again.`
5668
+ );
5669
+ }
5670
+ task.title = `Authenticated as ${_chalk2.default.hex(colors.yellow)(authStatus.username)}`;
5671
+ }
5672
+ },
5673
+ {
5674
+ title: "Initializing localization provider",
5675
+ async task(ctx, task) {
5676
+ const isLingoDotDev = ctx.localizer.id === "Lingo.dev";
5677
+ const subTasks = isLingoDotDev ? [
5678
+ "Brand voice enabled",
5679
+ "Translation memory connected",
5680
+ "Glossary enabled",
5681
+ "Quality assurance enabled"
5682
+ ].map((title) => ({ title, task: () => {
5683
+ } })) : [
5684
+ "Skipping brand voice",
5685
+ "Skipping glossary",
5686
+ "Skipping translation memory",
5687
+ "Skipping quality assurance"
5688
+ ].map((title) => ({ title, task: () => {
5689
+ }, skip: true }));
5690
+ return task.newListr(subTasks, {
5691
+ concurrent: true,
5692
+ rendererOptions: { collapseSubtasks: false }
5693
+ });
5694
+ }
5801
5695
  }
5802
- );
5803
- }
5804
- get platformConfig() {
5805
- const env = _zod2.default.object({
5806
- BITBUCKET_BRANCH: _zod2.default.string(),
5807
- BITBUCKET_REPO_FULL_NAME: _zod2.default.string(),
5808
- BB_TOKEN: _zod2.default.string().optional()
5809
- }).parse(process.env);
5810
- const [repositoryOwner, repositoryName] = env.BITBUCKET_REPO_FULL_NAME.split("/");
5811
- return {
5812
- baseBranchName: env.BITBUCKET_BRANCH,
5813
- repositoryOwner,
5814
- repositoryName,
5815
- bbToken: env.BB_TOKEN
5816
- };
5817
- }
5818
- buildPullRequestUrl(pullRequestNumber) {
5819
- const { repositoryOwner, repositoryName } = this.platformConfig;
5820
- return `https://bitbucket.org/${repositoryOwner}/${repositoryName}/pull-requests/${pullRequestNumber}`;
5821
- }
5822
- };
5696
+ ],
5697
+ {
5698
+ rendererOptions: commonTaskRendererOptions
5699
+ }
5700
+ ).run(input2);
5701
+ }
5823
5702
 
5824
- // src/cli/cmd/ci/platforms/github.ts
5825
- var _octokit2 = require('octokit');
5703
+ // src/cli/cmd/run/plan.ts
5826
5704
 
5827
- var GitHubPlatformKit = class extends PlatformKit {
5828
-
5829
- get octokit() {
5830
- if (!this._octokit) {
5831
- this._octokit = new (0, _octokit2.Octokit)({ auth: this.platformConfig.ghToken });
5832
- }
5833
- return this._octokit;
5834
- }
5835
- async branchExists({ branch }) {
5836
- return await this.octokit.rest.repos.getBranch({
5837
- branch,
5838
- owner: this.platformConfig.repositoryOwner,
5839
- repo: this.platformConfig.repositoryName
5840
- }).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
5841
- }
5842
- async getOpenPullRequestNumber({ branch }) {
5843
- return await this.octokit.rest.pulls.list({
5844
- head: `${this.platformConfig.repositoryOwner}:${branch}`,
5845
- owner: this.platformConfig.repositoryOwner,
5846
- repo: this.platformConfig.repositoryName,
5847
- base: this.platformConfig.baseBranchName,
5848
- state: "open"
5849
- }).then(({ data }) => data[0]).then((pr) => _optionalChain([pr, 'optionalAccess', _197 => _197.number]));
5850
- }
5851
- async closePullRequest({ pullRequestNumber }) {
5852
- await this.octokit.rest.pulls.update({
5853
- pull_number: pullRequestNumber,
5854
- owner: this.platformConfig.repositoryOwner,
5855
- repo: this.platformConfig.repositoryName,
5856
- state: "closed"
5857
- });
5858
- }
5859
- async createPullRequest({
5860
- head,
5861
- title,
5862
- body
5863
- }) {
5864
- return await this.octokit.rest.pulls.create({
5865
- head,
5866
- title,
5867
- body,
5868
- owner: this.platformConfig.repositoryOwner,
5869
- repo: this.platformConfig.repositoryName,
5870
- base: this.platformConfig.baseBranchName
5871
- }).then(({ data }) => data.number);
5872
- }
5873
- async commentOnPullRequest({
5874
- pullRequestNumber,
5875
- body
5876
- }) {
5877
- await this.octokit.rest.issues.createComment({
5878
- issue_number: pullRequestNumber,
5879
- body,
5880
- owner: this.platformConfig.repositoryOwner,
5881
- repo: this.platformConfig.repositoryName
5882
- });
5883
- }
5884
- async gitConfig() {
5885
- const { ghToken, repositoryOwner, repositoryName } = this.platformConfig;
5886
- const { processOwnCommits } = this.config;
5887
- if (ghToken && processOwnCommits) {
5888
- console.log(
5889
- "Using provided GH_TOKEN. This will trigger your CI/CD pipeline to run again."
5890
- );
5891
- const url = `https://${ghToken}@github.com/${repositoryOwner}/${repositoryName}.git`;
5892
- super.gitConfig(ghToken, url);
5893
- }
5894
- }
5895
- get platformConfig() {
5896
- const env = _zod2.default.object({
5897
- GITHUB_REPOSITORY: _zod2.default.string(),
5898
- GITHUB_REPOSITORY_OWNER: _zod2.default.string(),
5899
- GITHUB_REF_NAME: _zod2.default.string(),
5900
- GITHUB_HEAD_REF: _zod2.default.string(),
5901
- GH_TOKEN: _zod2.default.string().optional()
5902
- }).parse(process.env);
5903
- const baseBranchName = !env.GITHUB_REF_NAME.endsWith("/merge") ? env.GITHUB_REF_NAME : env.GITHUB_HEAD_REF;
5904
- return {
5905
- ghToken: env.GH_TOKEN,
5906
- baseBranchName,
5907
- repositoryOwner: env.GITHUB_REPOSITORY_OWNER,
5908
- repositoryName: env.GITHUB_REPOSITORY.split("/")[1]
5909
- };
5910
- }
5911
- buildPullRequestUrl(pullRequestNumber) {
5912
- const { repositoryOwner, repositoryName } = this.platformConfig;
5913
- return `https://github.com/${repositoryOwner}/${repositoryName}/pull/${pullRequestNumber}`;
5914
- }
5915
- };
5916
5705
 
5917
- // src/cli/cmd/ci/platforms/gitlab.ts
5918
- var _rest = require('@gitbeaker/rest');
5919
5706
 
5920
- var gl = new (0, _rest.Gitlab)({ token: "" });
5921
- var GitlabPlatformKit = class extends PlatformKit {
5922
-
5923
- constructor() {
5924
- super();
5925
- process.chdir(this.platformConfig.projectDir);
5707
+ async function plan(input2) {
5708
+ console.log(_chalk2.default.hex(colors.orange)("[Planning]"));
5709
+ let buckets = getBuckets(input2.config);
5710
+ if (input2.flags.bucket) {
5711
+ buckets = buckets.filter((b) => input2.flags.bucket.includes(b.type));
5926
5712
  }
5927
- get gitlab() {
5928
- if (!this._gitlab) {
5929
- this._gitlab = new (0, _rest.Gitlab)({
5930
- token: this.platformConfig.glToken || ""
5931
- });
5932
- }
5933
- return this._gitlab;
5934
- }
5935
- get platformConfig() {
5936
- const env = _zod2.default.object({
5937
- GL_TOKEN: _zod2.default.string().optional(),
5938
- CI_COMMIT_BRANCH: _zod2.default.string(),
5939
- CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: _zod2.default.string().optional(),
5940
- CI_PROJECT_NAMESPACE: _zod2.default.string(),
5941
- CI_PROJECT_NAME: _zod2.default.string(),
5942
- CI_PROJECT_ID: _zod2.default.string(),
5943
- CI_PROJECT_DIR: _zod2.default.string(),
5944
- CI_REPOSITORY_URL: _zod2.default.string()
5945
- }).parse(process.env);
5946
- const config = {
5947
- glToken: env.GL_TOKEN,
5948
- baseBranchName: _nullishCoalesce(env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME, () => ( env.CI_COMMIT_BRANCH)),
5949
- repositoryOwner: env.CI_PROJECT_NAMESPACE,
5950
- repositoryName: env.CI_PROJECT_NAME,
5951
- gitlabProjectId: env.CI_PROJECT_ID,
5952
- projectDir: env.CI_PROJECT_DIR,
5953
- reporitoryUrl: env.CI_REPOSITORY_URL
5954
- };
5955
- return config;
5956
- }
5957
- async branchExists({ branch }) {
5958
- try {
5959
- await this.gitlab.Branches.show(
5960
- this.platformConfig.gitlabProjectId,
5961
- branch
5962
- );
5963
- return true;
5964
- } catch (e2) {
5965
- return false;
5966
- }
5967
- }
5968
- async getOpenPullRequestNumber({
5969
- branch
5970
- }) {
5971
- const mergeRequests = await this.gitlab.MergeRequests.all({
5972
- projectId: this.platformConfig.gitlabProjectId,
5973
- sourceBranch: branch,
5974
- state: "opened"
5975
- });
5976
- return _optionalChain([mergeRequests, 'access', _198 => _198[0], 'optionalAccess', _199 => _199.iid]);
5713
+ const _sourceLocale = input2.flags.sourceLocale || input2.config.locale.source;
5714
+ if (!_sourceLocale) {
5715
+ throw new Error(
5716
+ `No source locale provided. Use --source-locale to specify the source locale or add it to i18n.json (locale.source)`
5717
+ );
5977
5718
  }
5978
- async closePullRequest({
5979
- pullRequestNumber
5980
- }) {
5981
- await this.gitlab.MergeRequests.edit(
5982
- this.platformConfig.gitlabProjectId,
5983
- pullRequestNumber,
5984
- {
5985
- stateEvent: "close"
5986
- }
5719
+ const _targetLocales = input2.flags.targetLocale || input2.config.locale.targets;
5720
+ if (!_targetLocales.length) {
5721
+ throw new Error(
5722
+ `No target locales provided. Use --target-locale to specify the target locales or add them to i18n.json (locale.targets)`
5987
5723
  );
5988
5724
  }
5989
- async createPullRequest({
5990
- head,
5991
- title,
5992
- body
5993
- }) {
5994
- const mr = await this.gitlab.MergeRequests.create(
5995
- this.platformConfig.gitlabProjectId,
5996
- head,
5997
- this.platformConfig.baseBranchName,
5998
- title,
5725
+ return new (0, _listr2.Listr)(
5726
+ [
5999
5727
  {
6000
- description: body
5728
+ title: "Locating content buckets",
5729
+ task: async (ctx, task) => {
5730
+ const bucketCount = buckets.length;
5731
+ const bucketFilter = input2.flags.bucket ? ` ${_chalk2.default.dim(`(filtered by: ${_chalk2.default.hex(colors.yellow)(input2.flags.bucket.join(", "))})`)}` : "";
5732
+ task.title = `Found ${_chalk2.default.hex(colors.yellow)(bucketCount.toString())} bucket(s)${bucketFilter}`;
5733
+ }
5734
+ },
5735
+ {
5736
+ title: "Detecting locales",
5737
+ task: async (ctx, task) => {
5738
+ task.title = `Found ${_chalk2.default.hex(colors.yellow)(_targetLocales.length.toString())} target locale(s)`;
5739
+ }
5740
+ },
5741
+ {
5742
+ title: "Locating localizable files",
5743
+ task: async (ctx, task) => {
5744
+ const patterns = [];
5745
+ for (const bucket of buckets) {
5746
+ for (const bucketPath of bucket.paths) {
5747
+ if (input2.flags.file) {
5748
+ if (!input2.flags.file.some(
5749
+ (f) => bucketPath.pathPattern.includes(f)
5750
+ )) {
5751
+ continue;
5752
+ }
5753
+ }
5754
+ patterns.push(bucketPath.pathPattern);
5755
+ }
5756
+ }
5757
+ const fileFilter = input2.flags.file ? ` ${_chalk2.default.dim(`(filtered by: ${_chalk2.default.hex(colors.yellow)(input2.flags.file.join(", "))})`)}` : "";
5758
+ task.title = `Found ${_chalk2.default.hex(colors.yellow)(patterns.length.toString())} path pattern(s)${fileFilter}`;
5759
+ }
5760
+ },
5761
+ {
5762
+ title: "Computing translation tasks",
5763
+ task: async (ctx, task) => {
5764
+ for (const bucket of buckets) {
5765
+ for (const bucketPath of bucket.paths) {
5766
+ if (input2.flags.file) {
5767
+ if (!input2.flags.file.some(
5768
+ (f) => bucketPath.pathPattern.includes(f)
5769
+ )) {
5770
+ continue;
5771
+ }
5772
+ }
5773
+ const sourceLocale = __spec.resolveOverriddenLocale.call(void 0,
5774
+ _sourceLocale,
5775
+ bucketPath.delimiter
5776
+ );
5777
+ for (const _targetLocale of _targetLocales) {
5778
+ const targetLocale = __spec.resolveOverriddenLocale.call(void 0,
5779
+ _targetLocale,
5780
+ bucketPath.delimiter
5781
+ );
5782
+ if (sourceLocale === targetLocale) continue;
5783
+ ctx.tasks.push({
5784
+ sourceLocale,
5785
+ targetLocale,
5786
+ bucketType: bucket.type,
5787
+ bucketPathPattern: bucketPath.pathPattern,
5788
+ injectLocale: bucket.injectLocale || [],
5789
+ lockedKeys: bucket.lockedKeys || [],
5790
+ lockedPatterns: bucket.lockedPatterns || []
5791
+ });
5792
+ }
5793
+ }
5794
+ }
5795
+ task.title = `Prepared ${_chalk2.default.hex(colors.green)(ctx.tasks.length.toString())} translation task(s)`;
5796
+ }
6001
5797
  }
6002
- );
6003
- return mr.iid;
6004
- }
6005
- async commentOnPullRequest({
6006
- pullRequestNumber,
6007
- body
6008
- }) {
6009
- await this.gitlab.MergeRequestNotes.create(
6010
- this.platformConfig.gitlabProjectId,
6011
- pullRequestNumber,
6012
- body
6013
- );
6014
- }
6015
- gitConfig() {
6016
- const glToken = this.platformConfig.glToken;
6017
- const url = `https://oauth2:${glToken}@gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}.git`;
6018
- super.gitConfig(glToken, url);
6019
- }
6020
- buildPullRequestUrl(pullRequestNumber) {
6021
- return `https://gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}/-/merge_requests/${pullRequestNumber}`;
6022
- }
6023
- };
6024
-
6025
- // src/cli/cmd/ci/platforms/index.ts
6026
- var getPlatformKit = () => {
6027
- if (process.env.BITBUCKET_PIPELINE_UUID) {
6028
- return new BitbucketPlatformKit();
6029
- }
6030
- if (process.env.GITHUB_ACTION) {
6031
- return new GitHubPlatformKit();
6032
- }
6033
- if (process.env.GITLAB_CI) {
6034
- return new GitlabPlatformKit();
6035
- }
6036
- throw new Error("This platform is not supported");
6037
- };
6038
-
6039
- // src/cli/cmd/ci/index.ts
6040
- var ci_default = new (0, _interactivecommander.Command)().command("ci").description("Run Lingo.dev CI/CD action").helpOption("-h, --help", "Show help").option("--api-key <key>", "API key").option("--pull-request [boolean]", "Create a pull request with the changes").option("--commit-message <message>", "Commit message").option("--pull-request-title <title>", "Pull request title").option("--working-directory <dir>", "Working directory").option(
6041
- "--process-own-commits [boolean]",
6042
- "Process commits made by this action"
6043
- ).action(async (options) => {
6044
- const settings = getSettings(options.apiKey);
6045
- if (!settings.auth.apiKey) {
6046
- console.error("No API key provided");
6047
- return;
6048
- }
6049
- const authenticator = createAuthenticator({
6050
- apiUrl: settings.auth.apiUrl,
6051
- apiKey: settings.auth.apiKey
6052
- });
6053
- const auth = await authenticator.whoami();
6054
- if (!auth) {
6055
- console.error("Not authenticated");
6056
- return;
6057
- }
6058
- const env = {
6059
- LINGODOTDEV_API_KEY: settings.auth.apiKey,
6060
- LINGODOTDEV_PULL_REQUEST: _optionalChain([options, 'access', _200 => _200.pullRequest, 'optionalAccess', _201 => _201.toString, 'call', _202 => _202()]) || "false",
6061
- ...options.commitMessage && {
6062
- LINGODOTDEV_COMMIT_MESSAGE: options.commitMessage
6063
- },
6064
- ...options.pullRequestTitle && {
6065
- LINGODOTDEV_PULL_REQUEST_TITLE: options.pullRequestTitle
6066
- },
6067
- ...options.workingDirectory && {
6068
- LINGODOTDEV_WORKING_DIRECTORY: options.workingDirectory
6069
- },
6070
- ...options.processOwnCommits && {
6071
- LINGODOTDEV_PROCESS_OWN_COMMITS: options.processOwnCommits.toString()
5798
+ ],
5799
+ {
5800
+ rendererOptions: commonTaskRendererOptions
6072
5801
  }
6073
- };
6074
- process.env = { ...process.env, ...env };
6075
- const ora = _ora2.default.call(void 0, );
6076
- const platformKit = getPlatformKit();
6077
- const { isPullRequestMode } = platformKit.config;
6078
- ora.info(`Pull request mode: ${isPullRequestMode ? "on" : "off"}`);
6079
- const flow = isPullRequestMode ? new PullRequestFlow(ora, platformKit) : new InBranchFlow(ora, platformKit);
6080
- const canRun = await _optionalChain([flow, 'access', _203 => _203.preRun, 'optionalCall', _204 => _204()]);
6081
- if (canRun === false) {
6082
- return;
6083
- }
6084
- const hasChanges = await flow.run();
6085
- if (!hasChanges) {
6086
- return;
6087
- }
6088
- await _optionalChain([flow, 'access', _205 => _205.postRun, 'optionalCall', _206 => _206()]);
6089
- });
6090
-
6091
- // src/cli/cmd/status.ts
6092
-
5802
+ ).run(input2);
5803
+ }
6093
5804
 
5805
+ // src/cli/cmd/run/execute.ts
6094
5806
 
6095
5807
 
5808
+ var _plimit = require('p-limit'); var _plimit2 = _interopRequireDefault(_plimit);
6096
5809
 
6097
- var _clitable3 = require('cli-table3'); var _clitable32 = _interopRequireDefault(_clitable3);
6098
- var status_default = new (0, _interactivecommander.Command)().command("status").description("Show the status of the localization process").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
6099
- "--file [files...]",
6100
- "File to process. Process only a specific path, may contain asterisk * to match multiple files."
6101
- ).option("--force", "Ignore lockfile and process all keys, useful for estimating full re-translation").option("--verbose", "Show detailed output including key-level word counts").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").action(async function(options) {
6102
- const ora = _ora2.default.call(void 0, );
6103
- const flags = parseFlags2(options);
6104
- let authId = null;
6105
- try {
6106
- ora.start("Loading configuration...");
6107
- const i18nConfig = getConfig();
6108
- const settings = getSettings(flags.apiKey);
6109
- ora.succeed("Configuration loaded");
6110
- try {
6111
- ora.start("Checking authentication status...");
6112
- const auth = await tryAuthenticate(settings);
6113
- if (auth) {
6114
- authId = auth.id;
6115
- ora.succeed(`Authenticated as ${auth.email}`);
6116
- } else {
6117
- ora.info(
6118
- "Not authenticated. Continuing without authentication. (Run `lingo.dev auth --login` to authenticate)"
6119
- );
6120
- }
6121
- } catch (error) {
6122
- ora.info("Authentication failed. Continuing without authentication.");
6123
- }
6124
- ora.start("Validating localization configuration...");
6125
- validateParams2(i18nConfig, flags);
6126
- ora.succeed("Localization configuration is valid");
6127
- trackEvent(authId || "status", "cmd.status.start", {
6128
- i18nConfig,
6129
- flags
6130
- });
6131
- let buckets = getBuckets(i18nConfig);
6132
- if (_optionalChain([flags, 'access', _207 => _207.bucket, 'optionalAccess', _208 => _208.length])) {
6133
- buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
6134
- }
6135
- ora.succeed("Buckets retrieved");
6136
- if (_optionalChain([flags, 'access', _209 => _209.file, 'optionalAccess', _210 => _210.length])) {
6137
- buckets = buckets.map((bucket) => {
6138
- const paths = bucket.paths.filter((path16) => flags.file.find((file) => _optionalChain([path16, 'access', _211 => _211.pathPattern, 'optionalAccess', _212 => _212.match, 'call', _213 => _213(file)])));
6139
- return { ...bucket, paths };
6140
- }).filter((bucket) => bucket.paths.length > 0);
6141
- if (buckets.length === 0) {
6142
- ora.fail("No buckets found. All buckets were filtered out by --file option.");
6143
- process.exit(1);
6144
- } else {
6145
- ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
6146
- buckets.map((bucket) => {
6147
- ora.info(` ${bucket.type}:`);
6148
- bucket.paths.forEach((path16) => {
6149
- ora.info(` - ${path16.pathPattern}`);
6150
- });
6151
- });
6152
- }
6153
- }
6154
- const targetLocales = _optionalChain([flags, 'access', _214 => _214.locale, 'optionalAccess', _215 => _215.length]) ? flags.locale : i18nConfig.locale.targets;
6155
- let totalSourceKeyCount = 0;
6156
- let uniqueKeysToTranslate = 0;
6157
- let totalExistingTranslations = 0;
6158
- const totalWordCount = /* @__PURE__ */ new Map();
6159
- const languageStats = {};
6160
- for (const locale of targetLocales) {
6161
- languageStats[locale] = {
6162
- complete: 0,
6163
- missing: 0,
6164
- updated: 0,
6165
- words: 0
6166
- };
6167
- totalWordCount.set(locale, 0);
6168
- }
6169
- const fileStats = {};
6170
- for (const bucket of buckets) {
6171
- try {
6172
- console.log();
6173
- ora.info(`Analyzing bucket: ${bucket.type}`);
6174
- for (const bucketPath of bucket.paths) {
6175
- const bucketOra = _ora2.default.call(void 0, { indent: 2 }).info(`Analyzing path: ${bucketPath.pathPattern}`);
6176
- const sourceLocale = __spec.resolveOverriddenLocale.call(void 0, i18nConfig.locale.source, bucketPath.delimiter);
6177
- const bucketLoader = createBucketLoader(
6178
- bucket.type,
6179
- bucketPath.pathPattern,
6180
- {
6181
- isCacheRestore: false,
6182
- defaultLocale: sourceLocale,
6183
- injectLocale: bucket.injectLocale
6184
- },
6185
- bucket.lockedKeys
6186
- );
6187
- bucketLoader.setDefaultLocale(sourceLocale);
6188
- await bucketLoader.init();
6189
- const filePath = bucketPath.pathPattern;
6190
- if (!fileStats[filePath]) {
6191
- fileStats[filePath] = {
6192
- path: filePath,
6193
- sourceKeys: 0,
6194
- wordCount: 0,
6195
- languageStats: {}
6196
- };
6197
- for (const locale of targetLocales) {
6198
- fileStats[filePath].languageStats[locale] = {
6199
- complete: 0,
6200
- missing: 0,
6201
- updated: 0,
6202
- words: 0
6203
- };
6204
- }
5810
+ var MAX_WORKER_COUNT = 10;
5811
+ async function execute(input2) {
5812
+ const effectiveConcurrency = Math.min(
5813
+ input2.flags.concurrency,
5814
+ input2.tasks.length,
5815
+ MAX_WORKER_COUNT
5816
+ );
5817
+ console.log(_chalk2.default.hex(colors.orange)(`[Localization]`));
5818
+ return new (0, _listr2.Listr)(
5819
+ [
5820
+ {
5821
+ title: "Initializing localization engine",
5822
+ task: async (ctx, task) => {
5823
+ task.title = `Localization engine ${_chalk2.default.hex(colors.green)("ready")} (${ctx.localizer.id})`;
5824
+ }
5825
+ },
5826
+ {
5827
+ title: `Processing localization tasks ${_chalk2.default.dim(`(tasks: ${input2.tasks.length}, concurrency: ${effectiveConcurrency})`)}`,
5828
+ task: (ctx, task) => {
5829
+ if (input2.tasks.length < 1) {
5830
+ task.title = `Skipping, nothing to localize.`;
5831
+ task.skip();
5832
+ return;
6205
5833
  }
6206
- const sourceData = await bucketLoader.pull(sourceLocale);
6207
- const sourceKeys = Object.keys(sourceData);
6208
- fileStats[filePath].sourceKeys = sourceKeys.length;
6209
- totalSourceKeyCount += sourceKeys.length;
6210
- let sourceWordCount = 0;
6211
- for (const key of sourceKeys) {
6212
- const value = sourceData[key];
6213
- if (typeof value === "string") {
6214
- const words = value.trim().split(/\s+/).length;
6215
- sourceWordCount += words;
6216
- }
5834
+ const i18nLimiter = _plimit2.default.call(void 0, effectiveConcurrency);
5835
+ const ioLimiter = _plimit2.default.call(void 0, 1);
5836
+ const workersCount = effectiveConcurrency;
5837
+ const workerTasks = [];
5838
+ for (let i = 0; i < workersCount; i++) {
5839
+ const assignedTasks = ctx.tasks.filter(
5840
+ (_33, idx) => idx % workersCount === i
5841
+ );
5842
+ workerTasks.push(
5843
+ createWorkerTask({
5844
+ ctx,
5845
+ assignedTasks,
5846
+ ioLimiter,
5847
+ i18nLimiter,
5848
+ onDone() {
5849
+ task.title = createExecutionProgressMessage(ctx);
5850
+ }
5851
+ })
5852
+ );
6217
5853
  }
6218
- fileStats[filePath].wordCount = sourceWordCount;
6219
- for (const _targetLocale of targetLocales) {
6220
- const targetLocale = __spec.resolveOverriddenLocale.call(void 0, _targetLocale, bucketPath.delimiter);
6221
- bucketOra.start(`[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`);
6222
- let targetData = {};
6223
- let fileExists = true;
6224
- try {
6225
- targetData = await bucketLoader.pull(targetLocale);
6226
- } catch (error) {
6227
- fileExists = false;
6228
- bucketOra.info(
6229
- `[${sourceLocale} -> ${targetLocale}] Target file not found, assuming all keys need translation.`
6230
- );
6231
- }
6232
- if (!fileExists) {
6233
- fileStats[filePath].languageStats[targetLocale].missing = sourceKeys.length;
6234
- fileStats[filePath].languageStats[targetLocale].words = sourceWordCount;
6235
- languageStats[targetLocale].missing += sourceKeys.length;
6236
- languageStats[targetLocale].words += sourceWordCount;
6237
- totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + sourceWordCount);
6238
- bucketOra.succeed(
6239
- `[${sourceLocale} -> ${targetLocale}] ${_chalk2.default.red(`0% complete`)} (0/${sourceKeys.length} keys) - file not found`
6240
- );
6241
- continue;
5854
+ return task.newListr(workerTasks, {
5855
+ concurrent: true,
5856
+ exitOnError: false,
5857
+ rendererOptions: {
5858
+ ...commonTaskRendererOptions,
5859
+ collapseSubtasks: true
6242
5860
  }
6243
- const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
5861
+ });
5862
+ }
5863
+ }
5864
+ ],
5865
+ {
5866
+ exitOnError: false,
5867
+ rendererOptions: commonTaskRendererOptions
5868
+ }
5869
+ ).run(input2);
5870
+ }
5871
+ function createWorkerStatusMessage(args) {
5872
+ const displayPath = args.assignedTask.bucketPathPattern.replace(
5873
+ "[locale]",
5874
+ args.assignedTask.targetLocale
5875
+ );
5876
+ return `[${_chalk2.default.hex(colors.yellow)(`${args.percentage}%`)}] Processing: ${_chalk2.default.dim(
5877
+ displayPath
5878
+ )} (${_chalk2.default.hex(colors.yellow)(args.assignedTask.sourceLocale)} -> ${_chalk2.default.hex(
5879
+ colors.yellow
5880
+ )(args.assignedTask.targetLocale)})`;
5881
+ }
5882
+ function createExecutionProgressMessage(ctx) {
5883
+ const succeededTasksCount = countTasks(
5884
+ ctx,
5885
+ (_t, result) => result.status === "success"
5886
+ );
5887
+ const failedTasksCount = countTasks(
5888
+ ctx,
5889
+ (_t, result) => result.status === "error"
5890
+ );
5891
+ const skippedTasksCount = countTasks(
5892
+ ctx,
5893
+ (_t, result) => result.status === "skipped"
5894
+ );
5895
+ return `Processed ${_chalk2.default.green(succeededTasksCount)}/${ctx.tasks.length}, Failed ${_chalk2.default.red(failedTasksCount)}, Skipped ${_chalk2.default.dim(skippedTasksCount)}`;
5896
+ }
5897
+ function createLoaderForTask(assignedTask) {
5898
+ const bucketLoader = createBucketLoader(
5899
+ assignedTask.bucketType,
5900
+ assignedTask.bucketPathPattern,
5901
+ {
5902
+ defaultLocale: assignedTask.sourceLocale,
5903
+ isCacheRestore: false,
5904
+ injectLocale: assignedTask.injectLocale
5905
+ },
5906
+ assignedTask.lockedKeys,
5907
+ assignedTask.lockedPatterns
5908
+ );
5909
+ bucketLoader.setDefaultLocale(assignedTask.sourceLocale);
5910
+ return bucketLoader;
5911
+ }
5912
+ function createWorkerTask(args) {
5913
+ return {
5914
+ title: "Initializing...",
5915
+ task: async (_subCtx, subTask) => {
5916
+ for (const assignedTask of args.assignedTasks) {
5917
+ subTask.title = createWorkerStatusMessage({
5918
+ assignedTask,
5919
+ percentage: 0
5920
+ });
5921
+ const bucketLoader = createLoaderForTask(assignedTask);
5922
+ const deltaProcessor = createDeltaProcessor(
5923
+ assignedTask.bucketPathPattern
5924
+ );
5925
+ const taskResult = await args.i18nLimiter(async () => {
5926
+ try {
5927
+ const sourceData = await bucketLoader.pull(
5928
+ assignedTask.sourceLocale
5929
+ );
5930
+ const targetData = await bucketLoader.pull(
5931
+ assignedTask.targetLocale
5932
+ );
6244
5933
  const checksums = await deltaProcessor.loadChecksums();
6245
5934
  const delta = await deltaProcessor.calculateDelta({
6246
5935
  sourceData,
6247
5936
  targetData,
6248
5937
  checksums
6249
5938
  });
6250
- const missingKeys = delta.added;
6251
- const updatedKeys = delta.updated;
6252
- const completeKeys = sourceKeys.filter((key) => !missingKeys.includes(key) && !updatedKeys.includes(key));
6253
- let wordsToTranslate = 0;
6254
- const keysToProcess = flags.force ? sourceKeys : [...missingKeys, ...updatedKeys];
6255
- for (const key of keysToProcess) {
6256
- const value = sourceData[String(key)];
6257
- if (typeof value === "string") {
6258
- const words = value.trim().split(/\s+/).length;
6259
- wordsToTranslate += words;
6260
- }
5939
+ const processableData = _lodash2.default.chain(sourceData).entries().filter(
5940
+ ([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!args.ctx.flags.force
5941
+ ).fromPairs().value();
5942
+ if (!Object.keys(processableData).length) {
5943
+ return { status: "skipped" };
6261
5944
  }
6262
- fileStats[filePath].languageStats[targetLocale].missing = missingKeys.length;
6263
- fileStats[filePath].languageStats[targetLocale].updated = updatedKeys.length;
6264
- fileStats[filePath].languageStats[targetLocale].complete = completeKeys.length;
6265
- fileStats[filePath].languageStats[targetLocale].words = wordsToTranslate;
6266
- languageStats[targetLocale].missing += missingKeys.length;
6267
- languageStats[targetLocale].updated += updatedKeys.length;
6268
- languageStats[targetLocale].complete += completeKeys.length;
6269
- languageStats[targetLocale].words += wordsToTranslate;
6270
- totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + wordsToTranslate);
6271
- const totalKeysInFile = sourceKeys.length;
6272
- const completionPercent = (completeKeys.length / totalKeysInFile * 100).toFixed(1);
6273
- if (missingKeys.length === 0 && updatedKeys.length === 0) {
6274
- bucketOra.succeed(
6275
- `[${sourceLocale} -> ${targetLocale}] ${_chalk2.default.green(`100% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`
5945
+ const processedTargetData = await args.ctx.localizer.localize(
5946
+ {
5947
+ sourceLocale: assignedTask.sourceLocale,
5948
+ targetLocale: assignedTask.targetLocale,
5949
+ sourceData,
5950
+ targetData,
5951
+ processableData
5952
+ },
5953
+ (progress) => {
5954
+ subTask.title = createWorkerStatusMessage({
5955
+ assignedTask,
5956
+ percentage: progress
5957
+ });
5958
+ }
5959
+ );
5960
+ let finalTargetData = _lodash2.default.merge(
5961
+ {},
5962
+ sourceData,
5963
+ targetData,
5964
+ processedTargetData
5965
+ );
5966
+ finalTargetData = _lodash2.default.chain(finalTargetData).entries().map(([key, value]) => {
5967
+ const renaming = delta.renamed.find(
5968
+ ([oldKey]) => oldKey === key
6276
5969
  );
6277
- } else {
6278
- const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? _chalk2.default.yellow(`${completionPercent}% complete`) : _chalk2.default.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
6279
- bucketOra.succeed(message);
6280
- if (flags.verbose) {
6281
- if (missingKeys.length > 0) {
6282
- console.log(` ${_chalk2.default.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`);
6283
- console.log(
6284
- ` ${_chalk2.default.dim(`Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`)}`
6285
- );
6286
- }
6287
- if (updatedKeys.length > 0) {
6288
- console.log(` ${_chalk2.default.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`);
6289
- }
5970
+ if (!renaming) {
5971
+ return [key, value];
6290
5972
  }
6291
- }
5973
+ return [renaming[1], value];
5974
+ }).fromPairs().value();
5975
+ await args.ioLimiter(async () => {
5976
+ await bucketLoader.pull(assignedTask.sourceLocale);
5977
+ await bucketLoader.push(
5978
+ assignedTask.targetLocale,
5979
+ finalTargetData
5980
+ );
5981
+ const checksums2 = await deltaProcessor.createChecksums(sourceData);
5982
+ await deltaProcessor.saveChecksums(checksums2);
5983
+ });
5984
+ return { status: "success" };
5985
+ } catch (error) {
5986
+ return {
5987
+ status: "error",
5988
+ error
5989
+ };
6292
5990
  }
6293
- }
6294
- } catch (error) {
6295
- ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);
5991
+ });
5992
+ args.ctx.results.set(assignedTask, taskResult);
6296
5993
  }
5994
+ subTask.title = "Done";
6297
5995
  }
6298
- const totalKeysNeedingTranslation = Object.values(languageStats).reduce((sum, stats) => {
6299
- return sum + stats.missing + stats.updated;
6300
- }, 0);
6301
- const totalCompletedKeys = totalSourceKeyCount - totalKeysNeedingTranslation / targetLocales.length;
6302
- console.log();
6303
- ora.succeed(_chalk2.default.green(`Localization status completed.`));
6304
- console.log(_chalk2.default.bold.cyan(`
6305
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
6306
- console.log(_chalk2.default.bold.cyan(`\u2551 LOCALIZATION STATUS REPORT \u2551`));
6307
- console.log(_chalk2.default.bold.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
6308
- console.log(_chalk2.default.bold(`
6309
- \u{1F4DD} SOURCE CONTENT:`));
6310
- console.log(`\u2022 Source language: ${_chalk2.default.green(i18nConfig.locale.source)}`);
6311
- console.log(`\u2022 Source keys: ${_chalk2.default.yellow(totalSourceKeyCount.toString())} keys across all files`);
6312
- console.log(_chalk2.default.bold(`
6313
- \u{1F310} LANGUAGE BY LANGUAGE BREAKDOWN:`));
6314
- const table = new (0, _clitable32.default)({
6315
- head: ["Language", "Status", "Complete", "Missing", "Updated", "Total Keys", "Words to Translate"],
6316
- style: {
6317
- head: ["white"],
6318
- // White color for headers
6319
- border: []
6320
- // No color for borders
6321
- },
6322
- colWidths: [12, 20, 18, 12, 12, 12, 15]
6323
- // Explicit column widths, making Status column wider
5996
+ };
5997
+ }
5998
+ function countTasks(ctx, predicate) {
5999
+ return Array.from(ctx.results.entries()).filter(
6000
+ ([task, result]) => predicate(task, result)
6001
+ ).length;
6002
+ }
6003
+
6004
+ // src/cli/cmd/run/_types.ts
6005
+
6006
+
6007
+
6008
+
6009
+ var flagsSchema2 = _zod.z.object({
6010
+ bucket: _zod.z.array(__spec.bucketTypeSchema).optional(),
6011
+ key: _zod.z.array(_zod.z.string()).optional(),
6012
+ file: _zod.z.array(_zod.z.string()).optional(),
6013
+ apiKey: _zod.z.string().optional(),
6014
+ force: _zod.z.boolean().optional(),
6015
+ frozen: _zod.z.boolean().optional(),
6016
+ verbose: _zod.z.boolean().optional(),
6017
+ strict: _zod.z.boolean().optional(),
6018
+ interactive: _zod.z.boolean().default(false),
6019
+ concurrency: _zod.z.number().positive().default(10),
6020
+ debug: _zod.z.boolean().default(false),
6021
+ sourceLocale: _zod.z.string().optional(),
6022
+ targetLocale: _zod.z.array(_zod.z.string()).optional()
6023
+ });
6024
+
6025
+ // src/cli/cmd/run/_render.ts
6026
+
6027
+
6028
+
6029
+ var _readline = require('readline'); var _readline2 = _interopRequireDefault(_readline);
6030
+ async function renderClear() {
6031
+ console.log("\x1Bc");
6032
+ }
6033
+ async function renderSpacer() {
6034
+ console.log(" ");
6035
+ }
6036
+ async function renderBanner() {
6037
+ console.log(
6038
+ _gradientstring.vice.call(void 0,
6039
+ _figlet2.default.textSync("LINGO.DEV", {
6040
+ font: "ANSI Shadow",
6041
+ horizontalLayout: "default",
6042
+ verticalLayout: "default"
6043
+ })
6044
+ )
6045
+ );
6046
+ }
6047
+ async function renderHero() {
6048
+ console.log(
6049
+ `\u26A1\uFE0F ${_chalk2.default.hex(colors.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
6050
+ );
6051
+ console.log("");
6052
+ const label1 = "\u2B50 GitHub Repo:";
6053
+ const label2 = "\u{1F4DA} Docs:";
6054
+ const label3 = "\u{1F4AC} 24/7 Support:";
6055
+ const maxLabelWidth = 17;
6056
+ console.log(
6057
+ `${_chalk2.default.hex(colors.blue)(label1.padEnd(maxLabelWidth))} ${_chalk2.default.hex(colors.blue)("https://lingo.dev/go/gh")}`
6058
+ );
6059
+ console.log(
6060
+ `${_chalk2.default.hex(colors.blue)(label2.padEnd(maxLabelWidth + 1))} ${_chalk2.default.hex(colors.blue)("https://lingo.dev/go/docs")}`
6061
+ );
6062
+ console.log(
6063
+ `${_chalk2.default.hex(colors.blue)(label3.padEnd(maxLabelWidth + 1))} ${_chalk2.default.hex(colors.blue)("hi@lingo.dev")}`
6064
+ );
6065
+ }
6066
+ async function pauseIfDebug(debug) {
6067
+ if (debug) {
6068
+ await waitForUserPrompt("Press Enter to continue...");
6069
+ }
6070
+ }
6071
+ async function waitForUserPrompt(message) {
6072
+ const rl = _readline2.default.createInterface({
6073
+ input: process.stdin,
6074
+ output: process.stdout
6075
+ });
6076
+ return new Promise((resolve) => {
6077
+ rl.question(_chalk2.default.dim(`[${message}]
6078
+ `), () => {
6079
+ rl.close();
6080
+ resolve();
6324
6081
  });
6325
- let totalWordsToTranslate = 0;
6326
- for (const locale of targetLocales) {
6327
- const stats = languageStats[locale];
6328
- const percentComplete = (stats.complete / totalSourceKeyCount * 100).toFixed(1);
6329
- const totalNeeded = stats.missing + stats.updated;
6330
- let statusText;
6331
- let statusColor;
6332
- if (stats.missing === totalSourceKeyCount) {
6333
- statusText = "\u{1F534} Not started";
6334
- statusColor = _chalk2.default.red;
6335
- } else if (stats.missing === 0 && stats.updated === 0) {
6336
- statusText = "\u2705 Complete";
6337
- statusColor = _chalk2.default.green;
6338
- } else if (parseFloat(percentComplete) > 80) {
6339
- statusText = "\u{1F7E1} Almost done";
6340
- statusColor = _chalk2.default.yellow;
6341
- } else if (parseFloat(percentComplete) > 0) {
6342
- statusText = "\u{1F7E0} In progress";
6343
- statusColor = _chalk2.default.yellow;
6344
- } else {
6345
- statusText = "\u{1F534} Not started";
6346
- statusColor = _chalk2.default.red;
6347
- }
6348
- const words = totalWordCount.get(locale) || 0;
6349
- totalWordsToTranslate += words;
6350
- table.push([
6351
- locale,
6352
- statusColor(statusText),
6353
- `${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,
6354
- stats.missing > 0 ? _chalk2.default.red(stats.missing.toString()) : "0",
6355
- stats.updated > 0 ? _chalk2.default.yellow(stats.updated.toString()) : "0",
6356
- totalNeeded > 0 ? _chalk2.default.magenta(totalNeeded.toString()) : "0",
6357
- words > 0 ? `~${words.toLocaleString()}` : "0"
6358
- ]);
6082
+ });
6083
+ }
6084
+ async function renderSummary(ctx) {
6085
+ console.log(_chalk2.default.hex(colors.green)("[Done]"));
6086
+ const skippedTasksCount = Array.from(ctx.results.values()).filter(
6087
+ (r) => r.status === "skipped"
6088
+ ).length;
6089
+ console.log(`\u2022 ${_chalk2.default.hex(colors.yellow)(skippedTasksCount)} from cache`);
6090
+ const succeededTasksCount = Array.from(ctx.results.values()).filter(
6091
+ (r) => r.status === "success"
6092
+ ).length;
6093
+ console.log(`\u2022 ${_chalk2.default.hex(colors.yellow)(succeededTasksCount)} processed`);
6094
+ const failedTasksCount = Array.from(ctx.results.values()).filter(
6095
+ (r) => r.status === "error"
6096
+ ).length;
6097
+ console.log(`\u2022 ${_chalk2.default.hex(colors.yellow)(failedTasksCount)} failed`);
6098
+ }
6099
+
6100
+ // src/cli/cmd/run/index.ts
6101
+
6102
+ var run_default = new (0, _interactivecommander.Command)().command("run").description("Run Lingo.dev localization engine").helpOption("-h, --help", "Show help").option(
6103
+ "--source-locale <source-locale>",
6104
+ "Locale to use as source locale. Defaults to i18n.json locale.source",
6105
+ (val, prev) => {
6106
+ if (!val) return prev;
6107
+ if (!process.argv.includes("--target-locale")) {
6108
+ console.error(
6109
+ `
6110
+ \u274C ${_chalk2.default.red("Error")}: --source-locale must be used together with --target-locale
6111
+ `
6112
+ );
6113
+ process.exit(1);
6359
6114
  }
6360
- console.log(table.toString());
6361
- console.log(_chalk2.default.bold(`
6362
- \u{1F4CA} USAGE ESTIMATE:`));
6363
- console.log(
6364
- `\u2022 WORDS TO BE CONSUMED: ~${_chalk2.default.yellow.bold(totalWordsToTranslate.toLocaleString())} words across all languages`
6365
- );
6366
- console.log(` (Words are counted from source language for keys that need translation in target languages)`);
6367
- if (targetLocales.length > 1) {
6368
- console.log(`\u2022 Per-language breakdown:`);
6369
- for (const locale of targetLocales) {
6370
- const words = totalWordCount.get(locale) || 0;
6371
- const percent = (words / totalWordsToTranslate * 100).toFixed(1);
6372
- console.log(` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`);
6373
- }
6115
+ return val;
6116
+ }
6117
+ ).option(
6118
+ "--target-locale <target-locale>",
6119
+ "Locale to use as target locale. Defaults to i18n.json locale.targets",
6120
+ (val, prev) => {
6121
+ if (!val) return prev;
6122
+ if (!process.argv.includes("--source-locale")) {
6123
+ console.error(
6124
+ `
6125
+ \u274C ${_chalk2.default.red("Error")}: --target-locale must be used together with --source-locale
6126
+ `
6127
+ );
6128
+ process.exit(1);
6374
6129
  }
6375
- if (flags.confirm && Object.keys(fileStats).length > 0) {
6376
- console.log(_chalk2.default.bold(`
6377
- \u{1F4D1} BREAKDOWN BY FILE:`));
6378
- Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path16, stats]) => {
6379
- if (stats.sourceKeys === 0) return;
6380
- console.log(_chalk2.default.bold(`
6381
- \u2022 ${path16}:`));
6382
- console.log(` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`);
6383
- const fileTable = new (0, _clitable32.default)({
6384
- head: ["Language", "Status", "Details"],
6385
- style: {
6386
- head: ["white"],
6387
- border: []
6388
- },
6389
- colWidths: [12, 20, 50]
6390
- // Explicit column widths for file detail table
6391
- });
6392
- for (const locale of targetLocales) {
6393
- const langStats = stats.languageStats[locale];
6394
- const complete = langStats.complete;
6395
- const total = stats.sourceKeys;
6396
- const completion = (complete / total * 100).toFixed(1);
6397
- let status = "\u2705 Complete";
6398
- let statusColor = _chalk2.default.green;
6399
- if (langStats.missing === total) {
6400
- status = "\u274C Not started";
6401
- statusColor = _chalk2.default.red;
6402
- } else if (langStats.missing > 0 || langStats.updated > 0) {
6403
- status = `\u26A0\uFE0F ${completion}% complete`;
6404
- statusColor = _chalk2.default.yellow;
6405
- }
6406
- let details = "";
6407
- if (langStats.missing > 0 || langStats.updated > 0) {
6408
- const parts = [];
6409
- if (langStats.missing > 0) parts.push(`${langStats.missing} missing`);
6410
- if (langStats.updated > 0) parts.push(`${langStats.updated} changed`);
6411
- details = `${parts.join(", ")}, ~${langStats.words} words`;
6412
- } else {
6413
- details = "All keys translated";
6414
- }
6415
- fileTable.push([locale, statusColor(status), details]);
6130
+ return prev ? [...prev, val] : [val];
6131
+ }
6132
+ ).option(
6133
+ "--bucket <bucket>",
6134
+ "Bucket to process",
6135
+ (val, prev) => prev ? [...prev, val] : [val]
6136
+ ).option(
6137
+ "--file <file>",
6138
+ "File to process. Process only files that include this string in their path. Useful if you have a lot of files and want to focus on a specific one. Specify more files separated by commas or spaces.",
6139
+ (val, prev) => prev ? [...prev, val] : [val]
6140
+ ).option(
6141
+ "--key <key>",
6142
+ "Key to process. Process only a specific translation key, useful for updating a single entry",
6143
+ (val, prev) => prev ? [...prev, val] : [val]
6144
+ ).option(
6145
+ "--force",
6146
+ "Ignore lockfile and process all keys, useful for full re-translation"
6147
+ ).option(
6148
+ "--api-key <api-key>",
6149
+ "Explicitly set the API key to use, override the default API key from settings"
6150
+ ).option(
6151
+ "--debug",
6152
+ "Pause execution at start for debugging purposes, waits for user confirmation before proceeding"
6153
+ ).option(
6154
+ "--concurrency <concurrency>",
6155
+ "Number of concurrent tasks to run",
6156
+ (val) => parseInt(val)
6157
+ ).action(async (args) => {
6158
+ try {
6159
+ const ctx = {
6160
+ flags: flagsSchema2.parse(args),
6161
+ config: null,
6162
+ results: /* @__PURE__ */ new Map(),
6163
+ tasks: [],
6164
+ localizer: null
6165
+ };
6166
+ await pauseIfDebug(ctx.flags.debug);
6167
+ await renderClear();
6168
+ await renderSpacer();
6169
+ await renderBanner();
6170
+ await renderHero();
6171
+ await renderSpacer();
6172
+ await setup(ctx);
6173
+ await renderSpacer();
6174
+ await plan(ctx);
6175
+ await renderSpacer();
6176
+ await execute(ctx);
6177
+ await renderSpacer();
6178
+ await renderSummary(ctx);
6179
+ await renderSpacer();
6180
+ } catch (error) {
6181
+ process.exit(1);
6182
+ }
6183
+ });
6184
+
6185
+ // src/cli/cmd/ci/flows/in-branch.ts
6186
+ var InBranchFlow = class extends IntegrationFlow {
6187
+ async preRun() {
6188
+ this.ora.start("Configuring git");
6189
+ const canContinue = this.configureGit();
6190
+ this.ora.succeed("Git configured");
6191
+ return canContinue;
6192
+ }
6193
+ async run(options) {
6194
+ this.ora.start("Running Lingo.dev");
6195
+ await this.runLingoDotDev(options.parallel);
6196
+ this.ora.succeed("Done running Lingo.dev");
6197
+ _child_process.execSync.call(void 0, `rm -f i18n.cache`, { stdio: "inherit" });
6198
+ this.ora.start("Checking for changes");
6199
+ const hasChanges = this.checkCommitableChanges();
6200
+ this.ora.succeed(hasChanges ? "Changes detected" : "No changes detected");
6201
+ if (hasChanges) {
6202
+ this.ora.start("Committing changes");
6203
+ _child_process.execSync.call(void 0, `git add .`, { stdio: "inherit" });
6204
+ _child_process.execSync.call(void 0, `git status --porcelain`, { stdio: "inherit" });
6205
+ _child_process.execSync.call(void 0,
6206
+ `git commit -m ${escapeShellArg(this.platformKit.config.commitMessage)} --no-verify`,
6207
+ {
6208
+ stdio: "inherit"
6416
6209
  }
6417
- console.log(fileTable.toString());
6418
- });
6210
+ );
6211
+ this.ora.succeed("Changes committed");
6212
+ this.ora.start("Pushing changes to remote");
6213
+ const currentBranch = _nullishCoalesce(this.i18nBranchName, () => ( this.platformKit.platformConfig.baseBranchName));
6214
+ _child_process.execSync.call(void 0,
6215
+ `git push origin ${currentBranch} ${options.force ? "--force" : ""}`,
6216
+ {
6217
+ stdio: "inherit"
6218
+ }
6219
+ );
6220
+ this.ora.succeed("Changes pushed to remote");
6419
6221
  }
6420
- const completeLanguages = targetLocales.filter(
6421
- (locale) => languageStats[locale].missing === 0 && languageStats[locale].updated === 0
6222
+ return hasChanges;
6223
+ }
6224
+ checkCommitableChanges() {
6225
+ return _child_process.execSync.call(void 0, 'git status --porcelain || echo "has_changes"', {
6226
+ encoding: "utf8"
6227
+ }).length > 0;
6228
+ }
6229
+ async runLingoDotDev(isParallel) {
6230
+ try {
6231
+ if (!isParallel) {
6232
+ await i18n_default.exitOverride().parseAsync(["--api-key", this.platformKit.config.replexicaApiKey], {
6233
+ from: "user"
6234
+ });
6235
+ } else {
6236
+ await run_default.exitOverride().parseAsync(["--api-key", this.platformKit.config.replexicaApiKey], {
6237
+ from: "user"
6238
+ });
6239
+ }
6240
+ } catch (err) {
6241
+ if (err.code === "commander.helpDisplayed") return;
6242
+ throw err;
6243
+ }
6244
+ }
6245
+ configureGit() {
6246
+ const { processOwnCommits } = this.platformKit.config;
6247
+ const { baseBranchName } = this.platformKit.platformConfig;
6248
+ this.ora.info(`Current working directory:`);
6249
+ _child_process.execSync.call(void 0, `pwd`, { stdio: "inherit" });
6250
+ _child_process.execSync.call(void 0, `ls -la`, { stdio: "inherit" });
6251
+ _child_process.execSync.call(void 0, `git config --global safe.directory ${process.cwd()}`);
6252
+ _child_process.execSync.call(void 0, `git config user.name "${gitConfig.userName}"`);
6253
+ _child_process.execSync.call(void 0, `git config user.email "${gitConfig.userEmail}"`);
6254
+ _optionalChain([this, 'access', _196 => _196.platformKit, 'optionalAccess', _197 => _197.gitConfig, 'call', _198 => _198()]);
6255
+ _child_process.execSync.call(void 0, `git fetch origin ${baseBranchName}`, { stdio: "inherit" });
6256
+ _child_process.execSync.call(void 0, `git checkout ${baseBranchName} --`, { stdio: "inherit" });
6257
+ if (!processOwnCommits) {
6258
+ const currentAuthor = `${gitConfig.userName} <${gitConfig.userEmail}>`;
6259
+ const authorOfLastCommit = _child_process.execSync.call(void 0,
6260
+ `git log -1 --pretty=format:'%an <%ae>'`
6261
+ ).toString();
6262
+ if (authorOfLastCommit === currentAuthor) {
6263
+ this.ora.warn(
6264
+ `The last commit was already made by ${currentAuthor}, so this run will be skipped, as running again would have no effect. See docs: https://docs.lingo.dev/ci-action/overview`
6265
+ );
6266
+ return false;
6267
+ }
6268
+ }
6269
+ const workingDir = path12.default.resolve(
6270
+ process.cwd(),
6271
+ this.platformKit.config.workingDir
6422
6272
  );
6423
- const missingLanguages = targetLocales.filter((locale) => languageStats[locale].complete === 0);
6424
- console.log(_chalk2.default.bold.green(`
6425
- \u{1F4A1} OPTIMIZATION TIPS:`));
6426
- if (missingLanguages.length > 0) {
6427
- console.log(
6428
- `\u2022 ${_chalk2.default.yellow(missingLanguages.join(", "))} ${missingLanguages.length === 1 ? "has" : "have"} no translations yet`
6273
+ if (workingDir !== process.cwd()) {
6274
+ this.ora.info(
6275
+ `Changing to working directory: ${this.platformKit.config.workingDir}`
6429
6276
  );
6277
+ process.chdir(workingDir);
6430
6278
  }
6431
- if (completeLanguages.length > 0) {
6432
- console.log(
6433
- `\u2022 ${_chalk2.default.green(completeLanguages.join(", "))} ${completeLanguages.length === 1 ? "is" : "are"} completely translated`
6434
- );
6279
+ return true;
6280
+ }
6281
+ };
6282
+
6283
+ // src/cli/cmd/ci/flows/pull-request.ts
6284
+ var PullRequestFlow = class extends InBranchFlow {
6285
+ async preRun() {
6286
+ const canContinue = await _optionalChain([super.preRun.bind(this), 'optionalCall', _199 => _199()]);
6287
+ if (!canContinue) {
6288
+ return false;
6435
6289
  }
6436
- if (targetLocales.length > 1) {
6437
- console.log(`\u2022 Translating one language at a time reduces complexity`);
6438
- console.log(`\u2022 Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`);
6290
+ this.ora.start("Calculating automated branch name");
6291
+ this.i18nBranchName = this.calculatePrBranchName();
6292
+ this.ora.succeed(
6293
+ `Automated branch name calculated: ${this.i18nBranchName}`
6294
+ );
6295
+ this.ora.start("Checking if branch exists");
6296
+ const branchExists = await this.checkBranchExistance(this.i18nBranchName);
6297
+ this.ora.succeed(branchExists ? "Branch exists" : "Branch does not exist");
6298
+ if (branchExists) {
6299
+ this.ora.start(`Checking out branch ${this.i18nBranchName}`);
6300
+ this.checkoutI18nBranch(this.i18nBranchName);
6301
+ this.ora.succeed(`Checked out branch ${this.i18nBranchName}`);
6302
+ this.ora.start(
6303
+ `Syncing with ${this.platformKit.platformConfig.baseBranchName}`
6304
+ );
6305
+ this.syncI18nBranch();
6306
+ this.ora.succeed(`Checked out and synced branch ${this.i18nBranchName}`);
6307
+ } else {
6308
+ this.ora.start(`Creating branch ${this.i18nBranchName}`);
6309
+ this.createI18nBranch(this.i18nBranchName);
6310
+ this.ora.succeed(`Created branch ${this.i18nBranchName}`);
6439
6311
  }
6440
- trackEvent(authId || "status", "cmd.status.success", {
6441
- i18nConfig,
6442
- flags,
6443
- totalSourceKeyCount,
6444
- languageStats,
6445
- totalWordsToTranslate,
6446
- authenticated: !!authId
6447
- });
6448
- } catch (error) {
6449
- ora.fail(error.message);
6450
- trackEvent(authId || "status", "cmd.status.error", {
6451
- flags,
6452
- error: error.message,
6453
- authenticated: !!authId
6454
- });
6455
- process.exit(1);
6456
- }
6457
- });
6458
- function parseFlags2(options) {
6459
- return _zod2.default.object({
6460
- locale: _zod2.default.array(__spec.localeCodeSchema).optional(),
6461
- bucket: _zod2.default.array(__spec.bucketTypeSchema).optional(),
6462
- force: _zod2.default.boolean().optional(),
6463
- confirm: _zod2.default.boolean().optional(),
6464
- verbose: _zod2.default.boolean().optional(),
6465
- file: _zod2.default.array(_zod2.default.string()).optional(),
6466
- apiKey: _zod2.default.string().optional()
6467
- }).parse(options);
6468
- }
6469
- async function tryAuthenticate(settings) {
6470
- if (!settings.auth.apiKey) {
6471
- return null;
6312
+ return true;
6472
6313
  }
6473
- try {
6474
- const authenticator = createAuthenticator({
6475
- apiKey: settings.auth.apiKey,
6476
- apiUrl: settings.auth.apiUrl
6314
+ async run(options) {
6315
+ return super.run({
6316
+ force: true,
6317
+ ...options
6477
6318
  });
6478
- const user = await authenticator.whoami();
6479
- return user;
6480
- } catch (error) {
6481
- return null;
6482
6319
  }
6483
- }
6484
- function validateParams2(i18nConfig, flags) {
6485
- if (!i18nConfig) {
6486
- throw new CLIError({
6487
- message: "i18n.json not found. Please run `lingo.dev init` to initialize the project.",
6488
- docUrl: "i18nNotFound"
6489
- });
6490
- } else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {
6491
- throw new CLIError({
6492
- message: "No buckets found in i18n.json. Please add at least one bucket containing i18n content.",
6493
- docUrl: "bucketNotFound"
6320
+ async postRun() {
6321
+ if (!this.i18nBranchName) {
6322
+ throw new Error(
6323
+ "i18nBranchName is not set. Did you forget to call preRun?"
6324
+ );
6325
+ }
6326
+ this.ora.start("Checking if PR already exists");
6327
+ const pullRequestNumber = await this.ensureFreshPr(this.i18nBranchName);
6328
+ this.ora.succeed(
6329
+ `Pull request ready: ${this.platformKit.buildPullRequestUrl(pullRequestNumber)}`
6330
+ );
6331
+ }
6332
+ calculatePrBranchName() {
6333
+ return `lingo.dev/${this.platformKit.platformConfig.baseBranchName}`;
6334
+ }
6335
+ async checkBranchExistance(prBranchName) {
6336
+ return this.platformKit.branchExists({
6337
+ branch: prBranchName
6494
6338
  });
6495
- } else if (_optionalChain([flags, 'access', _216 => _216.locale, 'optionalAccess', _217 => _217.some, 'call', _218 => _218((locale) => !i18nConfig.locale.targets.includes(locale))])) {
6496
- throw new CLIError({
6497
- message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
6498
- docUrl: "localeTargetNotFound"
6339
+ }
6340
+ async ensureFreshPr(i18nBranchName) {
6341
+ this.ora.start(
6342
+ `Checking for existing PR with head ${i18nBranchName} and base ${this.platformKit.platformConfig.baseBranchName}`
6343
+ );
6344
+ let prNumber = await this.platformKit.getOpenPullRequestNumber({
6345
+ branch: i18nBranchName
6499
6346
  });
6500
- } else if (_optionalChain([flags, 'access', _219 => _219.bucket, 'optionalAccess', _220 => _220.some, 'call', _221 => _221((bucket) => !i18nConfig.buckets[bucket])])) {
6501
- throw new CLIError({
6502
- message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
6503
- docUrl: "bucketNotFound"
6347
+ if (prNumber) {
6348
+ this.ora.succeed(`Existing PR found: #${prNumber}`);
6349
+ } else {
6350
+ this.ora.start(`Creating new PR`);
6351
+ prNumber = await this.platformKit.createPullRequest({
6352
+ head: i18nBranchName,
6353
+ title: this.platformKit.config.pullRequestTitle,
6354
+ body: this.getPrBodyContent()
6355
+ });
6356
+ this.ora.succeed(`Created new PR: #${prNumber}`);
6357
+ }
6358
+ return prNumber;
6359
+ }
6360
+ checkoutI18nBranch(i18nBranchName) {
6361
+ _child_process.execSync.call(void 0, `git fetch origin ${i18nBranchName}`, { stdio: "inherit" });
6362
+ _child_process.execSync.call(void 0, `git checkout -b ${i18nBranchName}`, {
6363
+ stdio: "inherit"
6504
6364
  });
6505
6365
  }
6506
- }
6366
+ createI18nBranch(i18nBranchName) {
6367
+ try {
6368
+ _child_process.execSync.call(void 0,
6369
+ `git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,
6370
+ { stdio: "inherit" }
6371
+ );
6372
+ _child_process.execSync.call(void 0,
6373
+ `git checkout -b ${i18nBranchName} origin/${this.platformKit.platformConfig.baseBranchName}`,
6374
+ {
6375
+ stdio: "inherit"
6376
+ }
6377
+ );
6378
+ } catch (error) {
6379
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
6380
+ this.ora.fail(`Failed to create branch: ${errorMessage}`);
6381
+ this.ora.info(`
6382
+ Troubleshooting tips:
6383
+ 1. Make sure you have permission to create branches
6384
+ 2. Check if the branch already exists locally (try 'git branch -a')
6385
+ 3. Verify connectivity to remote repository
6386
+ `);
6387
+ throw new Error(`Branch creation failed: ${errorMessage}`);
6388
+ }
6389
+ }
6390
+ syncI18nBranch() {
6391
+ if (!this.i18nBranchName) {
6392
+ throw new Error("i18nBranchName is not set");
6393
+ }
6394
+ this.ora.start(
6395
+ `Fetching latest changes from ${this.platformKit.platformConfig.baseBranchName}`
6396
+ );
6397
+ _child_process.execSync.call(void 0,
6398
+ `git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,
6399
+ { stdio: "inherit" }
6400
+ );
6401
+ this.ora.succeed(
6402
+ `Fetched latest changes from ${this.platformKit.platformConfig.baseBranchName}`
6403
+ );
6404
+ try {
6405
+ this.ora.start("Attempting to rebase branch");
6406
+ _child_process.execSync.call(void 0,
6407
+ `git rebase origin/${this.platformKit.platformConfig.baseBranchName}`,
6408
+ { stdio: "inherit" }
6409
+ );
6410
+ this.ora.succeed("Successfully rebased branch");
6411
+ } catch (error) {
6412
+ this.ora.warn("Rebase failed, falling back to alternative sync method");
6413
+ this.ora.start("Aborting failed rebase");
6414
+ _child_process.execSync.call(void 0, "git rebase --abort", { stdio: "inherit" });
6415
+ this.ora.succeed("Aborted failed rebase");
6416
+ this.ora.start(
6417
+ `Resetting to ${this.platformKit.platformConfig.baseBranchName}`
6418
+ );
6419
+ _child_process.execSync.call(void 0,
6420
+ `git reset --hard origin/${this.platformKit.platformConfig.baseBranchName}`,
6421
+ { stdio: "inherit" }
6422
+ );
6423
+ this.ora.succeed(
6424
+ `Reset to ${this.platformKit.platformConfig.baseBranchName}`
6425
+ );
6426
+ this.ora.start("Restoring target files");
6427
+ const targetFiles = ["i18n.lock"];
6428
+ const targetFileNames = _child_process.execSync.call(void 0,
6429
+ `npx lingo.dev@latest show files --target ${this.platformKit.platformConfig.baseBranchName}`,
6430
+ { encoding: "utf8" }
6431
+ ).split("\n").filter(Boolean);
6432
+ targetFiles.push(...targetFileNames);
6433
+ _child_process.execSync.call(void 0, `git fetch origin ${this.i18nBranchName}`, { stdio: "inherit" });
6434
+ for (const file of targetFiles) {
6435
+ try {
6436
+ _child_process.execSync.call(void 0, `git checkout FETCH_HEAD -- ${file}`, { stdio: "inherit" });
6437
+ } catch (error2) {
6438
+ this.ora.warn(`Skipping non-existent file: ${file}`);
6439
+ continue;
6440
+ }
6441
+ }
6442
+ this.ora.succeed("Restored target files");
6443
+ }
6444
+ this.ora.start("Checking for changes to commit");
6445
+ const hasChanges = this.checkCommitableChanges();
6446
+ if (hasChanges) {
6447
+ _child_process.execSync.call(void 0, "git add .", { stdio: "inherit" });
6448
+ _child_process.execSync.call(void 0,
6449
+ `git commit -m "chore: sync with ${this.platformKit.platformConfig.baseBranchName}" --no-verify`,
6450
+ {
6451
+ stdio: "inherit"
6452
+ }
6453
+ );
6454
+ this.ora.succeed("Committed additional changes");
6455
+ } else {
6456
+ this.ora.succeed("No changes to commit");
6457
+ }
6458
+ }
6459
+ getPrBodyContent() {
6460
+ return `
6461
+ Hey team,
6507
6462
 
6508
- // src/cli/cmd/may-the-fourth.ts
6463
+ [**Lingo.dev**](https://lingo.dev) here with fresh translations!
6464
+
6465
+ ### In this update
6466
+
6467
+ - Added missing translations
6468
+ - Performed brand voice, context and glossary checks
6469
+ - Enhanced translations using Lingo.dev Localization Engine
6470
+
6471
+ ### Next Steps
6472
+
6473
+ - [ ] Review the changes
6474
+ - [ ] Merge when ready
6475
+ `.trim();
6476
+ }
6477
+ };
6478
+
6479
+ // src/cli/cmd/ci/platforms/bitbucket.ts
6509
6480
 
6481
+ var _bitbucket = require('bitbucket'); var _bitbucket2 = _interopRequireDefault(_bitbucket);
6510
6482
 
6511
6483
 
6484
+ // src/cli/cmd/ci/platforms/_base.ts
6512
6485
 
6513
6486
 
6514
- var colors2 = {
6515
- orange: "#ff6600",
6516
- green: "#6ae300",
6517
- blue: "#0090ff",
6518
- yellow: "#ffcc00",
6519
- grey: "#808080",
6520
- red: "#ff0000"
6487
+ var defaultMessage = "feat: update translations via @lingodotdev";
6488
+ var PlatformKit = class {
6489
+ gitConfig(token, repoUrl) {
6490
+ if (token && repoUrl) {
6491
+ _child_process.execSync.call(void 0, `git remote set-url origin ${repoUrl}`, {
6492
+ stdio: "inherit"
6493
+ });
6494
+ }
6495
+ }
6496
+ get config() {
6497
+ const env = _zod2.default.object({
6498
+ LINGODOTDEV_API_KEY: _zod2.default.string(),
6499
+ LINGODOTDEV_PULL_REQUEST: _zod2.default.preprocess((val) => val === "true" || val === true, _zod2.default.boolean()),
6500
+ LINGODOTDEV_COMMIT_MESSAGE: _zod2.default.string().optional(),
6501
+ LINGODOTDEV_PULL_REQUEST_TITLE: _zod2.default.string().optional(),
6502
+ LINGODOTDEV_WORKING_DIRECTORY: _zod2.default.string().optional(),
6503
+ LINGODOTDEV_PROCESS_OWN_COMMITS: _zod2.default.preprocess((val) => val === "true" || val === true, _zod2.default.boolean()).optional()
6504
+ }).parse(process.env);
6505
+ return {
6506
+ replexicaApiKey: env.LINGODOTDEV_API_KEY,
6507
+ isPullRequestMode: env.LINGODOTDEV_PULL_REQUEST,
6508
+ commitMessage: env.LINGODOTDEV_COMMIT_MESSAGE || defaultMessage,
6509
+ pullRequestTitle: env.LINGODOTDEV_PULL_REQUEST_TITLE || defaultMessage,
6510
+ workingDir: env.LINGODOTDEV_WORKING_DIRECTORY || ".",
6511
+ processOwnCommits: env.LINGODOTDEV_PROCESS_OWN_COMMITS || false
6512
+ };
6513
+ }
6521
6514
  };
6522
- var may_the_fourth_default = new (0, _interactivecommander.Command)().command("may-the-fourth").description("May the Fourth be with you").helpOption("-h, --help", "Show help").action(async () => {
6523
- await renderClear();
6524
- await renderBanner();
6525
- await renderSpacer();
6526
- console.log(_chalk2.default.hex(colors2.yellow)("Loading the Star Wars movie..."));
6527
- await renderSpacer();
6528
- await new Promise((resolve, reject) => {
6529
- const ssh = cp.spawn("ssh", ["starwarstel.net"], {
6530
- stdio: "inherit"
6531
- });
6532
- ssh.on("close", (code) => {
6533
- if (code !== 0) {
6534
- console.error(`SSH process exited with code ${code}`);
6535
- }
6536
- resolve();
6537
- });
6538
- ssh.on("error", (err) => {
6539
- console.error("Failed to start SSH process:", err);
6540
- reject(err);
6541
- });
6542
- });
6543
- await renderSpacer();
6544
- console.log(
6545
- `${_chalk2.default.hex(colors2.green)("We hope you enjoyed it! :)")} ${_chalk2.default.hex(colors2.blue)("May the Fourth be with you! \u{1F680}")}`
6546
- );
6547
- await renderSpacer();
6548
- console.log(_chalk2.default.dim(`---`));
6549
- await renderSpacer();
6550
- await renderHero();
6551
- });
6552
- async function renderClear() {
6553
- console.log("\x1Bc");
6554
- }
6555
- async function renderSpacer() {
6556
- console.log(" ");
6557
- }
6558
- async function renderBanner() {
6559
- console.log(
6560
- _gradientstring.vice.call(void 0,
6561
- _figlet2.default.textSync("LINGO.DEV", {
6562
- font: "ANSI Shadow",
6563
- horizontalLayout: "default",
6564
- verticalLayout: "default"
6565
- })
6566
- )
6567
- );
6568
- }
6569
- async function renderHero() {
6570
- console.log(
6571
- `\u26A1\uFE0F ${_chalk2.default.hex(colors2.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
6572
- );
6573
- console.log(" ");
6574
- console.log(
6575
- _chalk2.default.hex(colors2.blue)("\u2B50 GitHub Repo: https://lingo.dev/go/gh")
6576
- );
6577
- console.log(_chalk2.default.hex(colors2.blue)("\u{1F4AC} 24/7 Support: hi@lingo.dev"));
6578
- }
6579
6515
 
6580
- // package.json
6581
- var package_default = {
6582
- name: "lingo.dev",
6583
- version: "0.93.5",
6584
- description: "Lingo.dev CLI",
6585
- private: false,
6586
- publishConfig: {
6587
- access: "public"
6588
- },
6589
- type: "module",
6590
- sideEffects: false,
6591
- exports: {
6592
- "./cli": {
6593
- types: "./build/cli.d.ts",
6594
- import: "./build/cli.mjs",
6595
- require: "./build/cli.cjs"
6596
- },
6597
- "./sdk": {
6598
- types: "./build/sdk.d.ts",
6599
- import: "./build/sdk.mjs",
6600
- require: "./build/sdk.cjs"
6601
- },
6602
- "./spec": {
6603
- types: "./build/spec.d.ts",
6604
- import: "./build/spec.mjs",
6605
- require: "./build/spec.cjs"
6606
- },
6607
- "./compiler": {
6608
- types: "./build/compiler.d.ts",
6609
- import: "./build/compiler.mjs",
6610
- require: "./build/compiler.cjs"
6611
- },
6612
- "./react": {
6613
- types: "./build/react.d.ts",
6614
- import: "./build/react.mjs",
6615
- require: "./build/react.cjs"
6616
- },
6617
- "./react-client": {
6618
- types: "./build/react/client.d.ts",
6619
- import: "./build/react/client.mjs",
6620
- require: "./build/react/client.cjs"
6621
- },
6622
- "./react/client": {
6623
- types: "./build/react/client.d.ts",
6624
- import: "./build/react/client.mjs",
6625
- require: "./build/react/client.cjs"
6626
- },
6627
- "./react-rsc": {
6628
- types: "./build/react/rsc.d.ts",
6629
- import: "./build/react/rsc.mjs",
6630
- require: "./build/react/rsc.cjs"
6631
- },
6632
- "./react/rsc": {
6633
- types: "./build/react/rsc.d.ts",
6634
- import: "./build/react/rsc.mjs",
6635
- require: "./build/react/rsc.cjs"
6636
- },
6637
- "./react-router": {
6638
- types: "./build/react/react-router.d.ts",
6639
- import: "./build/react/react-router.mjs",
6640
- require: "./build/react/react-router.cjs"
6641
- },
6642
- "./react/react-router": {
6643
- types: "./build/react/react-router.d.ts",
6644
- import: "./build/react/react-router.mjs",
6645
- require: "./build/react/react-router.cjs"
6646
- }
6647
- },
6648
- typesVersions: {
6649
- "*": {
6650
- sdk: [
6651
- "./build/sdk.d.ts"
6652
- ],
6653
- cli: [
6654
- "./build/cli.d.ts"
6655
- ],
6656
- spec: [
6657
- "./build/spec.d.ts"
6658
- ],
6659
- compiler: [
6660
- "./build/compiler.d.ts"
6661
- ],
6662
- react: [
6663
- "./build/react.d.ts"
6664
- ],
6665
- "react/client": [
6666
- "./build/react/client.d.ts"
6667
- ],
6668
- "react/rsc": [
6669
- "./build/react/rsc.d.ts"
6670
- ],
6671
- "react/react-router": [
6672
- "./build/react/react-router.d.ts"
6673
- ]
6516
+ // src/cli/cmd/ci/platforms/bitbucket.ts
6517
+ var { Bitbucket } = _bitbucket2.default;
6518
+ var BitbucketPlatformKit = class extends PlatformKit {
6519
+
6520
+ get bb() {
6521
+ if (!this._bb) {
6522
+ this._bb = new Bitbucket({
6523
+ auth: { token: this.platformConfig.bbToken || "" }
6524
+ });
6674
6525
  }
6675
- },
6676
- bin: {
6677
- "lingo.dev": "./bin/cli.mjs"
6678
- },
6679
- files: [
6680
- "bin",
6681
- "build"
6682
- ],
6683
- scripts: {
6684
- "lingo.dev": "node --inspect=9229 ./bin/cli.mjs",
6685
- dev: "tsup --watch",
6686
- build: "tsc --noEmit && tsup",
6687
- test: "vitest run",
6688
- "test:watch": "vitest",
6689
- clean: "rm -rf build"
6690
- },
6691
- keywords: [],
6692
- author: "",
6693
- license: "Apache-2.0",
6694
- dependencies: {
6695
- "@ai-sdk/anthropic": "^1.2.11",
6696
- "@ai-sdk/openai": "^1.3.22",
6697
- "@babel/generator": "^7.27.1",
6698
- "@babel/parser": "^7.27.1",
6699
- "@babel/traverse": "^7.27.4",
6700
- "@babel/types": "^7.27.1",
6701
- "@datocms/cma-client-node": "^4.0.1",
6702
- "@gitbeaker/rest": "^39.34.3",
6703
- "@inkjs/ui": "^2.0.0",
6704
- "@inquirer/prompts": "^7.4.1",
6705
- "@lingo.dev/_sdk": "workspace:*",
6706
- "@lingo.dev/_spec": "workspace:*",
6707
- "@lingo.dev/_react": "workspace:*",
6708
- "@lingo.dev/_compiler": "workspace:*",
6709
- "@modelcontextprotocol/sdk": "^1.5.0",
6710
- "@paralleldrive/cuid2": "^2.2.2",
6711
- ai: "^4.3.15",
6712
- bitbucket: "^2.12.0",
6713
- chalk: "^5.4.1",
6714
- "cli-progress": "^3.12.0",
6715
- "cli-table3": "^0.6.5",
6716
- cors: "^2.8.5",
6717
- "csv-parse": "^5.6.0",
6718
- "csv-stringify": "^6.5.2",
6719
- "date-fns": "^4.1.0",
6720
- dedent: "^1.5.3",
6721
- diff: "^7.0.0",
6722
- dotenv: "^16.4.7",
6723
- express: "^5.1.0",
6724
- "external-editor": "^3.1.0",
6725
- figlet: "^1.8.0",
6726
- flat: "^6.0.1",
6727
- "gettext-parser": "^8.0.0",
6728
- glob: "<11.0.0",
6729
- "gradient-string": "^3.0.0",
6730
- "gray-matter": "^4.0.3",
6731
- ini: "^5.0.0",
6732
- ink: "^4.2.0",
6733
- "ink-progress-bar": "^3.0.0",
6734
- "ink-spinner": "^5.0.0",
6735
- inquirer: "^12.6.0",
6736
- "interactive-commander": "^0.5.194",
6737
- "is-url": "^1.2.4",
6738
- jsdom: "^25.0.1",
6739
- json5: "^2.2.3",
6740
- jsonrepair: "^3.11.2",
6741
- listr2: "^8.3.2",
6742
- lodash: "^4.17.21",
6743
- marked: "^15.0.6",
6744
- "mdast-util-from-markdown": "^2.0.2",
6745
- "mdast-util-gfm": "^3.1.0",
6746
- "micromark-extension-gfm": "^3.0.0",
6747
- "node-machine-id": "^1.1.12",
6748
- "node-webvtt": "^1.9.4",
6749
- "object-hash": "^3.0.0",
6750
- octokit: "^4.0.2",
6751
- open: "^10.1.2",
6752
- ora: "^8.1.1",
6753
- "p-limit": "^6.2.0",
6754
- "php-array-reader": "^2.1.2",
6755
- plist: "^3.1.0",
6756
- "posthog-node": "^4.17.0",
6757
- prettier: "^3.4.2",
6758
- react: "^18.3.1",
6759
- "rehype-stringify": "^10.0.1",
6760
- "remark-disable-tokenizers": "^1.1.1",
6761
- "remark-frontmatter": "^5.0.0",
6762
- "remark-gfm": "^4.0.1",
6763
- "remark-mdx": "^3.1.0",
6764
- "remark-mdx-frontmatter": "^5.1.0",
6765
- "remark-parse": "^11.0.0",
6766
- "remark-rehype": "^11.1.2",
6767
- "remark-stringify": "^11.0.0",
6768
- "srt-parser-2": "^1.2.3",
6769
- unified: "^11.0.5",
6770
- "unist-util-visit": "^5.0.0",
6771
- vfile: "^6.0.3",
6772
- xliff: "^6.2.1",
6773
- xml2js: "^0.6.2",
6774
- xpath: "^0.0.34",
6775
- yaml: "^2.7.0",
6776
- zod: "^3.24.1"
6777
- },
6778
- devDependencies: {
6779
- "@types/babel__generator": "^7.27.0",
6780
- "@types/cli-progress": "^3.11.6",
6781
- "@types/cors": "^2.8.17",
6782
- "@types/diff": "^7.0.0",
6783
- "@types/express": "^5.0.1",
6784
- "@types/figlet": "^1.7.0",
6785
- "@types/gettext-parser": "^4.0.4",
6786
- "@types/glob": "^8.1.0",
6787
- "@types/ini": "^4.1.1",
6788
- "@types/is-url": "^1.2.32",
6789
- "@types/jsdom": "^21.1.7",
6790
- "@types/lodash": "^4.17.16",
6791
- "@types/mdast": "^4.0.4",
6792
- "@types/node": "^22.10.2",
6793
- "@types/node-gettext": "^3.0.6",
6794
- "@types/object-hash": "^3.0.6",
6795
- "@types/plist": "^3.0.5",
6796
- "@types/react": "^18.3.20",
6797
- "@types/xml2js": "^0.4.14",
6798
- tsup: "^8.3.5",
6799
- typescript: "^5.8.3",
6800
- vitest: "^3.1.2"
6801
- },
6802
- engines: {
6803
- node: ">=18"
6804
- },
6805
- packageManager: "pnpm@9.12.3"
6806
- };
6807
-
6808
- // src/cli/cmd/run/index.ts
6809
-
6810
-
6811
- // src/cli/cmd/run/setup.ts
6812
-
6813
- var _listr2 = require('listr2');
6814
-
6815
- // src/cli/cmd/run/_const.ts
6816
-
6817
-
6818
- var commonTaskRendererOptions = {
6819
- color: {
6820
- [_listr2.ListrDefaultRendererLogLevels.COMPLETED]: (msg) => msg ? _chalk2.default.hex(colors.green)(msg) : _chalk2.default.hex(colors.green)("")
6821
- },
6822
- icon: {
6823
- [_listr2.ListrDefaultRendererLogLevels.COMPLETED]: _chalk2.default.hex(colors.green)("\u2713")
6526
+ return this._bb;
6824
6527
  }
6825
- };
6826
-
6827
- // src/cli/localizer/lingodotdev.ts
6828
-
6829
-
6830
-
6831
- function createLingoDotDevLocalizer(explicitApiKey) {
6832
- const { auth } = getSettings(explicitApiKey);
6833
- if (!auth) {
6834
- throw new Error(
6835
- _dedent2.default`
6836
- You're trying to use ${_chalk2.default.hex(colors.green)("Lingo.dev")} provider, however, you are not authenticated.
6837
-
6838
- To fix this issue:
6839
- 1. Run ${_chalk2.default.dim("lingo.dev login")} to authenticate, or
6840
- 2. Use the ${_chalk2.default.dim("--api-key")} flag to provide an API key.
6841
- 3. Set ${_chalk2.default.dim("LINGODOTDEV_API_KEY")} environment variable.
6842
- `
6843
- );
6528
+ async branchExists({ branch }) {
6529
+ return await this.bb.repositories.getBranch({
6530
+ workspace: this.platformConfig.repositoryOwner,
6531
+ repo_slug: this.platformConfig.repositoryName,
6532
+ name: branch
6533
+ }).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
6844
6534
  }
6845
- const engine = new (0, __sdk.LingoDotDevEngine)({
6846
- apiKey: auth.apiKey,
6847
- apiUrl: auth.apiUrl
6848
- });
6849
- return {
6850
- id: "Lingo.dev",
6851
- checkAuth: async () => {
6852
- try {
6853
- const response = await engine.whoami();
6854
- return {
6855
- authenticated: !!response,
6856
- username: _optionalChain([response, 'optionalAccess', _222 => _222.email])
6857
- };
6858
- } catch (e3) {
6859
- return { authenticated: false };
6535
+ async getOpenPullRequestNumber({ branch }) {
6536
+ return await this.bb.repositories.listPullRequests({
6537
+ workspace: this.platformConfig.repositoryOwner,
6538
+ repo_slug: this.platformConfig.repositoryName,
6539
+ state: "OPEN"
6540
+ }).then(({ data: { values } }) => {
6541
+ return _optionalChain([values, 'optionalAccess', _200 => _200.find, 'call', _201 => _201(
6542
+ ({ source, destination }) => _optionalChain([source, 'optionalAccess', _202 => _202.branch, 'optionalAccess', _203 => _203.name]) === branch && _optionalChain([destination, 'optionalAccess', _204 => _204.branch, 'optionalAccess', _205 => _205.name]) === this.platformConfig.baseBranchName
6543
+ )]);
6544
+ }).then((pr) => _optionalChain([pr, 'optionalAccess', _206 => _206.id]));
6545
+ }
6546
+ async closePullRequest({ pullRequestNumber }) {
6547
+ await this.bb.repositories.declinePullRequest({
6548
+ workspace: this.platformConfig.repositoryOwner,
6549
+ repo_slug: this.platformConfig.repositoryName,
6550
+ pull_request_id: pullRequestNumber
6551
+ });
6552
+ }
6553
+ async createPullRequest({
6554
+ title,
6555
+ body,
6556
+ head
6557
+ }) {
6558
+ return await this.bb.repositories.createPullRequest({
6559
+ workspace: this.platformConfig.repositoryOwner,
6560
+ repo_slug: this.platformConfig.repositoryName,
6561
+ _body: {
6562
+ title,
6563
+ description: body,
6564
+ source: { branch: { name: head } },
6565
+ destination: { branch: { name: this.platformConfig.baseBranchName } }
6860
6566
  }
6861
- },
6862
- localize: async (input2, onProgress) => {
6863
- if (!Object.keys(input2.processableData).length) {
6864
- return input2;
6567
+ }).then(({ data }) => _nullishCoalesce(data.id, () => ( 0)));
6568
+ }
6569
+ async commentOnPullRequest({
6570
+ pullRequestNumber,
6571
+ body
6572
+ }) {
6573
+ await this.bb.repositories.createPullRequestComment({
6574
+ workspace: this.platformConfig.repositoryOwner,
6575
+ repo_slug: this.platformConfig.repositoryName,
6576
+ pull_request_id: pullRequestNumber,
6577
+ _body: {
6578
+ content: {
6579
+ raw: body
6580
+ }
6865
6581
  }
6866
- const processedData = await engine.localizeObject(
6867
- input2.processableData,
6868
- {
6869
- sourceLocale: input2.sourceLocale,
6870
- targetLocale: input2.targetLocale,
6871
- reference: {
6872
- [input2.sourceLocale]: input2.sourceData,
6873
- [input2.targetLocale]: input2.targetData
6874
- }
6875
- },
6876
- onProgress
6877
- );
6878
- return processedData;
6879
- }
6880
- };
6881
- }
6882
-
6883
- // src/cli/localizer/explicit.ts
6884
-
6885
-
6886
-
6887
-
6888
-
6889
-
6890
- function createExplicitLocalizer(provider) {
6891
- switch (provider.id) {
6892
- default:
6893
- throw new Error(
6894
- _dedent2.default`
6895
- You're trying to use unsupported provider: ${_chalk2.default.dim(provider.id)}.
6896
-
6897
- To fix this issue:
6898
- 1. Switch to one of the supported providers, or
6899
- 2. Remove the ${_chalk2.default.italic("provider")} node from your i18n.json configuration to switch to ${_chalk2.default.hex(colors.green)("Lingo.dev")}
6900
-
6901
- ${_chalk2.default.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
6902
- `
6903
- );
6904
- case "openai":
6905
- return createAiSdkLocalizer({
6906
- factory: (params) => _openai.createOpenAI.call(void 0, params).languageModel(provider.model),
6907
- id: provider.id,
6908
- prompt: provider.prompt,
6909
- apiKeyName: "OPENAI_API_KEY",
6910
- baseUrl: provider.baseUrl
6911
- });
6912
- case "anthropic":
6913
- return createAiSdkLocalizer({
6914
- factory: (params) => _anthropic.createAnthropic.call(void 0, params).languageModel(provider.model),
6915
- id: provider.id,
6916
- prompt: provider.prompt,
6917
- apiKeyName: "ANTHROPIC_API_KEY",
6918
- baseUrl: provider.baseUrl
6919
- });
6582
+ });
6920
6583
  }
6921
- }
6922
- function createAiSdkLocalizer(params) {
6923
- const apiKey = process.env[params.apiKeyName];
6924
- if (!apiKey) {
6925
- throw new Error(
6926
- _dedent2.default`
6927
- You're trying to use raw ${_chalk2.default.dim(params.id)} API for translation, however, ${_chalk2.default.dim(params.apiKeyName)} environment variable is not set.
6928
-
6929
- To fix this issue:
6930
- 1. Set ${_chalk2.default.dim(params.apiKeyName)} in your environment variables, or
6931
- 2. Remove the ${_chalk2.default.italic("provider")} node from your i18n.json configuration to switch to ${_chalk2.default.hex(colors.green)("Lingo.dev")}
6932
-
6933
- ${_chalk2.default.hex(colors.blue)("Docs: https://lingo.dev/go/docs")}
6934
- `
6584
+ async gitConfig() {
6585
+ _child_process.execSync.call(void 0, "git config --unset http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy", {
6586
+ stdio: "inherit"
6587
+ });
6588
+ _child_process.execSync.call(void 0,
6589
+ "git config http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy http://host.docker.internal:29418/",
6590
+ {
6591
+ stdio: "inherit"
6592
+ }
6935
6593
  );
6936
6594
  }
6937
- const model = params.factory({
6938
- apiKey,
6939
- baseUrl: params.baseUrl
6940
- });
6941
- return {
6942
- id: params.id,
6943
- checkAuth: async () => {
6944
- try {
6945
- await _ai.generateText.call(void 0, {
6946
- model,
6947
- messages: [
6948
- { role: "system", content: "You are an echo server" },
6949
- { role: "user", content: "OK" },
6950
- { role: "assistant", content: "OK" },
6951
- { role: "user", content: "OK" }
6952
- ]
6953
- });
6954
- return { authenticated: true, username: "anonymous" };
6955
- } catch (error) {
6956
- return { authenticated: false };
6957
- }
6958
- },
6959
- localize: async (input2) => {
6960
- const systemPrompt = params.prompt.replaceAll("{source}", input2.sourceLocale).replaceAll("{target}", input2.targetLocale);
6961
- const shots = [
6962
- [
6963
- {
6964
- sourceLocale: "en",
6965
- targetLocale: "es",
6966
- data: {
6967
- message: "Hello, world!"
6968
- }
6969
- },
6970
- {
6971
- sourceLocale: "en",
6972
- targetLocale: "es",
6973
- data: {
6974
- message: "Hola, mundo!"
6975
- }
6976
- }
6977
- ]
6978
- ];
6979
- const payload = {
6980
- sourceLocale: input2.sourceLocale,
6981
- targetLocale: input2.targetLocale,
6982
- data: input2.processableData
6983
- };
6984
- const response = await _ai.generateText.call(void 0, {
6985
- model,
6986
- messages: [
6987
- { role: "system", content: systemPrompt },
6988
- { role: "user", content: "OK" },
6989
- ...shots.flatMap(
6990
- ([userShot, assistantShot]) => [
6991
- { role: "user", content: JSON.stringify(userShot) },
6992
- { role: "assistant", content: JSON.stringify(assistantShot) }
6993
- ]
6994
- ),
6995
- { role: "user", content: JSON.stringify(payload) }
6996
- ]
6997
- });
6998
- const result = JSON.parse(response.text);
6999
- const index = result.data.indexOf("{");
7000
- const lastIndex = result.data.lastIndexOf("}");
7001
- const trimmed = result.data.slice(index, lastIndex + 1);
7002
- const repaired = _jsonrepair.jsonrepair.call(void 0, trimmed);
7003
- const finalResult = JSON.parse(repaired);
7004
- return finalResult.data;
7005
- }
7006
- };
7007
- }
7008
-
7009
- // src/cli/localizer/index.ts
7010
- function createLocalizer(provider) {
7011
- if (!provider) {
7012
- return createLingoDotDevLocalizer();
7013
- } else {
7014
- return createExplicitLocalizer(provider);
6595
+ get platformConfig() {
6596
+ const env = _zod2.default.object({
6597
+ BITBUCKET_BRANCH: _zod2.default.string(),
6598
+ BITBUCKET_REPO_FULL_NAME: _zod2.default.string(),
6599
+ BB_TOKEN: _zod2.default.string().optional()
6600
+ }).parse(process.env);
6601
+ const [repositoryOwner, repositoryName] = env.BITBUCKET_REPO_FULL_NAME.split("/");
6602
+ return {
6603
+ baseBranchName: env.BITBUCKET_BRANCH,
6604
+ repositoryOwner,
6605
+ repositoryName,
6606
+ bbToken: env.BB_TOKEN
6607
+ };
7015
6608
  }
7016
- }
7017
-
7018
- // src/cli/cmd/run/setup.ts
7019
- async function setup(input2) {
7020
- console.log(_chalk2.default.hex(colors.orange)("[Setup]"));
7021
- return new (0, _listr2.Listr)(
7022
- [
7023
- {
7024
- title: "Setting up the environment",
7025
- task: async (ctx, task) => {
7026
- task.title = `Environment setup completed`;
7027
- }
7028
- },
7029
- {
7030
- title: "Loading i18n configuration",
7031
- task: async (ctx, task) => {
7032
- ctx.config = getConfig(true);
7033
- if (!ctx.config) {
7034
- throw new Error(
7035
- "i18n.json not found. Please run `lingo.dev init` to initialize the project."
7036
- );
7037
- } else if (!ctx.config.buckets || !Object.keys(ctx.config.buckets).length) {
7038
- throw new Error(
7039
- "No buckets found in i18n.json. Please add at least one bucket containing i18n content."
7040
- );
7041
- } else if (_optionalChain([ctx, 'access', _223 => _223.flags, 'access', _224 => _224.locale, 'optionalAccess', _225 => _225.some, 'call', _226 => _226(
7042
- (locale) => !_optionalChain([ctx, 'access', _227 => _227.config, 'optionalAccess', _228 => _228.locale, 'access', _229 => _229.targets, 'access', _230 => _230.includes, 'call', _231 => _231(locale)])
7043
- )])) {
7044
- throw new Error(
7045
- `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list first and try again.`
7046
- );
7047
- } else if (_optionalChain([ctx, 'access', _232 => _232.flags, 'access', _233 => _233.bucket, 'optionalAccess', _234 => _234.some, 'call', _235 => _235(
7048
- (bucket) => !_optionalChain([ctx, 'access', _236 => _236.config, 'optionalAccess', _237 => _237.buckets, 'access', _238 => _238[bucket]])
7049
- )])) {
7050
- throw new Error(
7051
- `One or more specified buckets do not exist in i18n.json. Please add them to the list first and try again.`
7052
- );
7053
- }
7054
- task.title = `Loaded i18n configuration`;
7055
- }
7056
- },
7057
- {
7058
- title: "Selecting localization provider",
7059
- task: async (ctx, task) => {
7060
- ctx.localizer = createLocalizer(_optionalChain([ctx, 'access', _239 => _239.config, 'optionalAccess', _240 => _240.provider]));
7061
- if (!ctx.localizer) {
7062
- throw new Error(
7063
- "Could not create localization provider. Please check your i18n.json configuration."
7064
- );
7065
- }
7066
- task.title = ctx.localizer.id === "Lingo.dev" ? `Using ${_chalk2.default.hex(colors.green)(ctx.localizer.id)} provider` : `Using raw ${_chalk2.default.hex(colors.yellow)(ctx.localizer.id)} API`;
7067
- }
7068
- },
7069
- {
7070
- title: "Checking authentication",
7071
- task: async (ctx, task) => {
7072
- const authStatus = await ctx.localizer.checkAuth();
7073
- if (!authStatus.authenticated) {
7074
- throw new Error(
7075
- `Failed to authenticate with ${_chalk2.default.hex(colors.yellow)(ctx.localizer.id)} provider. Please check your API key and try again.`
7076
- );
7077
- }
7078
- task.title = `Authenticated as ${_chalk2.default.hex(colors.yellow)(authStatus.username)}`;
7079
- }
7080
- },
7081
- {
7082
- title: "Initializing localization provider",
7083
- async task(ctx, task) {
7084
- const isLingoDotDev = ctx.localizer.id === "Lingo.dev";
7085
- const subTasks = isLingoDotDev ? [
7086
- "Brand voice enabled",
7087
- "Translation memory connected",
7088
- "Glossary enabled",
7089
- "Quality assurance enabled"
7090
- ].map((title) => ({ title, task: () => {
7091
- } })) : [
7092
- "Skipping brand voice",
7093
- "Skipping glossary",
7094
- "Skipping translation memory",
7095
- "Skipping quality assurance"
7096
- ].map((title) => ({ title, task: () => {
7097
- }, skip: true }));
7098
- return task.newListr(subTasks, {
7099
- concurrent: true,
7100
- rendererOptions: { collapseSubtasks: false }
7101
- });
7102
- }
7103
- }
7104
- ],
7105
- {
7106
- rendererOptions: commonTaskRendererOptions
7107
- }
7108
- ).run(input2);
7109
- }
7110
-
7111
- // src/cli/cmd/run/plan.ts
7112
-
6609
+ buildPullRequestUrl(pullRequestNumber) {
6610
+ const { repositoryOwner, repositoryName } = this.platformConfig;
6611
+ return `https://bitbucket.org/${repositoryOwner}/${repositoryName}/pull-requests/${pullRequestNumber}`;
6612
+ }
6613
+ };
7113
6614
 
6615
+ // src/cli/cmd/ci/platforms/github.ts
6616
+ var _octokit2 = require('octokit');
7114
6617
 
7115
- async function plan(input2) {
7116
- console.log(_chalk2.default.hex(colors.orange)("[Planning]"));
7117
- let buckets = getBuckets(input2.config);
7118
- if (input2.flags.bucket) {
7119
- buckets = buckets.filter((b) => input2.flags.bucket.includes(b.type));
6618
+ var GitHubPlatformKit = class extends PlatformKit {
6619
+
6620
+ get octokit() {
6621
+ if (!this._octokit) {
6622
+ this._octokit = new (0, _octokit2.Octokit)({ auth: this.platformConfig.ghToken });
6623
+ }
6624
+ return this._octokit;
7120
6625
  }
7121
- let locales = input2.config.locale.targets;
7122
- if (input2.flags.locale) {
7123
- locales = locales.filter((l) => input2.flags.locale.includes(l));
6626
+ async branchExists({ branch }) {
6627
+ return await this.octokit.rest.repos.getBranch({
6628
+ branch,
6629
+ owner: this.platformConfig.repositoryOwner,
6630
+ repo: this.platformConfig.repositoryName
6631
+ }).then((r) => r.data).then((v) => !!v).catch((r) => r.status === 404 ? false : Promise.reject(r));
7124
6632
  }
7125
- return new (0, _listr2.Listr)(
7126
- [
7127
- {
7128
- title: "Locating content buckets",
7129
- task: async (ctx, task) => {
7130
- const bucketCount = buckets.length;
7131
- const bucketFilter = input2.flags.bucket ? ` ${_chalk2.default.dim(`(filtered by: ${_chalk2.default.hex(colors.yellow)(input2.flags.bucket.join(", "))})`)}` : "";
7132
- task.title = `Found ${_chalk2.default.hex(colors.yellow)(bucketCount.toString())} bucket(s)${bucketFilter}`;
7133
- }
7134
- },
7135
- {
7136
- title: "Detecting locales",
7137
- task: async (ctx, task) => {
7138
- if (!locales.length) {
7139
- throw new Error(
7140
- `No target locales found in config. Please add locales to your i18n.json config file.`
7141
- );
7142
- }
7143
- const localeFilter = input2.flags.locale ? ` ${_chalk2.default.dim(`(filtered by: ${_chalk2.default.hex(colors.yellow)(input2.flags.locale.join(", "))})`)}` : "";
7144
- task.title = `Found ${_chalk2.default.hex(colors.yellow)(locales.length.toString())} target locale(s)${localeFilter}`;
7145
- }
7146
- },
7147
- {
7148
- title: "Locating localizable files",
7149
- task: async (ctx, task) => {
7150
- const patterns = [];
7151
- for (const bucket of buckets) {
7152
- for (const bucketPath of bucket.paths) {
7153
- if (input2.flags.file) {
7154
- if (!input2.flags.file.some(
7155
- (f) => bucketPath.pathPattern.includes(f)
7156
- )) {
7157
- continue;
7158
- }
7159
- }
7160
- patterns.push(bucketPath.pathPattern);
7161
- }
7162
- }
7163
- const fileFilter = input2.flags.file ? ` ${_chalk2.default.dim(`(filtered by: ${_chalk2.default.hex(colors.yellow)(input2.flags.file.join(", "))})`)}` : "";
7164
- task.title = `Found ${_chalk2.default.hex(colors.yellow)(patterns.length.toString())} path pattern(s)${fileFilter}`;
7165
- }
7166
- },
7167
- {
7168
- title: "Computing translation tasks",
7169
- task: async (ctx, task) => {
7170
- for (const bucket of buckets) {
7171
- for (const bucketPath of bucket.paths) {
7172
- if (input2.flags.file) {
7173
- if (!input2.flags.file.some(
7174
- (f) => bucketPath.pathPattern.includes(f)
7175
- )) {
7176
- continue;
7177
- }
7178
- }
7179
- const sourceLocale = __spec.resolveOverriddenLocale.call(void 0,
7180
- ctx.config.locale.source,
7181
- bucketPath.delimiter
7182
- );
7183
- for (const _targetLocale of locales) {
7184
- const targetLocale = __spec.resolveOverriddenLocale.call(void 0,
7185
- _targetLocale,
7186
- bucketPath.delimiter
7187
- );
7188
- if (sourceLocale === targetLocale) continue;
7189
- ctx.tasks.push({
7190
- sourceLocale,
7191
- targetLocale,
7192
- bucketType: bucket.type,
7193
- bucketPathPattern: bucketPath.pathPattern,
7194
- injectLocale: bucket.injectLocale || [],
7195
- lockedKeys: bucket.lockedKeys || [],
7196
- lockedPatterns: bucket.lockedPatterns || []
7197
- });
7198
- }
7199
- }
7200
- }
7201
- task.title = `Prepared ${_chalk2.default.hex(colors.green)(ctx.tasks.length.toString())} translation task(s)`;
7202
- }
7203
- }
7204
- ],
7205
- {
7206
- rendererOptions: commonTaskRendererOptions
6633
+ async getOpenPullRequestNumber({ branch }) {
6634
+ return await this.octokit.rest.pulls.list({
6635
+ head: `${this.platformConfig.repositoryOwner}:${branch}`,
6636
+ owner: this.platformConfig.repositoryOwner,
6637
+ repo: this.platformConfig.repositoryName,
6638
+ base: this.platformConfig.baseBranchName,
6639
+ state: "open"
6640
+ }).then(({ data }) => data[0]).then((pr) => _optionalChain([pr, 'optionalAccess', _207 => _207.number]));
6641
+ }
6642
+ async closePullRequest({ pullRequestNumber }) {
6643
+ await this.octokit.rest.pulls.update({
6644
+ pull_number: pullRequestNumber,
6645
+ owner: this.platformConfig.repositoryOwner,
6646
+ repo: this.platformConfig.repositoryName,
6647
+ state: "closed"
6648
+ });
6649
+ }
6650
+ async createPullRequest({
6651
+ head,
6652
+ title,
6653
+ body
6654
+ }) {
6655
+ return await this.octokit.rest.pulls.create({
6656
+ head,
6657
+ title,
6658
+ body,
6659
+ owner: this.platformConfig.repositoryOwner,
6660
+ repo: this.platformConfig.repositoryName,
6661
+ base: this.platformConfig.baseBranchName
6662
+ }).then(({ data }) => data.number);
6663
+ }
6664
+ async commentOnPullRequest({
6665
+ pullRequestNumber,
6666
+ body
6667
+ }) {
6668
+ await this.octokit.rest.issues.createComment({
6669
+ issue_number: pullRequestNumber,
6670
+ body,
6671
+ owner: this.platformConfig.repositoryOwner,
6672
+ repo: this.platformConfig.repositoryName
6673
+ });
6674
+ }
6675
+ async gitConfig() {
6676
+ const { ghToken, repositoryOwner, repositoryName } = this.platformConfig;
6677
+ const { processOwnCommits } = this.config;
6678
+ if (ghToken && processOwnCommits) {
6679
+ console.log(
6680
+ "Using provided GH_TOKEN. This will trigger your CI/CD pipeline to run again."
6681
+ );
6682
+ const url = `https://${ghToken}@github.com/${repositoryOwner}/${repositoryName}.git`;
6683
+ super.gitConfig(ghToken, url);
7207
6684
  }
7208
- ).run(input2);
7209
- }
6685
+ }
6686
+ get platformConfig() {
6687
+ const env = _zod2.default.object({
6688
+ GITHUB_REPOSITORY: _zod2.default.string(),
6689
+ GITHUB_REPOSITORY_OWNER: _zod2.default.string(),
6690
+ GITHUB_REF_NAME: _zod2.default.string(),
6691
+ GITHUB_HEAD_REF: _zod2.default.string(),
6692
+ GH_TOKEN: _zod2.default.string().optional()
6693
+ }).parse(process.env);
6694
+ const baseBranchName = !env.GITHUB_REF_NAME.endsWith("/merge") ? env.GITHUB_REF_NAME : env.GITHUB_HEAD_REF;
6695
+ return {
6696
+ ghToken: env.GH_TOKEN,
6697
+ baseBranchName,
6698
+ repositoryOwner: env.GITHUB_REPOSITORY_OWNER,
6699
+ repositoryName: env.GITHUB_REPOSITORY.split("/")[1]
6700
+ };
6701
+ }
6702
+ buildPullRequestUrl(pullRequestNumber) {
6703
+ const { repositoryOwner, repositoryName } = this.platformConfig;
6704
+ return `https://github.com/${repositoryOwner}/${repositoryName}/pull/${pullRequestNumber}`;
6705
+ }
6706
+ };
7210
6707
 
7211
- // src/cli/cmd/run/execute.ts
6708
+ // src/cli/cmd/ci/platforms/gitlab.ts
6709
+ var _rest = require('@gitbeaker/rest');
7212
6710
 
7213
-
7214
- var _plimit = require('p-limit'); var _plimit2 = _interopRequireDefault(_plimit);
7215
-
7216
- var MAX_WORKER_COUNT = 10;
7217
- async function execute(input2) {
7218
- const effectiveConcurrency = Math.min(
7219
- input2.flags.concurrency,
7220
- input2.tasks.length,
7221
- MAX_WORKER_COUNT
7222
- );
7223
- console.log(_chalk2.default.hex(colors.orange)(`[Localization]`));
7224
- return new (0, _listr2.Listr)(
7225
- [
6711
+ var gl = new (0, _rest.Gitlab)({ token: "" });
6712
+ var GitlabPlatformKit = class extends PlatformKit {
6713
+
6714
+ constructor() {
6715
+ super();
6716
+ process.chdir(this.platformConfig.projectDir);
6717
+ }
6718
+ get gitlab() {
6719
+ if (!this._gitlab) {
6720
+ this._gitlab = new (0, _rest.Gitlab)({
6721
+ token: this.platformConfig.glToken || ""
6722
+ });
6723
+ }
6724
+ return this._gitlab;
6725
+ }
6726
+ get platformConfig() {
6727
+ const env = _zod2.default.object({
6728
+ GL_TOKEN: _zod2.default.string().optional(),
6729
+ CI_COMMIT_BRANCH: _zod2.default.string(),
6730
+ CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: _zod2.default.string().optional(),
6731
+ CI_PROJECT_NAMESPACE: _zod2.default.string(),
6732
+ CI_PROJECT_NAME: _zod2.default.string(),
6733
+ CI_PROJECT_ID: _zod2.default.string(),
6734
+ CI_PROJECT_DIR: _zod2.default.string(),
6735
+ CI_REPOSITORY_URL: _zod2.default.string()
6736
+ }).parse(process.env);
6737
+ const config = {
6738
+ glToken: env.GL_TOKEN,
6739
+ baseBranchName: _nullishCoalesce(env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME, () => ( env.CI_COMMIT_BRANCH)),
6740
+ repositoryOwner: env.CI_PROJECT_NAMESPACE,
6741
+ repositoryName: env.CI_PROJECT_NAME,
6742
+ gitlabProjectId: env.CI_PROJECT_ID,
6743
+ projectDir: env.CI_PROJECT_DIR,
6744
+ reporitoryUrl: env.CI_REPOSITORY_URL
6745
+ };
6746
+ return config;
6747
+ }
6748
+ async branchExists({ branch }) {
6749
+ try {
6750
+ await this.gitlab.Branches.show(
6751
+ this.platformConfig.gitlabProjectId,
6752
+ branch
6753
+ );
6754
+ return true;
6755
+ } catch (e3) {
6756
+ return false;
6757
+ }
6758
+ }
6759
+ async getOpenPullRequestNumber({
6760
+ branch
6761
+ }) {
6762
+ const mergeRequests = await this.gitlab.MergeRequests.all({
6763
+ projectId: this.platformConfig.gitlabProjectId,
6764
+ sourceBranch: branch,
6765
+ state: "opened"
6766
+ });
6767
+ return _optionalChain([mergeRequests, 'access', _208 => _208[0], 'optionalAccess', _209 => _209.iid]);
6768
+ }
6769
+ async closePullRequest({
6770
+ pullRequestNumber
6771
+ }) {
6772
+ await this.gitlab.MergeRequests.edit(
6773
+ this.platformConfig.gitlabProjectId,
6774
+ pullRequestNumber,
7226
6775
  {
7227
- title: "Initializing localization engine",
7228
- task: async (ctx, task) => {
7229
- task.title = `Localization engine ${_chalk2.default.hex(colors.green)("ready")} (${ctx.localizer.id})`;
7230
- }
7231
- },
6776
+ stateEvent: "close"
6777
+ }
6778
+ );
6779
+ }
6780
+ async createPullRequest({
6781
+ head,
6782
+ title,
6783
+ body
6784
+ }) {
6785
+ const mr = await this.gitlab.MergeRequests.create(
6786
+ this.platformConfig.gitlabProjectId,
6787
+ head,
6788
+ this.platformConfig.baseBranchName,
6789
+ title,
7232
6790
  {
7233
- title: `Processing localization tasks ${_chalk2.default.dim(`(tasks: ${input2.tasks.length}, concurrency: ${effectiveConcurrency})`)}`,
7234
- task: (ctx, task) => {
7235
- if (input2.tasks.length < 1) {
7236
- task.title = `Skipping, nothing to localize.`;
7237
- task.skip();
7238
- return;
7239
- }
7240
- const i18nLimiter = _plimit2.default.call(void 0, effectiveConcurrency);
7241
- const ioLimiter = _plimit2.default.call(void 0, 1);
7242
- const workersCount = effectiveConcurrency;
7243
- const workerTasks = [];
7244
- for (let i = 0; i < workersCount; i++) {
7245
- const assignedTasks = ctx.tasks.filter(
7246
- (_33, idx) => idx % workersCount === i
7247
- );
7248
- workerTasks.push(
7249
- createWorkerTask({
7250
- ctx,
7251
- assignedTasks,
7252
- ioLimiter,
7253
- i18nLimiter,
7254
- onDone() {
7255
- task.title = createExecutionProgressMessage(ctx);
7256
- }
7257
- })
7258
- );
7259
- }
7260
- return task.newListr(workerTasks, {
7261
- concurrent: true,
7262
- exitOnError: false,
7263
- rendererOptions: {
7264
- ...commonTaskRendererOptions,
7265
- collapseSubtasks: true
7266
- }
7267
- });
7268
- }
6791
+ description: body
7269
6792
  }
7270
- ],
7271
- {
7272
- exitOnError: false,
7273
- rendererOptions: commonTaskRendererOptions
7274
- }
7275
- ).run(input2);
7276
- }
7277
- function createWorkerStatusMessage(args) {
7278
- const displayPath = args.assignedTask.bucketPathPattern.replace(
7279
- "[locale]",
7280
- args.assignedTask.targetLocale
7281
- );
7282
- return `[${_chalk2.default.hex(colors.yellow)(`${args.percentage}%`)}] Processing: ${_chalk2.default.dim(
7283
- displayPath
7284
- )} (${_chalk2.default.hex(colors.yellow)(args.assignedTask.sourceLocale)} -> ${_chalk2.default.hex(
7285
- colors.yellow
7286
- )(args.assignedTask.targetLocale)})`;
7287
- }
7288
- function createExecutionProgressMessage(ctx) {
7289
- const succeededTasksCount = countTasks(
7290
- ctx,
7291
- (_t, result) => result.status === "success"
7292
- );
7293
- const failedTasksCount = countTasks(
7294
- ctx,
7295
- (_t, result) => result.status === "error"
7296
- );
7297
- const skippedTasksCount = countTasks(
7298
- ctx,
7299
- (_t, result) => result.status === "skipped"
7300
- );
7301
- return `Processed ${_chalk2.default.green(succeededTasksCount)}/${ctx.tasks.length}, Failed ${_chalk2.default.red(failedTasksCount)}, Skipped ${_chalk2.default.dim(skippedTasksCount)}`;
7302
- }
7303
- function createLoaderForTask(assignedTask) {
7304
- const bucketLoader = createBucketLoader(
7305
- assignedTask.bucketType,
7306
- assignedTask.bucketPathPattern,
7307
- {
7308
- defaultLocale: assignedTask.sourceLocale,
7309
- isCacheRestore: false,
7310
- injectLocale: assignedTask.injectLocale
6793
+ );
6794
+ return mr.iid;
6795
+ }
6796
+ async commentOnPullRequest({
6797
+ pullRequestNumber,
6798
+ body
6799
+ }) {
6800
+ await this.gitlab.MergeRequestNotes.create(
6801
+ this.platformConfig.gitlabProjectId,
6802
+ pullRequestNumber,
6803
+ body
6804
+ );
6805
+ }
6806
+ gitConfig() {
6807
+ const glToken = this.platformConfig.glToken;
6808
+ const url = `https://oauth2:${glToken}@gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}.git`;
6809
+ super.gitConfig(glToken, url);
6810
+ }
6811
+ buildPullRequestUrl(pullRequestNumber) {
6812
+ return `https://gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}/-/merge_requests/${pullRequestNumber}`;
6813
+ }
6814
+ };
6815
+
6816
+ // src/cli/cmd/ci/platforms/index.ts
6817
+ var getPlatformKit = () => {
6818
+ if (process.env.BITBUCKET_PIPELINE_UUID) {
6819
+ return new BitbucketPlatformKit();
6820
+ }
6821
+ if (process.env.GITHUB_ACTION) {
6822
+ return new GitHubPlatformKit();
6823
+ }
6824
+ if (process.env.GITLAB_CI) {
6825
+ return new GitlabPlatformKit();
6826
+ }
6827
+ throw new Error("This platform is not supported");
6828
+ };
6829
+
6830
+ // src/cli/cmd/ci/index.ts
6831
+ var ci_default = new (0, _interactivecommander.Command)().command("ci").description("Run Lingo.dev CI/CD action").helpOption("-h, --help", "Show help").option("--parallel", "Run in parallel mode", (val) => {
6832
+ if (typeof val === "boolean") return val;
6833
+ return _optionalChain([val, 'optionalAccess', _210 => _210.toLowerCase, 'call', _211 => _211()]) === "true";
6834
+ }).option("--api-key <key>", "API key").option("--pull-request [boolean]", "Create a pull request with the changes").option("--commit-message <message>", "Commit message").option("--pull-request-title <title>", "Pull request title").option("--working-directory <dir>", "Working directory").option(
6835
+ "--process-own-commits [boolean]",
6836
+ "Process commits made by this action"
6837
+ ).action(async (options) => {
6838
+ const settings = getSettings(options.apiKey);
6839
+ console.log(options);
6840
+ if (!settings.auth.apiKey) {
6841
+ console.error("No API key provided");
6842
+ return;
6843
+ }
6844
+ const authenticator = createAuthenticator({
6845
+ apiUrl: settings.auth.apiUrl,
6846
+ apiKey: settings.auth.apiKey
6847
+ });
6848
+ const auth = await authenticator.whoami();
6849
+ if (!auth) {
6850
+ console.error("Not authenticated");
6851
+ return;
6852
+ }
6853
+ const env = {
6854
+ LINGODOTDEV_API_KEY: settings.auth.apiKey,
6855
+ LINGODOTDEV_PULL_REQUEST: _optionalChain([options, 'access', _212 => _212.pullRequest, 'optionalAccess', _213 => _213.toString, 'call', _214 => _214()]) || "false",
6856
+ ...options.commitMessage && {
6857
+ LINGODOTDEV_COMMIT_MESSAGE: options.commitMessage
7311
6858
  },
7312
- assignedTask.lockedKeys,
7313
- assignedTask.lockedPatterns
7314
- );
7315
- bucketLoader.setDefaultLocale(assignedTask.sourceLocale);
7316
- return bucketLoader;
7317
- }
7318
- function createWorkerTask(args) {
7319
- return {
7320
- title: "Initializing...",
7321
- task: async (_subCtx, subTask) => {
7322
- for (const assignedTask of args.assignedTasks) {
7323
- subTask.title = createWorkerStatusMessage({
7324
- assignedTask,
7325
- percentage: 0
7326
- });
7327
- const bucketLoader = createLoaderForTask(assignedTask);
7328
- const deltaProcessor = createDeltaProcessor(
7329
- assignedTask.bucketPathPattern
7330
- );
7331
- const taskResult = await args.i18nLimiter(async () => {
7332
- try {
7333
- const sourceData = await bucketLoader.pull(
7334
- assignedTask.sourceLocale
7335
- );
7336
- const targetData = await bucketLoader.pull(
7337
- assignedTask.targetLocale
7338
- );
7339
- const checksums = await deltaProcessor.loadChecksums();
7340
- const delta = await deltaProcessor.calculateDelta({
7341
- sourceData,
7342
- targetData,
7343
- checksums
7344
- });
7345
- const processableData = _lodash2.default.chain(sourceData).entries().filter(
7346
- ([key, value]) => delta.added.includes(key) || delta.updated.includes(key) || !!args.ctx.flags.force
7347
- ).fromPairs().value();
7348
- if (!Object.keys(processableData).length) {
7349
- return { status: "skipped" };
7350
- }
7351
- const processedTargetData = await args.ctx.localizer.localize(
7352
- {
7353
- sourceLocale: assignedTask.sourceLocale,
7354
- targetLocale: assignedTask.targetLocale,
7355
- sourceData,
7356
- targetData,
7357
- processableData
7358
- },
7359
- (progress) => {
7360
- subTask.title = createWorkerStatusMessage({
7361
- assignedTask,
7362
- percentage: progress
7363
- });
7364
- }
7365
- );
7366
- let finalTargetData = _lodash2.default.merge(
7367
- {},
7368
- sourceData,
7369
- targetData,
7370
- processedTargetData
7371
- );
7372
- finalTargetData = _lodash2.default.chain(finalTargetData).entries().map(([key, value]) => {
7373
- const renaming = delta.renamed.find(
7374
- ([oldKey]) => oldKey === key
7375
- );
7376
- if (!renaming) {
7377
- return [key, value];
7378
- }
7379
- return [renaming[1], value];
7380
- }).fromPairs().value();
7381
- await args.ioLimiter(async () => {
7382
- await bucketLoader.pull(assignedTask.sourceLocale);
7383
- await bucketLoader.push(
7384
- assignedTask.targetLocale,
7385
- finalTargetData
7386
- );
7387
- const checksums2 = await deltaProcessor.createChecksums(sourceData);
7388
- await deltaProcessor.saveChecksums(checksums2);
7389
- });
7390
- return { status: "success" };
7391
- } catch (error) {
7392
- return {
7393
- status: "error",
7394
- error
7395
- };
7396
- }
7397
- });
7398
- args.ctx.results.set(assignedTask, taskResult);
7399
- }
7400
- subTask.title = "Done";
6859
+ ...options.pullRequestTitle && {
6860
+ LINGODOTDEV_PULL_REQUEST_TITLE: options.pullRequestTitle
6861
+ },
6862
+ ...options.workingDirectory && {
6863
+ LINGODOTDEV_WORKING_DIRECTORY: options.workingDirectory
6864
+ },
6865
+ ...options.processOwnCommits && {
6866
+ LINGODOTDEV_PROCESS_OWN_COMMITS: options.processOwnCommits.toString()
7401
6867
  }
7402
6868
  };
7403
- }
7404
- function countTasks(ctx, predicate) {
7405
- return Array.from(ctx.results.entries()).filter(
7406
- ([task, result]) => predicate(task, result)
7407
- ).length;
7408
- }
7409
-
7410
- // src/cli/cmd/run/_types.ts
7411
-
7412
-
7413
-
7414
-
7415
-
7416
- var flagsSchema2 = _zod.z.object({
7417
- locale: _zod.z.array(__spec.localeCodeSchema).optional(),
7418
- bucket: _zod.z.array(__spec.bucketTypeSchema).optional(),
7419
- key: _zod.z.array(_zod.z.string()).optional(),
7420
- file: _zod.z.array(_zod.z.string()).optional(),
7421
- apiKey: _zod.z.string().optional(),
7422
- force: _zod.z.boolean().optional(),
7423
- frozen: _zod.z.boolean().optional(),
7424
- verbose: _zod.z.boolean().optional(),
7425
- strict: _zod.z.boolean().optional(),
7426
- interactive: _zod.z.boolean().default(false),
7427
- concurrency: _zod.z.number().positive().default(10),
7428
- debug: _zod.z.boolean().default(false)
6869
+ process.env = { ...process.env, ...env };
6870
+ const ora = _ora2.default.call(void 0, );
6871
+ const platformKit = getPlatformKit();
6872
+ const { isPullRequestMode } = platformKit.config;
6873
+ ora.info(`Pull request mode: ${isPullRequestMode ? "on" : "off"}`);
6874
+ const flow = isPullRequestMode ? new PullRequestFlow(ora, platformKit) : new InBranchFlow(ora, platformKit);
6875
+ const canRun = await _optionalChain([flow, 'access', _215 => _215.preRun, 'optionalCall', _216 => _216()]);
6876
+ if (canRun === false) {
6877
+ return;
6878
+ }
6879
+ const hasChanges = await flow.run({
6880
+ parallel: options.parallel
6881
+ });
6882
+ if (!hasChanges) {
6883
+ return;
6884
+ }
6885
+ await _optionalChain([flow, 'access', _217 => _217.postRun, 'optionalCall', _218 => _218()]);
7429
6886
  });
7430
6887
 
7431
- // src/cli/cmd/run/_render.ts
6888
+ // src/cli/cmd/status.ts
7432
6889
 
7433
6890
 
7434
6891
 
7435
- var _readline = require('readline'); var _readline2 = _interopRequireDefault(_readline);
7436
- async function renderClear2() {
7437
- console.log("\x1Bc");
7438
- }
7439
- async function renderSpacer2() {
7440
- console.log(" ");
7441
- }
7442
- async function renderBanner2() {
7443
- console.log(
7444
- _gradientstring.vice.call(void 0,
7445
- _figlet2.default.textSync("LINGO.DEV", {
7446
- font: "ANSI Shadow",
7447
- horizontalLayout: "default",
7448
- verticalLayout: "default"
7449
- })
7450
- )
7451
- );
7452
- }
7453
- async function renderHero2() {
7454
- console.log(
7455
- `\u26A1\uFE0F ${_chalk2.default.hex(colors.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
7456
- );
7457
- console.log("");
7458
- const label1 = "\u2B50 GitHub Repo:";
7459
- const label2 = "\u{1F4DA} Docs:";
7460
- const label3 = "\u{1F4AC} 24/7 Support:";
7461
- const maxLabelWidth = 17;
7462
- console.log(
7463
- `${_chalk2.default.hex(colors.blue)(label1.padEnd(maxLabelWidth))} ${_chalk2.default.hex(colors.blue)("https://lingo.dev/go/gh")}`
7464
- );
7465
- console.log(
7466
- `${_chalk2.default.hex(colors.blue)(label2.padEnd(maxLabelWidth + 1))} ${_chalk2.default.hex(colors.blue)("https://lingo.dev/go/docs")}`
7467
- );
7468
- console.log(
7469
- `${_chalk2.default.hex(colors.blue)(label3.padEnd(maxLabelWidth + 1))} ${_chalk2.default.hex(colors.blue)("hi@lingo.dev")}`
7470
- );
7471
- }
7472
- async function pauseIfDebug(debug) {
7473
- if (debug) {
7474
- await waitForUserPrompt("Press Enter to continue...");
7475
- }
7476
- }
7477
- async function waitForUserPrompt(message) {
7478
- const rl = _readline2.default.createInterface({
7479
- input: process.stdin,
7480
- output: process.stdout
7481
- });
7482
- return new Promise((resolve) => {
7483
- rl.question(_chalk2.default.dim(`[${message}]
7484
- `), () => {
7485
- rl.close();
7486
- resolve();
7487
- });
7488
- });
7489
- }
7490
- async function renderSummary(ctx) {
7491
- console.log(_chalk2.default.hex(colors.green)("[Done]"));
7492
- const skippedTasksCount = Array.from(ctx.results.values()).filter(
7493
- (r) => r.status === "skipped"
7494
- ).length;
7495
- console.log(`\u2022 ${_chalk2.default.hex(colors.yellow)(skippedTasksCount)} from cache`);
7496
- const succeededTasksCount = Array.from(ctx.results.values()).filter(
7497
- (r) => r.status === "success"
7498
- ).length;
7499
- console.log(`\u2022 ${_chalk2.default.hex(colors.yellow)(succeededTasksCount)} processed`);
7500
- const failedTasksCount = Array.from(ctx.results.values()).filter(
7501
- (r) => r.status === "error"
7502
- ).length;
7503
- console.log(`\u2022 ${_chalk2.default.hex(colors.yellow)(failedTasksCount)} failed`);
7504
- }
7505
6892
 
7506
- // src/cli/cmd/run/index.ts
7507
- var run_default = new (0, _interactivecommander.Command)().command("run").description("Run Lingo.dev localization engine").helpOption("-h, --help", "Show help").option(
7508
- "--locale <locale>",
7509
- "Locale to process",
7510
- (val, prev) => prev ? [...prev, val] : [val]
7511
- ).option(
7512
- "--bucket <bucket>",
7513
- "Bucket to process",
7514
- (val, prev) => prev ? [...prev, val] : [val]
7515
- ).option(
7516
- "--file <file>",
7517
- "File to process. Process only files that include this string in their path. Useful if you have a lot of files and want to focus on a specific one. Specify more files separated by commas or spaces.",
7518
- (val, prev) => prev ? [...prev, val] : [val]
7519
- ).option(
7520
- "--key <key>",
7521
- "Key to process. Process only a specific translation key, useful for updating a single entry",
7522
- (val, prev) => prev ? [...prev, val] : [val]
7523
- ).option(
7524
- "--force",
7525
- "Ignore lockfile and process all keys, useful for full re-translation"
7526
- ).option(
7527
- "--api-key <api-key>",
7528
- "Explicitly set the API key to use, override the default API key from settings"
7529
- ).option(
7530
- "--debug",
7531
- "Pause execution at start for debugging purposes, waits for user confirmation before proceeding"
7532
- ).option(
7533
- "--concurrency <concurrency>",
7534
- "Number of concurrent tasks to run",
7535
- (val) => parseInt(val)
7536
- ).action(async (args) => {
6893
+
6894
+ var _clitable3 = require('cli-table3'); var _clitable32 = _interopRequireDefault(_clitable3);
6895
+ var status_default = new (0, _interactivecommander.Command)().command("status").description("Show the status of the localization process").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
6896
+ "--file [files...]",
6897
+ "File to process. Process only a specific path, may contain asterisk * to match multiple files."
6898
+ ).option("--force", "Ignore lockfile and process all keys, useful for estimating full re-translation").option("--verbose", "Show detailed output including key-level word counts").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").action(async function(options) {
6899
+ const ora = _ora2.default.call(void 0, );
6900
+ const flags = parseFlags2(options);
6901
+ let authId = null;
7537
6902
  try {
7538
- const ctx = {
7539
- flags: flagsSchema2.parse(args),
7540
- config: null,
7541
- results: /* @__PURE__ */ new Map(),
7542
- tasks: [],
7543
- localizer: null
7544
- };
7545
- await pauseIfDebug(ctx.flags.debug);
7546
- await renderClear2();
7547
- await renderSpacer2();
7548
- await renderBanner2();
7549
- await renderHero2();
7550
- await renderSpacer2();
7551
- await setup(ctx);
7552
- await renderSpacer2();
7553
- await plan(ctx);
7554
- await renderSpacer2();
7555
- await execute(ctx);
7556
- await renderSpacer2();
7557
- await renderSummary(ctx);
7558
- await renderSpacer2();
7559
- } catch (error) {
7560
- process.exit(1);
7561
- }
7562
- });
6903
+ ora.start("Loading configuration...");
6904
+ const i18nConfig = getConfig();
6905
+ const settings = getSettings(flags.apiKey);
6906
+ ora.succeed("Configuration loaded");
6907
+ try {
6908
+ ora.start("Checking authentication status...");
6909
+ const auth = await tryAuthenticate(settings);
6910
+ if (auth) {
6911
+ authId = auth.id;
6912
+ ora.succeed(`Authenticated as ${auth.email}`);
6913
+ } else {
6914
+ ora.info(
6915
+ "Not authenticated. Continuing without authentication. (Run `lingo.dev auth --login` to authenticate)"
6916
+ );
6917
+ }
6918
+ } catch (error) {
6919
+ ora.info("Authentication failed. Continuing without authentication.");
6920
+ }
6921
+ ora.start("Validating localization configuration...");
6922
+ validateParams2(i18nConfig, flags);
6923
+ ora.succeed("Localization configuration is valid");
6924
+ trackEvent(authId || "status", "cmd.status.start", {
6925
+ i18nConfig,
6926
+ flags
6927
+ });
6928
+ let buckets = getBuckets(i18nConfig);
6929
+ if (_optionalChain([flags, 'access', _219 => _219.bucket, 'optionalAccess', _220 => _220.length])) {
6930
+ buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
6931
+ }
6932
+ ora.succeed("Buckets retrieved");
6933
+ if (_optionalChain([flags, 'access', _221 => _221.file, 'optionalAccess', _222 => _222.length])) {
6934
+ buckets = buckets.map((bucket) => {
6935
+ const paths = bucket.paths.filter((path16) => flags.file.find((file) => _optionalChain([path16, 'access', _223 => _223.pathPattern, 'optionalAccess', _224 => _224.match, 'call', _225 => _225(file)])));
6936
+ return { ...bucket, paths };
6937
+ }).filter((bucket) => bucket.paths.length > 0);
6938
+ if (buckets.length === 0) {
6939
+ ora.fail("No buckets found. All buckets were filtered out by --file option.");
6940
+ process.exit(1);
6941
+ } else {
6942
+ ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
6943
+ buckets.map((bucket) => {
6944
+ ora.info(` ${bucket.type}:`);
6945
+ bucket.paths.forEach((path16) => {
6946
+ ora.info(` - ${path16.pathPattern}`);
6947
+ });
6948
+ });
6949
+ }
6950
+ }
6951
+ const targetLocales = _optionalChain([flags, 'access', _226 => _226.locale, 'optionalAccess', _227 => _227.length]) ? flags.locale : i18nConfig.locale.targets;
6952
+ let totalSourceKeyCount = 0;
6953
+ let uniqueKeysToTranslate = 0;
6954
+ let totalExistingTranslations = 0;
6955
+ const totalWordCount = /* @__PURE__ */ new Map();
6956
+ const languageStats = {};
6957
+ for (const locale of targetLocales) {
6958
+ languageStats[locale] = {
6959
+ complete: 0,
6960
+ missing: 0,
6961
+ updated: 0,
6962
+ words: 0
6963
+ };
6964
+ totalWordCount.set(locale, 0);
6965
+ }
6966
+ const fileStats = {};
6967
+ for (const bucket of buckets) {
6968
+ try {
6969
+ console.log();
6970
+ ora.info(`Analyzing bucket: ${bucket.type}`);
6971
+ for (const bucketPath of bucket.paths) {
6972
+ const bucketOra = _ora2.default.call(void 0, { indent: 2 }).info(`Analyzing path: ${bucketPath.pathPattern}`);
6973
+ const sourceLocale = __spec.resolveOverriddenLocale.call(void 0, i18nConfig.locale.source, bucketPath.delimiter);
6974
+ const bucketLoader = createBucketLoader(
6975
+ bucket.type,
6976
+ bucketPath.pathPattern,
6977
+ {
6978
+ isCacheRestore: false,
6979
+ defaultLocale: sourceLocale,
6980
+ injectLocale: bucket.injectLocale
6981
+ },
6982
+ bucket.lockedKeys
6983
+ );
6984
+ bucketLoader.setDefaultLocale(sourceLocale);
6985
+ await bucketLoader.init();
6986
+ const filePath = bucketPath.pathPattern;
6987
+ if (!fileStats[filePath]) {
6988
+ fileStats[filePath] = {
6989
+ path: filePath,
6990
+ sourceKeys: 0,
6991
+ wordCount: 0,
6992
+ languageStats: {}
6993
+ };
6994
+ for (const locale of targetLocales) {
6995
+ fileStats[filePath].languageStats[locale] = {
6996
+ complete: 0,
6997
+ missing: 0,
6998
+ updated: 0,
6999
+ words: 0
7000
+ };
7001
+ }
7002
+ }
7003
+ const sourceData = await bucketLoader.pull(sourceLocale);
7004
+ const sourceKeys = Object.keys(sourceData);
7005
+ fileStats[filePath].sourceKeys = sourceKeys.length;
7006
+ totalSourceKeyCount += sourceKeys.length;
7007
+ let sourceWordCount = 0;
7008
+ for (const key of sourceKeys) {
7009
+ const value = sourceData[key];
7010
+ if (typeof value === "string") {
7011
+ const words = value.trim().split(/\s+/).length;
7012
+ sourceWordCount += words;
7013
+ }
7014
+ }
7015
+ fileStats[filePath].wordCount = sourceWordCount;
7016
+ for (const _targetLocale of targetLocales) {
7017
+ const targetLocale = __spec.resolveOverriddenLocale.call(void 0, _targetLocale, bucketPath.delimiter);
7018
+ bucketOra.start(`[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`);
7019
+ let targetData = {};
7020
+ let fileExists = true;
7021
+ try {
7022
+ targetData = await bucketLoader.pull(targetLocale);
7023
+ } catch (error) {
7024
+ fileExists = false;
7025
+ bucketOra.info(
7026
+ `[${sourceLocale} -> ${targetLocale}] Target file not found, assuming all keys need translation.`
7027
+ );
7028
+ }
7029
+ if (!fileExists) {
7030
+ fileStats[filePath].languageStats[targetLocale].missing = sourceKeys.length;
7031
+ fileStats[filePath].languageStats[targetLocale].words = sourceWordCount;
7032
+ languageStats[targetLocale].missing += sourceKeys.length;
7033
+ languageStats[targetLocale].words += sourceWordCount;
7034
+ totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + sourceWordCount);
7035
+ bucketOra.succeed(
7036
+ `[${sourceLocale} -> ${targetLocale}] ${_chalk2.default.red(`0% complete`)} (0/${sourceKeys.length} keys) - file not found`
7037
+ );
7038
+ continue;
7039
+ }
7040
+ const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
7041
+ const checksums = await deltaProcessor.loadChecksums();
7042
+ const delta = await deltaProcessor.calculateDelta({
7043
+ sourceData,
7044
+ targetData,
7045
+ checksums
7046
+ });
7047
+ const missingKeys = delta.added;
7048
+ const updatedKeys = delta.updated;
7049
+ const completeKeys = sourceKeys.filter((key) => !missingKeys.includes(key) && !updatedKeys.includes(key));
7050
+ let wordsToTranslate = 0;
7051
+ const keysToProcess = flags.force ? sourceKeys : [...missingKeys, ...updatedKeys];
7052
+ for (const key of keysToProcess) {
7053
+ const value = sourceData[String(key)];
7054
+ if (typeof value === "string") {
7055
+ const words = value.trim().split(/\s+/).length;
7056
+ wordsToTranslate += words;
7057
+ }
7058
+ }
7059
+ fileStats[filePath].languageStats[targetLocale].missing = missingKeys.length;
7060
+ fileStats[filePath].languageStats[targetLocale].updated = updatedKeys.length;
7061
+ fileStats[filePath].languageStats[targetLocale].complete = completeKeys.length;
7062
+ fileStats[filePath].languageStats[targetLocale].words = wordsToTranslate;
7063
+ languageStats[targetLocale].missing += missingKeys.length;
7064
+ languageStats[targetLocale].updated += updatedKeys.length;
7065
+ languageStats[targetLocale].complete += completeKeys.length;
7066
+ languageStats[targetLocale].words += wordsToTranslate;
7067
+ totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + wordsToTranslate);
7068
+ const totalKeysInFile = sourceKeys.length;
7069
+ const completionPercent = (completeKeys.length / totalKeysInFile * 100).toFixed(1);
7070
+ if (missingKeys.length === 0 && updatedKeys.length === 0) {
7071
+ bucketOra.succeed(
7072
+ `[${sourceLocale} -> ${targetLocale}] ${_chalk2.default.green(`100% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`
7073
+ );
7074
+ } else {
7075
+ const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? _chalk2.default.yellow(`${completionPercent}% complete`) : _chalk2.default.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
7076
+ bucketOra.succeed(message);
7077
+ if (flags.verbose) {
7078
+ if (missingKeys.length > 0) {
7079
+ console.log(` ${_chalk2.default.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`);
7080
+ console.log(
7081
+ ` ${_chalk2.default.dim(`Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`)}`
7082
+ );
7083
+ }
7084
+ if (updatedKeys.length > 0) {
7085
+ console.log(` ${_chalk2.default.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`);
7086
+ }
7087
+ }
7088
+ }
7089
+ }
7090
+ }
7091
+ } catch (error) {
7092
+ ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);
7093
+ }
7094
+ }
7095
+ const totalKeysNeedingTranslation = Object.values(languageStats).reduce((sum, stats) => {
7096
+ return sum + stats.missing + stats.updated;
7097
+ }, 0);
7098
+ const totalCompletedKeys = totalSourceKeyCount - totalKeysNeedingTranslation / targetLocales.length;
7099
+ console.log();
7100
+ ora.succeed(_chalk2.default.green(`Localization status completed.`));
7101
+ console.log(_chalk2.default.bold.cyan(`
7102
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
7103
+ console.log(_chalk2.default.bold.cyan(`\u2551 LOCALIZATION STATUS REPORT \u2551`));
7104
+ console.log(_chalk2.default.bold.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
7105
+ console.log(_chalk2.default.bold(`
7106
+ \u{1F4DD} SOURCE CONTENT:`));
7107
+ console.log(`\u2022 Source language: ${_chalk2.default.green(i18nConfig.locale.source)}`);
7108
+ console.log(`\u2022 Source keys: ${_chalk2.default.yellow(totalSourceKeyCount.toString())} keys across all files`);
7109
+ console.log(_chalk2.default.bold(`
7110
+ \u{1F310} LANGUAGE BY LANGUAGE BREAKDOWN:`));
7111
+ const table = new (0, _clitable32.default)({
7112
+ head: ["Language", "Status", "Complete", "Missing", "Updated", "Total Keys", "Words to Translate"],
7113
+ style: {
7114
+ head: ["white"],
7115
+ // White color for headers
7116
+ border: []
7117
+ // No color for borders
7118
+ },
7119
+ colWidths: [12, 20, 18, 12, 12, 12, 15]
7120
+ // Explicit column widths, making Status column wider
7121
+ });
7122
+ let totalWordsToTranslate = 0;
7123
+ for (const locale of targetLocales) {
7124
+ const stats = languageStats[locale];
7125
+ const percentComplete = (stats.complete / totalSourceKeyCount * 100).toFixed(1);
7126
+ const totalNeeded = stats.missing + stats.updated;
7127
+ let statusText;
7128
+ let statusColor;
7129
+ if (stats.missing === totalSourceKeyCount) {
7130
+ statusText = "\u{1F534} Not started";
7131
+ statusColor = _chalk2.default.red;
7132
+ } else if (stats.missing === 0 && stats.updated === 0) {
7133
+ statusText = "\u2705 Complete";
7134
+ statusColor = _chalk2.default.green;
7135
+ } else if (parseFloat(percentComplete) > 80) {
7136
+ statusText = "\u{1F7E1} Almost done";
7137
+ statusColor = _chalk2.default.yellow;
7138
+ } else if (parseFloat(percentComplete) > 0) {
7139
+ statusText = "\u{1F7E0} In progress";
7140
+ statusColor = _chalk2.default.yellow;
7141
+ } else {
7142
+ statusText = "\u{1F534} Not started";
7143
+ statusColor = _chalk2.default.red;
7144
+ }
7145
+ const words = totalWordCount.get(locale) || 0;
7146
+ totalWordsToTranslate += words;
7147
+ table.push([
7148
+ locale,
7149
+ statusColor(statusText),
7150
+ `${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,
7151
+ stats.missing > 0 ? _chalk2.default.red(stats.missing.toString()) : "0",
7152
+ stats.updated > 0 ? _chalk2.default.yellow(stats.updated.toString()) : "0",
7153
+ totalNeeded > 0 ? _chalk2.default.magenta(totalNeeded.toString()) : "0",
7154
+ words > 0 ? `~${words.toLocaleString()}` : "0"
7155
+ ]);
7156
+ }
7157
+ console.log(table.toString());
7158
+ console.log(_chalk2.default.bold(`
7159
+ \u{1F4CA} USAGE ESTIMATE:`));
7160
+ console.log(
7161
+ `\u2022 WORDS TO BE CONSUMED: ~${_chalk2.default.yellow.bold(totalWordsToTranslate.toLocaleString())} words across all languages`
7162
+ );
7163
+ console.log(` (Words are counted from source language for keys that need translation in target languages)`);
7164
+ if (targetLocales.length > 1) {
7165
+ console.log(`\u2022 Per-language breakdown:`);
7166
+ for (const locale of targetLocales) {
7167
+ const words = totalWordCount.get(locale) || 0;
7168
+ const percent = (words / totalWordsToTranslate * 100).toFixed(1);
7169
+ console.log(` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`);
7170
+ }
7171
+ }
7172
+ if (flags.confirm && Object.keys(fileStats).length > 0) {
7173
+ console.log(_chalk2.default.bold(`
7174
+ \u{1F4D1} BREAKDOWN BY FILE:`));
7175
+ Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path16, stats]) => {
7176
+ if (stats.sourceKeys === 0) return;
7177
+ console.log(_chalk2.default.bold(`
7178
+ \u2022 ${path16}:`));
7179
+ console.log(` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`);
7180
+ const fileTable = new (0, _clitable32.default)({
7181
+ head: ["Language", "Status", "Details"],
7182
+ style: {
7183
+ head: ["white"],
7184
+ border: []
7185
+ },
7186
+ colWidths: [12, 20, 50]
7187
+ // Explicit column widths for file detail table
7188
+ });
7189
+ for (const locale of targetLocales) {
7190
+ const langStats = stats.languageStats[locale];
7191
+ const complete = langStats.complete;
7192
+ const total = stats.sourceKeys;
7193
+ const completion = (complete / total * 100).toFixed(1);
7194
+ let status = "\u2705 Complete";
7195
+ let statusColor = _chalk2.default.green;
7196
+ if (langStats.missing === total) {
7197
+ status = "\u274C Not started";
7198
+ statusColor = _chalk2.default.red;
7199
+ } else if (langStats.missing > 0 || langStats.updated > 0) {
7200
+ status = `\u26A0\uFE0F ${completion}% complete`;
7201
+ statusColor = _chalk2.default.yellow;
7202
+ }
7203
+ let details = "";
7204
+ if (langStats.missing > 0 || langStats.updated > 0) {
7205
+ const parts = [];
7206
+ if (langStats.missing > 0) parts.push(`${langStats.missing} missing`);
7207
+ if (langStats.updated > 0) parts.push(`${langStats.updated} changed`);
7208
+ details = `${parts.join(", ")}, ~${langStats.words} words`;
7209
+ } else {
7210
+ details = "All keys translated";
7211
+ }
7212
+ fileTable.push([locale, statusColor(status), details]);
7213
+ }
7214
+ console.log(fileTable.toString());
7215
+ });
7216
+ }
7217
+ const completeLanguages = targetLocales.filter(
7218
+ (locale) => languageStats[locale].missing === 0 && languageStats[locale].updated === 0
7219
+ );
7220
+ const missingLanguages = targetLocales.filter((locale) => languageStats[locale].complete === 0);
7221
+ console.log(_chalk2.default.bold.green(`
7222
+ \u{1F4A1} OPTIMIZATION TIPS:`));
7223
+ if (missingLanguages.length > 0) {
7224
+ console.log(
7225
+ `\u2022 ${_chalk2.default.yellow(missingLanguages.join(", "))} ${missingLanguages.length === 1 ? "has" : "have"} no translations yet`
7226
+ );
7227
+ }
7228
+ if (completeLanguages.length > 0) {
7229
+ console.log(
7230
+ `\u2022 ${_chalk2.default.green(completeLanguages.join(", "))} ${completeLanguages.length === 1 ? "is" : "are"} completely translated`
7231
+ );
7232
+ }
7233
+ if (targetLocales.length > 1) {
7234
+ console.log(`\u2022 Translating one language at a time reduces complexity`);
7235
+ console.log(`\u2022 Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`);
7236
+ }
7237
+ trackEvent(authId || "status", "cmd.status.success", {
7238
+ i18nConfig,
7239
+ flags,
7240
+ totalSourceKeyCount,
7241
+ languageStats,
7242
+ totalWordsToTranslate,
7243
+ authenticated: !!authId
7244
+ });
7245
+ } catch (error) {
7246
+ ora.fail(error.message);
7247
+ trackEvent(authId || "status", "cmd.status.error", {
7248
+ flags,
7249
+ error: error.message,
7250
+ authenticated: !!authId
7251
+ });
7252
+ process.exit(1);
7253
+ }
7254
+ });
7255
+ function parseFlags2(options) {
7256
+ return _zod2.default.object({
7257
+ locale: _zod2.default.array(__spec.localeCodeSchema).optional(),
7258
+ bucket: _zod2.default.array(__spec.bucketTypeSchema).optional(),
7259
+ force: _zod2.default.boolean().optional(),
7260
+ confirm: _zod2.default.boolean().optional(),
7261
+ verbose: _zod2.default.boolean().optional(),
7262
+ file: _zod2.default.array(_zod2.default.string()).optional(),
7263
+ apiKey: _zod2.default.string().optional()
7264
+ }).parse(options);
7265
+ }
7266
+ async function tryAuthenticate(settings) {
7267
+ if (!settings.auth.apiKey) {
7268
+ return null;
7269
+ }
7270
+ try {
7271
+ const authenticator = createAuthenticator({
7272
+ apiKey: settings.auth.apiKey,
7273
+ apiUrl: settings.auth.apiUrl
7274
+ });
7275
+ const user = await authenticator.whoami();
7276
+ return user;
7277
+ } catch (error) {
7278
+ return null;
7279
+ }
7280
+ }
7281
+ function validateParams2(i18nConfig, flags) {
7282
+ if (!i18nConfig) {
7283
+ throw new CLIError({
7284
+ message: "i18n.json not found. Please run `lingo.dev init` to initialize the project.",
7285
+ docUrl: "i18nNotFound"
7286
+ });
7287
+ } else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {
7288
+ throw new CLIError({
7289
+ message: "No buckets found in i18n.json. Please add at least one bucket containing i18n content.",
7290
+ docUrl: "bucketNotFound"
7291
+ });
7292
+ } else if (_optionalChain([flags, 'access', _228 => _228.locale, 'optionalAccess', _229 => _229.some, 'call', _230 => _230((locale) => !i18nConfig.locale.targets.includes(locale))])) {
7293
+ throw new CLIError({
7294
+ message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
7295
+ docUrl: "localeTargetNotFound"
7296
+ });
7297
+ } else if (_optionalChain([flags, 'access', _231 => _231.bucket, 'optionalAccess', _232 => _232.some, 'call', _233 => _233((bucket) => !i18nConfig.buckets[bucket])])) {
7298
+ throw new CLIError({
7299
+ message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
7300
+ docUrl: "bucketNotFound"
7301
+ });
7302
+ }
7303
+ }
7304
+
7305
+ // src/cli/cmd/may-the-fourth.ts
7306
+
7307
+
7308
+
7309
+
7310
+
7311
+ var colors2 = {
7312
+ orange: "#ff6600",
7313
+ green: "#6ae300",
7314
+ blue: "#0090ff",
7315
+ yellow: "#ffcc00",
7316
+ grey: "#808080",
7317
+ red: "#ff0000"
7318
+ };
7319
+ var may_the_fourth_default = new (0, _interactivecommander.Command)().command("may-the-fourth").description("May the Fourth be with you").helpOption("-h, --help", "Show help").action(async () => {
7320
+ await renderClear2();
7321
+ await renderBanner2();
7322
+ await renderSpacer2();
7323
+ console.log(_chalk2.default.hex(colors2.yellow)("Loading the Star Wars movie..."));
7324
+ await renderSpacer2();
7325
+ await new Promise((resolve, reject) => {
7326
+ const ssh = cp.spawn("ssh", ["starwarstel.net"], {
7327
+ stdio: "inherit"
7328
+ });
7329
+ ssh.on("close", (code) => {
7330
+ if (code !== 0) {
7331
+ console.error(`SSH process exited with code ${code}`);
7332
+ }
7333
+ resolve();
7334
+ });
7335
+ ssh.on("error", (err) => {
7336
+ console.error("Failed to start SSH process:", err);
7337
+ reject(err);
7338
+ });
7339
+ });
7340
+ await renderSpacer2();
7341
+ console.log(
7342
+ `${_chalk2.default.hex(colors2.green)("We hope you enjoyed it! :)")} ${_chalk2.default.hex(colors2.blue)("May the Fourth be with you! \u{1F680}")}`
7343
+ );
7344
+ await renderSpacer2();
7345
+ console.log(_chalk2.default.dim(`---`));
7346
+ await renderSpacer2();
7347
+ await renderHero2();
7348
+ });
7349
+ async function renderClear2() {
7350
+ console.log("\x1Bc");
7351
+ }
7352
+ async function renderSpacer2() {
7353
+ console.log(" ");
7354
+ }
7355
+ async function renderBanner2() {
7356
+ console.log(
7357
+ _gradientstring.vice.call(void 0,
7358
+ _figlet2.default.textSync("LINGO.DEV", {
7359
+ font: "ANSI Shadow",
7360
+ horizontalLayout: "default",
7361
+ verticalLayout: "default"
7362
+ })
7363
+ )
7364
+ );
7365
+ }
7366
+ async function renderHero2() {
7367
+ console.log(
7368
+ `\u26A1\uFE0F ${_chalk2.default.hex(colors2.green)("Lingo.dev")} - open-source, AI-powered i18n CLI for web & mobile localization.`
7369
+ );
7370
+ console.log(" ");
7371
+ console.log(
7372
+ _chalk2.default.hex(colors2.blue)("\u2B50 GitHub Repo: https://lingo.dev/go/gh")
7373
+ );
7374
+ console.log(_chalk2.default.hex(colors2.blue)("\u{1F4AC} 24/7 Support: hi@lingo.dev"));
7375
+ }
7376
+
7377
+ // package.json
7378
+ var package_default = {
7379
+ name: "lingo.dev",
7380
+ version: "0.93.6",
7381
+ description: "Lingo.dev CLI",
7382
+ private: false,
7383
+ publishConfig: {
7384
+ access: "public"
7385
+ },
7386
+ type: "module",
7387
+ sideEffects: false,
7388
+ exports: {
7389
+ "./cli": {
7390
+ types: "./build/cli.d.ts",
7391
+ import: "./build/cli.mjs",
7392
+ require: "./build/cli.cjs"
7393
+ },
7394
+ "./sdk": {
7395
+ types: "./build/sdk.d.ts",
7396
+ import: "./build/sdk.mjs",
7397
+ require: "./build/sdk.cjs"
7398
+ },
7399
+ "./spec": {
7400
+ types: "./build/spec.d.ts",
7401
+ import: "./build/spec.mjs",
7402
+ require: "./build/spec.cjs"
7403
+ },
7404
+ "./compiler": {
7405
+ types: "./build/compiler.d.ts",
7406
+ import: "./build/compiler.mjs",
7407
+ require: "./build/compiler.cjs"
7408
+ },
7409
+ "./react": {
7410
+ types: "./build/react.d.ts",
7411
+ import: "./build/react.mjs",
7412
+ require: "./build/react.cjs"
7413
+ },
7414
+ "./react-client": {
7415
+ types: "./build/react/client.d.ts",
7416
+ import: "./build/react/client.mjs",
7417
+ require: "./build/react/client.cjs"
7418
+ },
7419
+ "./react/client": {
7420
+ types: "./build/react/client.d.ts",
7421
+ import: "./build/react/client.mjs",
7422
+ require: "./build/react/client.cjs"
7423
+ },
7424
+ "./react-rsc": {
7425
+ types: "./build/react/rsc.d.ts",
7426
+ import: "./build/react/rsc.mjs",
7427
+ require: "./build/react/rsc.cjs"
7428
+ },
7429
+ "./react/rsc": {
7430
+ types: "./build/react/rsc.d.ts",
7431
+ import: "./build/react/rsc.mjs",
7432
+ require: "./build/react/rsc.cjs"
7433
+ },
7434
+ "./react-router": {
7435
+ types: "./build/react/react-router.d.ts",
7436
+ import: "./build/react/react-router.mjs",
7437
+ require: "./build/react/react-router.cjs"
7438
+ },
7439
+ "./react/react-router": {
7440
+ types: "./build/react/react-router.d.ts",
7441
+ import: "./build/react/react-router.mjs",
7442
+ require: "./build/react/react-router.cjs"
7443
+ }
7444
+ },
7445
+ typesVersions: {
7446
+ "*": {
7447
+ sdk: [
7448
+ "./build/sdk.d.ts"
7449
+ ],
7450
+ cli: [
7451
+ "./build/cli.d.ts"
7452
+ ],
7453
+ spec: [
7454
+ "./build/spec.d.ts"
7455
+ ],
7456
+ compiler: [
7457
+ "./build/compiler.d.ts"
7458
+ ],
7459
+ react: [
7460
+ "./build/react.d.ts"
7461
+ ],
7462
+ "react/client": [
7463
+ "./build/react/client.d.ts"
7464
+ ],
7465
+ "react/rsc": [
7466
+ "./build/react/rsc.d.ts"
7467
+ ],
7468
+ "react/react-router": [
7469
+ "./build/react/react-router.d.ts"
7470
+ ]
7471
+ }
7472
+ },
7473
+ bin: {
7474
+ "lingo.dev": "./bin/cli.mjs"
7475
+ },
7476
+ files: [
7477
+ "bin",
7478
+ "build"
7479
+ ],
7480
+ scripts: {
7481
+ "lingo.dev": "node --inspect=9229 ./bin/cli.mjs",
7482
+ dev: "tsup --watch",
7483
+ build: "tsc --noEmit && tsup",
7484
+ test: "vitest run",
7485
+ "test:watch": "vitest",
7486
+ clean: "rm -rf build"
7487
+ },
7488
+ keywords: [],
7489
+ author: "",
7490
+ license: "Apache-2.0",
7491
+ dependencies: {
7492
+ "@ai-sdk/anthropic": "^1.2.11",
7493
+ "@ai-sdk/openai": "^1.3.22",
7494
+ "@babel/generator": "^7.27.1",
7495
+ "@babel/parser": "^7.27.1",
7496
+ "@babel/traverse": "^7.27.4",
7497
+ "@babel/types": "^7.27.1",
7498
+ "@datocms/cma-client-node": "^4.0.1",
7499
+ "@gitbeaker/rest": "^39.34.3",
7500
+ "@inkjs/ui": "^2.0.0",
7501
+ "@inquirer/prompts": "^7.4.1",
7502
+ "@lingo.dev/_sdk": "workspace:*",
7503
+ "@lingo.dev/_spec": "workspace:*",
7504
+ "@lingo.dev/_react": "workspace:*",
7505
+ "@lingo.dev/_compiler": "workspace:*",
7506
+ "@modelcontextprotocol/sdk": "^1.5.0",
7507
+ "@paralleldrive/cuid2": "^2.2.2",
7508
+ ai: "^4.3.15",
7509
+ bitbucket: "^2.12.0",
7510
+ chalk: "^5.4.1",
7511
+ "cli-progress": "^3.12.0",
7512
+ "cli-table3": "^0.6.5",
7513
+ cors: "^2.8.5",
7514
+ "csv-parse": "^5.6.0",
7515
+ "csv-stringify": "^6.5.2",
7516
+ "date-fns": "^4.1.0",
7517
+ dedent: "^1.5.3",
7518
+ diff: "^7.0.0",
7519
+ dotenv: "^16.4.7",
7520
+ express: "^5.1.0",
7521
+ "external-editor": "^3.1.0",
7522
+ figlet: "^1.8.0",
7523
+ flat: "^6.0.1",
7524
+ "gettext-parser": "^8.0.0",
7525
+ glob: "<11.0.0",
7526
+ "gradient-string": "^3.0.0",
7527
+ "gray-matter": "^4.0.3",
7528
+ ini: "^5.0.0",
7529
+ ink: "^4.2.0",
7530
+ "ink-progress-bar": "^3.0.0",
7531
+ "ink-spinner": "^5.0.0",
7532
+ inquirer: "^12.6.0",
7533
+ "interactive-commander": "^0.5.194",
7534
+ "is-url": "^1.2.4",
7535
+ jsdom: "^25.0.1",
7536
+ json5: "^2.2.3",
7537
+ jsonrepair: "^3.11.2",
7538
+ listr2: "^8.3.2",
7539
+ lodash: "^4.17.21",
7540
+ marked: "^15.0.6",
7541
+ "mdast-util-from-markdown": "^2.0.2",
7542
+ "mdast-util-gfm": "^3.1.0",
7543
+ "micromark-extension-gfm": "^3.0.0",
7544
+ "node-machine-id": "^1.1.12",
7545
+ "node-webvtt": "^1.9.4",
7546
+ "object-hash": "^3.0.0",
7547
+ octokit: "^4.0.2",
7548
+ open: "^10.1.2",
7549
+ ora: "^8.1.1",
7550
+ "p-limit": "^6.2.0",
7551
+ "php-array-reader": "^2.1.2",
7552
+ plist: "^3.1.0",
7553
+ "posthog-node": "^4.17.0",
7554
+ prettier: "^3.4.2",
7555
+ react: "^18.3.1",
7556
+ "rehype-stringify": "^10.0.1",
7557
+ "remark-disable-tokenizers": "^1.1.1",
7558
+ "remark-frontmatter": "^5.0.0",
7559
+ "remark-gfm": "^4.0.1",
7560
+ "remark-mdx": "^3.1.0",
7561
+ "remark-mdx-frontmatter": "^5.1.0",
7562
+ "remark-parse": "^11.0.0",
7563
+ "remark-rehype": "^11.1.2",
7564
+ "remark-stringify": "^11.0.0",
7565
+ "srt-parser-2": "^1.2.3",
7566
+ unified: "^11.0.5",
7567
+ "unist-util-visit": "^5.0.0",
7568
+ vfile: "^6.0.3",
7569
+ xliff: "^6.2.1",
7570
+ xml2js: "^0.6.2",
7571
+ xpath: "^0.0.34",
7572
+ yaml: "^2.7.0",
7573
+ zod: "^3.24.1"
7574
+ },
7575
+ devDependencies: {
7576
+ "@types/babel__generator": "^7.27.0",
7577
+ "@types/cli-progress": "^3.11.6",
7578
+ "@types/cors": "^2.8.17",
7579
+ "@types/diff": "^7.0.0",
7580
+ "@types/express": "^5.0.1",
7581
+ "@types/figlet": "^1.7.0",
7582
+ "@types/gettext-parser": "^4.0.4",
7583
+ "@types/glob": "^8.1.0",
7584
+ "@types/ini": "^4.1.1",
7585
+ "@types/is-url": "^1.2.32",
7586
+ "@types/jsdom": "^21.1.7",
7587
+ "@types/lodash": "^4.17.16",
7588
+ "@types/mdast": "^4.0.4",
7589
+ "@types/node": "^22.10.2",
7590
+ "@types/node-gettext": "^3.0.6",
7591
+ "@types/object-hash": "^3.0.6",
7592
+ "@types/plist": "^3.0.5",
7593
+ "@types/react": "^18.3.20",
7594
+ "@types/xml2js": "^0.4.14",
7595
+ tsup: "^8.3.5",
7596
+ typescript: "^5.8.3",
7597
+ vitest: "^3.1.2"
7598
+ },
7599
+ engines: {
7600
+ node: ">=18"
7601
+ },
7602
+ packageManager: "pnpm@9.12.3"
7603
+ };
7563
7604
 
7564
7605
  // src/cli/index.ts
7565
7606
  _dotenv2.default.config();