@tolgee/cli 1.3.2 → 2.0.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.
Files changed (39) hide show
  1. package/dist/{index.js → cli.js} +54 -63
  2. package/dist/client/ApiClient.js +72 -0
  3. package/dist/client/ExportClient.js +19 -0
  4. package/dist/client/ImportClient.js +22 -0
  5. package/dist/client/TolgeeClient.js +18 -0
  6. package/dist/client/errorFromLoadable.js +35 -0
  7. package/dist/client/getApiKeyInformation.js +39 -0
  8. package/dist/client/internal/requester.js +2 -5
  9. package/dist/commands/extract/check.js +10 -7
  10. package/dist/commands/extract/print.js +10 -7
  11. package/dist/commands/extract.js +3 -5
  12. package/dist/commands/login.js +6 -14
  13. package/dist/commands/pull.js +38 -32
  14. package/dist/commands/push.js +89 -71
  15. package/dist/commands/sync/compare.js +14 -10
  16. package/dist/commands/sync/sync.js +41 -23
  17. package/dist/commands/tag.js +49 -0
  18. package/dist/config/tolgeerc.js +59 -36
  19. package/dist/constants.js +0 -1
  20. package/dist/extractor/machines/vue/decoder.js +1 -1
  21. package/dist/extractor/runner.js +2 -2
  22. package/dist/options.js +31 -7
  23. package/dist/utils/checkPathNotAFile.js +16 -0
  24. package/dist/utils/getSingleOption.js +10 -0
  25. package/dist/utils/getStackTrace.js +7 -0
  26. package/dist/utils/logger.js +8 -0
  27. package/dist/utils/mapExportFormat.js +62 -0
  28. package/dist/utils/mapImportFormat.js +18 -0
  29. package/dist/utils/prepareDir.js +12 -0
  30. package/dist/utils/zip.js +2 -7
  31. package/package.json +17 -13
  32. package/schema.json +175 -0
  33. package/dist/client/errors.js +0 -37
  34. package/dist/client/export.js +0 -20
  35. package/dist/client/import.js +0 -55
  36. package/dist/client/index.js +0 -73
  37. package/dist/client/languages.js +0 -13
  38. package/dist/client/project.js +0 -41
  39. package/dist/utils/overwriteDir.js +0 -34
@@ -0,0 +1,7 @@
1
+ export const getStackTrace = () => {
2
+ const obj = {};
3
+ Error.captureStackTrace(obj, getStackTrace);
4
+ const stack = obj.stack;
5
+ const parts = stack.split('\n');
6
+ return parts.slice(2).join('\n');
7
+ };
@@ -1,3 +1,4 @@
1
+ import { getStackTrace } from './getStackTrace.js';
1
2
  const SYMBOLS = [' 🐁', ' 🐁 ', ' 🐁 ', '🐁 '];
2
3
  let debugEnabled = false;
