@flowscripter/dynamic-cli-framework 11.0.0 → 11.1.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/dist/src/cli/BaseCLI.d.ts.map +1 -1
- package/dist/src/cli/BaseCLI.js +6 -2
- package/dist/src/runtime/DefaultContext.d.ts +2 -1
- package/dist/src/runtime/DefaultContext.d.ts.map +1 -1
- package/dist/src/runtime/DefaultContext.js +3 -1
- package/dist/src/runtime/command/CommandValidator.d.ts.map +1 -1
- package/dist/src/runtime/command/CommandValidator.js +18 -6
- package/dist/src/runtime/parser.d.ts.map +1 -1
- package/dist/src/runtime/parser.js +13 -4
- package/dist/src/runtime/values/argumentValueValidation.d.ts +26 -4
- package/dist/src/runtime/values/argumentValueValidation.d.ts.map +1 -1
- package/dist/src/runtime/values/argumentValueValidation.js +53 -50
- package/dist/src/service/argumentPrompter/DefaultArgumentPrompterService.d.ts +4 -0
- package/dist/src/service/argumentPrompter/DefaultArgumentPrompterService.d.ts.map +1 -1
- package/dist/src/service/argumentPrompter/DefaultArgumentPrompterService.js +53 -21
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts.map +1 -1
- package/dist/src/service/configuration/KeyValueServiceProvider.js +1 -0
- package/package.json +2 -2
- package/src/cli/BaseCLI.ts +6 -2
- package/src/runtime/DefaultContext.ts +4 -1
- package/src/runtime/command/CommandValidator.ts +35 -5
- package/src/runtime/parser.ts +14 -0
- package/src/runtime/values/argumentValueValidation.ts +82 -50
- package/src/service/argumentPrompter/DefaultArgumentPrompterService.ts +92 -26
- package/src/service/configuration/KeyValueServiceProvider.ts +1 -0
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
ComplexValueTypeName,
|
|
15
15
|
type PopulatedSingleValueType,
|
|
16
16
|
type PopulatedValues,
|
|
17
|
+
type Values,
|
|
17
18
|
} from "@flowscripter/dynamic-cli-framework-api";
|
|
19
|
+
import type { Command } from "@flowscripter/dynamic-cli-framework-api";
|
|
18
20
|
import type { Argument } from "@flowscripter/dynamic-cli-framework-api";
|
|
19
21
|
import type { Option } from "@flowscripter/dynamic-cli-framework-api";
|
|
20
22
|
import type { Positional } from "@flowscripter/dynamic-cli-framework-api";
|
|
@@ -23,6 +25,20 @@ import type { SubCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
|
23
25
|
import type { GlobalCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
24
26
|
import { isGlobalCommand, isSubCommand } from "../../runtime/command/CommandTypeGuards.ts";
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The command and values visible to {@link Argument.validate} functions while prompting.
|
|
30
|
+
*/
|
|
31
|
+
interface PromptScope {
|
|
32
|
+
readonly command: Command;
|
|
33
|
+
readonly values: PopulatedValues | PopulatedSingleValueType;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Used as {@link ValidationContext.command} when prompting via {@link ArgumentPrompterService.promptForArguments},
|
|
38
|
+
* which is not associated with a specific command.
|
|
39
|
+
*/
|
|
40
|
+
const NO_COMMAND = { name: "" } as Command;
|
|
41
|
+
|
|
26
42
|
export default class DefaultArgumentPrompterService implements ArgumentPrompterService {
|
|
27
43
|
readonly #prompterService: PrompterService;
|
|
28
44
|
|
|
@@ -63,6 +79,53 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
63
79
|
return parseResult;
|
|
64
80
|
}
|
|
65
81
|
|
|
82
|
+
async promptForArguments(
|
|
83
|
+
args: ReadonlyArray<Option | ComplexOption>,
|
|
84
|
+
existing: Values = {},
|
|
85
|
+
): Promise<Values | undefined> {
|
|
86
|
+
if (!this.#prompterService.promptEnabled) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
const prompted: Record<string, Values[string]> = {};
|
|
90
|
+
const merged: Record<string, Values[string]> = { ...existing };
|
|
91
|
+
const scope: PromptScope = { command: NO_COMMAND, values: merged as PopulatedValues };
|
|
92
|
+
|
|
93
|
+
for (const arg of args) {
|
|
94
|
+
if (arg.name in existing) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const value = (await this.#promptForArgument(arg.name, arg, scope)) as Values[string];
|
|
98
|
+
prompted[arg.name] = value;
|
|
99
|
+
merged[arg.name] = value;
|
|
100
|
+
}
|
|
101
|
+
return prompted as Values;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async #promptForArgument(
|
|
105
|
+
name: string,
|
|
106
|
+
arg: Option | Positional | ComplexOption,
|
|
107
|
+
scope: PromptScope,
|
|
108
|
+
): Promise<PopulatedValues[string]> {
|
|
109
|
+
if ("type" in arg && (arg as ComplexOption).type === ComplexValueTypeName.COMPLEX) {
|
|
110
|
+
return await this.#promptForComplexOption(name, arg as ComplexOption, scope);
|
|
111
|
+
}
|
|
112
|
+
if (DefaultArgumentPrompterService.#isArrayArgument(arg)) {
|
|
113
|
+
return await this.#promptForArrayArgument(name, arg as Option | Positional, scope);
|
|
114
|
+
}
|
|
115
|
+
const prompt = DefaultArgumentPrompterService.#argumentToPrompt(
|
|
116
|
+
name,
|
|
117
|
+
(arg as Option | Positional).description ?? name,
|
|
118
|
+
arg as Argument,
|
|
119
|
+
name,
|
|
120
|
+
scope,
|
|
121
|
+
);
|
|
122
|
+
const result = await this.#prompterService.prompt(prompt);
|
|
123
|
+
return DefaultArgumentPrompterService.#coerceValue(
|
|
124
|
+
result.value as SingleValueType,
|
|
125
|
+
(arg as Argument).type,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
66
129
|
async #promptForGlobalCommand(parseResult: ParseResult): Promise<ParseResult> {
|
|
67
130
|
const command = parseResult.command as GlobalCommand;
|
|
68
131
|
if (!command.argument) {
|
|
@@ -73,6 +136,8 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
73
136
|
command.name,
|
|
74
137
|
command.name,
|
|
75
138
|
command.argument,
|
|
139
|
+
command.name,
|
|
140
|
+
{ command, values: undefined },
|
|
76
141
|
);
|
|
77
142
|
const result = await this.#prompterService.prompt(prompt);
|
|
78
143
|
const value = DefaultArgumentPrompterService.#coerceValue(
|
|
@@ -96,6 +161,7 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
96
161
|
...existingValues,
|
|
97
162
|
};
|
|
98
163
|
const remainingInvalid: InvalidArgument[] = [];
|
|
164
|
+
const scope: PromptScope = { command, values: newValues as PopulatedValues };
|
|
99
165
|
|
|
100
166
|
for (const invalid of parseResult.invalidArguments) {
|
|
101
167
|
if (invalid.reason !== InvalidArgumentReason.MISSING_VALUE || !invalid.name) {
|
|
@@ -109,30 +175,7 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
109
175
|
continue;
|
|
110
176
|
}
|
|
111
177
|
|
|
112
|
-
|
|
113
|
-
const complexValues = await this.#promptForComplexOption(
|
|
114
|
-
invalid.name,
|
|
115
|
-
arg as ComplexOption,
|
|
116
|
-
);
|
|
117
|
-
newValues[invalid.name] = complexValues;
|
|
118
|
-
} else if (DefaultArgumentPrompterService.#isArrayArgument(arg)) {
|
|
119
|
-
const arrayValues = await this.#promptForArrayArgument(
|
|
120
|
-
invalid.name,
|
|
121
|
-
arg as Option | Positional,
|
|
122
|
-
);
|
|
123
|
-
newValues[invalid.name] = arrayValues;
|
|
124
|
-
} else {
|
|
125
|
-
const prompt = DefaultArgumentPrompterService.#argumentToPrompt(
|
|
126
|
-
invalid.name,
|
|
127
|
-
(arg as Option | Positional).description ?? invalid.name,
|
|
128
|
-
arg as Argument,
|
|
129
|
-
);
|
|
130
|
-
const result = await this.#prompterService.prompt(prompt);
|
|
131
|
-
newValues[invalid.name] = DefaultArgumentPrompterService.#coerceValue(
|
|
132
|
-
result.value as SingleValueType,
|
|
133
|
-
(arg as Argument).type,
|
|
134
|
-
);
|
|
135
|
-
}
|
|
178
|
+
newValues[invalid.name] = await this.#promptForArgument(invalid.name, arg, scope);
|
|
136
179
|
}
|
|
137
180
|
|
|
138
181
|
return {
|
|
@@ -147,6 +190,7 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
147
190
|
async #promptForComplexOption(
|
|
148
191
|
name: string,
|
|
149
192
|
complexOption: ComplexOption,
|
|
193
|
+
scope: PromptScope,
|
|
150
194
|
): Promise<PopulatedValues> {
|
|
151
195
|
const values: Record<string, PopulatedValues[string]> = {};
|
|
152
196
|
|
|
@@ -155,6 +199,7 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
155
199
|
values[(prop as ComplexOption).name] = await this.#promptForComplexOption(
|
|
156
200
|
`${name}.${(prop as ComplexOption).name}`,
|
|
157
201
|
prop as ComplexOption,
|
|
202
|
+
scope,
|
|
158
203
|
);
|
|
159
204
|
} else {
|
|
160
205
|
const option = prop as Option;
|
|
@@ -165,6 +210,8 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
165
210
|
`${name}.${option.name}`,
|
|
166
211
|
option.description ?? `${name}.${option.name}`,
|
|
167
212
|
option,
|
|
213
|
+
`${name}.${option.name}`,
|
|
214
|
+
scope,
|
|
168
215
|
);
|
|
169
216
|
const result = await this.#prompterService.prompt(prompt);
|
|
170
217
|
values[option.name] = DefaultArgumentPrompterService.#coerceValue(
|
|
@@ -180,6 +227,7 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
180
227
|
async #promptForArrayArgument(
|
|
181
228
|
name: string,
|
|
182
229
|
arg: Option | Positional,
|
|
230
|
+
scope: PromptScope,
|
|
183
231
|
): Promise<Array<SingleValueType>> {
|
|
184
232
|
const values: SingleValueType[] = [];
|
|
185
233
|
|
|
@@ -188,6 +236,8 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
188
236
|
name,
|
|
189
237
|
`${arg.description ?? name}${values.length > 0 ? ` [${values.length + 1}]` : ""}`,
|
|
190
238
|
arg as Argument,
|
|
239
|
+
name,
|
|
240
|
+
scope,
|
|
191
241
|
);
|
|
192
242
|
const result = await this.#prompterService.prompt(prompt);
|
|
193
243
|
values.push(
|
|
@@ -212,7 +262,13 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
212
262
|
return values;
|
|
213
263
|
}
|
|
214
264
|
|
|
215
|
-
static #argumentToPrompt(
|
|
265
|
+
static #argumentToPrompt(
|
|
266
|
+
name: string,
|
|
267
|
+
description: string,
|
|
268
|
+
arg: Argument,
|
|
269
|
+
path: string,
|
|
270
|
+
scope: PromptScope,
|
|
271
|
+
): Prompt {
|
|
216
272
|
if (arg.type === ValueTypeName.BOOLEAN) {
|
|
217
273
|
return {
|
|
218
274
|
name,
|
|
@@ -240,7 +296,17 @@ export default class DefaultArgumentPrompterService implements ArgumentPrompterS
|
|
|
240
296
|
returnedValue: "",
|
|
241
297
|
min: arg.minValueInclusive,
|
|
242
298
|
max: arg.maxValueInclusive,
|
|
243
|
-
validate: arg.validate
|
|
299
|
+
validate: arg.validate
|
|
300
|
+
? (v) => {
|
|
301
|
+
const value = DefaultArgumentPrompterService.#coerceValue(v, arg.type);
|
|
302
|
+
return arg.validate!(value, {
|
|
303
|
+
argument: arg,
|
|
304
|
+
path,
|
|
305
|
+
values: scope.values === undefined ? value : scope.values,
|
|
306
|
+
command: scope.command,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
: undefined,
|
|
244
310
|
};
|
|
245
311
|
|
|
246
312
|
return {
|
|
@@ -226,6 +226,7 @@ export default class KeyValueServiceProvider implements ServiceProvider {
|
|
|
226
226
|
const scopedKeyValueService = this.getScopedKeyValueService(scopeType, scopeKey);
|
|
227
227
|
return {
|
|
228
228
|
cliConfig: context.cliConfig,
|
|
229
|
+
isTty: context.isTty,
|
|
229
230
|
getServiceById: (id: string): unknown =>
|
|
230
231
|
id === KEY_VALUE_SERVICE_ID ? scopedKeyValueService : context.getServiceById(id),
|
|
231
232
|
doesServiceExist: (id: string): boolean => context.doesServiceExist(id),
|