@intlpullhq/cli 0.1.13 → 0.1.15
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/dist/index.js +111 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -74,8 +74,19 @@ var icons = {
|
|
|
74
74
|
arrow: "\u2192",
|
|
75
75
|
bullet: "\u2022",
|
|
76
76
|
check: "\u2714",
|
|
77
|
-
cross: "\u2716"
|
|
77
|
+
cross: "\u2716",
|
|
78
|
+
// Brand icon - globe represents internationalization
|
|
79
|
+
brand: "\u{1F310}"
|
|
78
80
|
};
|
|
81
|
+
var brand = {
|
|
82
|
+
name: "IntlPull",
|
|
83
|
+
icon: "\u{1F310}",
|
|
84
|
+
url: "https://intlpull.com",
|
|
85
|
+
appUrl: "https://app.intlpull.com"
|
|
86
|
+
};
|
|
87
|
+
function terminalLink(text, url) {
|
|
88
|
+
return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
|
|
89
|
+
}
|
|
79
90
|
var spinners = {
|
|
80
91
|
dots: {
|
|
81
92
|
interval: 80,
|
|
@@ -1086,6 +1097,10 @@ import { join as join5, dirname as dirname3 } from "path";
|
|
|
1086
1097
|
var DEFAULT_TIMEOUT = 3e4;
|
|
1087
1098
|
var MAX_RETRIES = 3;
|
|
1088
1099
|
var RETRY_DELAYS = [1e3, 2e3, 4e3];
|
|
1100
|
+
var CONNECTION_HEADERS = {
|
|
1101
|
+
Connection: "keep-alive",
|
|
1102
|
+
"Accept-Encoding": "gzip, deflate, br"
|
|
1103
|
+
};
|
|
1089
1104
|
function sleep(ms) {
|
|
1090
1105
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1091
1106
|
}
|
|
@@ -1097,6 +1112,10 @@ async function fetchWithRetry(url, options = {}, retries = MAX_RETRIES) {
|
|
|
1097
1112
|
try {
|
|
1098
1113
|
const response = await fetch(url, {
|
|
1099
1114
|
...fetchOptions,
|
|
1115
|
+
headers: {
|
|
1116
|
+
...CONNECTION_HEADERS,
|
|
1117
|
+
...fetchOptions.headers
|
|
1118
|
+
},
|
|
1100
1119
|
signal: controller.signal
|
|
1101
1120
|
});
|
|
1102
1121
|
clearTimeout(timeoutId);
|
|
@@ -1280,12 +1299,43 @@ async function fetchTranslationsByNamespace(projectId, apiUrl, apiKey, language,
|
|
|
1280
1299
|
return { language, namespace, translations: {} };
|
|
1281
1300
|
}
|
|
1282
1301
|
}
|
|
1302
|
+
var Semaphore = class {
|
|
1303
|
+
constructor(concurrency) {
|
|
1304
|
+
this.concurrency = concurrency;
|
|
1305
|
+
}
|
|
1306
|
+
queue = [];
|
|
1307
|
+
running = 0;
|
|
1308
|
+
async acquire() {
|
|
1309
|
+
if (this.running < this.concurrency) {
|
|
1310
|
+
this.running++;
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
return new Promise((resolve2) => this.queue.push(resolve2));
|
|
1314
|
+
}
|
|
1315
|
+
release() {
|
|
1316
|
+
this.running--;
|
|
1317
|
+
const next = this.queue.shift();
|
|
1318
|
+
if (next) {
|
|
1319
|
+
this.running++;
|
|
1320
|
+
next();
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
async run(fn) {
|
|
1324
|
+
await this.acquire();
|
|
1325
|
+
try {
|
|
1326
|
+
return await fn();
|
|
1327
|
+
} finally {
|
|
1328
|
+
this.release();
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1283
1332
|
async function fetchTranslationsParallel(projectId, apiUrl, apiKey, options) {
|
|
1284
1333
|
const headers = { Accept: "application/json" };
|
|
1285
1334
|
if (apiKey) headers["X-API-Key"] = apiKey;
|
|
1286
|
-
const projectResponse = await
|
|
1287
|
-
headers
|
|
1288
|
-
|
|
1335
|
+
const [projectResponse, namespacesResult] = await Promise.all([
|
|
1336
|
+
fetchWithRetry(`${apiUrl}/api/v1/projects/${projectId}`, { headers }),
|
|
1337
|
+
fetchNamespaces(projectId, apiUrl, apiKey)
|
|
1338
|
+
]);
|
|
1289
1339
|
if (!projectResponse.ok) {
|
|
1290
1340
|
if (projectResponse.status === 401) {
|
|
1291
1341
|
throw new Error("Authentication required. Run `npx @intlpullhq/cli login` first.");
|
|
@@ -1298,7 +1348,6 @@ async function fetchTranslationsParallel(projectId, apiUrl, apiKey, options) {
|
|
|
1298
1348
|
const projectData = await projectResponse.json();
|
|
1299
1349
|
const availableLanguages = projectData.languages?.map((l) => l.code) || ["en"];
|
|
1300
1350
|
const targetLanguages = options?.languages?.length ? options.languages : availableLanguages;
|
|
1301
|
-
const namespacesResult = await fetchNamespaces(projectId, apiUrl, apiKey);
|
|
1302
1351
|
let namespaces = namespacesResult.namespaces;
|
|
1303
1352
|
if (options?.namespaces?.length) {
|
|
1304
1353
|
const requestedNamespaces = new Set(options.namespaces);
|
|
@@ -1314,7 +1363,7 @@ async function fetchTranslationsParallel(projectId, apiUrl, apiKey, options) {
|
|
|
1314
1363
|
}
|
|
1315
1364
|
}
|
|
1316
1365
|
const totalTasks = taskParams.length;
|
|
1317
|
-
const concurrency = options?.concurrency ||
|
|
1366
|
+
const concurrency = options?.concurrency || 10;
|
|
1318
1367
|
let completed = 0;
|
|
1319
1368
|
const languageProgress = /* @__PURE__ */ new Map();
|
|
1320
1369
|
for (const lang of targetLanguages) {
|
|
@@ -1347,28 +1396,33 @@ async function fetchTranslationsParallel(projectId, apiUrl, apiKey, options) {
|
|
|
1347
1396
|
};
|
|
1348
1397
|
};
|
|
1349
1398
|
const results = [];
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1399
|
+
const semaphore = new Semaphore(concurrency);
|
|
1400
|
+
const fetchPromises = taskParams.map(
|
|
1401
|
+
({ language, namespace }) => semaphore.run(async () => {
|
|
1402
|
+
const result = await fetchTranslationsByNamespace(
|
|
1403
|
+
projectId,
|
|
1404
|
+
apiUrl,
|
|
1405
|
+
apiKey,
|
|
1406
|
+
language,
|
|
1407
|
+
namespace,
|
|
1408
|
+
{
|
|
1355
1409
|
branch: options?.branch,
|
|
1356
1410
|
platform: options?.platform
|
|
1357
|
-
}
|
|
1358
|
-
)
|
|
1359
|
-
);
|
|
1360
|
-
for (const result of batchResults) {
|
|
1411
|
+
}
|
|
1412
|
+
);
|
|
1361
1413
|
const langProg = languageProgress.get(result.language);
|
|
1362
1414
|
if (langProg) {
|
|
1363
1415
|
langProg.completed++;
|
|
1364
1416
|
langProg.keys += Object.keys(result.translations).length;
|
|
1365
1417
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1418
|
+
completed++;
|
|
1419
|
+
options?.onProgress?.(completed, totalTasks);
|
|
1420
|
+
options?.onEnhancedProgress?.(buildEnhancedProgress());
|
|
1421
|
+
return result;
|
|
1422
|
+
})
|
|
1423
|
+
);
|
|
1424
|
+
const allResults = await Promise.all(fetchPromises);
|
|
1425
|
+
results.push(...allResults);
|
|
1372
1426
|
const bundle = {};
|
|
1373
1427
|
const namespacedBundle = {};
|
|
1374
1428
|
for (const result of results) {
|
|
@@ -2813,7 +2867,11 @@ function DownloadProgress({
|
|
|
2813
2867
|
const hiddenCount = languages.length - visibleLanguages.length;
|
|
2814
2868
|
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", paddingX: 1, children: [
|
|
2815
2869
|
/* @__PURE__ */ jsxs11(Box10, { marginBottom: 1, children: [
|
|
2816
|
-
/* @__PURE__ */
|
|
2870
|
+
/* @__PURE__ */ jsxs11(Text11, { children: [
|
|
2871
|
+
brand.icon,
|
|
2872
|
+
" "
|
|
2873
|
+
] }),
|
|
2874
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: colors.primary, children: brand.name }),
|
|
2817
2875
|
/* @__PURE__ */ jsx11(Text11, { color: colors.text, children: " Download" }),
|
|
2818
2876
|
projectName && /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
2819
2877
|
/* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: " \u2022 " }),
|
|
@@ -2867,7 +2925,11 @@ function DownloadComplete({
|
|
|
2867
2925
|
}) {
|
|
2868
2926
|
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", paddingX: 1, children: [
|
|
2869
2927
|
/* @__PURE__ */ jsxs11(Box10, { marginBottom: 1, children: [
|
|
2870
|
-
/* @__PURE__ */
|
|
2928
|
+
/* @__PURE__ */ jsxs11(Text11, { children: [
|
|
2929
|
+
brand.icon,
|
|
2930
|
+
" "
|
|
2931
|
+
] }),
|
|
2932
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: colors.primary, children: brand.name }),
|
|
2871
2933
|
/* @__PURE__ */ jsx11(Text11, { color: colors.text, children: " Download" })
|
|
2872
2934
|
] }),
|
|
2873
2935
|
/* @__PURE__ */ jsxs11(Box10, { children: [
|
|
@@ -2909,7 +2971,8 @@ function DownloadComplete({
|
|
|
2909
2971
|
files.length > 10 && /* @__PURE__ */ jsx11(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { color: colors.textDim, children: [
|
|
2910
2972
|
files.length,
|
|
2911
2973
|
" files written"
|
|
2912
|
-
] }) })
|
|
2974
|
+
] }) }),
|
|
2975
|
+
/* @__PURE__ */ jsx11(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: terminalLink(`${icons.arrow} Open dashboard`, brand.appUrl) }) })
|
|
2913
2976
|
] });
|
|
2914
2977
|
}
|
|
2915
2978
|
|
|
@@ -3247,11 +3310,15 @@ ${writeErrors.join("\n")}`);
|
|
|
3247
3310
|
return /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", paddingX: 1, children: [
|
|
3248
3311
|
(state.status === "loading" || state.status === "detecting") && /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
3249
3312
|
/* @__PURE__ */ jsxs12(Box11, { marginBottom: 1, children: [
|
|
3250
|
-
/* @__PURE__ */
|
|
3251
|
-
|
|
3313
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3314
|
+
brand.icon,
|
|
3315
|
+
" "
|
|
3316
|
+
] }),
|
|
3317
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, color: colors.primary, children: brand.name }),
|
|
3318
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.text, children: " Download" })
|
|
3252
3319
|
] }),
|
|
3253
3320
|
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3254
|
-
/* @__PURE__ */ jsx12(Text12, { color:
|
|
3321
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: /* @__PURE__ */ jsx12(Spinner4, { type: "dots" }) }),
|
|
3255
3322
|
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3256
3323
|
" ",
|
|
3257
3324
|
state.message
|
|
@@ -3273,11 +3340,15 @@ ${writeErrors.join("\n")}`);
|
|
|
3273
3340
|
),
|
|
3274
3341
|
state.status === "downloading" && !showEnhancedProgress && /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
3275
3342
|
/* @__PURE__ */ jsxs12(Box11, { marginBottom: 1, children: [
|
|
3276
|
-
/* @__PURE__ */
|
|
3277
|
-
|
|
3343
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3344
|
+
brand.icon,
|
|
3345
|
+
" "
|
|
3346
|
+
] }),
|
|
3347
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, color: colors.primary, children: brand.name }),
|
|
3348
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.text, children: " Download" })
|
|
3278
3349
|
] }),
|
|
3279
3350
|
/* @__PURE__ */ jsxs12(Box11, { children: [
|
|
3280
|
-
/* @__PURE__ */ jsx12(Text12, { color:
|
|
3351
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: /* @__PURE__ */ jsx12(Spinner4, { type: "dots" }) }),
|
|
3281
3352
|
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3282
3353
|
" ",
|
|
3283
3354
|
state.message
|
|
@@ -3297,18 +3368,22 @@ ${writeErrors.join("\n")}`);
|
|
|
3297
3368
|
),
|
|
3298
3369
|
state.status === "error" && /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", children: [
|
|
3299
3370
|
/* @__PURE__ */ jsxs12(Box11, { marginBottom: 1, children: [
|
|
3300
|
-
/* @__PURE__ */
|
|
3301
|
-
|
|
3371
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3372
|
+
brand.icon,
|
|
3373
|
+
" "
|
|
3374
|
+
] }),
|
|
3375
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, color: colors.primary, children: brand.name }),
|
|
3376
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.text, children: " Download" })
|
|
3302
3377
|
] }),
|
|
3303
|
-
/* @__PURE__ */ jsxs12(Text12, { color:
|
|
3378
|
+
/* @__PURE__ */ jsxs12(Text12, { color: colors.error, children: [
|
|
3304
3379
|
"\u2715 ",
|
|
3305
3380
|
state.message
|
|
3306
3381
|
] }),
|
|
3307
3382
|
/* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
3308
3383
|
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Tips:" }),
|
|
3309
|
-
/* @__PURE__ */ jsx12(Text12, { color:
|
|
3310
|
-
/* @__PURE__ */ jsx12(Text12, { color:
|
|
3311
|
-
/* @__PURE__ */ jsx12(Text12, { color:
|
|
3384
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " - Run `npx @intlpullhq/cli login` to authenticate" }),
|
|
3385
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " - Use `npx @intlpullhq/cli download --project <id>` to specify project" }),
|
|
3386
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " - Use a project-scoped API key for automatic project selection" })
|
|
3312
3387
|
] })
|
|
3313
3388
|
] })
|
|
3314
3389
|
] });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlpullhq/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
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": {
|