langctl 0.1.2 → 0.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 +42 -0
- package/LICENSE +1 -1
- package/README.md +159 -1151
- package/dist/commands/auth.js +82 -24
- package/dist/commands/config.js +36 -25
- package/dist/commands/export.js +11 -112
- package/dist/commands/import.js +32 -164
- package/dist/commands/init.js +59 -80
- package/dist/commands/keys.js +145 -381
- package/dist/commands/org.js +45 -140
- package/dist/commands/projects.js +125 -390
- package/dist/commands/pull.js +78 -183
- package/dist/commands/push.js +125 -0
- package/dist/commands/team.js +68 -272
- package/dist/core/config.js +130 -0
- package/dist/core/errors.js +67 -0
- package/dist/core/files.js +38 -0
- package/dist/core/http.js +121 -0
- package/dist/core/output.js +96 -0
- package/dist/core/project.js +58 -0
- package/dist/core/prompts.js +56 -0
- package/dist/formats/index.js +285 -0
- package/dist/index.js +261 -508
- package/dist/version.js +4 -0
- package/package.json +22 -16
- package/.gitattributes +0 -2
- package/NEXT_PHASE.md +0 -618
- package/WORKING_INSTRUCTIONS.md +0 -1389
- package/dist/auth.d.ts +0 -37
- package/dist/auth.js +0 -112
- package/dist/commands/auth.d.ts +0 -3
- package/dist/commands/config.d.ts +0 -2
- package/dist/commands/debug.d.ts +0 -3
- package/dist/commands/debug.js +0 -57
- package/dist/commands/export.d.ts +0 -14
- package/dist/commands/import.d.ts +0 -11
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/keys.d.ts +0 -25
- package/dist/commands/org.d.ts +0 -13
- package/dist/commands/projects.d.ts +0 -30
- package/dist/commands/pull.d.ts +0 -9
- package/dist/commands/team.d.ts +0 -29
- package/dist/config.d.ts +0 -47
- package/dist/config.js +0 -69
- package/dist/exporters/index.d.ts +0 -36
- package/dist/exporters/index.js +0 -214
- package/dist/index.d.ts +0 -3
- package/dist/supabase.d.ts +0 -14
- package/dist/supabase.js +0 -30
- package/dist/utils/banner.d.ts +0 -9
- package/dist/utils/banner.js +0 -24
- package/translations/en.json +0 -5
- package/translations/en.xml +0 -9
- package/translations/fr.json +0 -4
- package/translations/fr.xml +0 -7
- package/translations/hi.json +0 -4
- package/translations/hi.xml +0 -7
- package/translations/ios/en.strings +0 -9
- package/translations/ios/fr.strings +0 -6
- package/translations/ios/hi.strings +0 -6
- package/translations/ios/ja.strings +0 -6
- package/translations/ja.json +0 -4
- package/translations/ja.xml +0 -7
package/dist/commands/keys.js
CHANGED
|
@@ -1,396 +1,160 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
import { CliError, ExitCode, usageError } from '../core/errors.js';
|
|
3
|
+
import { getSession } from '../core/http.js';
|
|
4
|
+
import { log, printJson, runtime, spinner, table } from '../core/output.js';
|
|
5
|
+
import { confirm } from '../core/prompts.js';
|
|
6
|
+
import { assertLanguages, getProject, listAllKeys, projectSlugFrom, requireKey } from '../core/project.js';
|
|
7
|
+
export async function listKeysCommand(projectArg, opts) {
|
|
8
|
+
const session = await getSession();
|
|
9
|
+
const project = await getProject(session, projectSlugFrom(projectArg));
|
|
10
|
+
const spin = spinner('Fetching keys…');
|
|
11
|
+
let keys;
|
|
10
12
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'X-API-Key': apiKey
|
|
16
|
-
}
|
|
13
|
+
keys = await listAllKeys(session, project, {
|
|
14
|
+
module: opts.module,
|
|
15
|
+
search: opts.search,
|
|
16
|
+
published: opts.published ? true : opts.drafts ? false : undefined,
|
|
17
17
|
});
|
|
18
|
-
if (!response.ok)
|
|
19
|
-
return null;
|
|
20
|
-
const data = await response.json();
|
|
21
|
-
if (!data.success || !data.projects)
|
|
22
|
-
return null;
|
|
23
|
-
return data.projects.find((p) => p.slug === slug) || null;
|
|
24
18
|
}
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
finally {
|
|
20
|
+
spin.stop();
|
|
27
21
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
const apiKey = getApiKey();
|
|
38
|
-
const spinner = ora('Fetching keys...').start();
|
|
39
|
-
try {
|
|
40
|
-
const project = await getProjectBySlug(apiKey, projectSlug);
|
|
41
|
-
if (!project) {
|
|
42
|
-
spinner.fail(chalk.red(`Project "${projectSlug}" not found`));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const response = await fetch(EDGE_FUNCTION_URL, {
|
|
46
|
-
method: 'POST',
|
|
47
|
-
headers: {
|
|
48
|
-
'Content-Type': 'application/json',
|
|
49
|
-
'X-API-Key': apiKey
|
|
50
|
-
},
|
|
51
|
-
body: JSON.stringify({
|
|
52
|
-
action: 'list',
|
|
53
|
-
projectId: project.id,
|
|
54
|
-
module: options.module,
|
|
55
|
-
published: options.published,
|
|
56
|
-
search: options.search,
|
|
57
|
-
limit: options.limit || 100,
|
|
58
|
-
offset: options.offset || 0
|
|
59
|
-
})
|
|
60
|
-
});
|
|
61
|
-
const data = await response.json();
|
|
62
|
-
if (!data.success) {
|
|
63
|
-
throw new Error(data.error);
|
|
64
|
-
}
|
|
65
|
-
spinner.stop();
|
|
66
|
-
if (!data.keys || data.keys.length === 0) {
|
|
67
|
-
console.log(chalk.yellow('\nNo keys found\n'));
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
console.log(chalk.blue.bold(`\n🔑 Translation Keys (${data.keys.length}${data.total ? ` of ${data.total}` : ''})\n`));
|
|
71
|
-
data.keys.forEach((key) => {
|
|
72
|
-
console.log(chalk.white.bold(key.key));
|
|
73
|
-
if (key.description) {
|
|
74
|
-
console.log(chalk.gray(` Description: ${key.description}`));
|
|
75
|
-
}
|
|
76
|
-
if (key.module) {
|
|
77
|
-
console.log(chalk.gray(` Module: ${key.module}`));
|
|
78
|
-
}
|
|
79
|
-
console.log(chalk.gray(` Published: ${key.published ? chalk.green('Yes') : chalk.yellow('No')}`));
|
|
80
|
-
const langs = Object.keys(key.translations);
|
|
81
|
-
if (langs.length > 0) {
|
|
82
|
-
console.log(chalk.gray(` Languages: ${langs.join(', ')}`));
|
|
83
|
-
}
|
|
84
|
-
console.log('');
|
|
85
|
-
});
|
|
22
|
+
const limit = opts.limit ? Number(opts.limit) : undefined;
|
|
23
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit < 1))
|
|
24
|
+
throw usageError('--limit must be a positive integer');
|
|
25
|
+
const shown = limit ? keys.slice(0, limit) : keys;
|
|
26
|
+
if (runtime.json) {
|
|
27
|
+
return printJson(shown.map(k => ({
|
|
28
|
+
key: k.key, published: k.published, module: k.module ?? null, description: k.description ?? null, translations: k.translations,
|
|
29
|
+
})));
|
|
86
30
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Get single key
|
|
94
|
-
*/
|
|
95
|
-
export async function getKeyCommand(projectSlug, keyName) {
|
|
96
|
-
if (!isAuthenticated()) {
|
|
97
|
-
console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
31
|
+
if (shown.length === 0) {
|
|
32
|
+
log.info('No keys match.');
|
|
98
33
|
return;
|
|
99
34
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
// First list to find the key ID
|
|
109
|
-
const listResponse = await fetch(EDGE_FUNCTION_URL, {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: {
|
|
112
|
-
'Content-Type': 'application/json',
|
|
113
|
-
'X-API-Key': apiKey
|
|
114
|
-
},
|
|
115
|
-
body: JSON.stringify({
|
|
116
|
-
action: 'list',
|
|
117
|
-
projectId: project.id,
|
|
118
|
-
search: keyName,
|
|
119
|
-
limit: 1
|
|
120
|
-
})
|
|
121
|
-
});
|
|
122
|
-
const listData = await listResponse.json();
|
|
123
|
-
if (!listData.success || !listData.keys || listData.keys.length === 0) {
|
|
124
|
-
spinner.fail(chalk.red(`Key "${keyName}" not found`));
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const key = listData.keys.find((k) => k.key === keyName);
|
|
128
|
-
if (!key) {
|
|
129
|
-
spinner.fail(chalk.red(`Key "${keyName}" not found`));
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
spinner.stop();
|
|
133
|
-
console.log(chalk.blue.bold('\n🔑 Translation Key Details\n'));
|
|
134
|
-
console.log(chalk.white.bold(`Key: ${key.key}`));
|
|
135
|
-
console.log(chalk.gray(`ID: ${key.id}`));
|
|
136
|
-
if (key.description) {
|
|
137
|
-
console.log(chalk.gray(`Description: ${key.description}`));
|
|
138
|
-
}
|
|
139
|
-
if (key.module) {
|
|
140
|
-
console.log(chalk.gray(`Module: ${key.module}`));
|
|
141
|
-
}
|
|
142
|
-
console.log(chalk.gray(`Published: ${key.published ? chalk.green('Yes') : chalk.yellow('No')}`));
|
|
143
|
-
console.log(chalk.blue.bold('\nTranslations:'));
|
|
144
|
-
Object.entries(key.translations).forEach(([lang, value]) => {
|
|
145
|
-
console.log(chalk.white(` ${lang}: ${value}`));
|
|
146
|
-
});
|
|
147
|
-
console.log('');
|
|
148
|
-
}
|
|
149
|
-
catch (error) {
|
|
150
|
-
spinner.fail(chalk.red('Failed to get key'));
|
|
151
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
152
|
-
}
|
|
35
|
+
table(shown.map(k => [
|
|
36
|
+
k.key,
|
|
37
|
+
k.published ? chalk.green('published') : chalk.yellow('draft'),
|
|
38
|
+
k.module ?? '',
|
|
39
|
+
`${project.languages.filter(l => k.translations?.[l]).length}/${project.languages.length}`,
|
|
40
|
+
]), ['KEY', 'STATUS', 'MODULE', 'LANGS']);
|
|
41
|
+
if (limit && keys.length > limit)
|
|
42
|
+
log.info(chalk.dim(`Showing ${limit} of ${keys.length}. Use --json for scripting.`));
|
|
153
43
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
// Build translations object from --value-* options
|
|
171
|
-
const translations = {};
|
|
172
|
-
Object.keys(options).forEach(opt => {
|
|
173
|
-
if (opt.startsWith('value')) {
|
|
174
|
-
const lang = opt.replace('value', '').toLowerCase();
|
|
175
|
-
if (lang) {
|
|
176
|
-
translations[lang] = options[opt];
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
const response = await fetch(EDGE_FUNCTION_URL, {
|
|
181
|
-
method: 'POST',
|
|
182
|
-
headers: {
|
|
183
|
-
'Content-Type': 'application/json',
|
|
184
|
-
'X-API-Key': apiKey
|
|
185
|
-
},
|
|
186
|
-
body: JSON.stringify({
|
|
187
|
-
action: 'create',
|
|
188
|
-
projectId: project.id,
|
|
189
|
-
key: keyName,
|
|
190
|
-
translations,
|
|
191
|
-
description: options.description,
|
|
192
|
-
module: options.module,
|
|
193
|
-
tags: options.tags ? options.tags.split(',') : []
|
|
194
|
-
})
|
|
195
|
-
});
|
|
196
|
-
const data = await response.json();
|
|
197
|
-
if (!data.success) {
|
|
198
|
-
throw new Error(data.error);
|
|
199
|
-
}
|
|
200
|
-
spinner.succeed(chalk.green(`Created key: ${keyName}`));
|
|
201
|
-
console.log('');
|
|
202
|
-
}
|
|
203
|
-
catch (error) {
|
|
204
|
-
spinner.fail(chalk.red('Failed to create key'));
|
|
205
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
44
|
+
export async function getKeyCommand(projectArg, keyName) {
|
|
45
|
+
const session = await getSession();
|
|
46
|
+
const project = await getProject(session, projectArg);
|
|
47
|
+
const key = await requireKey(session, project, keyName);
|
|
48
|
+
if (runtime.json)
|
|
49
|
+
return printJson(key);
|
|
50
|
+
log.out(chalk.bold(key.key) + ' ' + (key.published ? chalk.green('published') : chalk.yellow('draft')));
|
|
51
|
+
if (key.description)
|
|
52
|
+
log.out(chalk.dim(key.description));
|
|
53
|
+
if (key.module)
|
|
54
|
+
log.out(chalk.dim(`module: ${key.module}`));
|
|
55
|
+
for (const lang of project.languages) {
|
|
56
|
+
const v = key.translations?.[lang];
|
|
57
|
+
log.out(` ${lang.padEnd(6)} ${v === undefined || v === '' ? chalk.dim('— missing') : v}`);
|
|
206
58
|
}
|
|
207
59
|
}
|
|
208
|
-
/**
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
// Find key ID
|
|
225
|
-
const listResponse = await fetch(EDGE_FUNCTION_URL, {
|
|
226
|
-
method: 'POST',
|
|
227
|
-
headers: {
|
|
228
|
-
'Content-Type': 'application/json',
|
|
229
|
-
'X-API-Key': apiKey
|
|
230
|
-
},
|
|
231
|
-
body: JSON.stringify({
|
|
232
|
-
action: 'list',
|
|
233
|
-
projectId: project.id,
|
|
234
|
-
search: keyName,
|
|
235
|
-
limit: 1
|
|
236
|
-
})
|
|
237
|
-
});
|
|
238
|
-
const listData = await listResponse.json();
|
|
239
|
-
const key = listData.keys?.find((k) => k.key === keyName);
|
|
240
|
-
if (!key) {
|
|
241
|
-
spinner.fail(chalk.red(`Key "${keyName}" not found`));
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
// Delete key
|
|
245
|
-
const response = await fetch(EDGE_FUNCTION_URL, {
|
|
246
|
-
method: 'POST',
|
|
247
|
-
headers: {
|
|
248
|
-
'Content-Type': 'application/json',
|
|
249
|
-
'X-API-Key': apiKey
|
|
250
|
-
},
|
|
251
|
-
body: JSON.stringify({
|
|
252
|
-
action: 'delete',
|
|
253
|
-
projectId: project.id,
|
|
254
|
-
keyId: key.id
|
|
255
|
-
})
|
|
256
|
-
});
|
|
257
|
-
const data = await response.json();
|
|
258
|
-
if (!data.success) {
|
|
259
|
-
throw new Error(data.error);
|
|
260
|
-
}
|
|
261
|
-
spinner.succeed(chalk.green(`Deleted key: ${keyName}`));
|
|
262
|
-
console.log('');
|
|
263
|
-
}
|
|
264
|
-
catch (error) {
|
|
265
|
-
spinner.fail(chalk.red('Failed to delete key'));
|
|
266
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
267
|
-
}
|
|
60
|
+
/** Collect `--value en=Hello --value es=Hola` plus the legacy `--value-en` style flags. */
|
|
61
|
+
function collectValues(opts) {
|
|
62
|
+
const out = {};
|
|
63
|
+
for (const pair of opts.value ?? []) {
|
|
64
|
+
const i = pair.indexOf('=');
|
|
65
|
+
if (i < 1)
|
|
66
|
+
throw usageError(`--value expects LANG=TEXT, got "${pair}"`, 'Example: --value en="Welcome" --value es="Bienvenido"');
|
|
67
|
+
out[pair.slice(0, i).trim()] = pair.slice(i + 1);
|
|
68
|
+
}
|
|
69
|
+
for (const lang of ['en', 'es', 'fr', 'de']) {
|
|
70
|
+
const legacy = opts[`value${lang[0].toUpperCase()}${lang[1]}`];
|
|
71
|
+
if (typeof legacy === 'string')
|
|
72
|
+
out[lang] = legacy;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
268
75
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// Find key ID
|
|
286
|
-
const listResponse = await fetch(EDGE_FUNCTION_URL, {
|
|
287
|
-
method: 'POST',
|
|
288
|
-
headers: {
|
|
289
|
-
'Content-Type': 'application/json',
|
|
290
|
-
'X-API-Key': apiKey
|
|
291
|
-
},
|
|
292
|
-
body: JSON.stringify({
|
|
293
|
-
action: 'list',
|
|
294
|
-
projectId: project.id,
|
|
295
|
-
search: keyName,
|
|
296
|
-
limit: 1
|
|
297
|
-
})
|
|
298
|
-
});
|
|
299
|
-
const listData = await listResponse.json();
|
|
300
|
-
const key = listData.keys?.find((k) => k.key === keyName);
|
|
301
|
-
if (!key) {
|
|
302
|
-
spinner.fail(chalk.red(`Key "${keyName}" not found`));
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
// Update translation
|
|
306
|
-
const response = await fetch(EDGE_FUNCTION_URL, {
|
|
307
|
-
method: 'POST',
|
|
308
|
-
headers: {
|
|
309
|
-
'Content-Type': 'application/json',
|
|
310
|
-
'X-API-Key': apiKey
|
|
311
|
-
},
|
|
312
|
-
body: JSON.stringify({
|
|
313
|
-
action: 'translate',
|
|
314
|
-
projectId: project.id,
|
|
315
|
-
keyId: key.id,
|
|
316
|
-
language: options.language,
|
|
317
|
-
value: options.value
|
|
318
|
-
})
|
|
319
|
-
});
|
|
320
|
-
const data = await response.json();
|
|
321
|
-
if (!data.success) {
|
|
322
|
-
throw new Error(data.error);
|
|
323
|
-
}
|
|
324
|
-
spinner.succeed(chalk.green(`Updated ${options.language} translation for ${keyName}`));
|
|
325
|
-
console.log('');
|
|
326
|
-
}
|
|
327
|
-
catch (error) {
|
|
328
|
-
spinner.fail(chalk.red('Failed to update translation'));
|
|
329
|
-
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
330
|
-
}
|
|
76
|
+
export async function createKeyCommand(projectArg, keyName, opts) {
|
|
77
|
+
const session = await getSession();
|
|
78
|
+
const project = await getProject(session, projectArg);
|
|
79
|
+
const translations = collectValues(opts);
|
|
80
|
+
assertLanguages(project, Object.keys(translations));
|
|
81
|
+
const key = await session.api.post(`/orgs/${session.orgId}/projects/${project.id}/keys`, {
|
|
82
|
+
key: keyName,
|
|
83
|
+
translations,
|
|
84
|
+
description: opts.description,
|
|
85
|
+
module: opts.module,
|
|
86
|
+
tags: typeof opts.tags === 'string' ? opts.tags.split(',').map(t => t.trim()).filter(Boolean) : undefined,
|
|
87
|
+
published: Boolean(opts.publish),
|
|
88
|
+
});
|
|
89
|
+
if (runtime.json)
|
|
90
|
+
return printJson(key);
|
|
91
|
+
log.success(`Created ${chalk.bold(keyName)}${opts.publish ? ' (published)' : ' (draft — publish with "langctl keys publish")'}`);
|
|
331
92
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
93
|
+
export async function updateKeyCommand(projectArg, keyName, opts) {
|
|
94
|
+
const session = await getSession();
|
|
95
|
+
const project = await getProject(session, projectArg);
|
|
96
|
+
const key = await requireKey(session, project, keyName);
|
|
97
|
+
const translations = collectValues(opts);
|
|
98
|
+
// legacy: keys translate <project> <key> -l es -v "Hola"
|
|
99
|
+
if (typeof opts.language === 'string') {
|
|
100
|
+
if (typeof opts.text !== 'string')
|
|
101
|
+
throw usageError('--language needs --text (or use --value LANG=TEXT)');
|
|
102
|
+
translations[opts.language] = opts.text;
|
|
103
|
+
}
|
|
104
|
+
assertLanguages(project, Object.keys(translations));
|
|
105
|
+
const body = {};
|
|
106
|
+
if (Object.keys(translations).length)
|
|
107
|
+
body.translations = { ...key.translations, ...translations };
|
|
108
|
+
if (opts.description !== undefined)
|
|
109
|
+
body.description = opts.description;
|
|
110
|
+
if (opts.module !== undefined)
|
|
111
|
+
body.module = opts.module;
|
|
112
|
+
if (Object.keys(body).length === 0)
|
|
113
|
+
throw usageError('Nothing to update.', 'Pass --value LANG=TEXT, --description or --module.');
|
|
114
|
+
const updated = await session.api.patch(`/orgs/${session.orgId}/projects/${project.id}/keys/${key.id}`, body);
|
|
115
|
+
if (runtime.json)
|
|
116
|
+
return printJson(updated);
|
|
117
|
+
log.success(`Updated ${chalk.bold(keyName)}${Object.keys(translations).length ? ` (${Object.keys(translations).join(', ')})` : ''}`);
|
|
118
|
+
}
|
|
119
|
+
export async function deleteKeyCommand(projectArg, keyNames) {
|
|
120
|
+
const session = await getSession();
|
|
121
|
+
const project = await getProject(session, projectArg);
|
|
122
|
+
const keys = [];
|
|
123
|
+
for (const name of keyNames)
|
|
124
|
+
keys.push(await requireKey(session, project, name));
|
|
125
|
+
if (!(await confirm(`Delete ${keys.length === 1 ? `key "${keys[0].key}"` : `${keys.length} keys`} from ${project.slug}?`))) {
|
|
126
|
+
throw new CliError('Cancelled.', ExitCode.Error);
|
|
127
|
+
}
|
|
128
|
+
for (const k of keys)
|
|
129
|
+
await session.api.delete(`/orgs/${session.orgId}/projects/${project.id}/keys/${k.id}`);
|
|
130
|
+
if (runtime.json)
|
|
131
|
+
return printJson({ deleted: keys.map(k => k.key) });
|
|
132
|
+
log.success(`Deleted ${keys.map(k => chalk.bold(k.key)).join(', ')}`);
|
|
133
|
+
}
|
|
134
|
+
export async function publishKeysCommand(projectArg, keyNames, opts, publish = true) {
|
|
135
|
+
const published = publish && !opts.unpublish;
|
|
136
|
+
const session = await getSession();
|
|
137
|
+
const project = await getProject(session, projectSlugFrom(projectArg));
|
|
138
|
+
let names = keyNames;
|
|
139
|
+
if (opts.all || opts.module) {
|
|
140
|
+
if (names.length)
|
|
141
|
+
throw usageError('Pass key names or --all/--module, not both.');
|
|
142
|
+
names = (await listAllKeys(session, project, { module: opts.module, published: !published })).map(k => k.key);
|
|
143
|
+
}
|
|
144
|
+
else if (names.length === 0) {
|
|
145
|
+
throw usageError('No keys given.', `Example: langctl keys ${published ? 'publish' : 'unpublish'} ${project.slug} home.title home.subtitle (or --all / --module <name>)`);
|
|
146
|
+
}
|
|
147
|
+
if (names.length === 0) {
|
|
148
|
+
if (runtime.json)
|
|
149
|
+
return printJson({ count: 0, notFound: [] });
|
|
150
|
+
return log.info(`Nothing to ${published ? 'publish' : 'unpublish'}.`);
|
|
151
|
+
}
|
|
152
|
+
const res = await session.api.post(`/orgs/${session.orgId}/projects/${project.id}/keys/bulk-publish`, { keys: names, published }, true);
|
|
153
|
+
if (runtime.json)
|
|
154
|
+
printJson(res);
|
|
155
|
+
else
|
|
156
|
+
log.success(`${published ? 'Published' : 'Unpublished'} ${res.count} key(s)`);
|
|
157
|
+
if (res.notFound?.length) {
|
|
158
|
+
throw new CliError(`Not found: ${res.notFound.join(', ')}`, ExitCode.NotFound);
|
|
394
159
|
}
|
|
395
160
|
}
|
|
396
|
-
//# sourceMappingURL=keys.js.map
|