@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.
@@ -1 +1 @@
1
- {"version":3,"file":"BaseCLI.d.ts","sourceRoot":"","sources":["../../../src/cli/BaseCLI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yCAAyC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAGvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsBzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAU3E,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAC;AAqCpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,CAAC,OAAO,OAAO,OAAQ,YAAW,GAAG;;IAazC,YACE,SAAS,EAAE,SAAS,EACpB,oBAAoB,EAAE,cAAc,EACpC,oBAAoB,EAAE,cAAc,EACpC,aAAa,EAAE,OAAO,EACtB,aAAa,EAAE,OAAO,EACtB,cAAc,EAAE,QAAQ,EACxB,cAAc,EAAE,QAAQ,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,qBAAqB,EAmEhC;IAED;;;;;;;OAOG;IACI,kBAAkB,CAAC,eAAe,EAAE,eAAe,QAEzD;IAED;;;;;;;;OAQG;IACH;;;;;OAKG;IACI,cAAc,CAAC,IAAI,EAAE,WAAW,QAEtC;IAEM,UAAU,CAAC,OAAO,EAAE,OAAO,QAQjC;IAEK,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAqQzD;CACF"}
1
+ {"version":3,"file":"BaseCLI.d.ts","sourceRoot":"","sources":["../../../src/cli/BaseCLI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yCAAyC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAGvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsBzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAU3E,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAC;AAqCpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,CAAC,OAAO,OAAO,OAAQ,YAAW,GAAG;;IAazC,YACE,SAAS,EAAE,SAAS,EACpB,oBAAoB,EAAE,cAAc,EACpC,oBAAoB,EAAE,cAAc,EACpC,aAAa,EAAE,OAAO,EACtB,aAAa,EAAE,OAAO,EACtB,cAAc,EAAE,QAAQ,EACxB,cAAc,EAAE,QAAQ,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,qBAAqB,EAuEhC;IAED;;;;;;;OAOG;IACI,kBAAkB,CAAC,eAAe,EAAE,eAAe,QAEzD;IAED;;;;;;;;OAQG;IACH;;;;;OAKG;IACI,cAAc,CAAC,IAAI,EAAE,WAAW,QAEtC;IAEM,UAAU,CAAC,OAAO,EAAE,OAAO,QAQjC;IAEK,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAqQzD;CACF"}
@@ -130,7 +130,11 @@ export default class BaseCLI {
130
130
  // create a command registry
131
131
  this.#commandRegistry = new DefaultCommandRegistry([], commandValidator);
132
132
  // create a context
133
- this.#context = new DefaultContext(this.#cliConfig);
133
+ this.#context = new DefaultContext(this.#cliConfig, {
134
+ stdin: keyReader !== undefined,
135
+ stdout: stdoutTerminal.isTty(),
136
+ stderr: stderrTerminal.isTty(),
137
+ });
134
138
  this.#startupServiceProvider = new StartupServiceProvider(STARTUP_SERVICE_PRIORITY);
135
139
  this.#stdoutTerminal = stdoutTerminal;
136
140
  this.#printerService = new DefaultPrinterService(stdoutWritableStream, stderrWritableStream, stdoutIsColor, stderrIsColor, stdoutTerminal, stderrTerminal, styler);
@@ -187,7 +191,7 @@ export default class BaseCLI {
187
191
  this.addServiceProvider(this.#startupServiceProvider);
188
192
  this.addServiceProvider(new PrinterServiceProvider(PRINTER_SERVICE_PRIORITY, this.#printerService));
189
193
  this.addServiceProvider(new TableGeneratorServiceProvider(TABLE_GENERATOR_SERVICE_PRIORITY));
190
- const canPrompt = this.#keyReader !== undefined && this.#stderrTerminal.isTty();
194
+ const canPrompt = this.#context.isTty.stdin && this.#context.isTty.stderr;
191
195
  if (this.#options.promptingEnabled && !canPrompt) {
192
196
  throw new Error("promptingEnabled requires a keyReader and a TTY stderr terminal");
193
197
  }
@@ -6,7 +6,8 @@ import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
6
6
  export default class DefaultContext implements Context {
7
7
  #private;
8
8
  readonly cliConfig: CLIConfig;
9
- constructor(cliConfig: CLIConfig);
9
+ readonly isTty: Context["isTty"];
10
+ constructor(cliConfig: CLIConfig, isTty?: Context["isTty"]);
10
11
  addServiceInstance(id: string, serviceInstance: unknown): void;
11
12
  getServiceById(id: string): unknown;
12
13
  doesServiceExist(id: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultContext.d.ts","sourceRoot":"","sources":["../../../src/runtime/DefaultContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,YAAW,OAAO;;IAGjC,QAAQ,CAAC,SAAS,EAAE,SAAS;IAAhD,YAA4B,SAAS,EAAE,SAAS,EAAI;IAE7C,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,QAK7D;IAEM,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAKzC;IAEM,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE3C;CACF"}
1
+ {"version":3,"file":"DefaultContext.d.ts","sourceRoot":"","sources":["../../../src/runtime/DefaultContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,YAAW,OAAO;;IAIlD,QAAQ,CAAC,SAAS,EAAE,SAAS;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC;IAFlC,YACW,SAAS,EAAE,SAAS,EACpB,KAAK,GAAE,OAAO,CAAC,OAAO,CAAkD,EAC/E;IAEG,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,QAK7D;IAEM,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAKzC;IAEM,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE3C;CACF"}
@@ -3,9 +3,11 @@
3
3
  */
4
4
  export default class DefaultContext {
5
5
  cliConfig;
6
+ isTty;
6
7
  #serviceInstancesById = new Map();
7
- constructor(cliConfig) {
8
+ constructor(cliConfig, isTty = { stdin: false, stdout: false, stderr: false }) {
8
9
  this.cliConfig = cliConfig;
10
+ this.isTty = isTty;
9
11
  }
10
12
  addServiceInstance(id, serviceInstance) {
11
13
  if (this.#serviceInstancesById.has(id)) {
@@ -1 +1 @@
1
- {"version":3,"file":"CommandValidator.d.ts","sourceRoot":"","sources":["../../../../src/runtime/command/CommandValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAiBvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmJzE,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IAKnC,YAAmB,SAAS,EAAE,SAAS,EAEtC;IAED;;;;;;OAMG;IACI,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAUtC;CAsWF"}
1
+ {"version":3,"file":"CommandValidator.d.ts","sourceRoot":"","sources":["../../../../src/runtime/command/CommandValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAmBvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmJzE,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IAKnC,YAAmB,SAAS,EAAE,SAAS,EAEtC;IAED;;;;;;OAMG;IACI,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAUtC;CAkYF"}
@@ -1,7 +1,7 @@
1
1
  import { isGroupCommand, isSubCommand } from "./CommandTypeGuards.js";
2
2
  import { ValueTypeName, ComplexValueTypeName } from "@flowscripter/dynamic-cli-framework-api";
3
3
  import { isComplexOption } from "../argument/ArgumentTypeGuards.js";
4
- import { getInvalidArgumentString, validateGlobalCommandArgumentValue, validateOptionValue, } from "../values/argumentValueValidation.js";
4
+ import { getInvalidArgumentString, validateGlobalCommandArgumentValue, validateOptionValue, runCustomValidation, } from "../values/argumentValueValidation.js";
5
5
  import getLogger from "../../util/logger.js";
6
6
  import { getGlobalCommandArgumentConfigurationKey, getSubCommandArgumentConfigurationKey, } from "../../util/configHelper.js";
7
7
  const logger = getLogger("commandValidation");
@@ -145,7 +145,11 @@ export default class CommandValidator {
145
145
  validateArgument(argument, globalCommand.name);
146
146
  if (argument.defaultValue) {
147
147
  const invalidArguments = [];
148
- validateGlobalCommandArgumentValue(globalCommand, argument.defaultValue, invalidArguments);
148
+ const deferred = [];
149
+ validateGlobalCommandArgumentValue(globalCommand, argument.defaultValue, invalidArguments, deferred);
150
+ if (invalidArguments.length === 0) {
151
+ runCustomValidation(deferred, globalCommand, argument.defaultValue, invalidArguments);
152
+ }
149
153
  if (invalidArguments.length > 0) {
150
154
  throw new Error(`Illegal default value for global command: '${globalCommand.name}' => ${invalidArguments
151
155
  .map((invalidArgument) => getInvalidArgumentString(invalidArgument, true))
@@ -303,18 +307,22 @@ export default class CommandValidator {
303
307
  ]);
304
308
  }
305
309
  else {
306
- this.#validateOption(option, currentOptionPaths, allOptionPaths);
310
+ this.#validateOption(subCommand, option, currentOptionPaths, allOptionPaths);
307
311
  this.#validateSubCommandArgumentConfigurationKey(subCommand, [...argumentAncestry, option]);
308
312
  }
309
313
  }
310
- #validateOption(option, currentOptionPaths, allOptionPaths) {
314
+ #validateOption(subCommand, option, currentOptionPaths, allOptionPaths) {
311
315
  validateSubCommandArgument(option);
312
316
  if (Array.isArray(option.defaultValue) && !option.isArray) {
313
317
  throw new Error(`Illegal array default value: '${option.defaultValue.join(",")}' for non-array option: '${option.name}'`);
314
318
  }
315
319
  if (option.defaultValue) {
316
320
  const invalidArguments = [];
317
- validateOptionValue(option, option.defaultValue, invalidArguments);
321
+ const deferred = [];
322
+ validateOptionValue(option, option.defaultValue, invalidArguments, deferred);
323
+ if (invalidArguments.length === 0) {
324
+ runCustomValidation(deferred, subCommand, { [option.name]: option.defaultValue }, invalidArguments);
325
+ }
318
326
  if (invalidArguments.length > 0) {
319
327
  throw new Error(`Illegal default value for option: '${option.name}' => ${invalidArguments
320
328
  .map((invalidArgument) => getInvalidArgumentString(invalidArgument, true))
@@ -350,7 +358,11 @@ export default class CommandValidator {
350
358
  }
351
359
  if (complexOption.defaultValue) {
352
360
  const invalidArguments = [];
353
- validateOptionValue(complexOption, complexOption.defaultValue, invalidArguments);
361
+ const deferred = [];
362
+ validateOptionValue(complexOption, complexOption.defaultValue, invalidArguments, deferred);
363
+ if (invalidArguments.length === 0) {
364
+ runCustomValidation(deferred, subCommand, { [complexOption.name]: complexOption.defaultValue }, invalidArguments);
365
+ }
354
366
  if (invalidArguments.length > 0) {
355
367
  throw new Error(`Illegal default value for option: '${complexOption.name}' => ${invalidArguments
356
368
  .map((invalidArgument) => getInvalidArgumentString(invalidArgument, true))
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/runtime/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,wBAAwB,EACxB,eAAe,EAChB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAelD,YAAY,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG;IACjD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,aAAa,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC9B,sBAAsB,CAiFxB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,CAAC,EAAE,wBAAwB,GACtC,sBAAsB,CAwDxB"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/runtime/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,wBAAwB,EACxB,eAAe,EAChB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAiBlD,YAAY,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG;IACjD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,aAAa,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC9B,sBAAsB,CAwFxB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,CAAC,EAAE,wBAAwB,GACtC,sBAAsB,CA6DxB"}
@@ -1,5 +1,5 @@
1
1
  import populateGlobalCommandValue from "./values/globalCommandValuePopulation.js";
2
- import { validateGlobalCommandArgumentValue, validateOptionValue, validatePositionalValue, } from "./values/argumentValueValidation.js";
2
+ import { validateGlobalCommandArgumentValue, validateOptionValue, validatePositionalValue, runCustomValidation, } from "./values/argumentValueValidation.js";
3
3
  import getLogger from "../util/logger.js";
4
4
  import { redactGlobalCommandValue, redactSubCommandValues } from "../util/secretRedaction.js";
5
5
  import populateSubCommandValues from "./values/subCommandValuePopulation.js";
@@ -30,9 +30,10 @@ export function parseSubCommandClause(commandClause, defaultValues) {
30
30
  invalidArguments.push(invalidArgument);
31
31
  }
32
32
  if (invalidArguments.length === 0) {
33
+ const deferred = [];
33
34
  // validate the option values
34
35
  command.options.forEach((option) => {
35
- const validatedValue = validateOptionValue(option, populatedArgumentValues[option.name] || option.defaultValue, invalidArguments);
36
+ const validatedValue = validateOptionValue(option, populatedArgumentValues[option.name] || option.defaultValue, invalidArguments, deferred);
36
37
  if (validatedValue !== undefined) {
37
38
  populatedArgumentValues[option.name] = validatedValue;
38
39
  }
@@ -41,11 +42,15 @@ export function parseSubCommandClause(commandClause, defaultValues) {
41
42
  if (invalidArguments.length === 0) {
42
43
  // validate the positional values
43
44
  command.positionals.forEach((positional) => {
44
- const validatedValue = validatePositionalValue(positional, populatedArgumentValues[positional.name], invalidArguments);
45
+ const validatedValue = validatePositionalValue(positional, populatedArgumentValues[positional.name], invalidArguments, deferred);
45
46
  if (validatedValue !== undefined) {
46
47
  populatedArgumentValues[positional.name] = validatedValue;
47
48
  }
48
49
  });
50
+ if (invalidArguments.length === 0) {
51
+ // custom validators run once all built-in validation has passed and see all values
52
+ runCustomValidation(deferred, command, populatedArgumentValues, invalidArguments);
53
+ }
49
54
  logger.debug("Command arguments after positionals validated: %O with invalid args: %O", redactSubCommandValues(command, populatedArgumentValues), invalidArguments);
50
55
  }
51
56
  }
@@ -94,10 +99,14 @@ export function parseGlobalCommandClause(commandClause, defaultValue) {
94
99
  logger.debug("Command arguments for command: %s after value population: %O with invalid args: %O and unused args: %O", command.name, redactGlobalCommandValue(command, populatedArgumentValue), invalidArguments, unusedArgs);
95
100
  // don't validate the value if the argument is already invalid
96
101
  if (invalidArguments.length === 0) {
97
- const validatedValue = validateGlobalCommandArgumentValue(command, populatedArgumentValue, invalidArguments);
102
+ const deferred = [];
103
+ const validatedValue = validateGlobalCommandArgumentValue(command, populatedArgumentValue, invalidArguments, deferred);
98
104
  if (validatedValue !== undefined) {
99
105
  populatedArgumentValue = validatedValue;
100
106
  }
107
+ if (invalidArguments.length === 0) {
108
+ runCustomValidation(deferred, command, populatedArgumentValue, invalidArguments);
109
+ }
101
110
  }
102
111
  logger.debug("Command arguments for command: %s after value validation: %O with invalid args: %O", command.name, redactGlobalCommandValue(command, populatedArgumentValue), invalidArguments);
103
112
  return {
@@ -1,25 +1,46 @@
1
- import type { PopulatedSingleValueType, PopulatedValues, PopulatedValueType } from "@flowscripter/dynamic-cli-framework-api";
1
+ import type { SingleValueType, Values, PopulatedSingleValueType, PopulatedValues, PopulatedValueType } from "@flowscripter/dynamic-cli-framework-api";
2
2
  import type { Positional } from "@flowscripter/dynamic-cli-framework-api";
3
3
  import type { Option } from "@flowscripter/dynamic-cli-framework-api";
4
4
  import type { ComplexOption } from "@flowscripter/dynamic-cli-framework-api";
5
5
  import type { InvalidArgument } from "@flowscripter/dynamic-cli-framework-api";
6
+ import type { Argument } from "@flowscripter/dynamic-cli-framework-api";
6
7
  import type { GlobalCommand } from "@flowscripter/dynamic-cli-framework-api";
8
+ import type { Command } from "@flowscripter/dynamic-cli-framework-api";
9
+ /**
10
+ * A custom validation to be invoked once all built-in validation has passed.
11
+ */
12
+ export interface DeferredCustomValidation {
13
+ readonly argument: Argument | ComplexOption;
14
+ readonly path: string;
15
+ readonly value: SingleValueType | Values | Array<SingleValueType | Values>;
16
+ }
17
+ /**
18
+ * Invokes the recorded custom validations in order, stopping at the first failure.
19
+ *
20
+ * @param deferred the validations recorded while validating the values of the command.
21
+ * @param command the {@link Command} the validated values belong to.
22
+ * @param values all validated and converted values of the command.
23
+ * @param invalidArguments an array of {@link InvalidArgument} which is added to if a custom validation fails.
24
+ */
25
+ export declare function runCustomValidation(deferred: ReadonlyArray<DeferredCustomValidation>, command: Command, values: PopulatedValues | PopulatedSingleValueType, invalidArguments: Array<InvalidArgument>): void;
7
26
  /**
8
27
  * Validates the provided value against the provided {@link Option}.
9
28
  *
10
29
  * @param option the {@link Option} to validate against.
11
30
  * @param value the value (if any) for the {@link Option}.
12
31
  * @param invalidArguments an array of {@link InvalidArgument} which may be added to if the provided value is invalid.
32
+ * @param deferred if provided, custom validations are recorded here for later invocation by {@link runCustomValidation}. If omitted, custom validations are not invoked.
13
33
  */
14
- export declare function validateOptionValue(option: Option | ComplexOption, value: PopulatedValueType | PopulatedValues | Array<PopulatedValues | undefined>, invalidArguments: Array<InvalidArgument>): PopulatedValueType | PopulatedValues | Array<PopulatedValues>;
34
+ export declare function validateOptionValue(option: Option | ComplexOption, value: PopulatedValueType | PopulatedValues | Array<PopulatedValues | undefined>, invalidArguments: Array<InvalidArgument>, deferred?: Array<DeferredCustomValidation>): PopulatedValueType | PopulatedValues | Array<PopulatedValues>;
15
35
  /**
16
36
  * Validates the provided value against the provided {@link Positional}.
17
37
  *
18
38
  * @param positional the {@link Positional} to validate against.
19
39
  * @param value the value (if any) for the {@link Positional}.
20
40
  * @param invalidArguments an array of {@link InvalidArgument} which may be added to if the provided value is invalid.
41
+ * @param deferred if provided, custom validations are recorded here for later invocation by {@link runCustomValidation}. If omitted, custom validations are not invoked.
21
42
  */
22
- export declare function validatePositionalValue(positional: Positional, value: PopulatedValueType, invalidArguments: Array<InvalidArgument>): PopulatedValueType;
43
+ export declare function validatePositionalValue(positional: Positional, value: PopulatedValueType, invalidArguments: Array<InvalidArgument>, deferred?: Array<DeferredCustomValidation>): PopulatedValueType;
23
44
  /**
24
45
  * Validates the provided value against the {@link GlobalCommandArgument} provided by the {@link GlobalCommand} and returns the validated
25
46
  * value or undefined if the value was invalid.
@@ -27,7 +48,8 @@ export declare function validatePositionalValue(positional: Positional, value: P
27
48
  * @param globalCommand the {@link GlobalCommand} providing the {@link GlobalCommandArgument}.
28
49
  * @param value the value (if any) for the {@link GlobalCommandArgument}.
29
50
  * @param invalidArguments an array of {@link InvalidArgument} which may be added to if the provided value is invalid.
51
+ * @param deferred if provided, custom validations are recorded here for later invocation by {@link runCustomValidation}. If omitted, custom validations are not invoked.
30
52
  */
31
- export declare function validateGlobalCommandArgumentValue(globalCommand: GlobalCommand, value: PopulatedSingleValueType, invalidArguments: Array<InvalidArgument>): PopulatedSingleValueType;
53
+ export declare function validateGlobalCommandArgumentValue(globalCommand: GlobalCommand, value: PopulatedSingleValueType, invalidArguments: Array<InvalidArgument>, deferred?: Array<DeferredCustomValidation>): PopulatedSingleValueType;
32
54
  export declare function getInvalidArgumentString(invalidArgument: InvalidArgument, skipArgName: boolean): string;
33
55
  //# sourceMappingURL=argumentValueValidation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"argumentValueValidation.d.ts","sourceRoot":"","sources":["../../../../src/runtime/values/argumentValueValidation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,wBAAwB,EACxB,eAAe,EACf,kBAAkB,EACnB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAI/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAgb7E;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GAAG,aAAa,EAC9B,KAAK,EAAE,kBAAkB,GAAG,eAAe,GAAG,KAAK,CAAC,eAAe,GAAG,SAAS,CAAC,EAChF,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,GACvC,kBAAkB,GAAG,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC,CAQ/D;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,kBAAkB,EACzB,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,GACvC,kBAAkB,CAQpB;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,wBAAwB,EAC/B,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,GACvC,wBAAwB,CA+C1B;AAED,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,OAAO,GACnB,MAAM,CA+DR"}
1
+ {"version":3,"file":"argumentValueValidation.d.ts","sourceRoot":"","sources":["../../../../src/runtime/values/argumentValueValidation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,MAAM,EAEN,wBAAwB,EACxB,eAAe,EACf,kBAAkB,EACnB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAG/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAyBvE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,aAAa,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,KAAK,CAAC,eAAe,GAAG,MAAM,CAAC,CAAC;CAC5E;AAiBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,CAAC,wBAAwB,CAAC,EACjD,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,eAAe,GAAG,wBAAwB,EAClD,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,GACvC,IAAI,CAmBN;AAyXD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GAAG,aAAa,EAC9B,KAAK,EAAE,kBAAkB,GAAG,eAAe,GAAG,KAAK,CAAC,eAAe,GAAG,SAAS,CAAC,EAChF,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,EACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,wBAAwB,CAAC,GACzC,kBAAkB,GAAG,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC,CAS/D;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,kBAAkB,EACzB,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,EACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,wBAAwB,CAAC,GACzC,kBAAkB,CASpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kCAAkC,CAChD,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,wBAAwB,EAC/B,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,EACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,wBAAwB,CAAC,GACzC,wBAAwB,CA4C1B;AAED,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,OAAO,GACnB,MAAM,CA+DR"}
@@ -8,28 +8,41 @@ function withPrefix(invalidArgument, prefix) {
8
8
  return { ...invalidArgument, name: `${prefix}${invalidArgument.name ?? ""}` };
9
9
  }
10
10
  /**
11
- * Invokes the custom validate function (if any) of the provided {@link Argument} and returns an
12
- * {@link InvalidArgument} if the custom validation failed.
13
- *
14
- * @param argument the {@link Argument} providing the custom validate function.
15
- * @param name the name to use for the {@link InvalidArgument}.
16
- * @param value the validated and type-converted value.
11
+ * Records a custom validation of the provided {@link Argument} (if it has one) for later invocation by
12
+ * {@link runCustomValidation}. Deferred validations are recorded bottom-up and depth-first.
17
13
  */
18
- function doCustomValidation(argument, name, value) {
19
- if (argument.validate === undefined) {
20
- return undefined;
14
+ function deferCustomValidation(deferred, argument, path, value) {
15
+ if (deferred !== undefined && argument.validate !== undefined) {
16
+ deferred.push({ argument, path, value });
21
17
  }
22
- const message = argument.validate(value);
23
- if (message === undefined) {
24
- return undefined;
18
+ }
19
+ /**
20
+ * Invokes the recorded custom validations in order, stopping at the first failure.
21
+ *
22
+ * @param deferred the validations recorded while validating the values of the command.
23
+ * @param command the {@link Command} the validated values belong to.
24
+ * @param values all validated and converted values of the command.
25
+ * @param invalidArguments an array of {@link InvalidArgument} which is added to if a custom validation fails.
26
+ */
27
+ export function runCustomValidation(deferred, command, values, invalidArguments) {
28
+ for (const { argument, path, value } of deferred) {
29
+ const message = argument.validate(value, {
30
+ argument: argument,
31
+ path,
32
+ values,
33
+ command,
34
+ });
35
+ if (message !== undefined) {
36
+ invalidArguments.push({
37
+ argument,
38
+ name: path,
39
+ value: value,
40
+ reason: InvalidArgumentReason.CUSTOM_VALIDATION,
41
+ message,
42
+ });
43
+ return;
44
+ }
25
45
  }
26
- return {
27
- argument,
28
- name,
29
- value: value,
30
- reason: InvalidArgumentReason.CUSTOM_VALIDATION,
31
- message,
32
- };
33
46
  }
34
47
  /**
35
48
  * Validates the provided primitive value against the provided {@link Argument}.
@@ -141,7 +154,7 @@ function validatePrimitiveValue(argument, value) {
141
154
  }
142
155
  return { validValue: convertedValue };
143
156
  }
144
- function validateArrayValue(subCommandArgument, arrayValue) {
157
+ function validateArrayValue(subCommandArgument, arrayValue, path, deferred) {
145
158
  const convertedArrayValue = [];
146
159
  for (let i = 0; i < arrayValue.length; i += 1) {
147
160
  const singleValue = arrayValue[i];
@@ -171,7 +184,7 @@ function validateArrayValue(subCommandArgument, arrayValue) {
171
184
  },
172
185
  };
173
186
  }
174
- validationResult = validateObjectValue(subCommandArgument, singleValue);
187
+ validationResult = validateObjectValue(subCommandArgument, singleValue, `${path}[${i}]`, deferred);
175
188
  } // if not array and not object, then must be primitive
176
189
  else {
177
190
  if (isComplexOption(subCommandArgument)) {
@@ -202,10 +215,11 @@ function validateArrayValue(subCommandArgument, arrayValue) {
202
215
  validValue: convertedArrayValue,
203
216
  };
204
217
  }
205
- function validateObjectValue(argument, objectValue) {
218
+ function validateObjectValue(argument, objectValue, path, deferred) {
206
219
  const convertedObjectValue = {};
207
220
  for (let i = 0; i < argument.properties.length; i++) {
208
221
  const propertyArg = argument.properties[i];
222
+ const propertyPath = `${path}.${propertyArg.name}`;
209
223
  let propertyValue = objectValue[propertyArg.name];
210
224
  if (propertyValue === undefined && propertyArg.defaultValue !== undefined) {
211
225
  propertyValue = propertyArg.defaultValue;
@@ -236,7 +250,7 @@ function validateObjectValue(argument, objectValue) {
236
250
  },
237
251
  };
238
252
  }
239
- validationResult = validateArrayValue(propertyArg, propertyValue);
253
+ validationResult = validateArrayValue(propertyArg, propertyValue, propertyPath, deferred);
240
254
  }
241
255
  else if (typeof propertyValue === "object") {
242
256
  if (propertyArg.type !== ComplexValueTypeName.COMPLEX) {
@@ -250,7 +264,7 @@ function validateObjectValue(argument, objectValue) {
250
264
  },
251
265
  };
252
266
  }
253
- validationResult = validateObjectValue(propertyArg, propertyValue);
267
+ validationResult = validateObjectValue(propertyArg, propertyValue, propertyPath, deferred);
254
268
  } // if not array and not object, then must be primitive
255
269
  else {
256
270
  if (isComplexOption(propertyArg)) {
@@ -276,20 +290,14 @@ function validateObjectValue(argument, objectValue) {
276
290
  invalidArgument: withPrefix(validationResult.invalidArgument, `.${propertyArg.name}`),
277
291
  };
278
292
  }
279
- // custom validation runs only once the property (and any nested properties) passed validation
280
- const customInvalidArgument = doCustomValidation(propertyArg, `.${propertyArg.name}`, validationResult.validValue);
281
- if (customInvalidArgument !== undefined) {
282
- return {
283
- validValue: convertedObjectValue,
284
- invalidArgument: customInvalidArgument,
285
- };
286
- }
293
+ // custom validation is deferred until all built-in validation has passed
294
+ deferCustomValidation(deferred, propertyArg, propertyPath, validationResult.validValue);
287
295
  }
288
296
  return {
289
297
  validValue: convertedObjectValue,
290
298
  };
291
299
  }
292
- function doSubCommandArgumentValidation(argument, value, isArray, isOptional, invalidArguments) {
300
+ function doSubCommandArgumentValidation(argument, value, isArray, isOptional, invalidArguments, deferred) {
293
301
  // if there is a value, check if it is valid
294
302
  if (value !== undefined) {
295
303
  let validationResult;
@@ -303,7 +311,7 @@ function doSubCommandArgumentValidation(argument, value, isArray, isOptional, in
303
311
  });
304
312
  return undefined;
305
313
  }
306
- validationResult = validateArrayValue(argument, value);
314
+ validationResult = validateArrayValue(argument, value, argument.name, deferred);
307
315
  }
308
316
  else if (typeof value === "object") {
309
317
  if (!isComplexOption(argument)) {
@@ -315,7 +323,7 @@ function doSubCommandArgumentValidation(argument, value, isArray, isOptional, in
315
323
  });
316
324
  return undefined;
317
325
  }
318
- validationResult = validateObjectValue(argument, value);
326
+ validationResult = validateObjectValue(argument, value, argument.name, deferred);
319
327
  }
320
328
  else {
321
329
  if (isComplexOption(argument)) {
@@ -333,11 +341,7 @@ function doSubCommandArgumentValidation(argument, value, isArray, isOptional, in
333
341
  invalidArguments.push(withPrefix(validationResult.invalidArgument, argument.name));
334
342
  return undefined;
335
343
  }
336
- const customInvalidArgument = doCustomValidation(argument, argument.name, validationResult.validValue);
337
- if (customInvalidArgument !== undefined) {
338
- invalidArguments.push(customInvalidArgument);
339
- return undefined;
340
- }
344
+ deferCustomValidation(deferred, argument, argument.name, validationResult.validValue);
341
345
  return validationResult.validValue;
342
346
  }
343
347
  // if there is no value, check if it was optional
@@ -356,9 +360,10 @@ function doSubCommandArgumentValidation(argument, value, isArray, isOptional, in
356
360
  * @param option the {@link Option} to validate against.
357
361
  * @param value the value (if any) for the {@link Option}.
358
362
  * @param invalidArguments an array of {@link InvalidArgument} which may be added to if the provided value is invalid.
363
+ * @param deferred if provided, custom validations are recorded here for later invocation by {@link runCustomValidation}. If omitted, custom validations are not invoked.
359
364
  */
360
- export function validateOptionValue(option, value, invalidArguments) {
361
- return doSubCommandArgumentValidation(option, value, option.isArray || false, option.isOptional || false, invalidArguments);
365
+ export function validateOptionValue(option, value, invalidArguments, deferred) {
366
+ return doSubCommandArgumentValidation(option, value, option.isArray || false, option.isOptional || false, invalidArguments, deferred);
362
367
  }
363
368
  /**
364
369
  * Validates the provided value against the provided {@link Positional}.
@@ -366,9 +371,10 @@ export function validateOptionValue(option, value, invalidArguments) {
366
371
  * @param positional the {@link Positional} to validate against.
367
372
  * @param value the value (if any) for the {@link Positional}.
368
373
  * @param invalidArguments an array of {@link InvalidArgument} which may be added to if the provided value is invalid.
374
+ * @param deferred if provided, custom validations are recorded here for later invocation by {@link runCustomValidation}. If omitted, custom validations are not invoked.
369
375
  */
370
- export function validatePositionalValue(positional, value, invalidArguments) {
371
- return doSubCommandArgumentValidation(positional, value, positional.isVarargMultiple || false, positional.isVarargOptional || false, invalidArguments);
376
+ export function validatePositionalValue(positional, value, invalidArguments, deferred) {
377
+ return doSubCommandArgumentValidation(positional, value, positional.isVarargMultiple || false, positional.isVarargOptional || false, invalidArguments, deferred);
372
378
  }
373
379
  /**
374
380
  * Validates the provided value against the {@link GlobalCommandArgument} provided by the {@link GlobalCommand} and returns the validated
@@ -377,8 +383,9 @@ export function validatePositionalValue(positional, value, invalidArguments) {
377
383
  * @param globalCommand the {@link GlobalCommand} providing the {@link GlobalCommandArgument}.
378
384
  * @param value the value (if any) for the {@link GlobalCommandArgument}.
379
385
  * @param invalidArguments an array of {@link InvalidArgument} which may be added to if the provided value is invalid.
386
+ * @param deferred if provided, custom validations are recorded here for later invocation by {@link runCustomValidation}. If omitted, custom validations are not invoked.
380
387
  */
381
- export function validateGlobalCommandArgumentValue(globalCommand, value, invalidArguments) {
388
+ export function validateGlobalCommandArgumentValue(globalCommand, value, invalidArguments, deferred) {
382
389
  // if this function is called it is because the argument is defined
383
390
  const globalCommandArgument = globalCommand.argument;
384
391
  // if there is a value, check if it is valid
@@ -402,11 +409,7 @@ export function validateGlobalCommandArgumentValue(globalCommand, value, invalid
402
409
  }
403
410
  return undefined;
404
411
  }
405
- const customInvalidArgument = doCustomValidation(globalCommandArgument, globalCommand.name, validationResult.validValue);
406
- if (customInvalidArgument !== undefined) {
407
- invalidArguments.push(customInvalidArgument);
408
- return undefined;
409
- }
412
+ deferCustomValidation(deferred, globalCommandArgument, globalCommand.name, validationResult.validValue);
410
413
  return validationResult.validValue;
411
414
  }
412
415
  // if there is no value, check if it was optional
@@ -1,9 +1,13 @@
1
1
  import type { ArgumentPrompterService } from "@flowscripter/dynamic-cli-framework-api";
2
2
  import type { ParseResult } from "../../runtime/parser.ts";
3
3
  import type { PrompterService } from "@flowscripter/dynamic-cli-framework-api";
4
+ import { type Values } from "@flowscripter/dynamic-cli-framework-api";
5
+ import type { Option } from "@flowscripter/dynamic-cli-framework-api";
6
+ import type { ComplexOption } from "@flowscripter/dynamic-cli-framework-api";
4
7
  export default class DefaultArgumentPrompterService implements ArgumentPrompterService {
5
8
  #private;
6
9
  constructor(prompterService: PrompterService);
7
10
  promptForMissingArguments(parseResult: ParseResult): Promise<ParseResult>;
11
+ promptForArguments(args: ReadonlyArray<Option | ComplexOption>, existing?: Values): Promise<Values | undefined>;
8
12
  }
9
13
  //# sourceMappingURL=DefaultArgumentPrompterService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultArgumentPrompterService.d.ts","sourceRoot":"","sources":["../../../../src/service/argumentPrompter/DefaultArgumentPrompterService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAuB/E,MAAM,CAAC,OAAO,OAAO,8BAA+B,YAAW,uBAAuB;;IAGpF,YAAmB,eAAe,EAAE,eAAe,EAElD;IAEK,yBAAyB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA+B9E;CAkOF"}
1
+ {"version":3,"file":"DefaultArgumentPrompterService.d.ts","sourceRoot":"","sources":["../../../../src/service/argumentPrompter/DefaultArgumentPrompterService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAQ/E,OAAO,EAML,KAAK,MAAM,EACZ,MAAM,yCAAyC,CAAC;AAGjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yCAAyC,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAmB7E,MAAM,CAAC,OAAO,OAAO,8BAA+B,YAAW,uBAAuB;;IAGpF,YAAmB,eAAe,EAAE,eAAe,EAElD;IAEK,yBAAyB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA+B9E;IAEK,kBAAkB,CACtB,IAAI,EAAE,aAAa,CAAC,MAAM,GAAG,aAAa,CAAC,EAC3C,QAAQ,GAAE,MAAW,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiB7B;CA8PF"}
@@ -2,6 +2,11 @@ import { PromptType, } from "@flowscripter/dynamic-cli-framework-api";
2
2
  import { InvalidArgumentReason } from "@flowscripter/dynamic-cli-framework-api";
3
3
  import { ValueTypeName, ComplexValueTypeName, } from "@flowscripter/dynamic-cli-framework-api";
4
4
  import { isGlobalCommand, isSubCommand } from "../../runtime/command/CommandTypeGuards.js";
5
+ /**
6
+ * Used as {@link ValidationContext.command} when prompting via {@link ArgumentPrompterService.promptForArguments},
7
+ * which is not associated with a specific command.
8
+ */
9
+ const NO_COMMAND = { name: "" };
5
10
  export default class DefaultArgumentPrompterService {
6
11
  #prompterService;
7
12
  constructor(prompterService) {
@@ -34,12 +39,40 @@ export default class DefaultArgumentPrompterService {
34
39
  }
35
40
  return parseResult;
36
41
  }
42
+ async promptForArguments(args, existing = {}) {
43
+ if (!this.#prompterService.promptEnabled) {
44
+ return undefined;
45
+ }
46
+ const prompted = {};
47
+ const merged = { ...existing };
48
+ const scope = { command: NO_COMMAND, values: merged };
49
+ for (const arg of args) {
50
+ if (arg.name in existing) {
51
+ continue;
52
+ }
53
+ const value = (await this.#promptForArgument(arg.name, arg, scope));
54
+ prompted[arg.name] = value;
55
+ merged[arg.name] = value;
56
+ }
57
+ return prompted;
58
+ }
59
+ async #promptForArgument(name, arg, scope) {
60
+ if ("type" in arg && arg.type === ComplexValueTypeName.COMPLEX) {
61
+ return await this.#promptForComplexOption(name, arg, scope);
62
+ }
63
+ if (DefaultArgumentPrompterService.#isArrayArgument(arg)) {
64
+ return await this.#promptForArrayArgument(name, arg, scope);
65
+ }
66
+ const prompt = DefaultArgumentPrompterService.#argumentToPrompt(name, arg.description ?? name, arg, name, scope);
67
+ const result = await this.#prompterService.prompt(prompt);
68
+ return DefaultArgumentPrompterService.#coerceValue(result.value, arg.type);
69
+ }
37
70
  async #promptForGlobalCommand(parseResult) {
38
71
  const command = parseResult.command;
39
72
  if (!command.argument) {
40
73
  return parseResult;
41
74
  }
42
- const prompt = DefaultArgumentPrompterService.#argumentToPrompt(command.name, command.name, command.argument);
75
+ const prompt = DefaultArgumentPrompterService.#argumentToPrompt(command.name, command.name, command.argument, command.name, { command, values: undefined });
43
76
  const result = await this.#prompterService.prompt(prompt);
44
77
  const value = DefaultArgumentPrompterService.#coerceValue(result.value, command.argument.type);
45
78
  return {
@@ -57,6 +90,7 @@ export default class DefaultArgumentPrompterService {
57
90
  ...existingValues,
58
91
  };
59
92
  const remainingInvalid = [];
93
+ const scope = { command, values: newValues };
60
94
  for (const invalid of parseResult.invalidArguments) {
61
95
  if (invalid.reason !== InvalidArgumentReason.MISSING_VALUE || !invalid.name) {
62
96
  remainingInvalid.push(invalid);
@@ -67,19 +101,7 @@ export default class DefaultArgumentPrompterService {
67
101
  remainingInvalid.push(invalid);
68
102
  continue;
69
103
  }
70
- if ("type" in arg && arg.type === ComplexValueTypeName.COMPLEX) {
71
- const complexValues = await this.#promptForComplexOption(invalid.name, arg);
72
- newValues[invalid.name] = complexValues;
73
- }
74
- else if (DefaultArgumentPrompterService.#isArrayArgument(arg)) {
75
- const arrayValues = await this.#promptForArrayArgument(invalid.name, arg);
76
- newValues[invalid.name] = arrayValues;
77
- }
78
- else {
79
- const prompt = DefaultArgumentPrompterService.#argumentToPrompt(invalid.name, arg.description ?? invalid.name, arg);
80
- const result = await this.#prompterService.prompt(prompt);
81
- newValues[invalid.name] = DefaultArgumentPrompterService.#coerceValue(result.value, arg.type);
82
- }
104
+ newValues[invalid.name] = await this.#promptForArgument(invalid.name, arg, scope);
83
105
  }
84
106
  return {
85
107
  command: parseResult.command,
@@ -89,28 +111,28 @@ export default class DefaultArgumentPrompterService {
89
111
  unusedArgs: parseResult.unusedArgs,
90
112
  };
91
113
  }
92
- async #promptForComplexOption(name, complexOption) {
114
+ async #promptForComplexOption(name, complexOption, scope) {
93
115
  const values = {};
94
116
  for (const prop of complexOption.properties) {
95
117
  if ("type" in prop && prop.type === ComplexValueTypeName.COMPLEX) {
96
- values[prop.name] = await this.#promptForComplexOption(`${name}.${prop.name}`, prop);
118
+ values[prop.name] = await this.#promptForComplexOption(`${name}.${prop.name}`, prop, scope);
97
119
  }
98
120
  else {
99
121
  const option = prop;
100
122
  if (option.isOptional && option.defaultValue !== undefined) {
101
123
  continue;
102
124
  }
103
- const prompt = DefaultArgumentPrompterService.#argumentToPrompt(`${name}.${option.name}`, option.description ?? `${name}.${option.name}`, option);
125
+ const prompt = DefaultArgumentPrompterService.#argumentToPrompt(`${name}.${option.name}`, option.description ?? `${name}.${option.name}`, option, `${name}.${option.name}`, scope);
104
126
  const result = await this.#prompterService.prompt(prompt);
105
127
  values[option.name] = DefaultArgumentPrompterService.#coerceValue(result.value, option.type);
106
128
  }
107
129
  }
108
130
  return values;
109
131
  }
110
- async #promptForArrayArgument(name, arg) {
132
+ async #promptForArrayArgument(name, arg, scope) {
111
133
  const values = [];
112
134
  while (true) {
113
- const prompt = DefaultArgumentPrompterService.#argumentToPrompt(name, `${arg.description ?? name}${values.length > 0 ? ` [${values.length + 1}]` : ""}`, arg);
135
+ const prompt = DefaultArgumentPrompterService.#argumentToPrompt(name, `${arg.description ?? name}${values.length > 0 ? ` [${values.length + 1}]` : ""}`, arg, name, scope);
114
136
  const result = await this.#prompterService.prompt(prompt);
115
137
  values.push(DefaultArgumentPrompterService.#coerceValue(result.value, arg.type));
116
138
  const addAnother = {
@@ -126,7 +148,7 @@ export default class DefaultArgumentPrompterService {
126
148
  }
127
149
  return values;
128
150
  }
129
- static #argumentToPrompt(name, description, arg) {
151
+ static #argumentToPrompt(name, description, arg, path, scope) {
130
152
  if (arg.type === ValueTypeName.BOOLEAN) {
131
153
  return {
132
154
  name,
@@ -152,7 +174,17 @@ export default class DefaultArgumentPrompterService {
152
174
  returnedValue: "",
153
175
  min: arg.minValueInclusive,
154
176
  max: arg.maxValueInclusive,
155
- validate: arg.validate ? (v) => arg.validate(v) : undefined,
177
+ validate: arg.validate
178
+ ? (v) => {
179
+ const value = DefaultArgumentPrompterService.#coerceValue(v, arg.type);
180
+ return arg.validate(value, {
181
+ argument: arg,
182
+ path,
183
+ values: scope.values === undefined ? value : scope.values,
184
+ command: scope.command,
185
+ });
186
+ }
187
+ : undefined,
156
188
  };
157
189
  return {
158
190
  name,