@nocobase/cli 2.1.0-beta.2 → 2.1.0-beta.21
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/LICENSE.txt +107 -0
- package/README.md +367 -19
- package/README.zh-CN.md +336 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +131 -0
- package/dist/commands/api/resource/create.js +15 -0
- package/dist/commands/api/resource/destroy.js +15 -0
- package/dist/commands/api/resource/get.js +15 -0
- package/dist/commands/api/resource/index.js +20 -0
- package/dist/commands/api/resource/list.js +16 -0
- package/dist/commands/api/resource/query.js +15 -0
- package/dist/commands/api/resource/update.js +15 -0
- package/dist/commands/build.js +57 -0
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +60 -0
- package/dist/commands/db/shared.js +95 -0
- package/dist/commands/db/start.js +70 -0
- package/dist/commands/db/stop.js +70 -0
- package/dist/commands/dev.js +156 -0
- package/dist/commands/down.js +197 -0
- package/dist/commands/download.js +865 -0
- package/dist/commands/env/add.js +307 -0
- package/dist/commands/env/auth.js +55 -0
- package/dist/commands/env/list.js +36 -0
- package/dist/commands/env/remove.js +59 -0
- package/dist/commands/env/update.js +67 -0
- package/dist/commands/env/use.js +28 -0
- package/dist/commands/init.js +950 -0
- package/dist/commands/install.js +1927 -0
- package/dist/commands/logs.js +97 -0
- package/dist/commands/pm/disable.js +63 -0
- package/dist/commands/pm/enable.js +63 -0
- package/dist/commands/pm/list.js +61 -0
- package/dist/commands/prompts-stages.js +150 -0
- package/dist/commands/prompts-test.js +181 -0
- package/dist/commands/ps.js +119 -0
- package/dist/commands/restart.js +74 -0
- package/dist/commands/scaffold/migration.js +38 -0
- package/dist/commands/scaffold/plugin.js +37 -0
- package/dist/commands/self/check.js +71 -0
- package/dist/commands/self/index.js +20 -0
- package/dist/commands/self/update.js +86 -0
- package/dist/commands/skills/check.js +69 -0
- package/dist/commands/skills/index.js +20 -0
- package/dist/commands/skills/install.js +71 -0
- package/dist/commands/skills/update.js +71 -0
- package/dist/commands/start.js +218 -0
- package/dist/commands/stop.js +97 -0
- package/dist/commands/test.js +466 -0
- package/dist/commands/upgrade.js +594 -0
- package/dist/generated/command-registry.js +133 -0
- package/dist/help/runtime-help.js +20 -0
- package/dist/lib/api-client.js +244 -0
- package/dist/lib/app-runtime.js +153 -0
- package/dist/lib/auth-store.js +357 -0
- package/dist/lib/bootstrap.js +388 -0
- package/dist/lib/build-config.js +10 -0
- package/dist/lib/cli-home.js +61 -0
- package/dist/lib/cli-locale.js +115 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/env-auth.js +872 -0
- package/dist/lib/generated-command.js +150 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/naming.js +70 -0
- package/dist/lib/openapi.js +62 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog.js +581 -0
- package/dist/lib/prompt-validators.js +185 -0
- package/dist/lib/prompt-web-ui.js +2096 -0
- package/dist/lib/resource-command.js +343 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +197 -0
- package/dist/lib/runtime-generator.js +419 -0
- package/dist/lib/runtime-store.js +56 -0
- package/dist/lib/self-manager.js +246 -0
- package/dist/lib/skills-manager.js +269 -0
- package/dist/lib/startup-update.js +203 -0
- package/dist/lib/ui.js +175 -0
- package/dist/locale/en-US.json +336 -0
- package/dist/locale/zh-CN.json +336 -0
- package/dist/post-processors/data-modeling.js +66 -0
- package/dist/post-processors/data-source-manager.js +114 -0
- package/dist/post-processors/index.js +19 -0
- package/nocobase-ctl.config.json +287 -0
- package/package.json +60 -26
- package/LICENSE +0 -661
- package/bin/index.js +0 -39
- package/nocobase.conf.tpl +0 -95
- package/src/cli.js +0 -19
- package/src/commands/benchmark.js +0 -73
- package/src/commands/build.js +0 -49
- package/src/commands/clean.js +0 -30
- package/src/commands/client.js +0 -166
- package/src/commands/create-nginx-conf.js +0 -37
- package/src/commands/create-plugin.js +0 -33
- package/src/commands/dev.js +0 -200
- package/src/commands/doc.js +0 -76
- package/src/commands/e2e.js +0 -265
- package/src/commands/global.js +0 -43
- package/src/commands/index.js +0 -45
- package/src/commands/instance-id.js +0 -47
- package/src/commands/locale/cronstrue.js +0 -122
- package/src/commands/locale/react-js-cron/en-US.json +0 -75
- package/src/commands/locale/react-js-cron/index.js +0 -17
- package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
- package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
- package/src/commands/locale.js +0 -81
- package/src/commands/p-test.js +0 -88
- package/src/commands/perf.js +0 -63
- package/src/commands/pkg.js +0 -321
- package/src/commands/pm2.js +0 -37
- package/src/commands/postinstall.js +0 -88
- package/src/commands/start.js +0 -148
- package/src/commands/tar.js +0 -36
- package/src/commands/test-coverage.js +0 -55
- package/src/commands/test.js +0 -107
- package/src/commands/umi.js +0 -33
- package/src/commands/update-deps.js +0 -72
- package/src/commands/upgrade.js +0 -47
- package/src/commands/view-license-key.js +0 -44
- package/src/index.js +0 -14
- package/src/license.js +0 -76
- package/src/logger.js +0 -75
- package/src/plugin-generator.js +0 -80
- package/src/util.js +0 -517
- package/templates/bundle-status.html +0 -338
- package/templates/create-app-package.json +0 -39
- package/templates/plugin/.npmignore.tpl +0 -2
- package/templates/plugin/README.md.tpl +0 -1
- package/templates/plugin/client.d.ts +0 -2
- package/templates/plugin/client.js +0 -1
- package/templates/plugin/package.json.tpl +0 -11
- package/templates/plugin/server.d.ts +0 -2
- package/templates/plugin/server.js +0 -1
- package/templates/plugin/src/client/client.d.ts +0 -249
- package/templates/plugin/src/client/index.tsx.tpl +0 -1
- package/templates/plugin/src/client/locale.ts +0 -21
- package/templates/plugin/src/client/models/index.ts +0 -12
- package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
- package/templates/plugin/src/index.ts +0 -2
- package/templates/plugin/src/locale/en-US.json +0 -1
- package/templates/plugin/src/locale/zh-CN.json +0 -1
- package/templates/plugin/src/server/collections/.gitkeep +0 -0
- package/templates/plugin/src/server/index.ts.tpl +0 -1
- package/templates/plugin/src/server/plugin.ts.tpl +0 -19
|
@@ -0,0 +1,581 @@
|
|
|
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 * as p from '@clack/prompts';
|
|
10
|
+
import { exit, stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
11
|
+
import { createCliTranslate, resolveCliLocale, resolveLocalizedText, } from "./cli-locale.js";
|
|
12
|
+
export function selectOptionValues(options) {
|
|
13
|
+
return options.map((o) => (typeof o === 'string' ? o : o.value));
|
|
14
|
+
}
|
|
15
|
+
function resolvePromptText(text, locale, fallback = '') {
|
|
16
|
+
return resolveLocalizedText(text, { locale, fallback });
|
|
17
|
+
}
|
|
18
|
+
function clackSelectOptions(options, locale) {
|
|
19
|
+
return options.map((o) => typeof o === 'string'
|
|
20
|
+
? { value: o, label: o }
|
|
21
|
+
: {
|
|
22
|
+
value: o.value,
|
|
23
|
+
label: resolvePromptText(o.label, locale, o.value),
|
|
24
|
+
...(o.hint !== undefined ? { hint: resolvePromptText(o.hint, locale) } : {}),
|
|
25
|
+
...(o.disabled !== undefined ? { disabled: o.disabled } : {}),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function enabledSelectOptionValues(options) {
|
|
29
|
+
return options
|
|
30
|
+
.filter((o) => typeof o === 'string' || o.disabled !== true)
|
|
31
|
+
.map((o) => (typeof o === 'string' ? o : o.value));
|
|
32
|
+
}
|
|
33
|
+
function defaultOnCancel(locale) {
|
|
34
|
+
p.cancel(createCliTranslate(locale)('promptCatalog.common.cancelled'));
|
|
35
|
+
exit(0);
|
|
36
|
+
}
|
|
37
|
+
function defaultOnMissingNonInteractive(message) {
|
|
38
|
+
console.error(message);
|
|
39
|
+
exit(1);
|
|
40
|
+
}
|
|
41
|
+
function hasIvKey(iv, key) {
|
|
42
|
+
return (Object.prototype.hasOwnProperty.call(iv, key) && iv[key] !== undefined && iv[key] !== null);
|
|
43
|
+
}
|
|
44
|
+
function resolveTextInitial(def, valuesSoFar) {
|
|
45
|
+
const iv = def.initialValue;
|
|
46
|
+
if (typeof iv === 'function') {
|
|
47
|
+
return iv(valuesSoFar);
|
|
48
|
+
}
|
|
49
|
+
return iv;
|
|
50
|
+
}
|
|
51
|
+
/** `useYesInitial`: when true (resolve under `yes`), use block `yesInitialValue` before catalog `initialValue`. */
|
|
52
|
+
function mergedText(key, def, iv, useYesInitial, valuesSoFar = {}) {
|
|
53
|
+
if (hasIvKey(iv, key)) {
|
|
54
|
+
return String(iv[key]);
|
|
55
|
+
}
|
|
56
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
57
|
+
return def.yesInitialValue;
|
|
58
|
+
}
|
|
59
|
+
return resolveTextInitial(def, valuesSoFar) ?? '';
|
|
60
|
+
}
|
|
61
|
+
function mergedBoolean(key, def, iv, useYesInitial) {
|
|
62
|
+
if (hasIvKey(iv, key)) {
|
|
63
|
+
return Boolean(iv[key]);
|
|
64
|
+
}
|
|
65
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
66
|
+
return def.yesInitialValue;
|
|
67
|
+
}
|
|
68
|
+
return def.initialValue ?? true;
|
|
69
|
+
}
|
|
70
|
+
function mergedSelect(key, def, iv, useYesInitial) {
|
|
71
|
+
const valueList = selectOptionValues(def.options);
|
|
72
|
+
const enabledValueList = enabledSelectOptionValues(def.options);
|
|
73
|
+
if (hasIvKey(iv, key)) {
|
|
74
|
+
const s = String(iv[key]);
|
|
75
|
+
if (enabledValueList.includes(s)) {
|
|
76
|
+
return s;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
if (useYesInitial && def.yesInitialValue !== undefined && enabledValueList.includes(def.yesInitialValue)) {
|
|
81
|
+
return def.yesInitialValue;
|
|
82
|
+
}
|
|
83
|
+
const d = def.initialValue;
|
|
84
|
+
if (d !== undefined && enabledValueList.includes(d)) {
|
|
85
|
+
return d;
|
|
86
|
+
}
|
|
87
|
+
return enabledValueList[0];
|
|
88
|
+
}
|
|
89
|
+
function mergedInteger(key, def, iv, useYesInitial) {
|
|
90
|
+
if (hasIvKey(iv, key)) {
|
|
91
|
+
const v = iv[key];
|
|
92
|
+
return typeof v === 'number' ? v : Number.parseInt(String(v), 10);
|
|
93
|
+
}
|
|
94
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
95
|
+
return def.yesInitialValue;
|
|
96
|
+
}
|
|
97
|
+
return def.initialValue;
|
|
98
|
+
}
|
|
99
|
+
function mergedPassword(key, def, iv, useYesInitial) {
|
|
100
|
+
if (hasIvKey(iv, key)) {
|
|
101
|
+
return String(iv[key] ?? '');
|
|
102
|
+
}
|
|
103
|
+
if (useYesInitial && def.yesInitialValue !== undefined) {
|
|
104
|
+
return def.yesInitialValue;
|
|
105
|
+
}
|
|
106
|
+
return def.initialValue;
|
|
107
|
+
}
|
|
108
|
+
function isBlankText(value) {
|
|
109
|
+
return value.trim() === '';
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Runs a block’s optional **`validate`**, if present. Used by {@link runPromptCatalog} and the
|
|
113
|
+
* local web form submit path.
|
|
114
|
+
*/
|
|
115
|
+
export async function runPromptFieldValidate(def, value, values) {
|
|
116
|
+
if (def.type === 'intro' || def.type === 'outro' || def.type === 'run') {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
if (!('validate' in def) || def.validate === undefined) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
const r = await def.validate(value, values);
|
|
123
|
+
if (r == null || r === '') {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return String(r);
|
|
127
|
+
}
|
|
128
|
+
/** When true, the block is skipped: no UI / no `out` (except `values` preset). Used by the web-UI builder and `runPromptCatalog`. */
|
|
129
|
+
export function isPromptBlockSkipped(def, values) {
|
|
130
|
+
if (def.type === 'run') {
|
|
131
|
+
return def.when !== undefined && !def.when(values);
|
|
132
|
+
}
|
|
133
|
+
return def.hidden !== undefined && def.hidden(values);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* If **`preset`** defines **`key`**, validate and set **`out[key]`**, return **`true`** (caller should `continue`).
|
|
137
|
+
* No-op for non-input block types.
|
|
138
|
+
*/
|
|
139
|
+
function tryApplyPreset(key, def, preset, out, hooks, locale) {
|
|
140
|
+
const t = createCliTranslate(locale);
|
|
141
|
+
if (!hasIvKey(preset, key)) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const raw = preset[key];
|
|
145
|
+
switch (def.type) {
|
|
146
|
+
case 'intro':
|
|
147
|
+
case 'outro':
|
|
148
|
+
case 'run':
|
|
149
|
+
return false;
|
|
150
|
+
case 'text': {
|
|
151
|
+
const s = String(raw ?? '');
|
|
152
|
+
if (def.required && isBlankText(s)) {
|
|
153
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
|
|
154
|
+
}
|
|
155
|
+
out[key] = s;
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
case 'boolean': {
|
|
159
|
+
out[key] = Boolean(raw);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
case 'select': {
|
|
163
|
+
const valueList = selectOptionValues(def.options);
|
|
164
|
+
const s = String(raw ?? '');
|
|
165
|
+
if (!valueList.includes(s)) {
|
|
166
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.invalidSelect', { key, value: s, options: valueList.join(', ') }));
|
|
167
|
+
}
|
|
168
|
+
out[key] = s;
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
case 'password': {
|
|
172
|
+
const s = String(raw ?? '');
|
|
173
|
+
if (def.required && isBlankText(s)) {
|
|
174
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
|
|
175
|
+
}
|
|
176
|
+
out[key] = s;
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
case 'integer': {
|
|
180
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
|
181
|
+
out[key] = raw;
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
const s = String(raw ?? '').trim();
|
|
185
|
+
if (s === '') {
|
|
186
|
+
if (def.required) {
|
|
187
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.required', { key }));
|
|
188
|
+
}
|
|
189
|
+
out[key] = def.initialValue ?? 0;
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
if (!/^-?\d+$/.test(s)) {
|
|
193
|
+
hooks.onMissingNonInteractive(t('promptCatalog.preset.invalidInteger', { key }));
|
|
194
|
+
}
|
|
195
|
+
out[key] = Number.parseInt(s, 10);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Run a declarative catalog with @clack/prompts. No oclif / argv dependency.
|
|
202
|
+
*
|
|
203
|
+
* **Interactive** when stdin and stdout are both TTYs **and** `yes` is not set; otherwise values are taken from defaults only (no prompts). Use **`yes: true`** for a TTY session that should still accept defaults without prompting (same as CI / piped stdin).
|
|
204
|
+
*
|
|
205
|
+
* **`intro` / `outro`** steps call Clack `intro` / `outro` and do not write to the result object.
|
|
206
|
+
*
|
|
207
|
+
* Defaults are **`initialValues[key] ?? catalog block initialValue`** (per type).
|
|
208
|
+
* With **`yes: true`**, resolve uses **`{ ...initialValues, ...yesInitialValues }`** then optional per-block **`yesInitialValue`**, then **`initialValue`**.
|
|
209
|
+
* **`values`:** preset result keys — matching steps are skipped (no UI); **`initialValues`** still prefill when a step is shown.
|
|
210
|
+
* **Interactive:** prompts when the key is not set in **`values`**; prefill from **`initialValues`** + catalog **`initialValue`** (not `yes*` fields).
|
|
211
|
+
* **Non-interactive or `yes`:** uses **`values`** first, else merged defaults (no prompts).
|
|
212
|
+
* **`text`** and **`integer`** blocks may set **`placeholder`** (Clack hint when the line is empty; interactive only).
|
|
213
|
+
* Any block may set **`hidden: (values) => boolean`** using **`PromptCatalogValues`** so far; when true, the step is skipped (no **`out`** key, no **`intro`/`outro`** line), unless **`values`** preset sets this key first (no UI; **`out[key]`** still written).
|
|
214
|
+
* **`type: 'run'`** runs **`run(values, command?)`** (sync or async) with no UI and no **`out`** entry; optional **`when`** runs only when it returns true. Pass **`command`** (e.g. oclif `this`) in options when handlers need it.
|
|
215
|
+
* Blocks may set **`required`** (text / password / integer / select): empty or missing values then fail validation or `onMissingNonInteractive`.
|
|
216
|
+
* Input blocks may set **`validate(value, values)`** (sync or async): return a string to fail; used after required/type checks, and by the local web form on submit. When **`validate`** is set, interactive TTY steps re-ask on failure (`log.error` + retry) except for simple fields without a custom `validate` (fast path).
|
|
217
|
+
*/
|
|
218
|
+
export async function runPromptCatalog(catalog, options = {}) {
|
|
219
|
+
const locale = resolveCliLocale(options.locale);
|
|
220
|
+
const t = createCliTranslate(locale);
|
|
221
|
+
const promptIv = options.initialValues ?? {};
|
|
222
|
+
const yesIv = options.yesInitialValues ?? {};
|
|
223
|
+
const resolveIv = options.yes ? { ...promptIv, ...yesIv } : promptIv;
|
|
224
|
+
const useYesInitial = Boolean(options.yes);
|
|
225
|
+
const hooks = {
|
|
226
|
+
onCancel: options.hooks?.onCancel ?? (() => defaultOnCancel(locale)),
|
|
227
|
+
onMissingNonInteractive: options.hooks?.onMissingNonInteractive ?? defaultOnMissingNonInteractive,
|
|
228
|
+
};
|
|
229
|
+
const interactive = Boolean(stdinStream.isTTY && stdoutStream.isTTY && !options.yes);
|
|
230
|
+
const preset = options.values ?? {};
|
|
231
|
+
const out = {};
|
|
232
|
+
for (const [key, def] of Object.entries(catalog)) {
|
|
233
|
+
// Apply `values` presets before `hidden` / `when` so CLI/env fixes still win without UI.
|
|
234
|
+
if (tryApplyPreset(key, def, preset, out, hooks, locale)) {
|
|
235
|
+
const errV = await runPromptFieldValidate(def, out[key], out);
|
|
236
|
+
if (errV) {
|
|
237
|
+
hooks.onMissingNonInteractive(errV);
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (isPromptBlockSkipped(def, out)) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (def.type === 'intro') {
|
|
245
|
+
p.intro(resolvePromptText(def.title, locale));
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (def.type === 'outro') {
|
|
249
|
+
p.outro(resolvePromptText(def.message, locale));
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (def.type === 'run') {
|
|
253
|
+
await def.run(out, options.command);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (def.type === 'text') {
|
|
257
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
258
|
+
const placeholder = def.placeholder !== undefined
|
|
259
|
+
? resolvePromptText(def.placeholder, locale)
|
|
260
|
+
: undefined;
|
|
261
|
+
if (!interactive) {
|
|
262
|
+
const merged = mergedText(key, def, resolveIv, useYesInitial, out);
|
|
263
|
+
if (def.required && isBlankText(merged)) {
|
|
264
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.textRequired', { key }));
|
|
265
|
+
}
|
|
266
|
+
out[key] = merged;
|
|
267
|
+
const errT = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
268
|
+
if (errT) {
|
|
269
|
+
hooks.onMissingNonInteractive(errT);
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const merged = mergedText(key, def, promptIv, false, out);
|
|
274
|
+
if (def.validate) {
|
|
275
|
+
let last = merged;
|
|
276
|
+
for (;;) {
|
|
277
|
+
const raw = await p.text({
|
|
278
|
+
message,
|
|
279
|
+
initialValue: last,
|
|
280
|
+
...(placeholder !== undefined ? { placeholder } : {}),
|
|
281
|
+
});
|
|
282
|
+
if (p.isCancel(raw)) {
|
|
283
|
+
hooks.onCancel();
|
|
284
|
+
}
|
|
285
|
+
const s = typeof raw === 'string' ? raw : last;
|
|
286
|
+
if (def.required && isBlankText(s)) {
|
|
287
|
+
p.log.error(t('promptCatalog.common.required'));
|
|
288
|
+
last = s;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const errT = await runPromptFieldValidate(def, s, { ...out, [key]: s });
|
|
292
|
+
if (errT) {
|
|
293
|
+
p.log.error(errT);
|
|
294
|
+
last = s;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
out[key] = s;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const raw = await p.text({
|
|
303
|
+
message,
|
|
304
|
+
initialValue: merged,
|
|
305
|
+
...(placeholder !== undefined ? { placeholder } : {}),
|
|
306
|
+
validate: def.required ? (value) => (isBlankText(value) ? t('promptCatalog.common.required') : undefined) : undefined,
|
|
307
|
+
});
|
|
308
|
+
if (p.isCancel(raw)) {
|
|
309
|
+
hooks.onCancel();
|
|
310
|
+
}
|
|
311
|
+
out[key] = typeof raw === 'string' ? raw : merged;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (def.type === 'boolean') {
|
|
315
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
316
|
+
if (!interactive) {
|
|
317
|
+
const b = mergedBoolean(key, def, resolveIv, useYesInitial);
|
|
318
|
+
out[key] = b;
|
|
319
|
+
const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
|
|
320
|
+
if (errB) {
|
|
321
|
+
hooks.onMissingNonInteractive(errB);
|
|
322
|
+
}
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const merged = mergedBoolean(key, def, promptIv, false);
|
|
326
|
+
if (def.validate) {
|
|
327
|
+
for (;;) {
|
|
328
|
+
const raw = await p.confirm({
|
|
329
|
+
message,
|
|
330
|
+
initialValue: merged,
|
|
331
|
+
});
|
|
332
|
+
if (p.isCancel(raw)) {
|
|
333
|
+
hooks.onCancel();
|
|
334
|
+
}
|
|
335
|
+
const b = Boolean(raw);
|
|
336
|
+
const errB = await runPromptFieldValidate(def, b, { ...out, [key]: b });
|
|
337
|
+
if (errB) {
|
|
338
|
+
p.log.error(errB);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
out[key] = b;
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const raw = await p.confirm({
|
|
347
|
+
message,
|
|
348
|
+
initialValue: merged,
|
|
349
|
+
});
|
|
350
|
+
if (p.isCancel(raw)) {
|
|
351
|
+
hooks.onCancel();
|
|
352
|
+
}
|
|
353
|
+
out[key] = Boolean(raw);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (def.type === 'select') {
|
|
357
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
358
|
+
const valueList = selectOptionValues(def.options);
|
|
359
|
+
if (def.required && def.options.length === 0) {
|
|
360
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.selectRequiredNoOptions', { key }));
|
|
361
|
+
}
|
|
362
|
+
if (!interactive) {
|
|
363
|
+
const merged = mergedSelect(key, def, resolveIv, useYesInitial);
|
|
364
|
+
if (merged === undefined || !valueList.includes(merged)) {
|
|
365
|
+
const bad = hasIvKey(resolveIv, key) && !valueList.includes(String(resolveIv[key]))
|
|
366
|
+
? String(resolveIv[key])
|
|
367
|
+
: hasIvKey(promptIv, key) && !valueList.includes(String(promptIv[key]))
|
|
368
|
+
? String(promptIv[key])
|
|
369
|
+
: undefined;
|
|
370
|
+
hooks.onMissingNonInteractive(bad !== undefined
|
|
371
|
+
? t('promptCatalog.nonInteractive.selectInvalidValue', { key, value: bad, options: valueList.join(', ') })
|
|
372
|
+
: t('promptCatalog.nonInteractive.selectMissingDefault', { key }));
|
|
373
|
+
}
|
|
374
|
+
out[key] = merged;
|
|
375
|
+
const errS = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
376
|
+
if (errS) {
|
|
377
|
+
hooks.onMissingNonInteractive(errS);
|
|
378
|
+
}
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const merged = mergedSelect(key, def, promptIv, false);
|
|
382
|
+
const uiInitial = merged ??
|
|
383
|
+
(def.initialValue && valueList.includes(def.initialValue) ? def.initialValue : undefined) ??
|
|
384
|
+
valueList[0];
|
|
385
|
+
if (uiInitial === undefined || !valueList.includes(uiInitial)) {
|
|
386
|
+
const hint = def.required
|
|
387
|
+
? t('promptCatalog.nonInteractive.selectRequiredInteractive', { key })
|
|
388
|
+
: t('promptCatalog.nonInteractive.selectMissingInteractiveDefault', { key });
|
|
389
|
+
hooks.onMissingNonInteractive(hint);
|
|
390
|
+
}
|
|
391
|
+
if (def.validate) {
|
|
392
|
+
for (;;) {
|
|
393
|
+
const raw = await p.select({
|
|
394
|
+
message,
|
|
395
|
+
options: clackSelectOptions(def.options, locale),
|
|
396
|
+
initialValue: uiInitial,
|
|
397
|
+
});
|
|
398
|
+
if (p.isCancel(raw)) {
|
|
399
|
+
hooks.onCancel();
|
|
400
|
+
}
|
|
401
|
+
const picked = raw;
|
|
402
|
+
const errS = await runPromptFieldValidate(def, picked, { ...out, [key]: picked });
|
|
403
|
+
if (errS) {
|
|
404
|
+
p.log.error(errS);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
out[key] = picked;
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const raw = await p.select({
|
|
413
|
+
message,
|
|
414
|
+
options: clackSelectOptions(def.options, locale),
|
|
415
|
+
initialValue: uiInitial,
|
|
416
|
+
});
|
|
417
|
+
if (p.isCancel(raw)) {
|
|
418
|
+
hooks.onCancel();
|
|
419
|
+
}
|
|
420
|
+
out[key] = raw;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (def.type === 'password') {
|
|
424
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
425
|
+
if (!interactive) {
|
|
426
|
+
const merged = mergedPassword(key, def, resolveIv, useYesInitial);
|
|
427
|
+
if (merged === undefined) {
|
|
428
|
+
if (def.required) {
|
|
429
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequired', { key }));
|
|
430
|
+
}
|
|
431
|
+
out[key] = '';
|
|
432
|
+
const errPE = await runPromptFieldValidate(def, '', { ...out, [key]: '' });
|
|
433
|
+
if (errPE) {
|
|
434
|
+
hooks.onMissingNonInteractive(errPE);
|
|
435
|
+
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (def.required && isBlankText(merged)) {
|
|
439
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.passwordRequiredNonEmpty', { key }));
|
|
440
|
+
}
|
|
441
|
+
out[key] = merged;
|
|
442
|
+
const errP = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
443
|
+
if (errP) {
|
|
444
|
+
hooks.onMissingNonInteractive(errP);
|
|
445
|
+
}
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (def.validate) {
|
|
449
|
+
for (;;) {
|
|
450
|
+
const raw = await p.password({
|
|
451
|
+
message,
|
|
452
|
+
validate: def.required ? (value) => (isBlankText(value) ? t('promptCatalog.common.required') : undefined) : undefined,
|
|
453
|
+
});
|
|
454
|
+
if (p.isCancel(raw)) {
|
|
455
|
+
hooks.onCancel();
|
|
456
|
+
}
|
|
457
|
+
const s = typeof raw === 'string' ? raw : '';
|
|
458
|
+
if (def.required && isBlankText(s)) {
|
|
459
|
+
p.log.error(t('promptCatalog.common.required'));
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
const errP = await runPromptFieldValidate(def, s, { ...out, [key]: s });
|
|
463
|
+
if (errP) {
|
|
464
|
+
p.log.error(errP);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
out[key] = s;
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const raw = await p.password({
|
|
473
|
+
message,
|
|
474
|
+
validate: def.required ? (value) => (isBlankText(value) ? t('promptCatalog.common.required') : undefined) : undefined,
|
|
475
|
+
});
|
|
476
|
+
if (p.isCancel(raw)) {
|
|
477
|
+
hooks.onCancel();
|
|
478
|
+
}
|
|
479
|
+
out[key] = typeof raw === 'string' ? raw : '';
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (def.type === 'integer') {
|
|
483
|
+
const message = resolvePromptText(def.message, locale, key);
|
|
484
|
+
const placeholder = def.placeholder !== undefined
|
|
485
|
+
? resolvePromptText(def.placeholder, locale)
|
|
486
|
+
: undefined;
|
|
487
|
+
if (!interactive) {
|
|
488
|
+
const merged = mergedInteger(key, def, resolveIv, useYesInitial);
|
|
489
|
+
if (merged === undefined) {
|
|
490
|
+
if (def.required) {
|
|
491
|
+
hooks.onMissingNonInteractive(t('promptCatalog.nonInteractive.integerRequired', { key }));
|
|
492
|
+
}
|
|
493
|
+
const z = def.initialValue ?? 0;
|
|
494
|
+
out[key] = z;
|
|
495
|
+
const errI = await runPromptFieldValidate(def, z, { ...out, [key]: z });
|
|
496
|
+
if (errI) {
|
|
497
|
+
hooks.onMissingNonInteractive(errI);
|
|
498
|
+
}
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
out[key] = merged;
|
|
502
|
+
const errI2 = await runPromptFieldValidate(def, merged, { ...out, [key]: merged });
|
|
503
|
+
if (errI2) {
|
|
504
|
+
hooks.onMissingNonInteractive(errI2);
|
|
505
|
+
}
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const merged = mergedInteger(key, def, promptIv, false);
|
|
509
|
+
const lineDefault = merged !== undefined ? String(merged) : String(def.initialValue ?? 0);
|
|
510
|
+
if (def.validate) {
|
|
511
|
+
let last = lineDefault;
|
|
512
|
+
for (;;) {
|
|
513
|
+
const raw = await p.text({
|
|
514
|
+
message,
|
|
515
|
+
initialValue: last,
|
|
516
|
+
...(placeholder !== undefined ? { placeholder } : {}),
|
|
517
|
+
validate: (value) => {
|
|
518
|
+
const trimmed = value.trim();
|
|
519
|
+
if (trimmed === '') {
|
|
520
|
+
return def.required ? t('promptCatalog.common.required') : undefined;
|
|
521
|
+
}
|
|
522
|
+
if (!/^-?\d+$/.test(trimmed)) {
|
|
523
|
+
return t('promptCatalog.common.mustBeInteger');
|
|
524
|
+
}
|
|
525
|
+
return undefined;
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
if (p.isCancel(raw)) {
|
|
529
|
+
hooks.onCancel();
|
|
530
|
+
}
|
|
531
|
+
if (typeof raw === 'string' && raw.trim() === '' && !def.required) {
|
|
532
|
+
const z = def.initialValue ?? 0;
|
|
533
|
+
out[key] = z;
|
|
534
|
+
const errI = await runPromptFieldValidate(def, z, { ...out, [key]: z });
|
|
535
|
+
if (errI) {
|
|
536
|
+
p.log.error(errI);
|
|
537
|
+
last = raw;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
const n = Number.parseInt(String(raw).trim(), 10);
|
|
543
|
+
const errI = await runPromptFieldValidate(def, n, { ...out, [key]: n });
|
|
544
|
+
if (errI) {
|
|
545
|
+
p.log.error(errI);
|
|
546
|
+
last = typeof raw === 'string' ? raw : last;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
out[key] = n;
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
const raw = await p.text({
|
|
555
|
+
message,
|
|
556
|
+
initialValue: lineDefault,
|
|
557
|
+
...(placeholder !== undefined ? { placeholder } : {}),
|
|
558
|
+
validate: (value) => {
|
|
559
|
+
const trimmed = value.trim();
|
|
560
|
+
if (trimmed === '') {
|
|
561
|
+
return def.required ? t('promptCatalog.common.required') : undefined;
|
|
562
|
+
}
|
|
563
|
+
if (!/^-?\d+$/.test(trimmed)) {
|
|
564
|
+
return t('promptCatalog.common.mustBeInteger');
|
|
565
|
+
}
|
|
566
|
+
return undefined;
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
if (p.isCancel(raw)) {
|
|
570
|
+
hooks.onCancel();
|
|
571
|
+
}
|
|
572
|
+
if (typeof raw === 'string' && raw.trim() === '' && !def.required) {
|
|
573
|
+
out[key] = def.initialValue ?? 0;
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
out[key] = Number.parseInt(String(raw).trim(), 10);
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return out;
|
|
581
|
+
}
|