@optique/core 0.9.0-dev.224 → 0.9.0-dev.225

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;
@@ -120,6 +122,7 @@ function string(options = {}) {
120
122
  const metavar = options.metavar ?? "STRING";
121
123
  require_nonempty.ensureNonEmptyString(metavar);
122
124
  return {
125
+ $mode: "sync",
123
126
  metavar,
124
127
  parse(input) {
125
128
  if (options.pattern != null && !options.pattern.test(input)) return {
@@ -174,6 +177,7 @@ function integer(options) {
174
177
  const metavar$1 = options.metavar ?? "INTEGER";
175
178
  require_nonempty.ensureNonEmptyString(metavar$1);
176
179
  return {
180
+ $mode: "sync",
177
181
  metavar: metavar$1,
178
182
  parse(input) {
179
183
  let value;
@@ -207,6 +211,7 @@ function integer(options) {
207
211
  const metavar = options?.metavar ?? "INTEGER";
208
212
  require_nonempty.ensureNonEmptyString(metavar);
209
213
  return {
214
+ $mode: "sync",
210
215
  metavar,
211
216
  parse(input) {
212
217
  if (!input.match(/^-?\d+$/)) return {
@@ -246,6 +251,7 @@ function float(options = {}) {
246
251
  const metavar = options.metavar ?? "NUMBER";
247
252
  require_nonempty.ensureNonEmptyString(metavar);
248
253
  return {
254
+ $mode: "sync",
249
255
  metavar,
250
256
  parse(input) {
251
257
  let value;
@@ -295,6 +301,7 @@ function url(options = {}) {
295
301
  const metavar = options.metavar ?? "URL";
296
302
  require_nonempty.ensureNonEmptyString(metavar);
297
303
  return {
304
+ $mode: "sync",
298
305
  metavar,
299
306
  parse(input) {
300
307
  if (!URL.canParse(input)) return {
@@ -339,6 +346,7 @@ function locale(options = {}) {
339
346
  const metavar = options.metavar ?? "LOCALE";
340
347
  require_nonempty.ensureNonEmptyString(metavar);
341
348
  return {
349
+ $mode: "sync",
342
350
  metavar,
343
351
  parse(input) {
344
352
  let locale$1;
@@ -608,6 +616,7 @@ function uuid(options = {}) {
608
616
  const metavar = options.metavar ?? "UUID";
609
617
  require_nonempty.ensureNonEmptyString(metavar);
610
618
  return {
619
+ $mode: "sync",
611
620
  metavar,
612
621
  parse(input) {
613
622
  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;
@@ -120,6 +122,7 @@ function string(options = {}) {
120
122
  const metavar = options.metavar ?? "STRING";
121
123
  ensureNonEmptyString(metavar);
122
124
  return {
125
+ $mode: "sync",
123
126
  metavar,
124
127
  parse(input) {
125
128
  if (options.pattern != null && !options.pattern.test(input)) return {
@@ -174,6 +177,7 @@ function integer(options) {
174
177
  const metavar$1 = options.metavar ?? "INTEGER";
175
178
  ensureNonEmptyString(metavar$1);
176
179
  return {
180
+ $mode: "sync",
177
181
  metavar: metavar$1,
178
182
  parse(input) {
179
183
  let value;
@@ -207,6 +211,7 @@ function integer(options) {
207
211
  const metavar = options?.metavar ?? "INTEGER";
208
212
  ensureNonEmptyString(metavar);
209
213
  return {
214
+ $mode: "sync",
210
215
  metavar,
211
216
  parse(input) {
212
217
  if (!input.match(/^-?\d+$/)) return {
@@ -246,6 +251,7 @@ function float(options = {}) {
246
251
  const metavar = options.metavar ?? "NUMBER";
247
252
  ensureNonEmptyString(metavar);
248
253
  return {
254
+ $mode: "sync",
249
255
  metavar,
250
256
  parse(input) {
251
257
  let value;
@@ -295,6 +301,7 @@ function url(options = {}) {
295
301
  const metavar = options.metavar ?? "URL";
296
302
  ensureNonEmptyString(metavar);
297
303
  return {
304
+ $mode: "sync",
298
305
  metavar,
299
306
  parse(input) {
300
307
  if (!URL.canParse(input)) return {
@@ -339,6 +346,7 @@ function locale(options = {}) {
339
346
  const metavar = options.metavar ?? "LOCALE";
340
347
  ensureNonEmptyString(metavar);
341
348
  return {
349
+ $mode: "sync",
342
350
  metavar,
343
351
  parse(input) {
344
352
  let locale$1;
@@ -608,6 +616,7 @@ function uuid(options = {}) {
608
616
  const metavar = options.metavar ?? "UUID";
609
617
  ensureNonEmptyString(metavar);
610
618
  return {
619
+ $mode: "sync",
611
620
  metavar,
612
621
  parse(input) {
613
622
  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.224+d01487f2",
3
+ "version": "0.9.0-dev.225+82a6f132",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",