@intlpullhq/cli 0.1.10 → 0.1.12

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.
@@ -12,19 +12,19 @@ import {
12
12
  WebhooksApi,
13
13
  WorkflowsApi,
14
14
  createApiClient
15
- } from "./chunk-WVCVQFBI.js";
15
+ } from "./chunk-QOUDOZX4.js";
16
16
  import {
17
17
  ProjectsApi
18
- } from "./chunk-KCZQUMQP.js";
18
+ } from "./chunk-RQI7KQQ5.js";
19
19
  import {
20
20
  TranslationsApi
21
- } from "./chunk-WSY27J6N.js";
21
+ } from "./chunk-OMGFIATE.js";
22
22
  import {
23
23
  KeysApi
24
- } from "./chunk-BULIQM4U.js";
24
+ } from "./chunk-DSTFGTTF.js";
25
25
  import {
26
26
  BaseApiClient
27
- } from "./chunk-KIDP7N6D.js";
27
+ } from "./chunk-ZYZSONV6.js";
28
28
  import "./chunk-IWYURZV2.js";
29
29
  export {
30
30
  ApiClient,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseApiClient
3
- } from "./chunk-KIDP7N6D.js";
3
+ } from "./chunk-ZYZSONV6.js";
4
4
 
5
5
  // src/lib/api/keys.ts
6
6
  var KeysApi = class extends BaseApiClient {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseApiClient
3
- } from "./chunk-KIDP7N6D.js";
3
+ } from "./chunk-ZYZSONV6.js";
4
4
  import {
5
5
  getProjectConfig
6
6
  } from "./chunk-IWYURZV2.js";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  ProjectsApi
3
- } from "./chunk-KCZQUMQP.js";
3
+ } from "./chunk-RQI7KQQ5.js";
4
4
  import {
5
5
  TranslationsApi
6
- } from "./chunk-WSY27J6N.js";
6
+ } from "./chunk-OMGFIATE.js";
7
7
  import {
8
8
  KeysApi
9
- } from "./chunk-BULIQM4U.js";
9
+ } from "./chunk-DSTFGTTF.js";
10
10
  import {
11
11
  BaseApiClient
12
- } from "./chunk-KIDP7N6D.js";
12
+ } from "./chunk-ZYZSONV6.js";
13
13
  import {
14
14
  getAuthErrorMessage
15
15
  } from "./chunk-IWYURZV2.js";
