gtx-cli 2.2.0 → 2.3.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/CHANGELOG.md +11 -0
- package/dist/api/sendFiles.js +82 -11
- package/dist/api/uploadFiles.d.ts +1 -1
- package/dist/api/uploadFiles.js +10 -2
- package/dist/formats/files/upload.js +2 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
+
## 2.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#651](https://github.com/generaltranslation/gt/pull/651) [`3e5705b`](https://github.com/generaltranslation/gt/commit/3e5705bc96005441798619fec97574fa15a5a2bd) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Split up file upload into source/translation specific uploads; added project setup visibility
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`3e5705b`](https://github.com/generaltranslation/gt/commit/3e5705bc96005441798619fec97574fa15a5a2bd)]:
|
|
12
|
+
- generaltranslation@7.6.0
|
|
13
|
+
|
|
3
14
|
## 2.2.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/dist/api/sendFiles.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { createSpinner, logMessage, logSuccess } from '../console/logging.js';
|
|
2
|
+
import { createSpinner, logError, logMessage, logSuccess, } from '../console/logging.js';
|
|
3
3
|
import { gt } from '../utils/gt.js';
|
|
4
4
|
import { TEMPLATE_FILE_NAME } from '../cli/commands/stage.js';
|
|
5
5
|
/**
|
|
@@ -9,6 +9,8 @@ import { TEMPLATE_FILE_NAME } from '../cli/commands/stage.js';
|
|
|
9
9
|
* @returns The translated content or version ID
|
|
10
10
|
*/
|
|
11
11
|
export async function sendFiles(files, options, settings) {
|
|
12
|
+
// Keep track of the most recent spinner so we can stop it on error
|
|
13
|
+
let currentSpinner = null;
|
|
12
14
|
logMessage(chalk.cyan('Files to translate:') +
|
|
13
15
|
'\n' +
|
|
14
16
|
files
|
|
@@ -19,26 +21,95 @@ export async function sendFiles(files, options, settings) {
|
|
|
19
21
|
return `- ${file.fileName}`;
|
|
20
22
|
})
|
|
21
23
|
.join('\n'));
|
|
22
|
-
const spinner = createSpinner('dots');
|
|
23
|
-
spinner.start(`Sending ${files.length} file${files.length !== 1 ? 's' : ''} to General Translation API...`);
|
|
24
24
|
try {
|
|
25
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
25
|
+
// Step 1: Upload files (get references)
|
|
26
|
+
const uploadSpinner = createSpinner('dots');
|
|
27
|
+
currentSpinner = uploadSpinner;
|
|
28
|
+
uploadSpinner.start(`Uploading ${files.length} file${files.length !== 1 ? 's' : ''} to General Translation API...`);
|
|
29
|
+
const sourceLocale = settings.defaultLocale;
|
|
30
|
+
if (!sourceLocale) {
|
|
31
|
+
uploadSpinner.stop(chalk.red('Missing default source locale'));
|
|
32
|
+
throw new Error('sendFiles: settings.defaultLocale is required to upload source files');
|
|
33
|
+
}
|
|
34
|
+
// Convert FileToTranslate[] -> { source: FileUpload }[]
|
|
35
|
+
const uploads = files.map(({ content, fileName, fileFormat, dataFormat }) => ({
|
|
36
|
+
source: {
|
|
37
|
+
content,
|
|
38
|
+
fileName,
|
|
39
|
+
fileFormat,
|
|
40
|
+
dataFormat,
|
|
41
|
+
locale: sourceLocale,
|
|
42
|
+
},
|
|
43
|
+
}));
|
|
44
|
+
const upload = await gt.uploadSourceFiles(uploads, {
|
|
45
|
+
sourceLocale,
|
|
46
|
+
modelProvider: settings.modelProvider,
|
|
47
|
+
});
|
|
48
|
+
uploadSpinner.stop(chalk.green('Files uploaded successfully'));
|
|
49
|
+
// Check if setup is needed
|
|
50
|
+
const setupDecision = await Promise.resolve(gt.shouldSetupProject?.())
|
|
51
|
+
.then((v) => v)
|
|
52
|
+
.catch(() => ({ shouldSetupProject: false }));
|
|
53
|
+
const shouldSetupProject = Boolean(setupDecision?.shouldSetupProject);
|
|
54
|
+
// Step 2: Setup if needed and poll until complete
|
|
55
|
+
if (shouldSetupProject) {
|
|
56
|
+
// Calculate timeout once for setup fetching
|
|
57
|
+
// Accept number or numeric string, default to 600s
|
|
58
|
+
const timeoutVal = options?.timeout !== undefined ? Number(options.timeout) : 600;
|
|
59
|
+
const setupTimeoutMs = (Number.isFinite(timeoutVal) ? timeoutVal : 600) * 1000;
|
|
60
|
+
const { setupJobId } = await gt.setupProject(upload.uploadedFiles);
|
|
61
|
+
const setupSpinner = createSpinner('dots');
|
|
62
|
+
currentSpinner = setupSpinner;
|
|
63
|
+
setupSpinner.start('Setting up project...');
|
|
64
|
+
const start = Date.now();
|
|
65
|
+
const pollInterval = 2000;
|
|
66
|
+
let setupCompleted = false;
|
|
67
|
+
let setupFailedMessage = null;
|
|
68
|
+
while (true) {
|
|
69
|
+
const status = await gt.checkSetupStatus(setupJobId);
|
|
70
|
+
if (status.status === 'completed') {
|
|
71
|
+
setupCompleted = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
if (status.status === 'failed') {
|
|
75
|
+
setupFailedMessage = status.error?.message || 'Unknown error';
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
if (Date.now() - start > setupTimeoutMs) {
|
|
79
|
+
setupFailedMessage = 'Timed out while waiting for setup generation';
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
83
|
+
}
|
|
84
|
+
if (setupCompleted) {
|
|
85
|
+
setupSpinner.stop(chalk.green('Setup successfully completed'));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
setupSpinner.stop(chalk.yellow(`Setup ${setupFailedMessage ? 'failed' : 'timed out'} — proceeding without setup${setupFailedMessage ? ` (${setupFailedMessage})` : ''}`));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Step 3: Enqueue translations by reference
|
|
92
|
+
const enqueueSpinner = createSpinner('dots');
|
|
93
|
+
currentSpinner = enqueueSpinner;
|
|
94
|
+
enqueueSpinner.start('Enqueuing translations...');
|
|
95
|
+
const enqueueResult = await gt.enqueueFiles(upload.uploadedFiles, {
|
|
28
96
|
sourceLocale: settings.defaultLocale,
|
|
29
97
|
targetLocales: settings.locales,
|
|
30
|
-
|
|
98
|
+
publish: settings.publish,
|
|
99
|
+
requireApproval: settings.stageTranslations,
|
|
31
100
|
modelProvider: settings.modelProvider,
|
|
32
101
|
force: options?.force,
|
|
33
102
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
spinner.stop(chalk.green('Files for translation uploaded successfully'));
|
|
103
|
+
const { data, message, locales, translations } = enqueueResult;
|
|
104
|
+
enqueueSpinner.stop(chalk.green('Files for translation uploaded successfully'));
|
|
37
105
|
logSuccess(message);
|
|
38
106
|
return { data, locales, translations };
|
|
39
107
|
}
|
|
40
108
|
catch (error) {
|
|
41
|
-
|
|
109
|
+
if (currentSpinner) {
|
|
110
|
+
currentSpinner.stop(chalk.red('Failed to send files for translation'));
|
|
111
|
+
}
|
|
112
|
+
logError('Failed to send files for translation');
|
|
42
113
|
throw error;
|
|
43
114
|
}
|
|
44
115
|
}
|
package/dist/api/uploadFiles.js
CHANGED
|
@@ -18,12 +18,20 @@ export async function uploadFiles(files, options) {
|
|
|
18
18
|
const spinner = createSpinner('dots');
|
|
19
19
|
spinner.start(`Uploading ${files.length} file${files.length !== 1 ? 's' : ''} to General Translation...`);
|
|
20
20
|
try {
|
|
21
|
-
|
|
21
|
+
// Upload sources
|
|
22
|
+
await gt.uploadSourceFiles(files, {
|
|
22
23
|
...options,
|
|
23
24
|
sourceLocale: options.defaultLocale,
|
|
24
25
|
});
|
|
26
|
+
// Upload translations (if any exist)
|
|
27
|
+
const withTranslations = files.filter((f) => f.translations.length > 0);
|
|
28
|
+
if (withTranslations.length > 0) {
|
|
29
|
+
await gt.uploadTranslations(withTranslations, {
|
|
30
|
+
...options,
|
|
31
|
+
sourceLocale: options.defaultLocale, // optional, safe to include
|
|
32
|
+
});
|
|
33
|
+
}
|
|
25
34
|
spinner.stop(chalk.green('Files uploaded successfully'));
|
|
26
|
-
return result;
|
|
27
35
|
}
|
|
28
36
|
catch {
|
|
29
37
|
spinner.stop(chalk.red('An unexpected error occurred while uploading files'));
|
|
@@ -100,9 +100,8 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
|
|
|
100
100
|
const fileMapping = createFileMapping(filePaths, placeholderPaths, transformPaths, locales, options.defaultLocale);
|
|
101
101
|
// construct object
|
|
102
102
|
const uploadData = allFiles.map((file) => {
|
|
103
|
-
const encodedContent = Buffer.from(file.content).toString('base64');
|
|
104
103
|
const sourceFile = {
|
|
105
|
-
content:
|
|
104
|
+
content: file.content,
|
|
106
105
|
fileName: file.fileName,
|
|
107
106
|
fileFormat: file.fileFormat,
|
|
108
107
|
dataFormat: file.dataFormat,
|
|
@@ -113,9 +112,8 @@ export async function upload(filePaths, placeholderPaths, transformPaths, dataFo
|
|
|
113
112
|
const translatedFileName = fileMapping[locale][file.fileName];
|
|
114
113
|
if (translatedFileName && existsSync(translatedFileName)) {
|
|
115
114
|
const translatedContent = readFileSync(translatedFileName, 'utf8');
|
|
116
|
-
const encodedTranslatedContent = Buffer.from(translatedContent).toString('base64');
|
|
117
115
|
translations.push({
|
|
118
|
-
content:
|
|
116
|
+
content: translatedContent,
|
|
119
117
|
fileName: translatedFileName,
|
|
120
118
|
fileFormat: file.fileFormat,
|
|
121
119
|
dataFormat: file.dataFormat,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtx-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": "dist/main.js",
|
|
6
6
|
"files": [
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"esbuild": "^0.25.4",
|
|
88
88
|
"fast-glob": "^3.3.3",
|
|
89
89
|
"form-data": "^4.0.4",
|
|
90
|
-
"generaltranslation": "^7.
|
|
90
|
+
"generaltranslation": "^7.6.0",
|
|
91
91
|
"json-pointer": "^0.6.2",
|
|
92
92
|
"jsonpath-plus": "^10.3.0",
|
|
93
93
|
"jsonpointer": "^5.0.1",
|