@optique/core 0.9.0-dev.201 → 0.9.0-dev.202

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.
@@ -8,7 +8,7 @@ const require_nonempty = require('./nonempty.cjs');
8
8
  * @return `true` if the object is a {@link ValueParser}, `false` otherwise.
9
9
  */
10
10
  function isValueParser(object) {
11
- return typeof object === "object" && object != null && "metavar" in object && typeof object.metavar === "string" && "parse" in object && typeof object.parse === "function" && "format" in object && typeof object.format === "function";
11
+ return typeof object === "object" && object != null && "$mode" in object && (object.$mode === "sync" || object.$mode === "async") && "metavar" in object && typeof object.metavar === "string" && "parse" in object && typeof object.parse === "function" && "format" in object && typeof object.format === "function";
12
12
  }
13
13
  /**
14
14
  * Implementation of the choice parser for both string and number types.
@@ -21,6 +21,7 @@ function choice(choices, options = {}) {
21
21
  const numberChoices = choices;
22
22
  const numberOptions = options;
23
23
  return {
24
+ $mode: "sync",
24
25
  metavar,
25
26
  parse(input) {
26
27
  const parsed = Number(input);
@@ -53,6 +54,7 @@ function choice(choices, options = {}) {
53
54
  const stringOptions = options;
54
55
  const normalizedValues = stringOptions.caseInsensitive ? stringChoices.map((v) => v.toLowerCase()) : stringChoices;
55
56
  return {
57
+ $mode: "sync",
56
58
  metavar,
57
59
  parse(input) {
58
60
  const normalizedInput = stringOptions.caseInsensitive ? input.toLowerCase() : input;
@@ -124,6 +126,7 @@ function string(options = {}) {
124
126
  const metavar = options.metavar ?? "STRING";
125
127
  require_nonempty.ensureNonEmptyString(metavar);
126
128
  return {
129
+ $mode: "sync",
127
130
  metavar,
128
131
  parse(input) {
129
132
  if (options.pattern != null && !options.pattern.test(input)) return {
@@ -178,6 +181,7 @@ function integer(options) {
178
181
  const metavar$1 = options.metavar ?? "INTEGER";
179
182
  require_nonempty.ensureNonEmptyString(metavar$1);
180
183
  return {
184
+ $mode: "sync",
181
185
  metavar: metavar$1,
182
186
  parse(input) {
183
187
  let value;
@@ -211,6 +215,7 @@ function integer(options) {
211
215
  const metavar = options?.metavar ?? "INTEGER";
212
216
  require_nonempty.ensureNonEmptyString(metavar);
213
217
  return {
218
+ $mode: "sync",
214
219
  metavar,
215
220
  parse(input) {
216
221
  if (!input.match(/^-?\d+$/)) return {
@@ -250,6 +255,7 @@ function float(options = {}) {
250
255
  const metavar = options.metavar ?? "NUMBER";
251
256
  require_nonempty.ensureNonEmptyString(metavar);
252
257
  return {
258
+ $mode: "sync",
253
259
  metavar,
254
260
  parse(input) {
255
261
  let value;
@@ -299,6 +305,7 @@ function url(options = {}) {
299
305
  const metavar = options.metavar ?? "URL";
300
306
  require_nonempty.ensureNonEmptyString(metavar);
301
307
  return {
308
+ $mode: "sync",
302
309
  metavar,
303
310
  parse(input) {
304
311
  if (!URL.canParse(input)) return {
@@ -343,6 +350,7 @@ function locale(options = {}) {
343
350
  const metavar = options.metavar ?? "LOCALE";
344
351
  require_nonempty.ensureNonEmptyString(metavar);
345
352
  return {
353
+ $mode: "sync",
346
354
  metavar,
347
355
  parse(input) {
348
356
  let locale$1;
@@ -612,6 +620,7 @@ function uuid(options = {}) {
612
620
  const metavar = options.metavar ?? "UUID";
613
621
  require_nonempty.ensureNonEmptyString(metavar);
614
622
  return {
623
+ $mode: "sync",
615
624
  metavar,
616
625
  parse(input) {
617
626
  if (!uuidRegex.test(input)) return {
@@ -1,6 +1,6 @@
1
1
  import { NonEmptyString, ensureNonEmptyString, isNonEmptyString } from "./nonempty.cjs";
2
2
  import { Message } from "./message.cjs";
3
- import { Suggestion } from "./parser.cjs";
3
+ import { Mode, ModeIterable, ModeValue, Suggestion } from "./parser.cjs";
4
4
 
5
5
  //#region src/valueparser.d.ts
6
6
 
@@ -10,9 +10,20 @@ import { Suggestion } from "./parser.cjs";
10
10
  * A `ValueParser` is responsible for converting string input (typically from
11
11
  * CLI arguments or option values) into strongly-typed values of type {@link T}.
12
12
  *
13
+ * @template M The execution mode of the parser (`"sync"` or `"async"`).
13
14
  * @template T The type of value this parser produces.
15
+ * @since 0.9.0 Added the `M` type parameter for sync/async mode support.
14
16
  */
