@tolgee/cli 1.2.0 → 1.3.1
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/client/errors.js +13 -8
- package/dist/client/export.js +4 -0
- package/dist/client/import.js +3 -1
- package/dist/client/internal/requester.js +13 -19
- package/dist/commands/login.js +1 -1
- package/dist/commands/pull.js +17 -4
- package/dist/commands/push.js +1 -1
- package/dist/config/tolgeerc.js +1 -1
- package/dist/constants.js +1 -1
- package/dist/extractor/machines/shared/properties.js +20 -3
- package/dist/index.js +3 -3
- package/package.json +1 -1
package/dist/client/errors.js
CHANGED
@@ -1,32 +1,37 @@
|
|
1
|
+
import { STATUS_CODES } from 'http';
|
1
2
|
export class HttpError extends Error {
|
2
3
|
request;
|
3
4
|
response;
|
4
5
|
constructor(request, response, options) {
|
5
|
-
super(response.
|
6
|
+
super(`HTTP Error ${response.statusCode} ${STATUS_CODES[response.statusCode]}`, options);
|
6
7
|
this.request = request;
|
7
8
|
this.response = response;
|
8
9
|
}
|
9
10
|
getErrorText() {
|
10
11
|
// Unauthorized
|
11
|
-
if (this.response.
|
12
|
+
if (this.response.statusCode === 400) {
|
12
13
|
return 'Invalid request data.';
|
13
14
|
}
|
14
15
|
// Unauthorized
|
15
|
-
if (this.response.
|
16
|
+
if (this.response.statusCode === 401) {
|
16
17
|
return 'Missing or invalid authentication token.';
|
17
18
|
}
|
18
19
|
// Forbidden
|
19
|
-
if (this.response.
|
20
|
+
if (this.response.statusCode === 403) {
|
20
21
|
return 'You are not allowed to perform this operation.';
|
21
22
|
}
|
23
|
+
// Rate limited
|
24
|
+
if (this.response.statusCode === 429) {
|
25
|
+
return "You've been rate limited. Please try again later.";
|
26
|
+
}
|
22
27
|
// Service Unavailable
|
23
|
-
if (this.response.
|
28
|
+
if (this.response.statusCode === 503) {
|
24
29
|
return 'API is temporarily unavailable. Please try again later.';
|
25
30
|
}
|
26
31
|
// Server error
|
27
|
-
if (this.response.
|
28
|
-
return `API reported a server error (${this.response.
|
32
|
+
if (this.response.statusCode >= 500) {
|
33
|
+
return `API reported a server error (${this.response.statusCode}). Please try again later.`;
|
29
34
|
}
|
30
|
-
return `Unknown error (HTTP ${this.response.
|
35
|
+
return `Unknown error (HTTP ${this.response.statusCode})`;
|
31
36
|
}
|
32
37
|
}
|
package/dist/client/export.js
CHANGED
@@ -8,6 +8,8 @@ export default class ExportClient {
|
|
8
8
|
method: 'POST',
|
9
9
|
path: `${this.requester.projectUrl}/export`,
|
10
10
|
body: { ...req, zip: true },
|
11
|
+
headersTimeout: 300,
|
12
|
+
bodyTimeout: 300,
|
11
13
|
});
|
12
14
|
}
|
13
15
|
async exportSingle(req) {
|
@@ -15,6 +17,8 @@ export default class ExportClient {
|
|
15
17
|
method: 'POST',
|
16
18
|
path: `${this.requester.projectUrl}/export`,
|
17
19
|
body: { ...req, zip: false },
|
20
|
+
headersTimeout: 300,
|
21
|
+
bodyTimeout: 300,
|
18
22
|
});
|
19
23
|
}
|
20
24
|
}
|
package/dist/client/import.js
CHANGED
@@ -33,6 +33,8 @@ export default class ImportClient {
|
|
33
33
|
method: 'PUT',
|
34
34
|
path: `${this.requester.projectUrl}/import/apply`,
|
35
35
|
query: { forceMode: req?.forceMode },
|
36
|
+
headersTimeout: 300,
|
37
|
+
bodyTimeout: 300,
|
36
38
|
});
|
37
39
|
}
|
38
40
|
async deleteImport() {
|
@@ -47,7 +49,7 @@ export default class ImportClient {
|
|
47
49
|
await this.deleteImport();
|
48
50
|
}
|
49
51
|
catch (e) {
|
50
|
-
if (e instanceof HttpError && e.response.
|
52
|
+
if (e instanceof HttpError && e.response.statusCode === 404)
|
51
53
|
return;
|
52
54
|
throw e;
|
53
55
|
}
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
1
|
+
import { STATUS_CODES } from 'http';
|
2
|
+
import { request } from 'undici';
|
3
3
|
import FormData from 'form-data';
|
4
4
|
import { HttpError } from '../errors.js';
|
5
5
|
import { debug } from '../../utils/logger.js';
|
@@ -54,16 +54,17 @@ export default class Requester {
|
|
54
54
|
body = JSON.stringify(req.body);
|
55
55
|
}
|
56
56
|
}
|
57
|
-
|
57
|
+
debug(`[HTTP] Requesting: ${req.method} ${url}`);
|
58
|
+
const response = await request(url, {
|
58
59
|
method: req.method,
|
59
60
|
headers: headers,
|
60
61
|
body: body,
|
62
|
+
headersTimeout: req.headersTimeout,
|
63
|
+
bodyTimeout: req.bodyTimeout,
|
61
64
|
});
|
62
|
-
debug(`[HTTP]
|
63
|
-
|
64
|
-
|
65
|
-
if (!response.ok)
|
66
|
-
throw new HttpError(request, response);
|
65
|
+
debug(`[HTTP] ${req.method} ${url} -> ${response.statusCode} ${STATUS_CODES[response.statusCode]}`);
|
66
|
+
if (response.statusCode >= 400)
|
67
|
+
throw new HttpError(req, response);
|
67
68
|
return response;
|
68
69
|
}
|
69
70
|
/**
|
@@ -73,7 +74,7 @@ export default class Requester {
|
|
73
74
|
* @returns The response data
|
74
75
|
*/
|
75
76
|
async requestJson(req) {
|
76
|
-
return this.request(req).then((r) => r.json());
|
77
|
+
return this.request(req).then((r) => r.body.json());
|
77
78
|
}
|
78
79
|
/**
|
79
80
|
* Performs an HTTP request to the API and returns the result as a Blob
|
@@ -82,20 +83,13 @@ export default class Requester {
|
|
82
83
|
* @returns The response blob
|
83
84
|
*/
|
84
85
|
async requestBlob(req) {
|
85
|
-
return this.request(req).then((r) => r.blob());
|
86
|
+
return this.request(req).then((r) => r.body.blob());
|
86
87
|
}
|
87
88
|
/**
|
88
|
-
* Performs an HTTP request to the API
|
89
|
-
*
|
90
|
-
* @see https://github.com/nodejs/undici#garbage-collection
|
91
|
-
* @param req Request data
|
89
|
+
* Performs an HTTP request to the API.
|
92
90
|
*/
|
93
91
|
async requestVoid(req) {
|
94
|
-
|
95
|
-
if (res.body) {
|
96
|
-
for await (const _chunk of res.body)
|
97
|
-
;
|
98
|
-
}
|
92
|
+
await this.request(req);
|
99
93
|
}
|
100
94
|
/**
|
101
95
|
* Performs an HTTP request to the API to a resource which is paginated.
|
package/dist/commands/login.js
CHANGED
@@ -10,7 +10,7 @@ async function loginHandler(key) {
|
|
10
10
|
keyInfo = await RestClient.getApiKeyInformation(opts.apiUrl, key);
|
11
11
|
}
|
12
12
|
catch (e) {
|
13
|
-
if (e instanceof HttpError && e.response.
|
13
|
+
if (e instanceof HttpError && e.response.statusCode === 401) {
|
14
14
|
error("Couldn't log in: the API key you provided is invalid.");
|
15
15
|
process.exit(1);
|
16
16
|
}
|
package/dist/commands/pull.js
CHANGED
@@ -1,21 +1,33 @@
|
|
1
1
|
import { Command, Option } from 'commander';
|
2
2
|
import { unzipBuffer } from '../utils/zip.js';
|
3
3
|
import { overwriteDir } from '../utils/overwriteDir.js';
|
4
|
-
import { loading, success } from '../utils/logger.js';
|
4
|
+
import { error, loading, success } from '../utils/logger.js';
|
5
|
+
import { HttpError } from '../client/errors.js';
|
5
6
|
async function fetchZipBlob(opts) {
|
6
7
|
return opts.client.export.export({
|
7
8
|
format: opts.format,
|
8
9
|
languages: opts.languages,
|
9
10
|
filterState: opts.states,
|
10
11
|
structureDelimiter: opts.delimiter,
|
12
|
+
filterNamespace: opts.namespaces,
|
11
13
|
});
|
12
14
|
}
|
13
15
|
async function pullHandler(path) {
|
14
16
|
const opts = this.optsWithGlobals();
|
15
17
|
await overwriteDir(path, opts.overwrite);
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
try {
|
19
|
+
const zipBlob = await loading('Fetching strings from Tolgee...', fetchZipBlob(opts));
|
20
|
+
await loading('Extracting strings...', unzipBuffer(zipBlob, path));
|
21
|
+
success('Done!');
|
22
|
+
}
|
23
|
+
catch (e) {
|
24
|
+
if (e instanceof HttpError && e.response.statusCode === 400) {
|
25
|
+
const res = await e.response.body.json();
|
26
|
+
error(`Please check if your parameters, including namespaces, are configured correctly. Tolgee responded with: ${res.code}`);
|
27
|
+
return;
|
28
|
+
}
|
29
|
+
throw e;
|
30
|
+
}
|
19
31
|
}
|
20
32
|
export default new Command()
|
21
33
|
.name('pull')
|
@@ -32,5 +44,6 @@ export default new Command()
|
|
32
44
|
.addOption(new Option('-d, --delimiter', 'Structure delimiter to use. By default, Tolgee interprets `.` as a nested structure. You can change the delimiter, or disable structure formatting by not specifying any value to the option')
|
33
45
|
.default('.')
|
34
46
|
.argParser((v) => v || ''))
|
47
|
+
.addOption(new Option('-n, --namespaces <namespaces...>', 'List of namespaces to pull. Defaults to all namespaces'))
|
35
48
|
.option('-o, --overwrite', 'Whether to automatically overwrite existing files. BE CAREFUL, THIS WILL WIPE *ALL* THE CONTENTS OF THE TARGET FOLDER. If unspecified, the user will be prompted interactively, or the command will fail when in non-interactive')
|
36
49
|
.action(pullHandler);
|
package/dist/commands/push.js
CHANGED
@@ -73,7 +73,7 @@ async function applyImport(client) {
|
|
73
73
|
await loading('Applying changes...', client.import.applyImport());
|
74
74
|
}
|
75
75
|
catch (e) {
|
76
|
-
if (e instanceof HttpError && e.response.
|
76
|
+
if (e instanceof HttpError && e.response.statusCode === 400) {
|
77
77
|
error("Some of the imported languages weren't recognized. Please create a language with corresponding tag in the Tolgee Platform.");
|
78
78
|
return;
|
79
79
|
}
|
package/dist/config/tolgeerc.js
CHANGED
@@ -49,7 +49,7 @@ function parseConfig(rc) {
|
|
49
49
|
if (typeof rc.delimiter !== 'string' && rc.delimiter !== null) {
|
50
50
|
throw new Error('Invalid config: delimiter is not a string');
|
51
51
|
}
|
52
|
-
cfg.delimiter = rc.delimiter ||
|
52
|
+
cfg.delimiter = rc.delimiter || '';
|
53
53
|
}
|
54
54
|
return cfg;
|
55
55
|
}
|
package/dist/constants.js
CHANGED
@@ -8,4 +8,4 @@ export const USER_AGENT = `Tolgee-CLI/${VERSION} (+https://github.com/tolgee/tol
|
|
8
8
|
export const DEFAULT_API_URL = new URL('https://app.tolgee.io');
|
9
9
|
export const API_KEY_PAT_PREFIX = 'tgpat_';
|
10
10
|
export const API_KEY_PAK_PREFIX = 'tgpak_';
|
11
|
-
export const SDKS = ['react'];
|
11
|
+
export const SDKS = ['react', 'vue', 'svelte'];
|
@@ -7,6 +7,7 @@ export default createMachine({
|
|
7
7
|
context: {
|
8
8
|
property: null,
|
9
9
|
depth: 0,
|
10
|
+
jsxDepth: 0,
|
10
11
|
static: false,
|
11
12
|
nextDynamic: false,
|
12
13
|
keyName: null,
|
@@ -268,10 +269,20 @@ export default createMachine({
|
|
268
269
|
cond: 'isCloseCurly',
|
269
270
|
},
|
270
271
|
],
|
271
|
-
'punctuation.definition.tag.
|
272
|
-
|
273
|
-
|
272
|
+
'punctuation.definition.tag.begin.tsx': {
|
273
|
+
actions: 'incrementJsxDepth',
|
274
|
+
cond: (_ctx, evt) => evt.token === '<',
|
274
275
|
},
|
276
|
+
'punctuation.definition.tag.end.tsx': [
|
277
|
+
{
|
278
|
+
target: 'end',
|
279
|
+
actions: 'markPropertyAsDynamic',
|
280
|
+
cond: (ctx) => ctx.jsxDepth === 0,
|
281
|
+
},
|
282
|
+
{
|
283
|
+
actions: 'decrementJsxDepth',
|
284
|
+
},
|
285
|
+
],
|
275
286
|
'punctuation.definition.tag.end.svelte': {
|
276
287
|
target: 'end',
|
277
288
|
actions: 'markPropertyAsDynamic',
|
@@ -346,6 +357,12 @@ export default createMachine({
|
|
346
357
|
decrementDepth: assign({
|
347
358
|
depth: (ctx, _evt) => ctx.depth - 1,
|
348
359
|
}),
|
360
|
+
incrementJsxDepth: assign({
|
361
|
+
jsxDepth: (ctx, _evt) => ctx.jsxDepth + 2,
|
362
|
+
}),
|
363
|
+
decrementJsxDepth: assign({
|
364
|
+
jsxDepth: (ctx, _evt) => ctx.jsxDepth - 1,
|
365
|
+
}),
|
349
366
|
markAsStatic: assign({
|
350
367
|
static: true,
|
351
368
|
}),
|
package/dist/index.js
CHANGED
@@ -112,10 +112,10 @@ async function loadConfig() {
|
|
112
112
|
}
|
113
113
|
async function handleHttpError(e) {
|
114
114
|
error('An error occurred while requesting the API.');
|
115
|
-
error(`${e.request.method} ${e.request.
|
115
|
+
error(`${e.request.method} ${e.request.path}`);
|
116
116
|
error(e.getErrorText());
|
117
117
|
// Remove token from store if necessary
|
118
|
-
if (e.response.
|
118
|
+
if (e.response.statusCode === 401) {
|
119
119
|
const removeFn = program.getOptionValue('_removeApiKeyFromStore');
|
120
120
|
if (removeFn) {
|
121
121
|
info('Removing the API key from the authentication store.');
|
@@ -129,7 +129,7 @@ async function handleHttpError(e) {
|
|
129
129
|
// catastrophic failure) which means the output is completely unpredictable. While some errors are
|
130
130
|
// formatted by the Tolgee server, reality is there's a huge chance the 5xx error hasn't been raised
|
131
131
|
// by Tolgee's error handler.
|
132
|
-
const res = await e.response.text();
|
132
|
+
const res = await e.response.body.text();
|
133
133
|
debug(`Server response:\n\n---\n${res}\n---`);
|
134
134
|
}
|
135
135
|
}
|