@intlpullhq/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +765 -0
- package/dist/api-D35M5BOX.js +29 -0
- package/dist/chunk-2T7ERBDS.js +394 -0
- package/dist/chunk-S3EWA4QD.js +995 -0
- package/dist/config-HHJ7OBGT.js +46 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10737 -0
- package/package.json +86 -0
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAuthErrorMessage,
|
|
3
|
+
getGlobalConfig,
|
|
4
|
+
getProjectConfig,
|
|
5
|
+
getResolvedApiKey
|
|
6
|
+
} from "./chunk-2T7ERBDS.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
|
+
};
|
|
303
|
+
|
|
304
|
+
// src/lib/api/import-export.ts
|
|
305
|
+
var ImportExportApi = class extends BaseApiClient {
|
|
306
|
+
// Export
|
|
307
|
+
async exportTranslations(projectId, options) {
|
|
308
|
+
const params = new URLSearchParams();
|
|
309
|
+
params.set("format", options.format);
|
|
310
|
+
if (options.languages?.length) {
|
|
311
|
+
params.set("languages", options.languages.join(","));
|
|
312
|
+
}
|
|
313
|
+
if (options.namespaces?.length) {
|
|
314
|
+
params.set("namespaces", options.namespaces.join(","));
|
|
315
|
+
}
|
|
316
|
+
if (options.branch) {
|
|
317
|
+
params.set("branch", options.branch);
|
|
318
|
+
}
|
|
319
|
+
if (options.platform) {
|
|
320
|
+
params.set("platform", options.platform);
|
|
321
|
+
}
|
|
322
|
+
return this.requestBlobWithJsonFallback(
|
|
323
|
+
`/api/v1/projects/${projectId}/export/download?${params.toString()}`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Import translations from file content
|
|
328
|
+
*/
|
|
329
|
+
async importTranslations(projectId, content, fileName, language, options) {
|
|
330
|
+
return this.request(`/api/v1/projects/${projectId}/import`, {
|
|
331
|
+
method: "POST",
|
|
332
|
+
body: JSON.stringify({
|
|
333
|
+
content,
|
|
334
|
+
file_name: fileName,
|
|
335
|
+
language,
|
|
336
|
+
options: options || {}
|
|
337
|
+
})
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// src/lib/api/workflows.ts
|
|
343
|
+
var WorkflowsApi = class extends BaseApiClient {
|
|
344
|
+
/**
|
|
345
|
+
* Get workflow settings for a project
|
|
346
|
+
*/
|
|
347
|
+
async getWorkflowSettings(projectId) {
|
|
348
|
+
return this.request(`/api/v1/projects/${projectId}/workflows/settings`);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* List all workflows for a project
|
|
352
|
+
*/
|
|
353
|
+
async listWorkflows(projectId) {
|
|
354
|
+
return this.request(`/api/v1/projects/${projectId}/workflows`);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Get pending approvals for a project
|
|
358
|
+
*/
|
|
359
|
+
async getPendingApprovals(projectId) {
|
|
360
|
+
return this.request(`/api/v1/projects/${projectId}/workflows/pending`);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Get approval history for a translation
|
|
364
|
+
*/
|
|
365
|
+
async getApprovalHistory(projectId, translationId) {
|
|
366
|
+
return this.request(`/api/v1/projects/${projectId}/translations/${translationId}/approvals`);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Approve a translation at the current stage
|
|
370
|
+
*/
|
|
371
|
+
async approveTranslation(projectId, translationId, comment) {
|
|
372
|
+
return this.request(`/api/v1/projects/${projectId}/translations/${translationId}/approve`, {
|
|
373
|
+
method: "POST",
|
|
374
|
+
body: JSON.stringify({ comment })
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Reject a translation with feedback
|
|
379
|
+
*/
|
|
380
|
+
async rejectTranslation(projectId, translationId, reason) {
|
|
381
|
+
return this.request(`/api/v1/projects/${projectId}/translations/${translationId}/reject`, {
|
|
382
|
+
method: "POST",
|
|
383
|
+
body: JSON.stringify({ reason })
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get lock status for a translation
|
|
388
|
+
*/
|
|
389
|
+
async getLockStatus(projectId, translationId) {
|
|
390
|
+
return this.request(`/api/v1/projects/${projectId}/translations/${translationId}/lock`);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Lock a translation
|
|
394
|
+
*/
|
|
395
|
+
async lockTranslation(projectId, translationId, reason) {
|
|
396
|
+
return this.request(`/api/v1/projects/${projectId}/translations/${translationId}/lock`, {
|
|
397
|
+
method: "POST",
|
|
398
|
+
body: JSON.stringify({ reason })
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Unlock a translation
|
|
403
|
+
*/
|
|
404
|
+
async unlockTranslation(projectId, translationId) {
|
|
405
|
+
return this.request(`/api/v1/projects/${projectId}/translations/${translationId}/lock`, {
|
|
406
|
+
method: "DELETE"
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get all locked translations for a project
|
|
411
|
+
*/
|
|
412
|
+
async getLockedTranslations(projectId) {
|
|
413
|
+
return this.request(`/api/v1/projects/${projectId}/workflows/locked`);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// src/lib/api/api-keys.ts
|
|
418
|
+
var ApiKeysApi = class extends BaseApiClient {
|
|
419
|
+
/**
|
|
420
|
+
* List organization-level API keys (not project-scoped)
|
|
421
|
+
*/
|
|
422
|
+
async listAPIKeys() {
|
|
423
|
+
return this.request("/api/v1/api-keys");
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* List all API keys (organization-level + project-scoped)
|
|
427
|
+
*/
|
|
428
|
+
async listAllAPIKeys() {
|
|
429
|
+
return this.request("/api/v1/api-keys/all");
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* List API keys for a specific project
|
|
433
|
+
*/
|
|
434
|
+
async listProjectAPIKeys(projectId) {
|
|
435
|
+
return this.request(`/api/v1/projects/${projectId}/api-keys`);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Create an organization-level API key
|
|
439
|
+
* If project_id is provided, creates a project-scoped key instead
|
|
440
|
+
*/
|
|
441
|
+
async createAPIKey(options) {
|
|
442
|
+
if (options.project_id) {
|
|
443
|
+
return this.request(`/api/v1/projects/${options.project_id}/api-keys`, {
|
|
444
|
+
method: "POST",
|
|
445
|
+
body: JSON.stringify({
|
|
446
|
+
name: options.name,
|
|
447
|
+
description: options.description,
|
|
448
|
+
expires_in_days: options.expires_in_days
|
|
449
|
+
})
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
return this.request("/api/v1/api-keys", {
|
|
453
|
+
method: "POST",
|
|
454
|
+
body: JSON.stringify(options)
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Create a project-scoped API key
|
|
459
|
+
*/
|
|
460
|
+
async createProjectAPIKey(projectId, options) {
|
|
461
|
+
return this.request(`/api/v1/projects/${projectId}/api-keys`, {
|
|
462
|
+
method: "POST",
|
|
463
|
+
body: JSON.stringify(options)
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Revoke an API key
|
|
468
|
+
*/
|
|
469
|
+
async revokeAPIKey(keyId) {
|
|
470
|
+
return this.request(`/api/v1/api-keys/${keyId}`, {
|
|
471
|
+
method: "DELETE"
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Revoke a project-scoped API key
|
|
476
|
+
*/
|
|
477
|
+
async revokeProjectAPIKey(projectId, keyId) {
|
|
478
|
+
return this.request(`/api/v1/projects/${projectId}/api-keys/${keyId}`, {
|
|
479
|
+
method: "DELETE"
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// src/lib/api/emails.ts
|
|
485
|
+
var EmailsApi = class extends BaseApiClient {
|
|
486
|
+
/**
|
|
487
|
+
* List email templates for a project
|
|
488
|
+
*/
|
|
489
|
+
async listEmailTemplates(projectId) {
|
|
490
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates`);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Get a single email template by slug with content
|
|
494
|
+
*/
|
|
495
|
+
async getEmailTemplateBySlug(projectId, slug, language) {
|
|
496
|
+
const url = language ? `/api/v1/projects/${projectId}/email-templates/slug/${slug}?lang=${language}` : `/api/v1/projects/${projectId}/email-templates/slug/${slug}`;
|
|
497
|
+
const response = await this.request(url);
|
|
498
|
+
return response.template;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Get a single email template by ID with content
|
|
502
|
+
*/
|
|
503
|
+
async getEmailTemplate(projectId, templateId, language) {
|
|
504
|
+
const url = language ? `/api/v1/projects/${projectId}/email-templates/${templateId}?lang=${language}` : `/api/v1/projects/${projectId}/email-templates/${templateId}`;
|
|
505
|
+
const response = await this.request(url);
|
|
506
|
+
return response.template;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Get HTML content for a specific language
|
|
510
|
+
*/
|
|
511
|
+
async getHtmlContent(projectId, templateId, language) {
|
|
512
|
+
return this.request(
|
|
513
|
+
`/api/v1/projects/${projectId}/email-templates/${templateId}/content/${language}`
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Create a new email template
|
|
518
|
+
*/
|
|
519
|
+
async createEmailTemplate(projectId, data) {
|
|
520
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates`, {
|
|
521
|
+
method: "POST",
|
|
522
|
+
body: JSON.stringify(data)
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Update an email template
|
|
527
|
+
*/
|
|
528
|
+
async updateEmailTemplate(projectId, templateId, data) {
|
|
529
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates/${templateId}`, {
|
|
530
|
+
method: "PUT",
|
|
531
|
+
body: JSON.stringify(data)
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Update HTML properties (source HTML/CSS)
|
|
536
|
+
*/
|
|
537
|
+
async updateHtmlProperties(projectId, templateId, properties) {
|
|
538
|
+
const response = await this.request(
|
|
539
|
+
`/api/v1/projects/${projectId}/email-templates/${templateId}/html/properties`,
|
|
540
|
+
{
|
|
541
|
+
method: "PATCH",
|
|
542
|
+
body: JSON.stringify({ html_properties: properties })
|
|
543
|
+
}
|
|
544
|
+
);
|
|
545
|
+
return response.template;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Set HTML translation for a specific language
|
|
549
|
+
*/
|
|
550
|
+
async setHtmlTranslation(projectId, templateId, data) {
|
|
551
|
+
return this.request(
|
|
552
|
+
`/api/v1/projects/${projectId}/email-templates/${templateId}/html/translation`,
|
|
553
|
+
{
|
|
554
|
+
method: "PUT",
|
|
555
|
+
body: JSON.stringify(data)
|
|
556
|
+
}
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Delete an email template
|
|
561
|
+
*/
|
|
562
|
+
async deleteEmailTemplate(projectId, templateId) {
|
|
563
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates/${templateId}`, {
|
|
564
|
+
method: "DELETE"
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Publish an email template
|
|
569
|
+
*/
|
|
570
|
+
async publishEmailTemplate(projectId, templateId, notes) {
|
|
571
|
+
const response = await this.request(
|
|
572
|
+
`/api/v1/projects/${projectId}/email-templates/${templateId}/publish`,
|
|
573
|
+
{
|
|
574
|
+
method: "POST",
|
|
575
|
+
body: JSON.stringify({ notes })
|
|
576
|
+
}
|
|
577
|
+
);
|
|
578
|
+
return response.template;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Render an email template with variables
|
|
582
|
+
*/
|
|
583
|
+
async renderEmailTemplate(projectId, slug, language, variables) {
|
|
584
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates/${slug}/render`, {
|
|
585
|
+
method: "POST",
|
|
586
|
+
body: JSON.stringify({ language, variables })
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get email template GitHub integration status
|
|
591
|
+
*/
|
|
592
|
+
async getEmailGitHubIntegration(projectId) {
|
|
593
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates/github`);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Sync email templates with GitHub (pull or push)
|
|
597
|
+
*/
|
|
598
|
+
async syncEmailTemplates(projectId, options) {
|
|
599
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates/github/sync`, {
|
|
600
|
+
method: "POST",
|
|
601
|
+
body: JSON.stringify(options)
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Get email template sync logs
|
|
606
|
+
*/
|
|
607
|
+
async getEmailSyncLogs(projectId) {
|
|
608
|
+
return this.request(`/api/v1/projects/${projectId}/email-templates/github/logs`);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// src/lib/api/billing.ts
|
|
613
|
+
var PLAN_LIMITS = {
|
|
614
|
+
free: {
|
|
615
|
+
strings: 500,
|
|
616
|
+
teamMembers: 30,
|
|
617
|
+
price: 0,
|
|
618
|
+
otaEnabled: false,
|
|
619
|
+
branchesEnabled: false,
|
|
620
|
+
ssoEnabled: false
|
|
621
|
+
},
|
|
622
|
+
starter: {
|
|
623
|
+
strings: 5e3,
|
|
624
|
+
teamMembers: 30,
|
|
625
|
+
price: 12,
|
|
626
|
+
otaEnabled: false,
|
|
627
|
+
branchesEnabled: false,
|
|
628
|
+
ssoEnabled: false
|
|
629
|
+
},
|
|
630
|
+
growth: {
|
|
631
|
+
strings: 15e3,
|
|
632
|
+
teamMembers: 30,
|
|
633
|
+
price: 49,
|
|
634
|
+
otaEnabled: false,
|
|
635
|
+
branchesEnabled: true,
|
|
636
|
+
ssoEnabled: false
|
|
637
|
+
},
|
|
638
|
+
business: {
|
|
639
|
+
strings: 5e4,
|
|
640
|
+
teamMembers: 30,
|
|
641
|
+
price: 149,
|
|
642
|
+
otaEnabled: true,
|
|
643
|
+
branchesEnabled: true,
|
|
644
|
+
ssoEnabled: false
|
|
645
|
+
},
|
|
646
|
+
pro: {
|
|
647
|
+
strings: 1e5,
|
|
648
|
+
teamMembers: 30,
|
|
649
|
+
price: 399,
|
|
650
|
+
otaEnabled: true,
|
|
651
|
+
branchesEnabled: true,
|
|
652
|
+
ssoEnabled: true
|
|
653
|
+
},
|
|
654
|
+
professional: {
|
|
655
|
+
// Legacy alias
|
|
656
|
+
strings: 1e5,
|
|
657
|
+
teamMembers: 30,
|
|
658
|
+
price: 399,
|
|
659
|
+
otaEnabled: true,
|
|
660
|
+
branchesEnabled: true,
|
|
661
|
+
ssoEnabled: true
|
|
662
|
+
},
|
|
663
|
+
enterprise: {
|
|
664
|
+
strings: -1,
|
|
665
|
+
// unlimited
|
|
666
|
+
teamMembers: -1,
|
|
667
|
+
price: -1,
|
|
668
|
+
otaEnabled: true,
|
|
669
|
+
branchesEnabled: true,
|
|
670
|
+
ssoEnabled: true
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
var BillingApi = class extends BaseApiClient {
|
|
674
|
+
/**
|
|
675
|
+
* Get current billing info including plan and usage
|
|
676
|
+
*/
|
|
677
|
+
async getBillingInfo() {
|
|
678
|
+
return this.request("/api/v1/billing");
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Get plan limits for a given plan name
|
|
682
|
+
*/
|
|
683
|
+
getPlanLimits(planName) {
|
|
684
|
+
return PLAN_LIMITS[planName.toLowerCase()] || PLAN_LIMITS.free;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Check if uploading a certain number of strings would exceed the plan limit
|
|
688
|
+
* This counts unique source language keys only (translations don't count toward limit)
|
|
689
|
+
*/
|
|
690
|
+
async checkUsageLimit(requestedStrings) {
|
|
691
|
+
const billing = await this.getBillingInfo();
|
|
692
|
+
const limits = this.getPlanLimits(billing.plan.name);
|
|
693
|
+
const isUnlimited = limits.strings < 0;
|
|
694
|
+
const currentStrings = billing.usage.strings_used;
|
|
695
|
+
const maxStrings = limits.strings;
|
|
696
|
+
if (isUnlimited) {
|
|
697
|
+
return {
|
|
698
|
+
allowed: true,
|
|
699
|
+
plan: billing.plan.name,
|
|
700
|
+
current_strings: currentStrings,
|
|
701
|
+
max_strings: -1,
|
|
702
|
+
requested_strings: requestedStrings,
|
|
703
|
+
would_exceed_by: 0,
|
|
704
|
+
is_unlimited: true
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const projectedTotal = Math.max(currentStrings, requestedStrings);
|
|
708
|
+
const wouldExceedBy = projectedTotal > maxStrings ? projectedTotal - maxStrings : 0;
|
|
709
|
+
return {
|
|
710
|
+
allowed: projectedTotal <= maxStrings,
|
|
711
|
+
plan: billing.plan.name,
|
|
712
|
+
current_strings: currentStrings,
|
|
713
|
+
max_strings: maxStrings,
|
|
714
|
+
requested_strings: requestedStrings,
|
|
715
|
+
would_exceed_by: wouldExceedBy,
|
|
716
|
+
is_unlimited: false
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Format a number for display (with commas)
|
|
721
|
+
*/
|
|
722
|
+
formatNumber(num) {
|
|
723
|
+
if (num < 0) return "Unlimited";
|
|
724
|
+
return num.toLocaleString();
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
// src/lib/api/documents.ts
|
|
729
|
+
var DocumentsApi = class extends BaseApiClient {
|
|
730
|
+
async listDocuments(projectId) {
|
|
731
|
+
return this.request(`/api/v1/projects/${projectId}/documents`);
|
|
732
|
+
}
|
|
733
|
+
async getDocument(projectId, documentId) {
|
|
734
|
+
return this.request(`/api/v1/projects/${projectId}/documents/${documentId}`);
|
|
735
|
+
}
|
|
736
|
+
async uploadDocument(projectId, file, filename, sourceLang, targetLangs) {
|
|
737
|
+
if (!this.apiKey) {
|
|
738
|
+
throw new Error(getAuthErrorMessage());
|
|
739
|
+
}
|
|
740
|
+
const formData = new FormData();
|
|
741
|
+
formData.append("file", file, filename);
|
|
742
|
+
if (sourceLang) formData.append("source_language", sourceLang);
|
|
743
|
+
if (targetLangs && targetLangs.length > 0) {
|
|
744
|
+
formData.append("target_languages", targetLangs.join(","));
|
|
745
|
+
}
|
|
746
|
+
const url = `${this.baseUrl}/api/v1/projects/${projectId}/documents`;
|
|
747
|
+
const controller = new AbortController();
|
|
748
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
749
|
+
try {
|
|
750
|
+
const response = await fetch(url, {
|
|
751
|
+
method: "POST",
|
|
752
|
+
headers: {
|
|
753
|
+
"X-API-Key": this.apiKey
|
|
754
|
+
// Do NOT set Content-Type, let browser/node set boundary
|
|
755
|
+
},
|
|
756
|
+
body: formData,
|
|
757
|
+
signal: controller.signal
|
|
758
|
+
});
|
|
759
|
+
clearTimeout(timeoutId);
|
|
760
|
+
if (!response.ok) {
|
|
761
|
+
const error = await response.json().catch(() => ({ error: `Request failed: ${response.status}` }));
|
|
762
|
+
throw new Error(error.error || `Request failed: ${response.status}`);
|
|
763
|
+
}
|
|
764
|
+
return response.json();
|
|
765
|
+
} catch (err) {
|
|
766
|
+
clearTimeout(timeoutId);
|
|
767
|
+
throw new Error(`Upload failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async downloadDocument(projectId, documentId, language) {
|
|
771
|
+
const blob = await this.requestBlob(`/api/v1/projects/${projectId}/documents/${documentId}/export?language=${language}`);
|
|
772
|
+
return blob.arrayBuffer();
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
// src/lib/api/index.ts
|
|
777
|
+
var ApiClient = class {
|
|
778
|
+
projects;
|
|
779
|
+
translations;
|
|
780
|
+
importExport;
|
|
781
|
+
workflows;
|
|
782
|
+
apiKeys;
|
|
783
|
+
emails;
|
|
784
|
+
billing;
|
|
785
|
+
documents;
|
|
786
|
+
constructor() {
|
|
787
|
+
this.projects = new ProjectsApi();
|
|
788
|
+
this.translations = new TranslationsApi();
|
|
789
|
+
this.importExport = new ImportExportApi();
|
|
790
|
+
this.workflows = new WorkflowsApi();
|
|
791
|
+
this.apiKeys = new ApiKeysApi();
|
|
792
|
+
this.emails = new EmailsApi();
|
|
793
|
+
this.emails = new EmailsApi();
|
|
794
|
+
this.billing = new BillingApi();
|
|
795
|
+
this.documents = new DocumentsApi();
|
|
796
|
+
}
|
|
797
|
+
// Documents API
|
|
798
|
+
listDocuments(projectId) {
|
|
799
|
+
return this.documents.listDocuments(projectId);
|
|
800
|
+
}
|
|
801
|
+
getDocument(projectId, documentId) {
|
|
802
|
+
return this.documents.getDocument(projectId, documentId);
|
|
803
|
+
}
|
|
804
|
+
// Helper type for file upload since passing Blob from CLI can be tricky (Node.js doesn't have File/Blob in older versions, but we target Node 18+)
|
|
805
|
+
uploadDocument(projectId, file, filename, sourceLang, targetLangs) {
|
|
806
|
+
return this.documents.uploadDocument(projectId, file, filename, sourceLang, targetLangs);
|
|
807
|
+
}
|
|
808
|
+
downloadDocument(projectId, documentId, language) {
|
|
809
|
+
return this.documents.downloadDocument(projectId, documentId, language);
|
|
810
|
+
}
|
|
811
|
+
// Projects API
|
|
812
|
+
listProjects() {
|
|
813
|
+
return this.projects.listProjects();
|
|
814
|
+
}
|
|
815
|
+
getProject(projectId) {
|
|
816
|
+
return this.projects.getProject(projectId);
|
|
817
|
+
}
|
|
818
|
+
createProject(data) {
|
|
819
|
+
return this.projects.createProject(data);
|
|
820
|
+
}
|
|
821
|
+
findProjectByName(name) {
|
|
822
|
+
return this.projects.findProjectByName(name);
|
|
823
|
+
}
|
|
824
|
+
getProjectLanguages(projectId) {
|
|
825
|
+
return this.projects.getProjectLanguages(projectId);
|
|
826
|
+
}
|
|
827
|
+
addLanguagesBulk(projectId, languages, skipTranslation = false) {
|
|
828
|
+
return this.projects.addLanguagesBulk(projectId, languages, skipTranslation);
|
|
829
|
+
}
|
|
830
|
+
listVersions(projectId) {
|
|
831
|
+
return this.projects.listVersions(projectId);
|
|
832
|
+
}
|
|
833
|
+
createVersion(projectId, data) {
|
|
834
|
+
return this.projects.createVersion(projectId, data);
|
|
835
|
+
}
|
|
836
|
+
downloadVersion(projectId, version) {
|
|
837
|
+
return this.projects.downloadVersion(projectId, version);
|
|
838
|
+
}
|
|
839
|
+
// Translations API
|
|
840
|
+
pushKeys(projectId, keys, language, namespace, options) {
|
|
841
|
+
return this.translations.pushKeys(projectId, keys, language, namespace, options);
|
|
842
|
+
}
|
|
843
|
+
getKeys(projectId, namespace) {
|
|
844
|
+
return this.translations.getKeys(projectId, namespace);
|
|
845
|
+
}
|
|
846
|
+
pullTranslations(projectId, options) {
|
|
847
|
+
return this.translations.pullTranslations(projectId, options);
|
|
848
|
+
}
|
|
849
|
+
searchGlossary(query, options) {
|
|
850
|
+
return this.translations.searchGlossary(query, options);
|
|
851
|
+
}
|
|
852
|
+
addGlossaryTerm(data) {
|
|
853
|
+
return this.translations.addGlossaryTerm(data);
|
|
854
|
+
}
|
|
855
|
+
exportGlossary(glossaryId) {
|
|
856
|
+
return this.translations.exportGlossary(glossaryId);
|
|
857
|
+
}
|
|
858
|
+
searchTM(data) {
|
|
859
|
+
return this.translations.searchTM(data);
|
|
860
|
+
}
|
|
861
|
+
addTMEntry(data) {
|
|
862
|
+
return this.translations.addTMEntry(data);
|
|
863
|
+
}
|
|
864
|
+
getTMStats() {
|
|
865
|
+
return this.translations.getTMStats();
|
|
866
|
+
}
|
|
867
|
+
// Import/Export API
|
|
868
|
+
exportTranslations(projectId, options) {
|
|
869
|
+
return this.importExport.exportTranslations(projectId, options);
|
|
870
|
+
}
|
|
871
|
+
importTranslations(projectId, content, fileName, language, options) {
|
|
872
|
+
return this.importExport.importTranslations(projectId, content, fileName, language, options);
|
|
873
|
+
}
|
|
874
|
+
// Workflows API
|
|
875
|
+
getWorkflowSettings(projectId) {
|
|
876
|
+
return this.workflows.getWorkflowSettings(projectId);
|
|
877
|
+
}
|
|
878
|
+
listWorkflows(projectId) {
|
|
879
|
+
return this.workflows.listWorkflows(projectId);
|
|
880
|
+
}
|
|
881
|
+
getPendingApprovals(projectId) {
|
|
882
|
+
return this.workflows.getPendingApprovals(projectId);
|
|
883
|
+
}
|
|
884
|
+
getApprovalHistory(projectId, translationId) {
|
|
885
|
+
return this.workflows.getApprovalHistory(projectId, translationId);
|
|
886
|
+
}
|
|
887
|
+
approveTranslation(projectId, translationId, comment) {
|
|
888
|
+
return this.workflows.approveTranslation(projectId, translationId, comment);
|
|
889
|
+
}
|
|
890
|
+
rejectTranslation(projectId, translationId, reason) {
|
|
891
|
+
return this.workflows.rejectTranslation(projectId, translationId, reason);
|
|
892
|
+
}
|
|
893
|
+
getLockStatus(projectId, translationId) {
|
|
894
|
+
return this.workflows.getLockStatus(projectId, translationId);
|
|
895
|
+
}
|
|
896
|
+
lockTranslation(projectId, translationId, reason) {
|
|
897
|
+
return this.workflows.lockTranslation(projectId, translationId, reason);
|
|
898
|
+
}
|
|
899
|
+
unlockTranslation(projectId, translationId) {
|
|
900
|
+
return this.workflows.unlockTranslation(projectId, translationId);
|
|
901
|
+
}
|
|
902
|
+
getLockedTranslations(projectId) {
|
|
903
|
+
return this.workflows.getLockedTranslations(projectId);
|
|
904
|
+
}
|
|
905
|
+
// API Keys API
|
|
906
|
+
listAPIKeys() {
|
|
907
|
+
return this.apiKeys.listAPIKeys();
|
|
908
|
+
}
|
|
909
|
+
listAllAPIKeys() {
|
|
910
|
+
return this.apiKeys.listAllAPIKeys();
|
|
911
|
+
}
|
|
912
|
+
listProjectAPIKeys(projectId) {
|
|
913
|
+
return this.apiKeys.listProjectAPIKeys(projectId);
|
|
914
|
+
}
|
|
915
|
+
createAPIKey(options) {
|
|
916
|
+
return this.apiKeys.createAPIKey(options);
|
|
917
|
+
}
|
|
918
|
+
createProjectAPIKey(projectId, options) {
|
|
919
|
+
return this.apiKeys.createProjectAPIKey(projectId, options);
|
|
920
|
+
}
|
|
921
|
+
revokeAPIKey(keyId) {
|
|
922
|
+
return this.apiKeys.revokeAPIKey(keyId);
|
|
923
|
+
}
|
|
924
|
+
revokeProjectAPIKey(projectId, keyId) {
|
|
925
|
+
return this.apiKeys.revokeProjectAPIKey(projectId, keyId);
|
|
926
|
+
}
|
|
927
|
+
// Emails API
|
|
928
|
+
listEmailTemplates(projectId) {
|
|
929
|
+
return this.emails.listEmailTemplates(projectId);
|
|
930
|
+
}
|
|
931
|
+
getEmailTemplateBySlug(projectId, slug, language) {
|
|
932
|
+
return this.emails.getEmailTemplateBySlug(projectId, slug, language);
|
|
933
|
+
}
|
|
934
|
+
getEmailTemplate(projectId, templateId, language) {
|
|
935
|
+
return this.emails.getEmailTemplate(projectId, templateId, language);
|
|
936
|
+
}
|
|
937
|
+
getHtmlContent(projectId, templateId, language) {
|
|
938
|
+
return this.emails.getHtmlContent(projectId, templateId, language);
|
|
939
|
+
}
|
|
940
|
+
createEmailTemplate(projectId, data) {
|
|
941
|
+
return this.emails.createEmailTemplate(projectId, data);
|
|
942
|
+
}
|
|
943
|
+
updateEmailTemplate(projectId, templateId, data) {
|
|
944
|
+
return this.emails.updateEmailTemplate(projectId, templateId, data);
|
|
945
|
+
}
|
|
946
|
+
updateHtmlProperties(projectId, templateId, properties) {
|
|
947
|
+
return this.emails.updateHtmlProperties(projectId, templateId, properties);
|
|
948
|
+
}
|
|
949
|
+
setHtmlTranslation(projectId, templateId, data) {
|
|
950
|
+
return this.emails.setHtmlTranslation(projectId, templateId, data);
|
|
951
|
+
}
|
|
952
|
+
deleteEmailTemplate(projectId, templateId) {
|
|
953
|
+
return this.emails.deleteEmailTemplate(projectId, templateId);
|
|
954
|
+
}
|
|
955
|
+
publishEmailTemplate(projectId, templateId, notes) {
|
|
956
|
+
return this.emails.publishEmailTemplate(projectId, templateId, notes);
|
|
957
|
+
}
|
|
958
|
+
renderEmailTemplate(projectId, slug, language, variables) {
|
|
959
|
+
return this.emails.renderEmailTemplate(projectId, slug, language, variables);
|
|
960
|
+
}
|
|
961
|
+
getEmailGitHubIntegration(projectId) {
|
|
962
|
+
return this.emails.getEmailGitHubIntegration(projectId);
|
|
963
|
+
}
|
|
964
|
+
syncEmailTemplates(projectId, options) {
|
|
965
|
+
return this.emails.syncEmailTemplates(projectId, options);
|
|
966
|
+
}
|
|
967
|
+
getEmailSyncLogs(projectId) {
|
|
968
|
+
return this.emails.getEmailSyncLogs(projectId);
|
|
969
|
+
}
|
|
970
|
+
// Billing API
|
|
971
|
+
getBillingInfo() {
|
|
972
|
+
return this.billing.getBillingInfo();
|
|
973
|
+
}
|
|
974
|
+
checkUsageLimit(requestedStrings) {
|
|
975
|
+
return this.billing.checkUsageLimit(requestedStrings);
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
function createApiClient() {
|
|
979
|
+
return new ApiClient();
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
export {
|
|
983
|
+
BaseApiClient,
|
|
984
|
+
ProjectsApi,
|
|
985
|
+
TranslationsApi,
|
|
986
|
+
ImportExportApi,
|
|
987
|
+
WorkflowsApi,
|
|
988
|
+
ApiKeysApi,
|
|
989
|
+
EmailsApi,
|
|
990
|
+
PLAN_LIMITS,
|
|
991
|
+
BillingApi,
|
|
992
|
+
DocumentsApi,
|
|
993
|
+
ApiClient,
|
|
994
|
+
createApiClient
|
|
995
|
+
};
|