@intlpullhq/cli 0.1.7 → 0.1.9

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.
@@ -1,305 +1,18 @@
1
1
  import {
2
- getAuthErrorMessage,
3
- getGlobalConfig,
4
- getProjectConfig,
5
- getResolvedApiKey
6
- } from "./chunk-GQADIM7O.js";
7
-
8
- // src/lib/api/base.ts
9
- var DEFAULT_API_URL = process.env.INTLPULL_API_URL || "https://api.intlpull.com";
10
- var DEFAULT_TIMEOUT_MS = 3e4;
11
- var BaseApiClient = class {
12
- baseUrl;
13
- apiKey;
14
- timeout;
15
- constructor() {
16
- const globalConfig = getGlobalConfig();
17
- this.baseUrl = globalConfig.apiUrl || DEFAULT_API_URL;
18
- const resolved = getResolvedApiKey();
19
- this.apiKey = resolved?.key || null;
20
- this.timeout = DEFAULT_TIMEOUT_MS;
21
- }
22
- async request(endpoint, options = {}) {
23
- if (!this.apiKey) {
24
- throw new Error(getAuthErrorMessage());
25
- }
26
- const url = `${this.baseUrl}${endpoint}`;
27
- const headers = {
28
- "Content-Type": "application/json",
29
- "X-API-Key": this.apiKey,
30
- ...options.headers || {}
31
- };
32
- const controller = new AbortController();
33
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
34
- let response;
35
- try {
36
- response = await fetch(url, {
37
- ...options,
38
- headers,
39
- signal: controller.signal
40
- });
41
- } catch (err) {
42
- clearTimeout(timeoutId);
43
- if (err instanceof Error && err.name === "AbortError") {
44
- throw new Error(`Request timeout: Server did not respond within ${this.timeout / 1e3}s`);
45
- }
46
- if (err instanceof TypeError) {
47
- throw new Error(`Network error: Unable to connect to ${this.baseUrl}. Check your internet connection.`);
48
- }
49
- throw new Error(`Request failed: ${err instanceof Error ? err.message : "Unknown network error"}`);
50
- } finally {
51
- clearTimeout(timeoutId);
52
- }
53
- if (!response.ok) {
54
- const error = await response.json().catch(() => ({ error: `Request failed: ${response.status}` }));
55
- throw new Error(error.error || `Request failed: ${response.status}`);
56
- }
57
- const text = await response.text();
58
- if (!text) {
59
- return {};
60
- }
61
- try {
62
- return JSON.parse(text);
63
- } catch {
64
- throw new Error(`Invalid JSON response from API`);
65
- }
66
- }
67
- async requestBlob(endpoint) {
68
- if (!this.apiKey) {
69
- throw new Error(getAuthErrorMessage());
70
- }
71
- const url = `${this.baseUrl}${endpoint}`;
72
- const controller = new AbortController();
73
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
74
- let response;
75
- try {
76
- response = await fetch(url, {
77
- headers: {
78
- "X-API-Key": this.apiKey
79
- },
80
- signal: controller.signal
81
- });
82
- } catch (err) {
83
- clearTimeout(timeoutId);
84
- if (err instanceof Error && err.name === "AbortError") {
85
- throw new Error(`Request timeout: Server did not respond within ${this.timeout / 1e3}s`);
86
- }
87
- if (err instanceof TypeError) {
88
- throw new Error(`Network error: Unable to connect to ${this.baseUrl}. Check your internet connection.`);
89
- }
90
- throw new Error(`Request failed: ${err instanceof Error ? err.message : "Unknown network error"}`);
91
- } finally {
92
- clearTimeout(timeoutId);
93
- }
94
- if (!response.ok) {
95
- const error = await response.json().catch(() => ({ error: "Request failed" }));
96
- throw new Error(error.error || "Request failed");
97
- }
98
- return response.blob();
99
- }
100
- async requestBlobWithJsonFallback(endpoint) {
101
- if (!this.apiKey) {
102
- throw new Error(getAuthErrorMessage());
103
- }
104
- const url = `${this.baseUrl}${endpoint}`;
105
- const controller = new AbortController();
106
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
107
- let response;
108
- try {
109
- response = await fetch(url, {
110
- headers: {
111
- "X-API-Key": this.apiKey
112
- },
113
- signal: controller.signal
114
- });
115
- } catch (err) {
116
- clearTimeout(timeoutId);
117
- if (err instanceof Error && err.name === "AbortError") {
118
- throw new Error(`Request timeout: Server did not respond within ${this.timeout / 1e3}s`);
119
- }
120
- if (err instanceof TypeError) {
121
- throw new Error(`Network error: Unable to connect to ${this.baseUrl}. Check your internet connection.`);
122
- }
123
- throw new Error(`Request failed: ${err instanceof Error ? err.message : "Unknown network error"}`);
124
- } finally {
125
- clearTimeout(timeoutId);
126
- }
127
- if (!response.ok) {
128
- const error = await response.json().catch(() => ({ error: "Request failed" }));
129
- throw new Error(error.error || "Request failed");
130
- }
131
- const contentType = response.headers.get("content-type") || "";
132
- if (contentType.includes("application/json")) {
133
- const jsonData = await response.json();
134
- const jsonString = JSON.stringify(jsonData, null, 2);
135
- return new Blob([jsonString], { type: "application/json" });
136
- }
137
- return response.blob();
138
- }
139
- };
140
-
141
- // src/lib/api/projects.ts
142
- var ProjectsApi = class extends BaseApiClient {
143
- async listProjects() {
144
- return this.request("/api/v1/projects");
145
- }
146
- async getProject(projectId) {
147
- return this.request(`/api/v1/projects/${projectId}`);
148
- }
149
- async createProject(data) {
150
- return this.request("/api/v1/projects", {
151
- method: "POST",
152
- body: JSON.stringify(data)
153
- });
154
- }
155
- /**
156
- * Find a project by name (searches through all projects)
157
- */
158
- async findProjectByName(name) {
159
- const { projects } = await this.listProjects();
160
- const normalizedName = name.toLowerCase().trim();
161
- return projects.find(
162
- (p) => p.name.toLowerCase() === normalizedName || p.slug.toLowerCase() === normalizedName
163
- ) || null;
164
- }
165
- /**
166
- * Get project languages/settings
167
- */
168
- async getProjectLanguages(projectId) {
169
- return this.request(`/api/v1/projects/${projectId}/language-settings`);
170
- }
171
- /**
172
- * Add multiple languages to a project with optional skip_translation flag
173
- */
174
- async addLanguagesBulk(projectId, languages, skipTranslation = false) {
175
- return this.request(`/api/v1/projects/${projectId}/language-settings/bulk`, {
176
- method: "POST",
177
- body: JSON.stringify({
178
- languages,
179
- skip_translation: skipTranslation
180
- })
181
- });
182
- }
183
- // Versions
184
- async listVersions(projectId) {
185
- return this.request(`/api/v1/projects/${projectId}/versions`);
186
- }
187
- async createVersion(projectId, data) {
188
- return this.request(`/api/v1/projects/${projectId}/versions`, {
189
- method: "POST",
190
- body: JSON.stringify(data)
191
- });
192
- }
193
- async downloadVersion(projectId, version) {
194
- return this.requestBlob(`/api/v1/projects/${projectId}/versions/${version}/download`);
195
- }
196
- };
197
-
198
- // src/lib/api/translations.ts
199
- var TranslationsApi = class extends BaseApiClient {
200
- // Keys - uses import endpoint to create/update keys
201
- async pushKeys(projectId, keys, language = "en", namespace, options) {
202
- const content = {};
203
- for (const k of keys) {
204
- content[k.key] = k.value;
205
- }
206
- const fileName = namespace ? `${namespace}.json` : "push.json";
207
- const result = await this.request(`/api/v1/projects/${projectId}/import`, {
208
- method: "POST",
209
- body: JSON.stringify({
210
- content: JSON.stringify(content),
211
- file_name: fileName,
212
- language,
213
- options: {
214
- namespace_name: namespace || "common",
215
- update_existing: true,
216
- branch_id: options?.branchId,
217
- platforms: options?.platforms
218
- }
219
- })
220
- });
221
- return {
222
- keys_inserted: result.keys_inserted,
223
- keys_updated: result.keys_updated,
224
- keys_skipped: result.keys_skipped,
225
- translations_inserted: result.translations_inserted,
226
- translations_updated: result.translations_updated
227
- };
228
- }
229
- async getKeys(projectId, namespace) {
230
- const params = namespace ? `?namespace=${namespace}` : "";
231
- return this.request(`/api/v1/projects/${projectId}/keys${params}`);
232
- }
233
- // Translations - uses export/download endpoint
234
- async pullTranslations(projectId, options) {
235
- const params = new URLSearchParams();
236
- params.set("format", "json");
237
- if (options.languages?.length) {
238
- options.languages.forEach((lang) => params.append("languages", lang));
239
- }
240
- if (options.branch) {
241
- params.set("branch", options.branch);
242
- }
243
- if (options.platform) {
244
- params.set("platform", options.platform);
245
- }
246
- return this.request(`/api/v1/projects/${projectId}/export/download?${params.toString()}`);
247
- }
248
- // Glossary
249
- async searchGlossary(query, options) {
250
- const params = new URLSearchParams();
251
- params.set("q", query);
252
- if (options?.limit) params.set("limit", String(options.limit));
253
- if (options?.language) params.set("language", options.language);
254
- return this.request(`/api/v1/glossary/search?${params.toString()}`);
255
- }
256
- async addGlossaryTerm(data) {
257
- const config = getProjectConfig();
258
- if (!config?.projectId) {
259
- throw new Error("No project configured. Run `npx @intlpullhq/cli init` first.");
260
- }
261
- return this.request(`/api/v1/projects/${config.projectId}/glossary`, {
262
- method: "POST",
263
- body: JSON.stringify(data)
264
- });
265
- }
266
- async exportGlossary(glossaryId) {
267
- const config = getProjectConfig();
268
- if (!config?.projectId) {
269
- throw new Error("No project configured. Run `npx @intlpullhq/cli init` first.");
270
- }
271
- const params = glossaryId ? `?glossary_id=${glossaryId}` : "";
272
- return this.request(`/api/v1/projects/${config.projectId}/glossary/export${params}`);
273
- }
274
- // Translation Memory
275
- async searchTM(data) {
276
- const config = getProjectConfig();
277
- if (!config?.projectId) {
278
- throw new Error("No project configured. Run `npx @intlpullhq/cli init` first.");
279
- }
280
- return this.request(`/api/v1/projects/${config.projectId}/memory/search`, {
281
- method: "POST",
282
- body: JSON.stringify(data)
283
- });
284
- }
285
- async addTMEntry(data) {
286
- const config = getProjectConfig();
287
- if (!config?.projectId) {
288
- throw new Error("No project configured. Run `npx @intlpullhq/cli init` first.");
289
- }
290
- return this.request(`/api/v1/projects/${config.projectId}/memory`, {
291
- method: "POST",
292
- body: JSON.stringify(data)
293
- });
294
- }
295
- async getTMStats() {
296
- const config = getProjectConfig();
297
- if (!config?.projectId) {
298
- throw new Error("No project configured. Run `npx @intlpullhq/cli init` first.");
299
- }
300
- return this.request(`/api/v1/projects/${config.projectId}/memory/stats`);
301
- }
302
- };
2
+ ProjectsApi
3
+ } from "./chunk-KCZQUMQP.js";
4
+ import {
5
+ TranslationsApi
6
+ } from "./chunk-WSY27J6N.js";
7
+ import {
8
+ KeysApi
9
+ } from "./chunk-BULIQM4U.js";
10
+ import {
11
+ BaseApiClient
12
+ } from "./chunk-KIDP7N6D.js";
13
+ import {
14
+ getAuthErrorMessage
15
+ } from "./chunk-IWYURZV2.js";
303
16
 