15
- interface ValueParser<T> {
17
+ interface ValueParser<M extends Mode = "sync", T = unknown> {
18
+ /**
19
+ * The execution mode of this value parser.
20
+ *
21
+ * - `"sync"`: The `parse` method returns values directly.
22
+ * - `"async"`: The `parse` method returns Promises.
23
+ *
24
+ * @since 0.9.0
25
+ */
26
+ readonly $mode: M;
16
27
  /**
17
28
  * The metavariable name for this parser. Used in help messages
18
29
  * to indicate what kind of value this parser expects. Usually
@@ -25,9 +36,10 @@ interface ValueParser<T> {
25
36
  * @param input The string input to parse
26
37
  * (e.g., the `value` part of `--option=value`).
27
38
  * @returns A result object indicating success or failure with an error
28
- * message.
39
+ * message. In async mode, returns a Promise that resolves to
40
+ * the result.
29
41
  */
30
- parse(input: string): ValueParserResult<T>;
42
+ parse(input: string): ModeValue<M, ValueParserResult<T>>;
31
43
  /**
32
44
  * Formats a value of type {@link T} into a string representation.
33
45
  * This is useful for displaying the value in help messages or
@@ -43,9 +55,10 @@ interface ValueParser<T> {
43
55
  *
44
56
  * @param prefix The current input prefix to complete.
45
57
  * @returns An iterable of suggestion objects.
58
+ * In async mode, returns an AsyncIterable.
46
59
  * @since 0.6.0
47
60
  */
48
- suggest?(prefix: string): Iterable<Suggestion>;
61
+ suggest?(prefix: string): ModeIterable<M, Suggestion>;
49
62
  }
50
63
  /**
51
64
  * Result type returned by {@link ValueParser#parse}.
@@ -171,7 +184,7 @@ type ChoiceOptions = ChoiceOptionsString;
171
184
  * @param object The object to check.
172
185
  * @return `true` if the object is a {@link ValueParser}, `false` otherwise.
173
186
  */
174
- declare function isValueParser<T>(object: unknown): object is ValueParser<T>;
187
+ declare function isValueParser<M extends Mode, T>(object: unknown): object is ValueParser<M, T>;
175
188
  /**
176
189
  * Creates a {@link ValueParser} that accepts one of multiple
177
190
  * string values, so-called enumerated values.
@@ -184,7 +197,7 @@ declare function isValueParser<T>(object: unknown): object is ValueParser<T>;
184
197
  * @returns A {@link ValueParser} that checks if the input matches one of the
185
198
  * specified values.
186
199
  */
187
- declare function choice<const T extends string>(choices: readonly T[], options?: ChoiceOptionsString): ValueParser<T>;
200
+ declare function choice<const T extends string>(choices: readonly T[], options?: ChoiceOptionsString): ValueParser<"sync", T>;
188
201
  /**
189
202
  * Creates a {@link ValueParser} that accepts one of multiple
190
203
  * number values.
@@ -198,7 +211,7 @@ declare function choice<const T extends string>(choices: readonly T[], options?:
198
211
  * specified values.
199
212
  * @since 0.9.0
200
213
  */
201
- declare function choice<const T extends number>(choices: readonly T[], options?: ChoiceOptionsNumber): ValueParser<T>;
214
+ declare function choice<const T extends number>(choices: readonly T[], options?: ChoiceOptionsNumber): ValueParser<"sync", T>;
202
215
  /**
203
216
  * Creates a {@link ValueParser} for strings.
204
217
  *
@@ -213,7 +226,7 @@ declare function choice<const T extends number>(choices: readonly T[], options?:
213
226
  * @returns A {@link ValueParser} that parses strings according to the
214
227
  * specified options.
215
228
  */
216
- declare function string(options?: StringOptions): ValueParser<string>;
229
+ declare function string(options?: StringOptions): ValueParser<"sync", string>;
217
230
  /**
218
231
  * Options for creating an integer parser that returns a JavaScript `number`.
219
232
  *
@@ -325,14 +338,14 @@ interface IntegerOptionsBigInt {
325
338
  * @param options Configuration options for the integer parser.
326
339
  * @returns A {@link ValueParser} that parses strings into numbers.
327
340
  */
328
- declare function integer(options?: IntegerOptionsNumber): ValueParser<number>;
341
+ declare function integer(options?: IntegerOptionsNumber): ValueParser<"sync", number>;
329
342
  /**
330
343
  * Creates a ValueParser for integers that returns `bigint` values.
331
344
  *
332
345
  * @param options Configuration options for the `bigint` parser.
333
346
  * @returns A {@link ValueParser} that parses strings into `bigint` values.
334
347
  */
335
- declare function integer(options: IntegerOptionsBigInt): ValueParser<bigint>;
348
+ declare function integer(options: IntegerOptionsBigInt): ValueParser<"sync", bigint>;
336
349
  /**
337
350
  * Options for creating a {@link float} parser.
338
351
  */
@@ -402,7 +415,7 @@ interface FloatOptions {
402
415
  * @returns A {@link ValueParser} that parses strings into floating-point
403
416
  * numbers.
404
417
  */
405
- declare function float(options?: FloatOptions): ValueParser<number>;
418
+ declare function float(options?: FloatOptions): ValueParser<"sync", number>;
406
419
  /**
407
420
  * Options for creating a {@link url} parser.
408
421
  */
@@ -449,7 +462,7 @@ interface UrlOptions {
449
462
  * @param options Configuration options for the URL parser.
450
463
  * @returns A {@link ValueParser} that converts string input to `URL` objects.
451
464
  */
452
- declare function url(options?: UrlOptions): ValueParser<URL>;
465
+ declare function url(options?: UrlOptions): ValueParser<"sync", URL>;
453
466
  /**
454
467
  * Options for creating a {@link locale} parser.
455
468
  */
@@ -484,7 +497,7 @@ interface LocaleOptions {
484
497
  * @returns A {@link ValueParser} that converts string input to `Intl.Locale`
485
498
  * objects.
486
499
  */
487
- declare function locale(options?: LocaleOptions): ValueParser<Intl.Locale>;
500
+ declare function locale(options?: LocaleOptions): ValueParser<"sync", Intl.Locale>;
488
501
  /**
489
502
  * Type representing a UUID string.
490
503
  *
@@ -541,6 +554,6 @@ interface UuidOptions {
541
554
  * @returns A {@link ValueParser} that converts string input to {@link Uuid}
542
555
  * strings.
543
556
  */
544
- declare function uuid(options?: UuidOptions): ValueParser<Uuid>;
557
+ declare function uuid(options?: UuidOptions): ValueParser<"sync", Uuid>;
545
558
  //#endregion
546
- export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, type NonEmptyString, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, ensureNonEmptyString, float, integer, isNonEmptyString, isValueParser, locale, string, url, uuid };
559
+ export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, type Mode, type ModeIterable, type ModeValue, type NonEmptyString, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, ensureNonEmptyString, float, integer, isNonEmptyString, isValueParser, locale, string, url, uuid };
@@ -1,6 +1,6 @@
1
1
  import { NonEmptyString, ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
2
2
  import { Message } from "./message.js";
3
- import { Suggestion } from "./parser.js";
3
+ import { Mode, ModeIterable, ModeValue, Suggestion } from "./parser.js";
4
4
 
5
5
  //#region src/valueparser.d.ts
6
6
 
@@ -10,9 +10,20 @@ import { Suggestion } from "./parser.js";
10
10
  * A `ValueParser` is responsible for converting string input (typically from
11
11
  * CLI arguments or option values) into strongly-typed values of type {@link T}.
12
12
  *
13
+ * @template M The execution mode of the parser (`"sync"` or `"async"`).
13
14
  * @template T The type of value this parser produces.
15
+ * @since 0.9.0 Added the `M` type parameter for sync/async mode support.
14
16
  */
15
- interface ValueParser<T> {
17
+ interface ValueParser<M extends Mode = "sync", T = unknown> {
18
+ /**
19
+ * The execution mode of this value parser.
20
+ *
21
+ * - `"sync"`: The `parse` method returns values directly.
22
+ * - `"async"`: The `parse` method returns Promises.
23
+ *
24
+ * @since 0.9.0
25
+ */
26
+ readonly $mode: M;
16
27
  /**
17
28
  * The metavariable name for this parser. Used in help messages
18
29
  * to indicate what kind of value this parser expects. Usually
@@ -25,9 +36,10 @@ interface ValueParser<T> {
25
36
  * @param input The string input to parse
26
37
  * (e.g., the `value` part of `--option=value`).
27
38
  * @returns A result object indicating success or failure with an error
28
- * message.
39
+ * message. In async mode, returns a Promise that resolves to
40
+ * the result.
29
41
  */
30
- parse(input: string): ValueParserResult<T>;
42
+ parse(input: string): ModeValue<M, ValueParserResult<T>>;
31
43
  /**
32
44
  * Formats a value of type {@link T} into a string representation.
33
45
  * This is useful for displaying the value in help messages or
@@ -43,9 +55,10 @@ interface ValueParser<T> {
43
55
  *
44
56
  * @param prefix The current input prefix to complete.
45
57
  * @returns An iterable of suggestion objects.
58
+ * In async mode, returns an AsyncIterable.
46
59
  * @since 0.6.0
47
60
  */
48
- suggest?(prefix: string): Iterable<Suggestion>;
61
+ suggest?(prefix: string): ModeIterable<M, Suggestion>;
49
62
  }
50
63
  /**
51
64
  * Result type returned by {@link ValueParser#parse}.
@@ -171,7 +184,7 @@ type ChoiceOptions = ChoiceOptionsString;
171
184
  * @param object The object to check.
172
185
  * @return `true` if the object is a {@link ValueParser}, `false` otherwise.
173
186
  */
174
- declare function isValueParser<T>(object: unknown): object is ValueParser<T>;
187
+ declare function isValueParser<M extends Mode, T>(object: unknown): object is ValueParser<M, T>;
175
188
  /**
176
189
  * Creates a {@link ValueParser} that accepts one of multiple
177
190
  * string values, so-called enumerated values.
@@ -184,7 +197,7 @@ declare function isValueParser<T>(object: unknown): object is ValueParser<T>;
184
197
  * @returns A {@link ValueParser} that checks if the input matches one of the
185
198
  * specified values.
186
199
  */
187
- declare function choice<const T extends string>(choices: readonly T[], options?: ChoiceOptionsString): ValueParser<T>;
200
+ declare function choice<const T extends string>(choices: readonly T[], options?: ChoiceOptionsString): ValueParser<"sync", T>;
188
201
  /**
189
202
  * Creates a {@link ValueParser} that accepts one of multiple
190
203
  * number values.
@@ -198,7 +211,7 @@ declare function choice<const T extends string>(choices: readonly T[], options?:
198
211
  * specified values.
199
212
  * @since 0.9.0
200
213
  */
201
- declare function choice<const T extends number>(choices: readonly T[], options?: ChoiceOptionsNumber): ValueParser<T>;
214
+ declare function choice<const T extends number>(choices: readonly T[], options?: ChoiceOptionsNumber): ValueParser<"sync", T>;
202
215
  /**
203
216
  * Creates a {@link ValueParser} for strings.
204
217
  *
@@ -213,7 +226,7 @@ declare function choice<const T extends number>(choices: readonly T[], options?:
213
226
  * @returns A {@link ValueParser} that parses strings according to the
214
227
  * specified options.
215
228
  */
216
- declare function string(options?: StringOptions): ValueParser<string>;
229
+ declare function string(options?: StringOptions): ValueParser<"sync", string>;
217
230
  /**
218
231
  * Options for creating an integer parser that returns a JavaScript `number`.
219
232
  *
@@ -325,14 +338,14 @@ interface IntegerOptionsBigInt {
325
338
  * @param options Configuration options for the integer parser.
326
339
  * @returns A {@link ValueParser} that parses strings into numbers.
327
340
  */
328
- declare function integer(options?: IntegerOptionsNumber): ValueParser<number>;
341
+ declare function integer(options?: IntegerOptionsNumber): ValueParser<"sync", number>;
329
342
  /**
330
343
  * Creates a ValueParser for integers that returns `bigint` values.
331
344
  *
332
345
  * @param options Configuration options for the `bigint` parser.
333
346
  * @returns A {@link ValueParser} that parses strings into `bigint` values.
334
347
  */
335
- declare function integer(options: IntegerOptionsBigInt): ValueParser<bigint>;
348
+ declare function integer(options: IntegerOptionsBigInt): ValueParser<"sync", bigint>;
336
349
  /**
337
350
  * Options for creating a {@link float} parser.
338
351
  */
@@ -402,7 +415,7 @@ interface FloatOptions {
402
415
  * @returns A {@link ValueParser} that parses strings into floating-point
403
416
  * numbers.
404
417
  */
405
- declare function float(options?: FloatOptions): ValueParser<number>;
418
+ declare function float(options?: FloatOptions): ValueParser<"sync", number>;
406
419
  /**
407
420
  * Options for creating a {@link url} parser.
408
421
  */
@@ -449,7 +462,7 @@ interface UrlOptions {
449
462
  * @param options Configuration options for the URL parser.
450
463
  * @returns A {@link ValueParser} that converts string input to `URL` objects.
451
464
  */
452
- declare function url(options?: UrlOptions): ValueParser<URL>;
465
+ declare function url(options?: UrlOptions): ValueParser<"sync", URL>;
453
466
  /**
454
467
  * Options for creating a {@link locale} parser.
455
468
  */
@@ -484,7 +497,7 @@ interface LocaleOptions {
484
497
  * @returns A {@link ValueParser} that converts string input to `Intl.Locale`
485
498
  * objects.
486
499
  */
487
- declare function locale(options?: LocaleOptions): ValueParser<Intl.Locale>;
500
+ declare function locale(options?: LocaleOptions): ValueParser<"sync", Intl.Locale>;
488
501
  /**
489
502
  * Type representing a UUID string.
490
503
  *
@@ -541,6 +554,6 @@ interface UuidOptions {
541
554
  * @returns A {@link ValueParser} that converts string input to {@link Uuid}
542
555
  * strings.
543
556
  */
544
- declare function uuid(options?: UuidOptions): ValueParser<Uuid>;
557
+ declare function uuid(options?: UuidOptions): ValueParser<"sync", Uuid>;
545
558
  //#endregion
546
- export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, type NonEmptyString, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, ensureNonEmptyString, float, integer, isNonEmptyString, isValueParser, locale, string, url, uuid };
559
+ export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, type Mode, type ModeIterable, type ModeValue, type NonEmptyString, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, ensureNonEmptyString, float, integer, isNonEmptyString, isValueParser, locale, string, url, uuid };
@@ -8,7 +8,7 @@ import { ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
8
8
  * @return `true` if the object is a {@link ValueParser}, `false` otherwise.
9
9
  */
10
10
  function isValueParser(object) {
11
- return typeof object === "object" && object != null && "metavar" in object && typeof object.metavar === "string" && "parse" in object && typeof object.parse === "function" && "format" in object && typeof object.format === "function";
11
+ return typeof object === "object" && object != null && "$mode" in object && (object.$mode === "sync" || object.$mode === "async") && "metavar" in object && typeof object.metavar === "string" && "parse" in object && typeof object.parse === "function" && "format" in object && typeof object.format === "function";
12
12
  }
13
13
  /**
14
14
  * Implementation of the choice parser for both string and number types.
@@ -21,6 +21,7 @@ function choice(choices, options = {}) {
21
21
  const numberChoices = choices;
22
22
  const numberOptions = options;
23
23
  return {
24
+ $mode: "sync",
24
25
  metavar,
25
26
  parse(input) {
26
27
  const parsed = Number(input);
@@ -53,6 +54,7 @@ function choice(choices, options = {}) {
53
54
  const stringOptions = options;
54
55
  const normalizedValues = stringOptions.caseInsensitive ? stringChoices.map((v) => v.toLowerCase()) : stringChoices;
55
56
  return {
57
+ $mode: "sync",
56
58
  metavar,
57
59
  parse(input) {
58
60
  const normalizedInput = stringOptions.caseInsensitive ? input.toLowerCase() : input;
@@ -124,6 +126,7 @@ function string(options = {}) {
124
126
  const metavar = options.metavar ?? "STRING";
125
127
  ensureNonEmptyString(metavar);
126
128
  return {
129
+ $mode: "sync",
127
130
  metavar,
128
131
  parse(input) {
129
132
  if (options.pattern != null && !options.pattern.test(input)) return {
@@ -178,6 +181,7 @@ function integer(options) {
178
181
  const metavar$1 = options.metavar ?? "INTEGER";
179
182
  ensureNonEmptyString(metavar$1);
180
183
  return {
184
+ $mode: "sync",
181
185
  metavar: metavar$1,
182
186
  parse(input) {
183
187
  let value;
@@ -211,6 +215,7 @@ function integer(options) {
211
215
  const metavar = options?.metavar ?? "INTEGER";
212
216
  ensureNonEmptyString(metavar);
213
217
  return {
218
+ $mode: "sync",
214
219
  metavar,
215
220
  parse(input) {
216
221
  if (!input.match(/^-?\d+$/)) return {
@@ -250,6 +255,7 @@ function float(options = {}) {
250
255
  const metavar = options.metavar ?? "NUMBER";
251
256
  ensureNonEmptyString(metavar);
252
257
  return {
258
+ $mode: "sync",
253
259
  metavar,
254
260
  parse(input) {
255
261
  let value;
@@ -299,6 +305,7 @@ function url(options = {}) {
299
305
  const metavar = options.metavar ?? "URL";
300
306
  ensureNonEmptyString(metavar);
301
307
  return {
308
+ $mode: "sync",
302
309
  metavar,
303
310
  parse(input) {
304
311
  if (!URL.canParse(input)) return {
@@ -343,6 +350,7 @@ function locale(options = {}) {
343
350
  const metavar = options.metavar ?? "LOCALE";
344
351
  ensureNonEmptyString(metavar);
345
352
  return {
353
+ $mode: "sync",
346
354
  metavar,
347
355
  parse(input) {
348
356
  let locale$1;
@@ -612,6 +620,7 @@ function uuid(options = {}) {
612
620
  const metavar = options.metavar ?? "UUID";
613
621
  ensureNonEmptyString(metavar);
614
622
  return {
623
+ $mode: "sync",
615
624
  metavar,
616
625
  parse(input) {
617
626
  if (!uuidRegex.test(input)) return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.9.0-dev.201+a103c62c",
3
+ "version": "0.9.0-dev.202+8c415cd6",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",