@@ -21,7 +21,8 @@ var ImportExportApi = class extends BaseApiClient {
21
21
  const params = new URLSearchParams();
22
22
  params.set("format", options.format);
23
23
  if (options.languages?.length) {
24
- params.set("languages", options.languages.join(","));
24
+ const normalizedLanguages = options.languages.map((lang) => lang.trim());
25
+ params.set("languages", normalizedLanguages.join(","));
25
26
  }
26
27
  if (options.namespaces?.length) {
27
28
  params.set("namespaces", options.namespaces.join(","));
@@ -493,7 +494,7 @@ var SnapshotsApi = class extends BaseApiClient {
493
494
  */
494
495
  async createSnapshot(projectId, data) {
495
496
  const response = await this.request(
496
- `/projects/${projectId}/snapshots`,
497
+ `/api/v1/projects/${projectId}/snapshots`,
497
498
  {
498
499
  method: "POST",
499
500
  body: JSON.stringify(data)
@@ -506,18 +507,18 @@ var SnapshotsApi = class extends BaseApiClient {
506
507
  */
507
508
  async listSnapshots(projectId) {
508
509
  const response = await this.request(
509
- `/projects/${projectId}/snapshots`,
510
+ `/api/v1/projects/${projectId}/snapshots`,
510
511
  {
511
512
  method: "GET"
512
513
  }
513
514
  );
514
- return response.snapshots;
515
+ return response?.snapshots ?? [];
515
516
  }
516
517
  /**
517
518
  * Get snapshot details
518
519
  */
519
520
  async getSnapshot(projectId, snapshotId) {
520
- return this.request(`/projects/${projectId}/snapshots/${snapshotId}`, {
521
+ return this.request(`/api/v1/projects/${projectId}/snapshots/${snapshotId}`, {
521
522
  method: "GET"
522
523
  });
523
524
  }
@@ -526,7 +527,7 @@ var SnapshotsApi = class extends BaseApiClient {
526
527
  */
527
528
  async restoreSnapshot(projectId, snapshotId) {
528
529
  return this.request(
529
- `/projects/${projectId}/snapshots/${snapshotId}/restore`,
530
+ `/api/v1/projects/${projectId}/snapshots/${snapshotId}/restore`,
530
531
  {
531
532
  method: "POST"
532
533
  }
@@ -536,7 +537,7 @@ var SnapshotsApi = class extends BaseApiClient {
536
537
  * Delete a snapshot
537
538
  */
538
539
  async deleteSnapshot(projectId, snapshotId) {
539
- await this.request(`/projects/${projectId}/snapshots/${snapshotId}`, {
540
+ await this.request(`/api/v1/projects/${projectId}/snapshots/${snapshotId}`, {
540
541
  method: "DELETE"
541
542
  });
542
543
  }
@@ -595,8 +596,8 @@ var TMApi = class extends BaseApiClient {
595
596
  * This fetches keys without translations and applies TM matches
596
597
  */
597
598
  async applyTMMatches(projectId, data) {
598
- const { KeysApi: KeysApi2 } = await import("./keys-P3F5IKS2.js");
599
- const { TranslationsApi: TranslationsApi2 } = await import("./translations-7GWEFVRG.js");
599
+ const { KeysApi: KeysApi2 } = await import("./keys-WDJKXFH3.js");
600
+ const { TranslationsApi: TranslationsApi2 } = await import("./translations-45NWLMJR.js");
600
601
  const keysApi = new KeysApi2();
601
602
  const translationsApi = new TranslationsApi2();
602
603
  const { keys } = await keysApi.listKeys(projectId, {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseApiClient
3
- } from "./chunk-KIDP7N6D.js";
3
+ } from "./chunk-ZYZSONV6.js";
4
4
 
5
5
  // src/lib/api/projects.ts
6
6
  var ProjectsApi = class extends BaseApiClient {
@@ -130,6 +130,9 @@ var BaseApiClient = class {
130
130
  const contentType = response.headers.get("content-type") || "";
131
131
  if (contentType.includes("application/json")) {
132
132
  const jsonData = await response.json();
133
+ if (jsonData.error && typeof jsonData.error === "string") {
134
+ throw new Error(jsonData.error);
135
+ }
133
136
  if (jsonData.url && typeof jsonData.url === "string") {
134
137
  const fileResponse = await fetch(jsonData.url);
135
138
  if (!fileResponse.ok) {
@@ -137,6 +140,9 @@ var BaseApiClient = class {
137
140
  }
138
141
  return fileResponse.blob();
139
142
  }
143
+ if (typeof jsonData === "object" && Object.keys(jsonData).length === 0) {
144
+ throw new Error("no translations found for the requested languages");
145
+ }
140
146
  const jsonString = JSON.stringify(jsonData, null, 2);
141
147
  return new Blob([jsonString], { type: "application/json" });
142
148
  }
package/dist/index.js CHANGED
@@ -5,15 +5,15 @@ import {
5
5
  TMApi,
6
6
  WebhooksApi,
7
7
  createApiClient
8
- } from "./chunk-WVCVQFBI.js";
8
+ } from "./chunk-QOUDOZX4.js";
9
9
  import {
10
10
  ProjectsApi
11
- } from "./chunk-KCZQUMQP.js";
12
- import "./chunk-WSY27J6N.js";
11
+ } from "./chunk-RQI7KQQ5.js";
12
+ import "./chunk-OMGFIATE.js";
13
13
  import {
14
14
  KeysApi
15
- } from "./chunk-BULIQM4U.js";
16
- import "./chunk-KIDP7N6D.js";
15
+ } from "./chunk-DSTFGTTF.js";
16
+ import "./chunk-ZYZSONV6.js";
17
17
  import {
18
18
  detectFramework,
19
19
  getAuthErrorMessage,
@@ -1613,6 +1613,9 @@ function detectCaseCollisions(names) {
1613
1613
  function hasNamespacePlaceholder(pattern) {
1614
1614
  return pattern.includes("[namespace]") || pattern.includes("{namespace}");
1615
1615
  }
1616
+ function hasLocalePlaceholder(pattern) {
1617
+ return pattern.includes("[locale]") || pattern.includes("{locale}");
1618
+ }
1616
1619
  function getFormatExtension(format) {
1617
1620
  switch (format) {
1618
1621
  case "yaml":
@@ -2692,9 +2695,10 @@ Or use a project-scoped API key for automatic selection.`
2692
2695
  let totalKeyCount = 0;
2693
2696
  const writeErrors = [];
2694
2697
  const newCache = cache || createEmptyCache();
2695
- const filePattern = libraryConfig?.filePattern || "{locale}.json";
2698
+ const userProvidedPattern = options.output && (hasLocalePlaceholder(options.output) || hasNamespacePlaceholder(options.output));
2699
+ const filePattern = userProvidedPattern ? options.output : libraryConfig?.filePattern || "{locale}.json";
2696
2700
  const useNamespaceFiles = hasNamespacePlaceholder(filePattern) && namespacedBundle && namespaces.length > 0;
2697
- const outputPattern = filePattern.startsWith("./") ? filePattern : join6(outputDir, filePattern);
2701
+ const outputPattern = userProvidedPattern ? filePattern : filePattern.startsWith("./") ? filePattern : join6(outputDir, filePattern);
2698
2702
  if (namespaces.length > 0) {
2699
2703
  const collisions = detectCaseCollisions(namespaces);
2700
2704
  if (collisions.length > 0) {
@@ -4816,8 +4820,8 @@ function ExportComponent({ options }) {
4816
4820
  json: "json",
4817
4821
  yaml: "yaml",
4818
4822
  ts: "ts",
4819
- android: "zip",
4820
- ios: "zip",
4823
+ android: "xml",
4824
+ ios: "strings",
4821
4825
  xliff: "xliff",
4822
4826
  po: "po",
4823
4827
  arb: "arb",
@@ -9268,6 +9272,22 @@ async function fetchProjects7(apiUrl, apiKey) {
9268
9272
  return data.projects || [];
9269
9273
  }
9270
9274
  var isInteractiveTerminal7 = process.stdin.isTTY && process.stdout.isTTY;
9275
+ var NON_LANGUAGE_FIELDS = /* @__PURE__ */ new Set(["url", "filename", "expires_at", "size", "download_url", "content_type", "error", "message"]);
9276
+ function isTranslationData(value) {
9277
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
9278
+ return false;
9279
+ }
9280
+ const entries = Object.entries(value);
9281
+ if (entries.length === 0) return false;
9282
+ const validCount = entries.filter(([, v]) => typeof v === "string" || typeof v === "object" && v !== null).length;
9283
+ return validCount >= entries.length * 0.5;
9284
+ }
9285
+ function isValidLanguageCode(key) {
9286
+ if (NON_LANGUAGE_FIELDS.has(key)) {
9287
+ return false;
9288
+ }
9289
+ return /^[a-z]{2,3}(-[A-Z]{2,3}|-[A-Za-z]{4})?$/i.test(key);
9290
+ }
9271
9291
  async function fetchTranslationsFromExport(projectId, apiUrl, apiKey, options) {
9272
9292
  const params = new URLSearchParams();
9273
9293
  params.set("format", "json");
@@ -9295,9 +9315,16 @@ async function fetchTranslationsFromExport(projectId, apiUrl, apiKey, options) {
9295
9315
  data = { [lang]: data };
9296
9316
  }
9297
9317
  }
9298
- const languages = Object.keys(data);
9299
- const keyCount = languages.length > 0 && data[languages[0]] ? Object.keys(data[languages[0]]).length : 0;
9300
- return { bundle: data, languages, keyCount };
9318
+ const filteredData = {};
9319
+ for (const key of Object.keys(data)) {
9320
+ const value = data[key];
9321
+ if (isValidLanguageCode(key) && isTranslationData(value)) {
9322
+ filteredData[key] = value;
9323
+ }
9324
+ }
9325
+ const languages = Object.keys(filteredData);
9326
+ const keyCount = languages.length > 0 && filteredData[languages[0]] ? Object.keys(filteredData[languages[0]]).length : 0;
9327
+ return { bundle: filteredData, languages, keyCount };
9301
9328
  }
9302
9329
  async function fetchNamespaces2(projectId, apiUrl, apiKey) {
9303
9330
  const response = await fetch(`${apiUrl}/api/v1/projects/${projectId}/namespaces`, {
@@ -11716,7 +11743,7 @@ var KeysCreate = ({ keyName, options }) => {
11716
11743
  }
11717
11744
  const api = new KeysApi();
11718
11745
  const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : void 0;
11719
- const { ProjectsApi: ProjectsApi2 } = await import("./projects-AMQMORAR.js");
11746
+ const { ProjectsApi: ProjectsApi2 } = await import("./projects-C6BDSZVS.js");
11720
11747
  const projectsApi = new ProjectsApi2();
11721
11748
  const project = await projectsApi.getProject(projectId);
11722
11749
  const created = await api.createKey(projectId, {
@@ -12619,37 +12646,40 @@ var TMStatsComponent = ({ options }) => {
12619
12646
  if (!stats) {
12620
12647
  return /* @__PURE__ */ jsx50(Text47, { children: "No statistics available" });
12621
12648
  }
12649
+ const totalEntries = stats.total_entries ?? 0;
12650
+ const avgQuality = stats.avg_quality ?? 0;
12651
+ const languagePairs = stats.language_pairs ?? [];
12622
12652
  return /* @__PURE__ */ jsxs47(Box44, { flexDirection: "column", children: [
12623
12653
  /* @__PURE__ */ jsx50(Text47, { bold: true, children: "Translation Memory Statistics" }),
12624
12654
  /* @__PURE__ */ jsx50(Newline6, {}),
12625
12655
  /* @__PURE__ */ jsxs47(Text47, { children: [
12626
12656
  /* @__PURE__ */ jsx50(Text47, { bold: true, children: "Total Entries:" }),
12627
12657
  " ",
12628
- stats.total_entries.toLocaleString()
12658
+ totalEntries.toLocaleString()
12629
12659
  ] }),
12630
12660
  /* @__PURE__ */ jsxs47(Text47, { children: [
12631
12661
  /* @__PURE__ */ jsx50(Text47, { bold: true, children: "Average Quality:" }),
12632
12662
  " ",
12633
- stats.avg_quality.toFixed(1),
12663
+ avgQuality.toFixed(1),
12634
12664
  "%"
12635
12665
  ] }),
12636
12666
  /* @__PURE__ */ jsx50(Newline6, {}),
12637
- stats.language_pairs.length > 0 && /* @__PURE__ */ jsxs47(Fragment8, { children: [
12667
+ languagePairs.length > 0 && /* @__PURE__ */ jsxs47(Fragment8, { children: [
12638
12668
  /* @__PURE__ */ jsx50(Text47, { bold: true, children: "Language Pairs:" }),
12639
12669
  /* @__PURE__ */ jsx50(Newline6, {}),
12640
- /* @__PURE__ */ jsx50(Box44, { flexDirection: "column", children: stats.language_pairs.map((pair, idx) => /* @__PURE__ */ jsxs47(Text47, { children: [
12670
+ /* @__PURE__ */ jsx50(Box44, { flexDirection: "column", children: languagePairs.map((pair, idx) => /* @__PURE__ */ jsxs47(Text47, { children: [
12641
12671
  /* @__PURE__ */ jsxs47(Text47, { color: "cyan", children: [
12642
12672
  pair.source,
12643
12673
  " \u2192 ",
12644
12674
  pair.target
12645
12675
  ] }),
12646
12676
  ": ",
12647
- pair.count.toLocaleString(),
12677
+ (pair.count ?? 0).toLocaleString(),
12648
12678
  " ",
12649
12679
  pair.count === 1 ? "entry" : "entries"
12650
12680
  ] }, idx)) })
12651
12681
  ] }),
12652
- stats.language_pairs.length === 0 && /* @__PURE__ */ jsx50(Text47, { dimColor: true, children: "No language pairs found. Add entries with `intlpull tm add`" })
12682
+ languagePairs.length === 0 && /* @__PURE__ */ jsx50(Text47, { dimColor: true, children: "No language pairs found. Add entries with `intlpull tm add`" })
12653
12683
  ] });
12654
12684
  };
12655
12685
  function runTMStats(options) {
@@ -13056,7 +13086,12 @@ var SnapshotsList = ({ options }) => {
13056
13086
  const result = await api.listSnapshots(projectId);
13057
13087
  setSnapshots(result);
13058
13088
  } catch (err) {
13059
- setError(err instanceof Error ? err.message : "Failed to list snapshots");
13089
+ const errorMessage = err instanceof Error ? err.message : "Failed to list snapshots";
13090
+ if (errorMessage.includes("404")) {
13091
+ setError("Snapshots feature is not available for this project. This feature may require a plan upgrade or is not yet enabled.");
13092
+ } else {
13093
+ setError(errorMessage);
13094
+ }
13060
13095
  } finally {
13061
13096
  setIsLoading(false);
13062
13097
  }
@@ -13421,7 +13456,7 @@ var WebhooksList = ({ options }) => {
13421
13456
  }
13422
13457
  const api = new WebhooksApi();
13423
13458
  const result = await api.listWebhooks(projectId);
13424
- setWebhooks(result.webhooks);
13459
+ setWebhooks(result?.webhooks ?? []);
13425
13460
  } catch (err) {
13426
13461
  setError(err instanceof Error ? err.message : "Failed to fetch webhooks");
13427
13462
  } finally {
@@ -13879,10 +13914,16 @@ var ContributorsList = ({ options }) => {
13879
13914
  console.log(JSON.stringify(members, null, 2));
13880
13915
  return null;
13881
13916
  }
13917
+ const formatJoinedDate = (dateString) => {
13918
+ if (!dateString) return "Unknown";
13919
+ const date = new Date(dateString);
13920
+ if (isNaN(date.getTime())) return "Unknown";
13921
+ return date.toLocaleDateString();
13922
+ };
13882
13923
  const data = members.map((member) => {
13883
13924
  const emailDisplay = member.email.length > 25 ? member.email.substring(0, 22) + "..." : member.email;
13884
13925
  const languagesDisplay = member.languages && member.languages.length > 0 ? member.languages.length > 3 ? member.languages.slice(0, 3).join(",") + ` +${member.languages.length - 3}` : member.languages.join(",") : "All";
13885
- const joinedDate = new Date(member.joined_at).toLocaleDateString();
13926
+ const joinedDate = formatJoinedDate(member.joined_at);
13886
13927
  return {
13887
13928
  Email: emailDisplay,
13888
13929
  Name: member.name || "-",
@@ -14249,7 +14290,7 @@ contributorsCommand.command("remove <userId>").description("Remove a team member
14249
14290
 
14250
14291
  // src/index.tsx
14251
14292
  var program = new Command7();
14252
- program.name("intlpull").description("Intelligent i18n CLI for modern apps").version("0.1.5").option("--env-file <path>", "Path to custom env file (e.g., .env.production)").hook("preAction", (thisCommand) => {
14293
+ program.name("intlpull").description("Intelligent i18n CLI for modern apps").version("0.1.11").option("--env-file <path>", "Path to custom env file (e.g., .env.production)").hook("preAction", (thisCommand) => {
14253
14294
  const envFile = thisCommand.opts().envFile;
14254
14295
  if (envFile) {
14255
14296
  setCustomEnvFile(envFile);
@@ -14281,7 +14322,7 @@ program.command("logout").description("Clear stored credentials").action(async (
14281
14322
  });
14282
14323
  program.command("whoami").description("Show current authenticated user").action(async () => {
14283
14324
  const { getResolvedApiKey: getResolvedApiKey2, getAuthErrorMessage: getAuthErrorMessage2 } = await import("./config-F3O3XWYX.js");
14284
- const { createApiClient: createApiClient2 } = await import("./api-77ZWU4IB.js");
14325
+ const { createApiClient: createApiClient2 } = await import("./api-BTTALAAQ.js");
14285
14326
  const resolved = getResolvedApiKey2();
14286
14327
  if (!resolved) {
14287
14328
  console.log(getAuthErrorMessage2());
@@ -14474,7 +14515,7 @@ projects.command("list").description("List all projects").action(async () => {
14474
14515
  console.log("Not authenticated. Run `npx @intlpullhq/cli login` first.");
14475
14516
  return;
14476
14517
  }
14477
- const { createApiClient: createApiClient2 } = await import("./api-77ZWU4IB.js");
14518
+ const { createApiClient: createApiClient2 } = await import("./api-BTTALAAQ.js");
14478
14519
  try {
14479
14520
  const api = createApiClient2();
14480
14521
  const { projects: projects2 } = await api.listProjects();
@@ -14500,7 +14541,7 @@ projects.command("create <name>").description("Create a new project").option("--
14500
14541
  console.log("Not authenticated. Run `npx @intlpullhq/cli login` first.");
14501
14542
  return;
14502
14543
  }
14503
- const { createApiClient: createApiClient2 } = await import("./api-77ZWU4IB.js");
14544
+ const { createApiClient: createApiClient2 } = await import("./api-BTTALAAQ.js");
14504
14545
  try {
14505
14546
  const api = createApiClient2();
14506
14547
  const languages = options.languages.split(",").map((l) => l.trim());
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  KeysApi
3
- } from "./chunk-BULIQM4U.js";
4
- import "./chunk-KIDP7N6D.js";
3
+ } from "./chunk-DSTFGTTF.js";
4
+ import "./chunk-ZYZSONV6.js";
5
5
  import "./chunk-IWYURZV2.js";
6
6
  export {
7
7
  KeysApi
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ProjectsApi
3
- } from "./chunk-KCZQUMQP.js";
4
- import "./chunk-KIDP7N6D.js";
3
+ } from "./chunk-RQI7KQQ5.js";
4
+ import "./chunk-ZYZSONV6.js";
5
5
  import "./chunk-IWYURZV2.js";
6
6
  export {
7
7
  ProjectsApi
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TranslationsApi
3
- } from "./chunk-WSY27J6N.js";
4
- import "./chunk-KIDP7N6D.js";
3
+ } from "./chunk-OMGFIATE.js";
4
+ import "./chunk-ZYZSONV6.js";
5
5
  import "./chunk-IWYURZV2.js";
6
6
  export {
7
7
  TranslationsApi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlpullhq/cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "The official CLI for IntlPull - intelligent i18n for modern apps. Manage translations, sync with cloud, and automate localization workflows.",
5
5
  "type": "module",
6
6
  "bin": {