304
17
  // src/lib/api/import-export.ts
305
18
  var ImportExportApi = class extends BaseApiClient {
@@ -773,6 +486,289 @@ var DocumentsApi = class extends BaseApiClient {
773
486
  }
774
487
  };
775
488
 
489
+ // src/lib/api/snapshots.ts
490
+ var SnapshotsApi = class extends BaseApiClient {
491
+ /**
492
+ * Create a snapshot of current project state
493
+ */
494
+ async createSnapshot(projectId, data) {
495
+ const response = await this.request(
496
+ `/projects/${projectId}/snapshots`,
497
+ {
498
+ method: "POST",
499
+ body: JSON.stringify(data)
500
+ }
501
+ );
502
+ return response.snapshot;
503
+ }
504
+ /**
505
+ * List all snapshots for a project
506
+ */
507
+ async listSnapshots(projectId) {
508
+ const response = await this.request(
509
+ `/projects/${projectId}/snapshots`,
510
+ {
511
+ method: "GET"
512
+ }
513
+ );
514
+ return response.snapshots;
515
+ }
516
+ /**
517
+ * Get snapshot details
518
+ */
519
+ async getSnapshot(projectId, snapshotId) {
520
+ return this.request(`/projects/${projectId}/snapshots/${snapshotId}`, {
521
+ method: "GET"
522
+ });
523
+ }
524
+ /**
525
+ * Restore project to snapshot state
526
+ */
527
+ async restoreSnapshot(projectId, snapshotId) {
528
+ return this.request(
529
+ `/projects/${projectId}/snapshots/${snapshotId}/restore`,
530
+ {
531
+ method: "POST"
532
+ }
533
+ );
534
+ }
535
+ /**
536
+ * Delete a snapshot
537
+ */
538
+ async deleteSnapshot(projectId, snapshotId) {
539
+ await this.request(`/projects/${projectId}/snapshots/${snapshotId}`, {
540
+ method: "DELETE"
541
+ });
542
+ }
543
+ };
544
+
545
+ // src/lib/api/tm.ts
546
+ var TMApi = class extends BaseApiClient {
547
+ /**
548
+ * Search TM for a single text
549
+ */
550
+ async searchTM(data) {
551
+ return this.request(`/api/v1/organization/memory/search`, {
552
+ method: "POST",
553
+ body: JSON.stringify(data)
554
+ });
555
+ }
556
+ /**
557
+ * Batch search TM for multiple texts
558
+ * Note: This is a convenience method that batches individual searches
559
+ * Backend batch endpoint would be more efficient but not required for MVP
560
+ */
561
+ async batchSearchTM(data) {
562
+ const results = await Promise.all(
563
+ data.texts.map(async (text) => {
564
+ const response = await this.searchTM({
565
+ text,
566
+ source_lang: data.source_lang,
567
+ target_lang: data.target_lang,
568
+ min_quality: data.min_quality
569
+ });
570
+ return {
571
+ text,
572
+ matches: response.matches
573
+ };
574
+ })
575
+ );
576
+ return { results };
577
+ }
578
+ /**
579
+ * Add a new TM entry
580
+ */
581
+ async addTMEntry(data) {
582
+ return this.request(`/api/v1/organization/memory`, {
583
+ method: "POST",
584
+ body: JSON.stringify(data)
585
+ });
586
+ }
587
+ /**
588
+ * Get TM statistics
589
+ */
590
+ async getTMStats() {
591
+ return this.request(`/api/v1/organization/memory`);
592
+ }
593
+ /**
594
+ * Apply TM matches to project translations
595
+ * This fetches keys without translations and applies TM matches
596
+ */
597
+ async applyTMMatches(projectId, data) {
598
+ const { KeysApi: KeysApi2 } = await import("./keys-P3F5IKS2.js");
599
+ const { TranslationsApi: TranslationsApi2 } = await import("./translations-7GWEFVRG.js");
600
+ const keysApi = new KeysApi2();
601
+ const translationsApi = new TranslationsApi2();
602
+ const { keys } = await keysApi.listKeys(projectId, {
603
+ namespace: data.namespace
604
+ });
605
+ const matches = [];
606
+ let applied = 0;
607
+ let skipped = 0;
608
+ for (const key of keys) {
609
+ const sourceText = key.key;
610
+ try {
611
+ const searchResult = await this.searchTM({
612
+ text: sourceText,
613
+ source_lang: "en",
614
+ // TODO: Get from project config
615
+ target_lang: data.target_language,
616
+ min_quality: data.min_threshold
617
+ });
618
+ if (searchResult.matches.length > 0) {
619
+ const bestMatch = searchResult.matches[0];
620
+ const score = bestMatch.quality;
621
+ if (score >= data.min_threshold) {
622
+ matches.push({
623
+ key_id: key.id,
624
+ key: key.key,
625
+ source: sourceText,
626
+ translation: bestMatch.target_text,
627
+ score,
628
+ applied: !data.dry_run
629
+ });
630
+ if (!data.dry_run) {
631
+ applied++;
632
+ }
633
+ } else {
634
+ matches.push({
635
+ key_id: key.id,
636
+ key: key.key,
637
+ source: sourceText,
638
+ translation: bestMatch.target_text,
639
+ score,
640
+ applied: false
641
+ });
642
+ skipped++;
643
+ }
644
+ } else {
645
+ skipped++;
646
+ }
647
+ } catch (err) {
648
+ skipped++;
649
+ }
650
+ }
651
+ return {
652
+ applied,
653
+ skipped,
654
+ matches
655
+ };
656
+ }
657
+ };
658
+
659
+ // src/lib/api/webhooks.ts
660
+ var WebhooksApi = class extends BaseApiClient {
661
+ /**
662
+ * List all webhooks for a project
663
+ */
664
+ async listWebhooks(projectId) {
665
+ return this.request(
666
+ `/api/v1/projects/${projectId}/webhooks`
667
+ );
668
+ }
669
+ /**
670
+ * Create a new webhook
671
+ */
672
+ async createWebhook(projectId, data) {
673
+ return this.request(
674
+ `/api/v1/projects/${projectId}/webhooks`,
675
+ {
676
+ method: "POST",
677
+ body: JSON.stringify(data)
678
+ }
679
+ );
680
+ }
681
+ /**
682
+ * Get a specific webhook
683
+ */
684
+ async getWebhook(projectId, webhookId) {
685
+ return this.request(
686
+ `/api/v1/projects/${projectId}/webhooks/${webhookId}`
687
+ );
688
+ }
689
+ /**
690
+ * Update a webhook
691
+ */
692
+ async updateWebhook(projectId, webhookId, data) {
693
+ return this.request(
694
+ `/api/v1/projects/${projectId}/webhooks/${webhookId}`,
695
+ {
696
+ method: "PUT",
697
+ body: JSON.stringify(data)
698
+ }
699
+ );
700
+ }
701
+ /**
702
+ * Delete a webhook
703
+ */
704
+ async deleteWebhook(projectId, webhookId) {
705
+ await this.request(
706
+ `/api/v1/projects/${projectId}/webhooks/${webhookId}`,
707
+ {
708
+ method: "DELETE"
709
+ }
710
+ );
711
+ }
712
+ /**
713
+ * Test a webhook by sending a test event
714
+ */
715
+ async testWebhook(projectId, webhookId) {
716
+ return this.request(
717
+ `/api/v1/projects/${projectId}/webhooks/${webhookId}/test`,
718
+ {
719
+ method: "POST"
720
+ }
721
+ );
722
+ }
723
+ };
724
+
725
+ // src/lib/api/contributors.ts
726
+ var ContributorsApi = class extends BaseApiClient {
727
+ /**
728
+ * List all team members for a project
729
+ */
730
+ async listTeamMembers(projectId) {
731
+ return this.request(
732
+ `/api/v1/projects/${projectId}/team`
733
+ );
734
+ }
735
+ /**
736
+ * Invite a new team member
737
+ */
738
+ async inviteTeamMember(projectId, data) {
739
+ return this.request(
740
+ `/api/v1/projects/${projectId}/team/invites`,
741
+ {
742
+ method: "POST",
743
+ body: JSON.stringify(data)
744
+ }
745
+ );
746
+ }
747
+ /**
748
+ * Update a team member's role or permissions
749
+ */
750
+ async updateTeamMember(projectId, userId, data) {
751
+ return this.request(
752
+ `/api/v1/projects/${projectId}/team/members/${userId}`,
753
+ {
754
+ method: "PUT",
755
+ body: JSON.stringify(data)
756
+ }
757
+ );
758
+ }
759
+ /**
760
+ * Remove a team member from the project
761
+ */
762
+ async removeTeamMember(projectId, userId) {
763
+ await this.request(
764
+ `/api/v1/projects/${projectId}/team/members/${userId}`,
765
+ {
766
+ method: "DELETE"
767
+ }
768
+ );
769
+ }
770
+ };
771
+
776
772
  // src/lib/api/index.ts
