@nocobase/cli 2.1.0-beta.32 → 2.1.0-beta.34
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/commands/app/down.js +10 -13
- package/dist/commands/app/logs.js +0 -1
- package/dist/commands/app/restart.js +63 -2
- package/dist/commands/app/start.js +41 -17
- package/dist/commands/app/stop.js +0 -1
- package/dist/commands/app/upgrade.js +9 -4
- package/dist/commands/env/add.js +3 -4
- package/dist/commands/env/auth.js +3 -2
- package/dist/commands/env/remove.js +38 -13
- package/dist/commands/env/update.js +9 -2
- package/dist/commands/examples/prompts-stages.js +4 -4
- package/dist/commands/examples/prompts-test.js +4 -4
- package/dist/commands/init.js +38 -31
- package/dist/commands/install.js +100 -63
- package/dist/commands/license/activate.js +66 -64
- package/dist/commands/license/id.js +0 -1
- package/dist/commands/license/plugins/clean.js +0 -1
- package/dist/commands/license/plugins/list.js +0 -1
- package/dist/commands/license/plugins/sync.js +0 -1
- package/dist/commands/license/shared.js +3 -3
- package/dist/commands/license/status.js +0 -1
- package/dist/commands/plugin/disable.js +0 -1
- package/dist/commands/plugin/enable.js +0 -1
- package/dist/commands/plugin/list.js +0 -1
- package/dist/commands/self/update.js +12 -3
- package/dist/commands/skills/install.js +12 -3
- package/dist/commands/skills/remove.js +12 -3
- package/dist/commands/skills/update.js +12 -3
- package/dist/commands/source/dev.js +0 -1
- package/dist/commands/source/download.js +29 -17
- package/dist/lib/app-managed-resources.js +8 -2
- package/dist/lib/bootstrap.js +11 -2
- package/dist/lib/db-connection-check.js +3 -23
- package/dist/lib/docker-env-file.js +52 -0
- package/dist/lib/env-auth.js +4 -3
- package/dist/lib/env-config.js +1 -0
- package/dist/lib/env-guard.js +8 -7
- package/dist/lib/generated-command.js +0 -1
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +244 -0
- package/dist/lib/object-utils.js +76 -0
- package/dist/lib/prompt-catalog-core.js +185 -0
- package/dist/lib/prompt-catalog-terminal.js +375 -0
- package/dist/lib/prompt-catalog.js +2 -573
- package/dist/lib/prompt-validators.js +56 -1
- package/dist/lib/resource-command.js +0 -1
- package/dist/lib/skills-manager.js +75 -11
- package/dist/lib/startup-update.js +12 -8
- package/dist/lib/ui.js +28 -51
- package/dist/locale/en-US.json +8 -3
- package/dist/locale/zh-CN.json +8 -3
- package/dist/post-processors/data-modeling.js +25 -7
- package/dist/post-processors/data-source-manager.js +24 -0
- package/nocobase-ctl.config.json +20 -1
- package/package.json +7 -5
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { createCliTranslate, resolveCliLocale, resolveLocalizedText, } from "./cli-locale.js";
|
|
10
|
+
export function selectOptionValues(options) {
|
|
11
|
+
return options.map((o) => (typeof o === 'string' ? o : o.value));
|
|
12
|
+
}
|
|
13
|
+
export function resolvePromptText(text, locale, fallback = '') {
|
|
14
|
+
return resolveLocalizedText(text, { locale, fallback });
|
|
15
|
+
}
|
|
16
|
+
export function enabledSelectOptionValues(options) {
|
|
17
|
+
return options
|
|
18
|
+
.filter((o) => typeof o === 'string' || o.disabled !== true)
|
|
19
|
+
.map((o) => (typeof o === 'string' ? o : o.value));
|
|
20
|
+
}
|
|
21
|
+
export function createPromptCatalogHooks(locale, overrides, defaults) {
|
|
22
|
+
return {
|
|
23
|
+
onCancel: overrides?.onCancel
|
|
24
|
+
?? defaults?.onCancel
|
|
25
|
+
?? (() => {
|
|
26
|
+
throw new Error(createCliTranslate(locale)('promptCatalog.common.cancelled'));
|
|
27
|
+
}),
|
|
28
|
+
onMissingNonInteractive: overrides?.onMissingNonInteractive
|
|
29
|
+
?? defaults?.onMissingNonInteractive
|
|
30
|
+
?? ((message) => {
|
|
31
|
+
throw new Error(message);
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function hasIvKey(iv, key) {
|
|
36
|
+
return (Object.prototype.hasOwnProperty.call(iv, key) && iv[key] !== undefined && iv[key] !== null);
|
|
37
|
+
}
|
|
38
|
+
export function resolveTextInitial(def, valuesSoFar) {
|
|
39
|
+
const iv = def.initialValue;
|
|
40
|
+
if (typeof iv === 'function') {
|
|
41
|
+
return iv(valuesSoFar);
|
|
42
|
+
}
|
|
43
|
+
return iv;
|
|
44
|
+
}
|
|
45
|
+
export function mergedText(key, def, iv, useYesInitial, valuesSoFar = {}) {
|
|
46
|
+
if (hasIvKey(iv, key)) {
|
|
47
|
+
return String(iv[key]);
|
|
48
|
+
}
|
|
49
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
50
|
+
return def.yesInitialValue;
|
|
51
|
+
}
|
|
52
|
+
return resolveTextInitial(def, valuesSoFar) ?? '';
|
|
53
|
+
}
|
|
54
|
+
export function mergedBoolean(key, def, iv, useYesInitial) {
|
|
55
|
+
if (hasIvKey(iv, key)) {
|
|
56
|
+
return Boolean(iv[key]);
|
|
57
|
+
}
|
|
58
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
59
|
+
return def.yesInitialValue;
|
|
60
|
+
}
|
|
61
|
+
return def.initialValue ?? true;
|
|
62
|
+
}
|
|
63
|
+
export function mergedSelect(key, def, iv, useYesInitial) {
|
|
64
|
+
const enabledValueList = enabledSelectOptionValues(def.options);
|
|
65
|
+
if (hasIvKey(iv, key)) {
|
|
66
|
+
const s = String(iv[key]);
|
|
67
|
+
if (enabledValueList.includes(s)) {
|
|
68
|
+
return s;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
if (useYesInitial && def.yesInitialValue !== undefined && enabledValueList.includes(def.yesInitialValue)) {
|
|
73
|
+
return def.yesInitialValue;
|
|
74
|
+
}
|
|
75
|
+
const d = def.initialValue;
|
|
76
|
+
if (d !== undefined && enabledValueList.includes(d)) {
|
|
77
|
+
return d;
|
|
78
|
+
}
|
|
79
|
+
return enabledValueList[0];
|
|
80
|
+
}
|
|
81
|
+
export function mergedInteger(key, def, iv, useYesInitial) {
|
|
82
|
+
if (hasIvKey(iv, key)) {
|
|
83
|
+
const v = iv[key];
|
|
84
|
+
return typeof v === 'number' ? v : Number.parseInt(String(v), 10);
|
|
85
|
+
}
|
|
86
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
87
|
+
return def.yesInitialValue;
|
|
88
|
+
}
|
|
89
|
+
return def.initialValue;
|
|
90
|
+
}
|
|
91
|
+
export function mergedPassword(key, def, iv, useYesInitial) {
|
|
92
|
+
if (hasIvKey(iv, key)) {
|
|
93
|
+
return String(iv[key] ?? '');
|
|
94
|
+
}
|
|
95
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
96
|
+
return def.yesInitialValue;
|
|
97
|
+
}
|
|
98
|
+
return def.initialValue;
|
|
99
|
+
}
|
|
100
|
+
export function isBlankText(value) {
|
|
101
|
+
return value.trim() === '';
|
|
102
|
+
}
|
|
103
|
+
export async function runPromptFieldValidate(def, value, values) {
|
|
104
|
+
if (def.type === 'intro' || def.type === 'outro' || def.type === 'run') {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
if (!('validate' in def) || def.validate === undefined) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const r = await def.validate(value, values);
|
|
111
|
+
if (r == null || r === '') {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
return String(r);
|
|
115
|
+
}
|
|
116
|
+
export function isPromptBlockSkipped(def, values) {
|
|
117
|
+
if (def.type === 'run') {
|
|
118
|
+
return def.when !== undefined && !def.when(values);
|
|
119
|
+
}
|
|
120
|
+
return def.hidden !== undefined && def.hidden(values);
|
|
121
|
+
}
|
|
122
|
+
export function tryApplyPreset(key, def, preset, out, hooks, locale) {
|
|
123
|
+
const t = createCliTranslate(locale);
|
|
124
|
+
if (!hasIvKey(preset, key)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const raw = preset[key];
|
|
128
|
+
switch (def.type) {
|
|
129
|
+
case 'intro':
|
|
130
|
+
case 'outro':
|
|
131
|
+
case 'run':
|
|
132
|
+
return false;
|
|
133
|
+
case 'text': {
|
|
134
|
+
const s = String(raw ?? '');
|
|
135
|
+
if (def.required && isBlankText(s)) {
|
|
136
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
|
|
137
|
+
}
|
|
138
|
+
out[key] = s;
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
case 'boolean': {
|
|
142
|
+
out[key] = Boolean(raw);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
case 'select': {
|
|
146
|
+
const valueList = selectOptionValues(def.options);
|
|
147
|
+
const s = String(raw ?? '');
|
|
148
|
+
if (!valueList.includes(s)) {
|
|
149
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.invalidSelect', { key, value: s, options: valueList.join(', ') }));
|
|
150
|
+
}
|
|
151
|
+
out[key] = s;
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
case 'password': {
|
|
155
|
+
const s = String(raw ?? '');
|
|
156
|
+
if (def.required && isBlankText(s)) {
|
|
157
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
|
|
158
|
+
}
|
|
159
|
+
out[key] = s;
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
case 'integer': {
|
|
163
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
|
164
|
+
out[key] = raw;
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
const s = String(raw ?? '').trim();
|
|
168
|
+
if (s === '') {
|
|
169
|
+
if (def.required) {
|
|
170
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
|
|
171
|
+
}
|
|
172
|
+
out[key] = def.initialValue ?? 0;
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (!/^-?\\d+$/.test(s)) {
|
|
176
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.invalidInteger', { key }));
|
|
177
|
+
}
|
|
178
|
+
out[key] = Number.parseInt(s, 10);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export function resolvePromptCatalogLocale(locale) {
|
|
184
|
+
return resolveCliLocale(locale);
|
|
185
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import pc from 'picocolors';
|
|
10
|
+
import { exit, stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
11
|
+
import { createCliTranslate } from "./cli-locale.js";
|
|
12
|
+
import { confirm, select, input, password } from "./inquirer.js";
|
|
13
|
+
import { createPromptCatalogHooks, hasIvKey, isBlankText, isPromptBlockSkipped, mergedBoolean, mergedInteger, mergedPassword, mergedSelect, mergedText, resolvePromptCatalogLocale, resolvePromptText, runPromptFieldValidate, selectOptionValues, tryApplyPreset, } from "./prompt-catalog-core.js";
|
|
14
|
+
function adaptInquirerValidate(validate) {
|
|
15
|
+
if (!validate) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
return async (value) => {
|
|
19
|
+
const result = await validate(value);
|
|
20
|
+
return result === undefined ? true : result;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function inquirerSelectOptions(options, locale) {
|
|
24
|
+
return options.map((o) => typeof o === 'string'
|
|
25
|
+
? { value: o, label: o }
|
|
26
|
+
: {
|
|
27
|
+
value: o.value,
|
|
28
|
+
label: resolvePromptText(o.label, locale, o.value),
|
|
29
|
+
...(o.hint !== undefined ? { hint: resolvePromptText(o.hint, locale) } : {}),
|
|
30
|
+
...(o.disabled !== undefined ? { disabled: o.disabled } : {}),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function createInquirerRenderer() {
|
|
34
|
+
return {
|
|
35
|
+
intro(message) {
|
|
36
|
+
console.log(message);
|
|
37
|
+
},
|
|
38
|
+
outro(message) {
|
|
39
|
+
console.log(message);
|
|
40
|
+
},
|
|
41
|
+
error(message) {
|
|
42
|
+
console.error(pc.red(`✖ ${message}`));
|
|
43
|
+
},
|
|
44
|
+
text(options) {
|
|
45
|
+
return input({
|
|
46
|
+
message: options.message,
|
|
47
|
+
default: options.initialValue,
|
|
48
|
+
placeholder: options.placeholder,
|
|
49
|
+
validate: adaptInquirerValidate(options.validate),
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
confirm(options) {
|
|
53
|
+
return confirm({
|
|
54
|
+
message: options.message,
|
|
55
|
+
default: options.initialValue,
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
select(options) {
|
|
59
|
+
return select({
|
|
60
|
+
message: options.message,
|
|
61
|
+
choices: options.options.map((option) => ({
|
|
62
|
+
value: option.value,
|
|
63
|
+
name: option.label,
|
|
64
|
+
...(option.hint !== undefined ? { description: option.hint } : {}),
|
|
65
|
+
...(option.disabled !== undefined ? { disabled: option.disabled } : {}),
|
|
66
|
+
})),
|
|
67
|
+
default: options.initialValue,
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
password(options) {
|
|
71
|
+
return password({
|
|
72
|
+
message: options.message,
|
|
73
|
+
mask: options.mask ?? '•',
|
|
74
|
+
validate: adaptInquirerValidate(options.validate),
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
isCancel(error) {
|
|
78
|
+
return error instanceof Error && error.name === 'ExitPromptError';
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function defaultOnCancel(locale) {
|
|
83
|
+
void locale;
|
|
84
|
+
exit(0);
|
|
85
|
+
}
|
|
86
|
+
function defaultOnMissingNonInteractive(message) {
|
|
87
|
+
console.error(message);
|
|
88
|
+
exit(1);
|
|
89
|
+
}
|
|
90
|
+
function createTerminalHooks(locale, overrides) {
|
|
91
|
+
return createPromptCatalogHooks(locale, overrides, {
|
|
92
|
+
onCancel: () => defaultOnCancel(locale),
|
|
93
|
+
onMissingNonInteractive: defaultOnMissingNonInteractive,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async function callPrompt(run, renderer, hooks) {
|
|
97
|
+
try {
|
|
98
|
+
return await run();
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (renderer.isCancel(error)) {
|
|
102
|
+
hooks.onCancel();
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export async function runPromptCatalog(catalog, options = {}) {
|
|
108
|
+
const locale = resolvePromptCatalogLocale(options.locale);
|
|
109
|
+
const t = createCliTranslate(locale);
|
|
110
|
+
const promptIv = options.initialValues ?? {};
|
|
111
|
+
const yesIv = options.yesInitialValues ?? {};
|
|
112
|
+
const resolveIv = options.yes ? { ...promptIv, ...yesIv } : promptIv;
|
|
113
|
+
const useYesInitial = Boolean(options.yes);
|
|
114
|
+
const hooks = createTerminalHooks(locale, options.hooks);
|
|
115
|
+
const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY && !options.yes);
|
|
116
|
+
const preset = options.values ?? {};
|
|
117
|
+
const out = {};
|
|
118
|
+
const renderer = createInquirerRenderer();
|
|
119
|
+
for (const [key, def] of Object.entries(catalog)) {
|
|
120
|
+
if (isPromptBlockSkipped(def, out)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (tryApplyPreset(key, def, preset, out, hooks, locale)) {
|
|
124
|
+
const errV = await runPromptFieldValidate(def, out[key], out);
|
|
125
|
+
if (errV) {
|
|
126
|
+
hooks.onMissingNonInteractive(errV);
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (def.type === 'intro') {
|
|
131
|
+
renderer.intro(resolvePromptText(def.title, locale));
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (def.type === 'outro') {
|
|
135
|
+
renderer.outro(resolvePromptText(def.message, locale));
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (def.type === 'run') {
|
|
139
|
+
await def.run(out, options.command);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (def.type === 'text') {
|
|
143
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
144
|
+
const placeholder = def.placeholder !== undefined
|
|
145
|
+
? resolvePromptText(def.placeholder, locale)
|
|
146
|
+
: undefined;
|
|
147
|
+
if (!interactive) {
|
|
148
|
+
const merged = mergedText(key, def, resolveIv, useYesInitial, out);
|
|
149
|
+
if (def.required && isBlankText(merged)) {
|
|
150
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.textRequired', { key }));
|
|
151
|
+
}
|
|
152
|
+
out[key] = merged;
|
|
153
|
+
const errT = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
154
|
+
if (errT) {
|
|
155
|
+
hooks.onMissingNonInteractive(errT);
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const merged = mergedText(key, def, promptIv, false, out);
|
|
160
|
+
const raw = await callPrompt(() => renderer.text({
|
|
161
|
+
message,
|
|
162
|
+
initialValue: merged,
|
|
163
|
+
...(placeholder !== undefined ? { placeholder } : {}),
|
|
164
|
+
validate: (value) => {
|
|
165
|
+
if (def.required && isBlankText(value)) {
|
|
166
|
+
return t('promptCatalog.common.required');
|
|
167
|
+
}
|
|
168
|
+
if (!def.validate) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
const currentValue = typeof value === 'string' ? value : String(value ?? '');
|
|
172
|
+
const result = runPromptFieldValidate(def, currentValue, { ...out, [key]: currentValue });
|
|
173
|
+
return result;
|
|
174
|
+
},
|
|
175
|
+
}), renderer, hooks);
|
|
176
|
+
out[key] = typeof raw === 'string' ? raw : merged;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (def.type === 'boolean') {
|
|
180
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
181
|
+
if (!interactive) {
|
|
182
|
+
const b = mergedBoolean(key, def, resolveIv, useYesInitial);
|
|
183
|
+
out[key] = b;
|
|
184
|
+
const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
|
|
185
|
+
if (errB) {
|
|
186
|
+
hooks.onMissingNonInteractive(errB);
|
|
187
|
+
}
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const merged = mergedBoolean(key, def, promptIv, false);
|
|
191
|
+
if (def.validate) {
|
|
192
|
+
for (;;) {
|
|
193
|
+
const raw = await callPrompt(() => renderer.confirm({ message, initialValue: merged }), renderer, hooks);
|
|
194
|
+
const b = Boolean(raw);
|
|
195
|
+
const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
|
|
196
|
+
if (errB) {
|
|
197
|
+
renderer.error(errB);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
out[key] = b;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const raw = await callPrompt(() => renderer.confirm({ message, initialValue: merged }), renderer, hooks);
|
|
206
|
+
out[key] = Boolean(raw);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (def.type === 'select') {
|
|
210
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
211
|
+
const valueList = selectOptionValues(def.options);
|
|
212
|
+
if (def.required && def.options.length === 0) {
|
|
213
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.selectRequiredNoOptions', { key }));
|
|
214
|
+
}
|
|
215
|
+
if (!interactive) {
|
|
216
|
+
const merged = mergedSelect(key, def, resolveIv, useYesInitial);
|
|
217
|
+
if (merged === undefined || !valueList.includes(merged)) {
|
|
218
|
+
const bad = hasIvKey(resolveIv, key) && !valueList.includes(String(resolveIv[key]))
|
|
219
|
+
? String(resolveIv[key])
|
|
220
|
+
: hasIvKey(promptIv, key) && !valueList.includes(String(promptIv[key]))
|
|
221
|
+
? String(promptIv[key])
|
|
222
|
+
: undefined;
|
|
223
|
+
hooks.onMissingNonInteractive(bad !== undefined
|
|
224
|
+
? t('promptCatalog.nonInteractive.selectInvalidValue', { key, value: bad, options: valueList.join(', ') })
|
|
225
|
+
: t('promptCatalog.nonInteractive.selectMissingDefault', { key }));
|
|
226
|
+
}
|
|
227
|
+
out[key] = merged;
|
|
228
|
+
const errS = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
229
|
+
if (errS) {
|
|
230
|
+
hooks.onMissingNonInteractive(errS);
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const merged = mergedSelect(key, def, promptIv, false);
|
|
235
|
+
const uiInitial = merged ??
|
|
236
|
+
(def.initialValue && valueList.includes(def.initialValue) ? def.initialValue : undefined) ??
|
|
237
|
+
valueList[0];
|
|
238
|
+
if (uiInitial === undefined || !valueList.includes(uiInitial)) {
|
|
239
|
+
const hint = def.required
|
|
240
|
+
? t('promptCatalog.nonInteractive.selectRequiredInteractive', { key })
|
|
241
|
+
: t('promptCatalog.nonInteractive.selectMissingInteractiveDefault', { key });
|
|
242
|
+
hooks.onMissingNonInteractive(hint);
|
|
243
|
+
}
|
|
244
|
+
if (def.validate) {
|
|
245
|
+
for (;;) {
|
|
246
|
+
const raw = await callPrompt(() => renderer.select({
|
|
247
|
+
message,
|
|
248
|
+
options: inquirerSelectOptions(def.options, locale),
|
|
249
|
+
initialValue: uiInitial,
|
|
250
|
+
}), renderer, hooks);
|
|
251
|
+
const picked = raw;
|
|
252
|
+
const errS = await runPromptFieldValidate(def, picked, { ...out, [key]: picked });
|
|
253
|
+
if (errS) {
|
|
254
|
+
renderer.error(errS);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
out[key] = picked;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const raw = await callPrompt(() => renderer.select({
|
|
263
|
+
message,
|
|
264
|
+
options: inquirerSelectOptions(def.options, locale),
|
|
265
|
+
initialValue: uiInitial,
|
|
266
|
+
}), renderer, hooks);
|
|
267
|
+
out[key] = raw;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (def.type === 'password') {
|
|
271
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
272
|
+
if (!interactive) {
|
|
273
|
+
const merged = mergedPassword(key, def, resolveIv, useYesInitial);
|
|
274
|
+
if (merged === undefined) {
|
|
275
|
+
if (def.required) {
|
|
276
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequired', { key }));
|
|
277
|
+
}
|
|
278
|
+
out[key] = '';
|
|
279
|
+
const errPE = await runPromptFieldValidate(def, '', { ...out, [key]: '' });
|
|
280
|
+
if (errPE) {
|
|
281
|
+
hooks.onMissingNonInteractive(errPE);
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (def.required && isBlankText(merged)) {
|
|
286
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequiredNonEmpty', { key }));
|
|
287
|
+
}
|
|
288
|
+
out[key] = merged;
|
|
289
|
+
const errP = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
290
|
+
if (errP) {
|
|
291
|
+
hooks.onMissingNonInteractive(errP);
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const raw = await callPrompt(() => renderer.password({
|
|
296
|
+
message,
|
|
297
|
+
mask: def.mask,
|
|
298
|
+
validate: (value) => {
|
|
299
|
+
if (def.required && isBlankText(value)) {
|
|
300
|
+
return t('promptCatalog.common.required');
|
|
301
|
+
}
|
|
302
|
+
if (!def.validate) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
const currentValue = typeof value === 'string' ? value : String(value ?? '');
|
|
306
|
+
const result = runPromptFieldValidate(def, currentValue, { ...out, [key]: currentValue });
|
|
307
|
+
return result;
|
|
308
|
+
},
|
|
309
|
+
}), renderer, hooks);
|
|
310
|
+
out[key] = typeof raw === 'string' ? raw : '';
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (def.type === 'integer') {
|
|
314
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
315
|
+
const placeholder = def.placeholder !== undefined
|
|
316
|
+
? resolvePromptText(def.placeholder, locale)
|
|
317
|
+
: undefined;
|
|
318
|
+
if (!interactive) {
|
|
319
|
+
const merged = mergedInteger(key, def, resolveIv, useYesInitial);
|
|
320
|
+
if (merged === undefined) {
|
|
321
|
+
if (def.required) {
|
|
322
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.integerRequired', { key }));
|
|
323
|
+
}
|
|
324
|
+
const z = def.initialValue ?? 0;
|
|
325
|
+
out[key] = z;
|
|
326
|
+
const errI = await runPromptFieldValidate(def, z, { ...out, [key]: z });
|
|
327
|
+
if (errI) {
|
|
328
|
+
hooks.onMissingNonInteractive(errI);
|
|
329
|
+
}
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
out[key] = merged;
|
|
333
|
+
const errI2 = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
334
|
+
if (errI2) {
|
|
335
|
+
hooks.onMissingNonInteractive(errI2);
|
|
336
|
+
}
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const merged = mergedInteger(key, def, promptIv, false);
|
|
340
|
+
const lineDefault = merged !== undefined ? String(merged) : String(def.initialValue ?? 0);
|
|
341
|
+
const raw = await callPrompt(() => renderer.text({
|
|
342
|
+
message,
|
|
343
|
+
initialValue: lineDefault,
|
|
344
|
+
...(placeholder !== undefined ? { placeholder } : {}),
|
|
345
|
+
validate: async (value) => {
|
|
346
|
+
const trimmed = value.trim();
|
|
347
|
+
if (trimmed === '') {
|
|
348
|
+
if (def.required) {
|
|
349
|
+
return t('promptCatalog.common.required');
|
|
350
|
+
}
|
|
351
|
+
if (def.validate) {
|
|
352
|
+
const z = def.initialValue ?? 0;
|
|
353
|
+
return runPromptFieldValidate(def, z, { ...out, [key]: z });
|
|
354
|
+
}
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
if (!/^-?\\d+$/.test(trimmed)) {
|
|
358
|
+
return t('promptCatalog.common.mustBeInteger');
|
|
359
|
+
}
|
|
360
|
+
if (!def.validate) {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
const n = Number.parseInt(trimmed, 10);
|
|
364
|
+
return runPromptFieldValidate(def, n, { ...out, [key]: n });
|
|
365
|
+
},
|
|
366
|
+
}), renderer, hooks);
|
|
367
|
+
if (typeof raw === 'string' && raw.trim() === '' && !def.required) {
|
|
368
|
+
out[key] = def.initialValue ?? 0;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
out[key] = Number.parseInt(String(raw).trim(), 10);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return out;
|
|
375
|
+
}
|