3
4
  /**
@@ -58,6 +59,13 @@ export function warn(msg) {
58
59
  export function error(msg) {
59
60
  console.log(`🔴 ${msg}`);
60
61
  }
62
+ export function exitWithError(msg) {
63
+ error(msg);
64
+ if (debugEnabled) {
65
+ console.log(getStackTrace());
66
+ }
67
+ process.exit(1);
68
+ }
61
69
  /**
62
70
  * Shows a loading indicator for a Promise until it resolves.
63
71
  *
@@ -0,0 +1,62 @@
1
+ export const mapExportFormat = (format) => {
2
+ switch (format) {
3
+ case 'ANDROID_XML':
4
+ return { format: 'ANDROID_XML', messageFormat: 'JAVA_STRING_FORMAT' };
5
+ case 'APPLE_STRINGS':
6
+ return {
7
+ format: 'APPLE_STRINGS_STRINGSDICT',
8
+ messageFormat: 'APPLE_SPRINTF',
9
+ };
10
+ case 'APPLE_XLIFF':
11
+ return { format: 'APPLE_XLIFF', messageFormat: 'APPLE_SPRINTF' };
12
+ case 'FLUTTER_ARB':
13
+ return { format: 'FLUTTER_ARB', messageFormat: 'ICU' };
14
+ case 'JSON_C':
15
+ return { format: 'JSON', messageFormat: 'C_SPRINTF' };
16
+ case 'JSON_ICU':
17
+ return { format: 'JSON', messageFormat: 'ICU' };
18
+ case 'JSON_JAVA':
19
+ return { format: 'JSON', messageFormat: 'JAVA_STRING_FORMAT' };
20
+ case 'JSON_PHP':
21
+ return { format: 'JSON', messageFormat: 'PHP_SPRINTF' };
22
+ case 'JSON_RUBY':
23
+ return { format: 'JSON', messageFormat: 'RUBY_SPRINTF' };
24
+ case 'JSON_TOLGEE':
25
+ return { format: 'JSON_TOLGEE', messageFormat: 'ICU' };
26
+ case 'PO_C':
27
+ return { format: 'PO', messageFormat: 'C_SPRINTF' };
28
+ case 'PO_ICU':
29
+ return { format: 'PO', messageFormat: 'ICU' };
30
+ case 'PO_JAVA':
31
+ return { format: 'PO', messageFormat: 'JAVA_STRING_FORMAT' };
32
+ case 'PO_PHP':
33
+ return { format: 'PO', messageFormat: 'PHP_SPRINTF' };
34
+ case 'PO_RUBY':
35
+ return { format: 'PO', messageFormat: 'RUBY_SPRINTF' };
36
+ case 'PROPERTIES_ICU':
37
+ return {
38
+ format: 'PROPERTIES',
39
+ messageFormat: 'ICU',
40
+ };
41
+ case 'PROPERTIES_JAVA':
42
+ return { format: 'PROPERTIES', messageFormat: 'JAVA_STRING_FORMAT' };
43
+ case 'XLIFF_ICU':
44
+ return { format: 'XLIFF', messageFormat: 'ICU' };
45
+ case 'XLIFF_JAVA':
46
+ return { format: 'XLIFF', messageFormat: 'JAVA_STRING_FORMAT' };
47
+ case 'XLIFF_PHP':
48
+ return { format: 'XLIFF', messageFormat: 'PHP_SPRINTF' };
49
+ case 'XLIFF_RUBY':
50
+ return { format: 'XLIFF', messageFormat: 'RUBY_SPRINTF' };
51
+ case 'YAML_ICU':
52
+ return { format: 'YAML', messageFormat: 'ICU' };
53
+ case 'YAML_JAVA':
54
+ return { format: 'YAML', messageFormat: 'JAVA_STRING_FORMAT' };
55
+ case 'YAML_PHP':
56
+ return { format: 'YAML', messageFormat: 'PHP_SPRINTF' };
57
+ case 'YAML_RUBY':
58
+ return { format: 'YAML_RUBY', messageFormat: 'RUBY_SPRINTF' };
59
+ case undefined:
60
+ return { format: 'JSON_TOLGEE', messageFormat: 'ICU' };
61
+ }
62
+ };
@@ -0,0 +1,18 @@
1
+ export const mapImportFormat = (format, extension) => {
2
+ switch (format) {
3
+ case 'APPLE_STRINGS': {
4
+ // apple separates translations to two separate files
5
+ // we keep it under one format for the cli
6
+ if (extension === '.stringsdict') {
7
+ return 'STRINGSDICT';
8
+ }
9
+ else {
10
+ return 'STRINGS';
11
+ }
12
+ }
13
+ case 'JSON_TOLGEE':
14
+ return 'JSON_ICU';
15
+ default:
16
+ return format ?? 'JSON_ICU';
17
+ }
18
+ };
@@ -0,0 +1,12 @@
1
+ import { mkdir, rm } from 'fs/promises';
2
+ import { existsSync } from 'fs';
3
+ export async function prepareDir(path, emptyDir) {
4
+ const exists = existsSync(path);
5
+ if (emptyDir && exists) {
6
+ await rm(path, { recursive: true });
7
+ }
8
+ if (!exists || emptyDir) {
9
+ // Create the directory
10
+ await mkdir(path, { recursive: true });
11
+ }
12
+ }
package/dist/utils/zip.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createWriteStream } from 'fs';
1
+ import { createWriteStream, existsSync } from 'fs';
2
2
  import { mkdir } from 'fs/promises';
3
3
  import { join, dirname } from 'path';
4
4
  import { fromBuffer } from 'yauzl';
@@ -53,11 +53,6 @@ export function unzip(zip, dest) {
53
53
  return new Promise((resolve, reject) => {
54
54
  zip.on('error', reject);
55
55
  zip.on('end', resolve);
56
- // There is no mechanism for zip files to contain directories
57
- // by standards, and implementations diverge. Some make an explicit
58
- // directory entry (ending with /), some don't make any specific treatment.
59
- // The "safest" way is to check the path on files and create them as necessary.
60
- const seenDirectories = new Set([dest]);
61
56
  zip.readEntry();
62
57
  zip.on('entry', (entry) => {
63
58
  if (entry.fileName.endsWith('/')) {
@@ -67,7 +62,7 @@ export function unzip(zip, dest) {
67
62
  const entryPath = join(dest, entry.fileName);
68
63
  // Handle directory creation
69
64
  const entryDirName = dirname(entryPath);
70
- if (!seenDirectories.has(entryDirName)) {
65
+ if (!existsSync(entryDirName)) {
71
66
  mkdir(entryDirName, { recursive: true }).then(() => dumpFile(zip, entry, entryPath));
72
67
  }
73
68
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tolgee/cli",
3
- "version": "1.3.2",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "A tool to interact with the Tolgee Platform through CLI",
6
6
  "repository": {
@@ -8,14 +8,12 @@
8
8
  "url": "https://github.com/tolgee/tolgee-cli.git"
9
9
  },
10
10
  "bin": {
11
- "tolgee": "./dist/index.js"
11
+ "tolgee": "./dist/cli.js"
12
12
  },
13
13
  "scripts": {
14
- "build": "rimraf dist dist-types && tsc && cp dist-types/extractor/index.d.ts extractor.d.ts",
14
+ "build": "rimraf dist dist-types && tsc && node scripts/copyExtractorTypes.mjs",
15
15
  "test": "npm run test:unit && npm run test:e2e && npm run test:package",
16
16
  "test:unit": "cross-env NODE_OPTIONS=\"--experimental-vm-modules\" jest -c jest.unit.config.ts",
17
- "pretest:e2e": "npm run build && npm run tolgee:start",
18
- "posttest:e2e": "npm run tolgee:stop",
19
17
  "test:e2e": "cross-env NODE_OPTIONS=\"--experimental-vm-modules\" jest -c jest.e2e.config.ts --runInBand",
20
18
  "test:e2e-run": "cross-env NODE_OPTIONS=\"--experimental-vm-modules\" jest -c jest.e2e.config.ts --runInBand",
21
19
  "test:package": "node scripts/validatePackage.js",
@@ -23,20 +21,23 @@
23
21
  "tolgee:stop": "docker stop tolgee_cli_e2e",
24
22
  "lint": "eslint --ext .ts --ext .js --ext .cjs ./src ./test ./scripts jest.config.ts jest.*.config.ts",
25
23
  "prettier": "prettier --write ./src ./test ./scripts jest.config.ts jest.*.config.ts",
26
- "run-dev": "cross-env NODE_OPTIONS=\"--loader=ts-node/esm\" node ./src/index.ts",
24
+ "run-dev": "cross-env NODE_OPTIONS=\"--import=./scripts/registerTsNode.js\" node ./src/cli.ts",
27
25
  "schema": "openapi-typescript http://localhost:22222/v3/api-docs/All%20Internal%20-%20for%20Tolgee%20Web%20application --output src/client/internal/schema.generated.ts",
28
- "release": "semantic-release"
26
+ "release": "semantic-release",
27
+ "config:type": "node scripts/configType.mjs"
29
28
  },
30
29
  "author": "Jan Cizmar",
31
30
  "license": "MIT",
32
31
  "dependencies": {
33
32
  "ansi-colors": "^4.1.3",
34
33
  "base32-decode": "^1.0.0",
35
- "commander": "^11.0.0",
34
+ "commander": "^12.0.0",
36
35
  "cosmiconfig": "^8.2.0",
37
36
  "form-data": "^4.0.0",
38
37
  "glob": "^10.3.3",
39
38
  "json5": "^2.2.3",
39
+ "jsonschema": "^1.4.1",
40
+ "openapi-fetch": "^0.9.7",
40
41
  "undici": "^5.22.1",
41
42
  "vscode-oniguruma": "^1.7.0",
42
43
  "vscode-textmate": "^9.0.0",
@@ -55,15 +56,17 @@
55
56
  "eslint": "^8.45.0",
56
57
  "eslint-plugin-jest": "^27.2.3",
57
58
  "eslint-plugin-prettier": "^5.0.0",
58
- "jest": "^29.6.1",
59
+ "jest": "^29.7.0",
59
60
  "js-yaml": "^4.1.0",
60
- "openapi-typescript": "^6.3.9",
61
+ "json-schema-to-typescript": "^13.1.2",
62
+ "openapi-typescript": "^6.7.6",
61
63
  "prettier": "^3.0.0",
62
64
  "rimraf": "^5.0.1",
63
65
  "semantic-release": "^21.0.7",
64
- "ts-jest": "^29.1.1",
65
- "ts-node": "^10.9.1",
66
- "typescript": "^5.1.6"
66
+ "tree-cli": "^0.6.7",
67
+ "ts-jest": "^29.1.3",
68
+ "ts-node": "^10.9.2",
69
+ "typescript": "^5.4.5"
67
70
  },
68
71
  "engines": {
69
72
  "node": ">= 18"
@@ -72,6 +75,7 @@
72
75
  "dist",
73
76
  "textmate",
74
77
  "extractor.d.ts",
78
+ "schema.json",
75
79
  "README.md",
76
80
  "LICENSE"
77
81
  ],
package/schema.json ADDED
@@ -0,0 +1,175 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "projectId": {
5
+ "description": "Project ID. Only required when using a Personal Access Token.",
6
+ "type": ["number", "string"]
7
+ },
8
+ "extractor": {
9
+ "description": "A path to a custom extractor to use instead of the default one.",
10
+ "type": "string"
11
+ },
12
+ "apiUrl": {
13
+ "description": "The url of Tolgee API.",
14
+ "type": "string"
15
+ },
16
+ "patterns": {
17
+ "description": "File glob patterns to your source code, used for keys extraction.",
18
+ "type": "array",
19
+ "items": {
20
+ "type": "string"
21
+ }
22
+ },
23
+ "format": {
24
+ "$ref": "#/$defs/format"
25
+ },
26
+ "push": {
27
+ "type": "object",
28
+ "properties": {
29
+ "files": {
30
+ "description": "Define, which files should be pushed and attach language/namespace to them. By default Tolgee pushes all files specified here, you can filter them by languages and namespaces properties.",
31
+ "type": "array",
32
+ "items": { "$ref": "#/$defs/fileMatch" }
33
+ },
34
+ "languages": {
35
+ "description": "Specifies which languages should be pushed from push.files.",
36
+ "type": "array",
37
+ "items": { "type": "string" }
38
+ },
39
+ "namespaces": {
40
+ "description": "Specifies which namespaces should be pushed from push.files.",
41
+ "type": "array",
42
+ "items": { "type": "string" }
43
+ },
44
+ "forceMode": {
45
+ "$ref": "#/$defs/forceMode"
46
+ },
47
+ "overrideKeyDescriptions": {
48
+ "description": "Override existing key descriptions from local files (only relevant for some formats).",
49
+ "type": "boolean"
50
+ },
51
+ "convertPlaceholdersToIcu": {
52
+ "description": "Convert placeholders in local files to ICU format. (Default: true)",
53
+ "type": "boolean"
54
+ },
55
+ "tagNewKeys": {
56
+ "description": "Specify tags that will be added to newly created keys.",
57
+ "type": "array",
58
+ "items": {
59
+ "type": "string"
60
+ }
61
+ }
62
+ }
63
+ },
64
+ "pull": {
65
+ "type": "object",
66
+ "properties": {
67
+ "path": {
68
+ "description": "Path to a folder where the localization files are stored. (Structure itself can be defined with `fileStructureTemplate`)",
69
+ "type": "string"
70
+ },
71
+ "languages": {
72
+ "description": "List of languages to pull. Leave unspecified to export them all.",
73
+ "type": "array",
74
+ "items": {
75
+ "type": "string"
76
+ }
77
+ },
78
+ "namespaces": {
79
+ "description": "List of namespaces to pull. Defaults to all namespaces.",
80
+ "type": "array",
81
+ "items": {
82
+ "type": "string"
83
+ }
84
+ },
85
+ "states": {
86
+ "description": "List of translation states to include. Defaults all except untranslated.",
87
+ "type": "array",
88
+ "items": {
89
+ "enum": ["UNTRANSLATED", "TRANSLATED", "REVIEWED"]
90
+ }
91
+ },
92
+ "tags": {
93
+ "description": "List of tags which to include.",
94
+ "type": "array",
95
+ "items": {
96
+ "type": "string"
97
+ }
98
+ },
99
+ "excludeTags": {
100
+ "description": "List of tags which to exclude.",
101
+ "type": "array",
102
+ "items": {
103
+ "type": "string"
104
+ }
105
+ },
106
+ "supportArrays": {
107
+ "description": "Export keys with array syntax (e.g. item[0]) as arrays.",
108
+ "type": "boolean"
109
+ },
110
+ "fileStructureTemplate": {
111
+ "description": "Defines exported file structure: https://tolgee.io/tolgee-cli/push-pull-strings#file-structure-template-format",
112
+ "type": "string"
113
+ },
114
+ "emptyDir": {
115
+ "description": "Empty [path] folder before inserting pulled files.",
116
+ "type": "boolean"
117
+ },
118
+ "delimiter": {
119
+ "description": "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.",
120
+ "type": ["string", "null"]
121
+ }
122
+ }
123
+ }
124
+ },
125
+ "$defs": {
126
+ "fileMatch": {
127
+ "type": "object",
128
+ "properties": {
129
+ "path": { "$ref": "#/$defs/path" },
130
+ "language": { "type": "string" },
131
+ "namespace": { "type": "string" }
132
+ },
133
+ "required": ["path", "language"]
134
+ },
135
+ "forceMode": {
136
+ "description": "Specifies how to solve potential conflicts in the pushed data.\n\nOptions:\n\n `OVERRIDE` - update everything according to local files\n `KEEP` - create only non-existent strings, don't touch existing ones\n `NO_FORCE` - throw an error, if there are any conflict",
137
+ "type": "string",
138
+ "enum": ["OVERRIDE", "KEEP", "NO_FORCE"]
139
+ },
140
+ "path": {
141
+ "description": "File glob specifying which files to include.",
142
+ "type": "string"
143
+ },
144
+ "format": {
145
+ "description": "Localization files format for push and pull operations.",
146
+ "enum": [
147
+ "JSON_TOLGEE",
148
+ "JSON_ICU",
149
+ "JSON_JAVA",
150
+ "JSON_PHP",
151
+ "JSON_RUBY",
152
+ "JSON_C",
153
+ "PO_PHP",
154
+ "PO_C",
155
+ "PO_JAVA",
156
+ "PO_ICU",
157
+ "PO_RUBY",
158
+ "APPLE_STRINGS",
159
+ "APPLE_XLIFF",
160
+ "PROPERTIES_ICU",
161
+ "PROPERTIES_JAVA",
162
+ "ANDROID_XML",
163
+ "FLUTTER_ARB",
164
+ "YAML_RUBY",
165
+ "YAML_JAVA",
166
+ "YAML_ICU",
167
+ "YAML_PHP",
168
+ "XLIFF_ICU",
169
+ "XLIFF_JAVA",
170
+ "XLIFF_PHP",
171
+ "XLIFF_RUBY"
172
+ ]
173
+ }
174
+ }
175
+ }
@@ -1,37 +0,0 @@
1
- import { STATUS_CODES } from 'http';
2
- export class HttpError extends Error {
3
- request;
4
- response;
5
- constructor(request, response, options) {
6
- super(`HTTP Error ${response.statusCode} ${STATUS_CODES[response.statusCode]}`, options);
7
- this.request = request;
8
- this.response = response;
9
- }
10
- getErrorText() {
11
- // Unauthorized
12
- if (this.response.statusCode === 400) {
13
- return 'Invalid request data.';
14
- }
15
- // Unauthorized
16
- if (this.response.statusCode === 401) {
17
- return 'Missing or invalid authentication token.';
18
- }
19
- // Forbidden
20
- if (this.response.statusCode === 403) {
21
- return 'You are not allowed to perform this operation.';
22
- }
23
- // Rate limited
24
- if (this.response.statusCode === 429) {
25
- return "You've been rate limited. Please try again later.";
26
- }
27
- // Service Unavailable
28
- if (this.response.statusCode === 503) {
29
- return 'API is temporarily unavailable. Please try again later.';
30
- }
31
- // Server error
32
- if (this.response.statusCode >= 500) {
33
- return `API reported a server error (${this.response.statusCode}). Please try again later.`;
34
- }
35
- return `Unknown error (HTTP ${this.response.statusCode})`;
36
- }
37
- }
@@ -1,20 +0,0 @@
1
- export default class ExportClient {
2
- requester;
3
- constructor(requester) {
4
- this.requester = requester;
5
- }
6
- async export(req) {
7
- return this.requester.requestBlob({
8
- method: 'POST',
9
- path: `${this.requester.projectUrl}/export`,
10
- body: { ...req, zip: true },
11
- });
12
- }
13
- async exportSingle(req) {
14
- return this.requester.requestJson({
15
- method: 'POST',
16
- path: `${this.requester.projectUrl}/export`,
17
- body: { ...req, zip: false },
18
- });
19
- }
20
- }
@@ -1,55 +0,0 @@
1
- import FormData from 'form-data';
2
- import { HttpError } from './errors.js';
3
- export default class ImportClient {
4
- requester;
5
- constructor(requester) {
6
- this.requester = requester;
7
- }
8
- async addFiles(req) {
9
- const body = new FormData();
10
- for (const file of req.files) {
11
- body.append('files', file.data, { filepath: file.name });
12
- }
13
- return this.requester.requestJson({
14
- method: 'POST',
15
- path: `${this.requester.projectUrl}/import`,
16
- body: body,
17
- });
18
- }
19
- async conflictsOverrideAll(languageId) {
20
- await this.requester.requestVoid({
21
- method: 'PUT',
22
- path: `${this.requester.projectUrl}/import/result/languages/${languageId}/resolve-all/set-override`,
23
- });
24
- }
25
- async conflictsKeepExistingAll(languageId) {
26
- await this.requester.requestVoid({
27
- method: 'PUT',
28
- path: `${this.requester.projectUrl}/import/result/languages/${languageId}/resolve-all/set-keep-existing`,
29
- });
30
- }
31
- async applyImport(req) {
32
- await this.requester.requestVoid({
33
- method: 'PUT',
34
- path: `${this.requester.projectUrl}/import/apply`,
35
- query: { forceMode: req?.forceMode },
36
- });
37
- }
38
- async deleteImport() {
39
- await this.requester.requestVoid({
40
- method: 'DELETE',
41
- path: `${this.requester.projectUrl}/import`,
42
- });
43
- }
44
- /* Helper functions */
45
- async deleteImportIfExists() {
46
- try {
47
- await this.deleteImport();
48
- }
49
- catch (e) {
50
- if (e instanceof HttpError && e.response.statusCode === 404)
51
- return;
52
- throw e;
53
- }
54
- }
55
- }
@@ -1,73 +0,0 @@
1
- import base32Decode from 'base32-decode';
2
- import Requester from './internal/requester.js';
3
- import ProjectClient from './project.js';
4
- import LanguagesClient from './languages.js';
5
- import ImportClient from './import.js';
6
- import ExportClient from './export.js';
7
- import { API_KEY_PAK_PREFIX } from '../constants.js';
8
- export default class RestClient {
9
- params;
10
- requester;
11
- project;
12
- languages;
13
- import;
14
- export;
15
- constructor(params) {
16
- this.params = params;
17
- this.requester = new Requester(params);
18
- this.project = new ProjectClient(this.requester);
19
- this.languages = new LanguagesClient(this.requester);
20
- this.import = new ImportClient(this.requester);
21
- this.export = new ExportClient(this.requester);
22
- }
23
- async getProjectApiKeyInformation() {
24
- return this.requester.requestJson({
25
- path: '/v2/api-keys/current',
26
- method: 'GET',
27
- });
28
- }
29
- getProjectId() {
30
- return this.params.projectId;
31
- }
32
- static projectIdFromKey(key) {
33
- const keyBuffer = base32Decode(key.slice(API_KEY_PAK_PREFIX.length).toUpperCase(), 'RFC4648');
34
- const decoded = Buffer.from(keyBuffer).toString('utf8');
35
- return Number(decoded.split('_')[0]);
36
- }
37
- static getProjectApiKeyInformation(api, key) {
38
- return new Requester({ apiUrl: api, apiKey: key }).requestJson({
39
- path: '/v2/api-keys/current',
40
- method: 'GET',
41
- });
42
- }
43
- static getPersonalAccessTokenInformation(api, key) {
44
- return new Requester({ apiUrl: api, apiKey: key }).requestJson({
45
- path: '/v2/pats/current',
46
- method: 'GET',
47
- });
48
- }
49
- static async getApiKeyInformation(api, key) {
50
- if (key.startsWith(API_KEY_PAK_PREFIX)) {
51
- const info = await RestClient.getProjectApiKeyInformation(api, key);
52
- const username = info.userFullName || info.username || '<unknown user>';
53
- return {
54
- type: 'PAK',
55
- key: key,
56
- username: username,
57
- project: {
58
- id: info.projectId,
59
- name: info.projectName,
60
- },
61
- expires: info.expiresAt ?? 0,
62
- };
63
- }
64
- const info = await RestClient.getPersonalAccessTokenInformation(api, key);
65
- const username = info.user.name || info.user.username;
66
- return {
67
- type: 'PAT',
68
- key: key,
69
- username: username,
70
- expires: info.expiresAt ?? 0,
71
- };
72
- }
73
- }
@@ -1,13 +0,0 @@
1
- export default class LanguagesClient {
2
- requester;
3
- constructor(requester) {
4
- this.requester = requester;
5
- }
6
- async getLanguages(req) {
7
- return this.requester.requestPaginatedResource({
8
- method: 'GET',
9
- path: `${this.requester.projectUrl}/languages`,
10
- query: req,
11
- });
12
- }
13
- }
@@ -1,41 +0,0 @@
1
- export default class ProjectClient {
2
- requester;
3
- constructor(requester) {
4
- this.requester = requester;
5
- }
6
- async fetchProjectInformation() {
7
- return this.requester.requestJson({
8
- method: 'GET',
9
- path: this.requester.projectUrl,
10
- });
11
- }
12
- async fetchAllKeys() {
13
- return this.requester
14
- .requestJson({
15
- method: 'GET',
16
- path: `${this.requester.projectUrl}/all-keys`,
17
- })
18
- .then((r) => r._embedded?.keys || []);
19
- }
20
- async createKey(key) {
21
- return this.requester.requestJson({
22
- method: 'POST',
23
- path: `${this.requester.projectUrl}/keys`,
24
- body: key,
25
- });
26
- }
27
- async createBulkKey(keys) {
28
- return this.requester.requestVoid({
29
- method: 'POST',
30
- path: `${this.requester.projectUrl}/keys/import`,
31
- body: { keys },
32
- });
33
- }
34
- async deleteBulkKeys(keyIds) {
35
- return this.requester.requestVoid({
36
- method: 'DELETE',
37
- path: `${this.requester.projectUrl}/keys`,
38
- body: { ids: keyIds },
39
- });
40
- }
41
- }