777
773
  var ApiClient = class {
778
774
  projects;
@@ -783,6 +779,8 @@ var ApiClient = class {
783
779
  emails;
784
780
  billing;
785
781
  documents;
782
+ keys;
783
+ snapshots;
786
784
  constructor() {
787
785
  this.projects = new ProjectsApi();
788
786
  this.translations = new TranslationsApi();
@@ -790,9 +788,10 @@ var ApiClient = class {
790
788
  this.workflows = new WorkflowsApi();
791
789
  this.apiKeys = new ApiKeysApi();
792
790
  this.emails = new EmailsApi();
793
- this.emails = new EmailsApi();
794
791
  this.billing = new BillingApi();
795
792
  this.documents = new DocumentsApi();
793
+ this.keys = new KeysApi();
794
+ this.snapshots = new SnapshotsApi();
796
795
  }
797
796
  // Documents API
798
797
  listDocuments(projectId) {
@@ -974,15 +973,47 @@ var ApiClient = class {
974
973
  checkUsageLimit(requestedStrings) {
975
974
  return this.billing.checkUsageLimit(requestedStrings);
976
975
  }
976
+ // Keys API
977
+ listKeys(projectId, options) {
978
+ return this.keys.listKeys(projectId, options);
979
+ }
980
+ getKey(projectId, keyId) {
981
+ return this.keys.getKey(projectId, keyId);
982
+ }
983
+ createKey(projectId, data) {
984
+ return this.keys.createKey(projectId, data);
985
+ }
986
+ updateKey(projectId, keyId, data) {
987
+ return this.keys.updateKey(projectId, keyId, data);
988
+ }
989
+ deleteKey(projectId, keyId) {
990
+ return this.keys.deleteKey(projectId, keyId);
991
+ }
992
+ bulkDeleteKeys(projectId, keyIds) {
993
+ return this.keys.bulkDeleteKeys(projectId, keyIds);
994
+ }
995
+ // Snapshots API
996
+ createSnapshot(projectId, data) {
997
+ return this.snapshots.createSnapshot(projectId, data);
998
+ }
999
+ listSnapshots(projectId) {
1000
+ return this.snapshots.listSnapshots(projectId);
1001
+ }
1002
+ getSnapshot(projectId, snapshotId) {
1003
+ return this.snapshots.getSnapshot(projectId, snapshotId);
1004
+ }
1005
+ restoreSnapshot(projectId, snapshotId) {
1006
+ return this.snapshots.restoreSnapshot(projectId, snapshotId);
1007
+ }
1008
+ deleteSnapshot(projectId, snapshotId) {
1009
+ return this.snapshots.deleteSnapshot(projectId, snapshotId);
1010
+ }
977
1011
  };
978
1012
  function createApiClient() {
979
1013
  return new ApiClient();
980
1014
  }
981
1015
 
982
1016
  export {
983
- BaseApiClient,
984
- ProjectsApi,
985
- TranslationsApi,
986
1017
  ImportExportApi,
987
1018
  WorkflowsApi,
988
1019
  ApiKeysApi,
@@ -990,6 +1021,10 @@ export {
990
1021
  PLAN_LIMITS,
991
1022
  BillingApi,
992
1023
  DocumentsApi,
1024
+ SnapshotsApi,
1025
+ TMApi,
1026
+ WebhooksApi,
1027
+ ContributorsApi,
993
1028
  ApiClient,
994
1029
  createApiClient
995
1030
  };
@@ -20,7 +20,7 @@ import {
20
20
  saveProjectConfig,
21
21
  setCustomEnvFile,
22
22
  writeFileAtomic
23
- } from "./chunk-GQADIM7O.js";
23
+ } from "./chunk-IWYURZV2.js";
24
24
  export {
25
25
  clearAuthConfig,
26
26
  detectFramework,