icore 1.0.3 → 1.0.5

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/readme.md +238 -289
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icore",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Declarative command line interface mechanics for Node.js",
5
5
  "keywords": [
6
6
  "command-line",
package/readme.md CHANGED
@@ -7,28 +7,9 @@
7
7
 
8
8
  Small dependency-free command line interface mechanics for [Node.js®](https://nodejs.org) applications.
9
9
 
10
- [API](#api) | [Option Schemas](#option-schemas) | [Type Inference](#type-inference)
11
-
12
- ### How it works?
13
-
14
- ![yuml diagram](http://yuml.me/diagram/scruffy;dir:LR;/class/[*argv*%20{bg:gray}|External;users%20get%20--limit%2010%20--json]->[*matches*%20{bg:lavender}|System;parse,%20resolve,%20validate,%20infer]->[*typed%20result*%20{bg:honeydew}|Container;command=users/get;%20limit=10;%20json=true]->[*your%20app*%20{bg:cornsilk}|System;business%20logic%20and%20output])
15
-
16
- Use `icore` when you need more than raw argument parsing:
17
-
18
- - typed command definitions;
19
- - command registry resolution;
20
- - required options;
21
- - string choices;
22
- - number parsing with integer, minimum, and maximum constraints;
23
- - option presence metadata;
24
- - typed command handler input.
25
-
26
- Use [`node:util.parseArgs`](https://nodejs.org/api/util.html#utilparseargsconfig)
27
- directly when you only need low-level argument parsing.
28
-
29
10
  ### Installation
30
11
 
31
- To use `icores` in your project, run:
12
+ To use `icore` in your project, run:
32
13
 
33
14
  ```sh
34
15
  npm install icore
@@ -36,141 +17,27 @@ npm install icore
36
17
 
37
18
  ### Table of Contents
38
19
 
39
- - [`parseArgv(args, schema?)`](#parseargvargs-schema)
40
- - [`parseOptions(schema, values)`](#parseoptionsschema-values)
41
- - [`parseOptionsDetailed(schema, values)`](#parseoptionsdetailedschema-values)
42
- - [`defineCommand(command)`](#definecommandcommand)
43
- - [`defineCommandRegistry(commands)`](#definecommandregistrycommands)
44
- - [`isCommandName(registry, value)`](#iscommandnameregistry-value)
45
- - [`resolveCommand(registry, positionals)`](#resolvecommandregistry-positionals)
46
- - [`resolveCommandFromArgs(registry, args)`](#resolvecommandfromargsregistry-args)
47
- - [`runCommandFromRegistry(registry, args, context)`](#runcommandfromregistryregistry-args-context)
48
- - [`mergeOptionsSchema(...schemas)`](#mergeoptionsschemaschemas)
49
- - [`runCommand(command, args, context)`](#runcommandcommand-args-context)
50
-
51
- ### Design Goals
52
-
53
- - describe CLI mechanics declaratively;
54
- - use literal option types: `type: 'string' | 'boolean' | 'number'`;
55
- - keep handlers free from repetitive option parsing;
56
- - keep domain and API semantics outside the framework;
57
- - provide predictable user-facing errors;
58
- - avoid runtime dependencies.
59
-
60
- #### Basic Usage
61
-
62
- ```ts
63
- import { defineCommand, runCommand } from 'icore';
64
-
65
- const helloCommand = defineCommand({
66
- path: ['hello'],
67
- options: {
68
- name: {
69
- type: 'string',
70
- default: 'world'
71
- },
72
- upper: {
73
- type: 'boolean'
74
- }
75
- },
76
- async handle({ options }) {
77
- const message = `Hello, ${options.name}!`;
78
-
79
- return options.upper ? message.toUpperCase() : message;
80
- }
81
- });
82
-
83
- const output = await runCommand(
84
- helloCommand,
85
- ['hello', '--name', 'Stanislav', '--upper'],
86
- {}
87
- );
88
-
89
- console.log(output);
90
- ```
91
-
92
- The command handler receives parsed options, user-provided option metadata,
93
- remaining positionals, and caller provided context.
94
-
95
- CommonJS is supported:
96
-
97
- ```js
98
- const {
99
- defineCommand,
100
- runCommand
101
- } = require('icore');
102
- ```
103
-
104
- ## Option Schemas
105
-
106
- Options are described as plain objects.
107
-
108
- Option names are exact. `icore` does not normalize camelCase to kebab-case or
109
- kebab-case to camelCase. Use quoted object keys when your public CLI option
110
- contains `-`.
111
-
112
- ### String Options
113
-
114
- ```ts
115
- const schema = {
116
- token: {
117
- type: 'string',
118
- required: true
119
- },
120
- format: {
121
- type: 'string',
122
- choices: ['json', 'table'],
123
- default: 'table'
124
- }
125
- } as const;
126
- ```
127
-
128
- String options reject missing required values, blank strings, boolean flag form,
129
- and values outside `choices`.
130
-
131
- ### Boolean Options
132
-
133
- ```ts
134
- const schema = {
135
- insecure: {
136
- type: 'boolean'
137
- }
138
- } as const;
139
- ```
140
-
141
- Boolean options accept flag form only:
142
-
143
- ```sh
144
- --insecure
145
- ```
146
-
147
- Explicit values are rejected:
148
-
149
- ```sh
150
- --insecure true
151
- --insecure=true
152
- ```
153
-
154
- ### Number Options
155
-
156
- ```ts
157
- const schema = {
158
- limit: {
159
- type: 'number',
160
- integer: true,
161
- min: 1,
162
- max: 1000,
163
- default: 100
164
- }
165
- } as const;
166
- ```
167
-
168
- Number options parse decimal numeric values and can validate integer and range
169
- constraints. Defaults are validated with the same rules as user-provided values.
20
+ * [API](#api)
21
+ * [`parseArgv(args, schema?)`](#parseargvargs-schema)
22
+ * [`parseOptions(schema, values)`](#parseoptionsschema-values)
23
+ * [`parseOptionsDetailed(schema, values)`](#parseoptionsdetailedschema-values)
24
+ * [`defineCommand(command)`](#definecommandcommand)
25
+ * [`defineCommandRegistry(commands)`](#definecommandregistrycommands)
26
+ * [`isCommandName(registry, value)`](#iscommandnameregistry-value)
27
+ * [`resolveCommand(registry, positionals)`](#resolvecommandregistry-positionals)
28
+ * [`resolveCommandFromArgs(registry, args)`](#resolvecommandfromargsregistry-args)
29
+ * [`runCommandFromRegistry(registry, args, context)`](#runcommandfromregistryregistry-args-context)
30
+ * [`mergeOptionsSchema(...schemas)`](#mergeoptionsschemaschemas)
31
+ * [`runCommand(command, args, context)`](#runcommandcommand-args-context)
32
+ * [Example](#example)
33
+ * [Option Schemas](#option-schemas)
34
+ * [Type Inference](#type-inference)
35
+ * [Facade of arguments](#facade-of-arguments)
36
+ * [Error Messages](#error-messages)
170
37
 
171
38
  ### API
172
39
 
173
- ### `parseArgv(args, schema?)`
40
+ #### `parseArgv(args, schema?)`
174
41
 
175
42
  Parses raw CLI arguments into positionals and raw option values.
176
43
 
@@ -178,10 +45,10 @@ Parses raw CLI arguments into positionals and raw option values.
178
45
  import { parseArgv } from 'icore';
179
46
 
180
47
  const argv = parseArgv([
181
- 'users',
182
- 'get-accounts',
183
- '--format=json',
184
- '--insecure'
48
+ 'hello',
49
+ '--name',
50
+ 'User',
51
+ '--upper'
185
52
  ]);
186
53
  ```
187
54
 
@@ -189,10 +56,10 @@ Result:
189
56
 
190
57
  ```ts
191
58
  {
192
- positionals: ['users', 'get-accounts'],
59
+ positionals: ['hello'],
193
60
  options: {
194
- format: 'json',
195
- insecure: true
61
+ name: 'User',
62
+ upper: true
196
63
  }
197
64
  }
198
65
  ```
@@ -202,12 +69,11 @@ options without consuming the following positional argument:
202
69
 
203
70
  ```ts
204
71
  const argv = parseArgv([
205
- 'users',
206
- 'get-accounts',
207
- '--insecure',
208
- 'extra'
72
+ 'hello',
73
+ '--upper',
74
+ 'User'
209
75
  ], {
210
- insecure: {
76
+ upper: {
211
77
  type: 'boolean'
212
78
  }
213
79
  });
@@ -217,14 +83,14 @@ Result:
217
83
 
218
84
  ```ts
219
85
  {
220
- positionals: ['users', 'get-accounts', 'extra'],
86
+ positionals: ['hello', 'User'],
221
87
  options: {
222
- insecure: true
88
+ upper: true
223
89
  }
224
90
  }
225
91
  ```
226
92
 
227
- ### `parseOptions(schema, values)`
93
+ #### `parseOptions(schema, values)`
228
94
 
229
95
  Validates raw option values using an option schema and returns typed options.
230
96
 
@@ -232,19 +98,16 @@ Validates raw option values using an option schema and returns typed options.
232
98
  import { parseOptions } from 'icore';
233
99
 
234
100
  const options = parseOptions({
235
- format: {
101
+ name: {
236
102
  type: 'string',
237
- choices: ['json', 'table'],
238
- default: 'table'
103
+ default: 'world'
239
104
  },
240
- depth: {
241
- type: 'number',
242
- integer: true,
243
- min: 1,
244
- required: true
105
+ upper: {
106
+ type: 'boolean'
245
107
  }
246
108
  } as const, {
247
- depth: '10'
109
+ name: 'User',
110
+ upper: true
248
111
  });
249
112
  ```
250
113
 
@@ -252,12 +115,12 @@ Result:
252
115
 
253
116
  ```ts
254
117
  {
255
- format: 'table',
256
- depth: 10
118
+ name: 'User',
119
+ upper: true
257
120
  }
258
121
  ```
259
122
 
260
- ### `parseOptionsDetailed(schema, values)`
123
+ #### `parseOptionsDetailed(schema, values)`
261
124
 
262
125
  Validates raw option values and returns parsed options together with
263
126
  user-provided metadata.
@@ -266,17 +129,15 @@ user-provided metadata.
266
129
  import { parseOptionsDetailed } from 'icore';
267
130
 
268
131
  const result = parseOptionsDetailed({
269
- token: {
132
+ name: {
270
133
  type: 'string',
271
- required: true
134
+ default: 'world'
272
135
  },
273
- format: {
274
- type: 'string',
275
- choices: ['json', 'table'],
276
- default: 'table'
136
+ upper: {
137
+ type: 'boolean'
277
138
  }
278
139
  } as const, {
279
- token: 'secret'
140
+ upper: true
280
141
  });
281
142
  ```
282
143
 
@@ -285,12 +146,12 @@ Result:
285
146
  ```ts
286
147
  {
287
148
  options: {
288
- token: 'secret',
289
- format: 'table'
149
+ name: 'world',
150
+ upper: true
290
151
  },
291
152
  provided: {
292
- token: true,
293
- format: false
153
+ name: false,
154
+ upper: true
294
155
  }
295
156
  }
296
157
  ```
@@ -298,7 +159,7 @@ Result:
298
159
  `provided` is useful when a default value and an omitted option have different
299
160
  application-level meaning.
300
161
 
301
- ### `defineCommand(command)`
162
+ #### `defineCommand(command)`
302
163
 
303
164
  Defines a command while preserving its option schema types.
304
165
 
@@ -306,36 +167,25 @@ Defines a command while preserving its option schema types.
306
167
  import { defineCommand } from 'icore';
307
168
 
308
169
  const command = defineCommand({
309
- path: ['marketdata', 'get-order-book'],
170
+ path: ['hello'],
310
171
  options: {
311
- 'instrument-id': {
172
+ name: {
312
173
  type: 'string',
313
- required: true
314
- },
315
- depth: {
316
- type: 'number',
317
- integer: true,
318
- min: 1,
319
- required: true
174
+ default: 'world'
320
175
  },
321
- format: {
322
- type: 'string',
323
- choices: ['json', 'table'],
324
- default: 'table'
176
+ upper: {
177
+ type: 'boolean'
325
178
  }
326
179
  },
327
- async handle({ options, context }) {
328
- const response = await context.sdk.marketdata.getOrderBook({
329
- instrumentId: options['instrument-id'],
330
- depth: options.depth
331
- });
180
+ async handle({ options }) {
181
+ const message = `Hello, ${options.name}!`;
332
182
 
333
- return context.formatOrderBook(response, options.format);
183
+ return options.upper ? message.toUpperCase() : message;
334
184
  }
335
185
  });
336
186
  ```
337
187
 
338
- ### `defineCommandRegistry(commands)`
188
+ #### `defineCommandRegistry(commands)`
339
189
 
340
190
  Defines a command registry while preserving literal command path types.
341
191
 
@@ -343,8 +193,8 @@ Defines a command registry while preserving literal command path types.
343
193
  import { defineCommandRegistry } from 'icore';
344
194
 
345
195
  const registry = defineCommandRegistry([
346
- usersGetAccountsCommand,
347
- marketdataGetOrderBookCommand
196
+ helloCommand,
197
+ helloFormalCommand
348
198
  ] as const);
349
199
  ```
350
200
 
@@ -358,14 +208,14 @@ Result:
358
208
 
359
209
  ```ts
360
210
  [
361
- 'users get-accounts',
362
- 'marketdata get-order-book'
211
+ 'hello',
212
+ 'hello formal'
363
213
  ]
364
214
  ```
365
215
 
366
216
  Duplicate command paths are rejected.
367
217
 
368
- ### `isCommandName(registry, value)`
218
+ #### `isCommandName(registry, value)`
369
219
 
370
220
  Checks whether an unknown value is a command name registered in the registry.
371
221
 
@@ -375,7 +225,7 @@ if (isCommandName(registry, value)) {
375
225
  }
376
226
  ```
377
227
 
378
- ### `resolveCommand(registry, positionals)`
228
+ #### `resolveCommand(registry, positionals)`
379
229
 
380
230
  Resolves a command from already parsed positional arguments.
381
231
 
@@ -383,9 +233,9 @@ Resolves a command from already parsed positional arguments.
383
233
  import { resolveCommand } from 'icore';
384
234
 
385
235
  const resolved = resolveCommand(registry, [
386
- 'users',
387
- 'get-accounts',
388
- 'extra'
236
+ 'hello',
237
+ 'formal',
238
+ 'User'
389
239
  ]);
390
240
  ```
391
241
 
@@ -393,17 +243,17 @@ Result:
393
243
 
394
244
  ```ts
395
245
  {
396
- name: 'users get-accounts',
397
- path: ['users', 'get-accounts'],
398
- command: usersGetAccountsCommand,
399
- positionals: ['extra']
246
+ name: 'hello formal',
247
+ path: ['hello', 'formal'],
248
+ command: helloFormalCommand,
249
+ positionals: ['User']
400
250
  }
401
251
  ```
402
252
 
403
253
  When several command paths match, the most specific command wins. For example,
404
- `users get-accounts` is preferred over `users`.
254
+ `hello formal` is preferred over `hello`.
405
255
 
406
- ### `resolveCommandFromArgs(registry, args)`
256
+ #### `resolveCommandFromArgs(registry, args)`
407
257
 
408
258
  Resolves a command from raw CLI arguments. Each candidate command is parsed with
409
259
  its own option schema, so boolean flags do not accidentally consume command path
@@ -411,28 +261,29 @@ segments.
411
261
 
412
262
  ```ts
413
263
  const resolved = resolveCommandFromArgs(registry, [
414
- '--verbose',
415
- 'users',
416
- 'get-accounts'
264
+ 'hello',
265
+ '--name',
266
+ 'User',
267
+ '--upper'
417
268
  ]);
418
269
  ```
419
270
 
420
- ### `runCommandFromRegistry(registry, args, context)`
271
+ #### `runCommandFromRegistry(registry, args, context)`
421
272
 
422
273
  Resolves a command from a registry and runs its handler.
423
274
 
424
275
  ```ts
425
276
  const output = await runCommandFromRegistry(
426
277
  registry,
427
- ['users', 'get-accounts', '--format', 'json'],
278
+ ['hello', '--name', 'User', '--upper'],
428
279
  context
429
280
  );
430
281
  ```
431
282
 
432
- This is registry-level mechanics only. Application-specific setup, API request
433
- building, and output formatting still belong outside `icore`.
283
+ This is **registry-level mechanics only**. Application-specific setup, side
284
+ effects, and output formatting still belong outside `icore`.
434
285
 
435
- ### `mergeOptionsSchema(...schemas)`
286
+ #### `mergeOptionsSchema(...schemas)`
436
287
 
437
288
  Merges multiple option schemas while preserving literal option definition types.
438
289
  Later schemas override earlier schemas with the same option name.
@@ -440,25 +291,23 @@ Later schemas override earlier schemas with the same option name.
440
291
  ```ts
441
292
  import { mergeOptionsSchema } from 'icore';
442
293
 
443
- const sdkOptions = {
444
- token: {
294
+ const nameOptions = {
295
+ name: {
445
296
  type: 'string',
446
- required: true
297
+ default: 'world'
447
298
  }
448
299
  } as const;
449
300
 
450
- const formatOptions = {
451
- format: {
452
- type: 'string',
453
- choices: ['json', 'table'],
454
- default: 'table'
301
+ const greetingOptions = {
302
+ upper: {
303
+ type: 'boolean'
455
304
  }
456
305
  } as const;
457
306
 
458
- const options = mergeOptionsSchema(sdkOptions, formatOptions);
307
+ const options = mergeOptionsSchema(nameOptions, greetingOptions);
459
308
  ```
460
309
 
461
- ### `runCommand(command, args, context)`
310
+ #### `runCommand(command, args, context)`
462
311
 
463
312
  Parses arguments, validates options, checks command positionals, and runs the
464
313
  handler.
@@ -466,21 +315,128 @@ handler.
466
315
  ```ts
467
316
  const output = await runCommand(
468
317
  command,
469
- [
470
- 'marketdata',
471
- 'get-order-book',
472
- '--instrument-id',
473
- 'instrument-id',
474
- '--depth',
475
- '10'
476
- ],
318
+ ['hello', '--name', 'User', '--upper'],
477
319
  context
478
320
  );
479
321
  ```
480
322
 
481
- By default, extra positionals are rejected. A command can opt in to extra
323
+ **By default**, extra positionals are rejected. A command can opt in to extra
482
324
  positionals with `allowExtraPositionals: true`.
483
325
 
326
+ ### How It Works
327
+
328
+ ![yuml diagram](http://yuml.me/diagram/scruffy;dir:LR;/class/[*argv*%20{bg:gray}|External;hello%20--name%20User%20--upper]->[*matches*%20{bg:lavender}|System;parse,%20resolve,%20validate,%20infer]->[*typed%20result*%20{bg:honeydew}|Container;command=hello;%20name=User;%20upper=true]->[*your%20app*%20{bg:cornsilk}|System;business%20logic%20and%20output])
329
+
330
+
331
+ ### Example
332
+
333
+ ```ts
334
+ import { defineCommand, runCommand } from 'icore';
335
+
336
+ const exampleCommand = defineCommand({
337
+ path: ['hello'],
338
+ options: {
339
+ name: {
340
+ type: 'string',
341
+ default: 'world'
342
+ },
343
+ upper: {
344
+ type: 'boolean'
345
+ }
346
+ },
347
+ async handle({ options }) {
348
+ const message = `Hello, ${options.name}!`;
349
+
350
+ return options.upper ? message.toUpperCase() : message;
351
+ }
352
+ });
353
+
354
+ const output = await runCommand(
355
+ exampleCommand,
356
+ ['hello', '--name', 'User', '--upper'],
357
+ {}
358
+ );
359
+
360
+ console.log(output);
361
+ ```
362
+
363
+ Terminal output:
364
+
365
+ ```console
366
+ $ node cli.js hello --name User --upper
367
+ HELLO, USER!
368
+ ```
369
+
370
+ The command handler receives parsed options, user-provided option metadata,
371
+ remaining positionals, and caller provided context.
372
+
373
+ ### Option Schemas
374
+
375
+ Options are described as plain objects.
376
+
377
+ **Option names are exact.** `icore` does not normalize `camelCase` to
378
+ `kebab-case`. Use quoted object keys when your public CLI option contains
379
+ `-`.
380
+
381
+ #### `type: 'string'`
382
+
383
+ ```ts
384
+ const schema = {
385
+ name: {
386
+ type: 'string',
387
+ required: true
388
+ },
389
+ style: {
390
+ type: 'string',
391
+ choices: ['short', 'long'],
392
+ default: 'short'
393
+ }
394
+ } as const;
395
+ ```
396
+
397
+ String options reject missing required values, blank strings, boolean flag form,
398
+ and values outside `choices`.
399
+
400
+ #### `type: 'boolean'`
401
+
402
+ ```ts
403
+ const schema = {
404
+ upper: {
405
+ type: 'boolean'
406
+ }
407
+ } as const;
408
+ ```
409
+
410
+ Boolean options accept **flag form only**:
411
+
412
+ ```sh
413
+ --upper
414
+ ```
415
+
416
+ Explicit values are rejected:
417
+
418
+ ```sh
419
+ --upper true
420
+ --upper=true
421
+ ```
422
+
423
+ #### `type: 'number'`
424
+
425
+ ```ts
426
+ const schema = {
427
+ limit: {
428
+ type: 'number',
429
+ integer: true,
430
+ min: 1,
431
+ max: 1000,
432
+ default: 100
433
+ }
434
+ } as const;
435
+ ```
436
+
437
+ Number options parse decimal numeric values and can validate integer and range
438
+ constraints. Defaults are validated with the same rules as user-provided values.
439
+
484
440
  ### Type Inference
485
441
 
486
442
  Use `InferOptions` when you need the parsed option type explicitly.
@@ -489,12 +445,11 @@ Use `InferOptions` when you need the parsed option type explicitly.
489
445
  import type { InferOptions } from 'icore';
490
446
 
491
447
  const schema = {
492
- format: {
448
+ name: {
493
449
  type: 'string',
494
- choices: ['json', 'table'],
495
- default: 'table'
450
+ default: 'world'
496
451
  },
497
- dryRun: {
452
+ upper: {
498
453
  type: 'boolean'
499
454
  }
500
455
  } as const;
@@ -506,13 +461,13 @@ type Options = InferOptions<typeof schema>;
506
461
 
507
462
  ```ts
508
463
  type Options = {
509
- format: 'json' | 'table';
510
- dryRun: boolean | undefined;
464
+ name: string;
465
+ upper: boolean | undefined;
511
466
  };
512
467
  ```
513
468
 
514
- Required options and options with defaults are always present. Optional options
515
- without defaults are returned as `T | undefined`.
469
+ **Required options and options with defaults are always present.** Optional
470
+ options without defaults are returned as `T | undefined`.
516
471
 
517
472
  Use `InferProvidedOptions` when you need the option presence type explicitly.
518
473
 
@@ -530,7 +485,7 @@ Use `MergeOptionsSchemas` when you need the merged schema type explicitly.
530
485
  ```ts
531
486
  import type { MergeOptionsSchemas } from 'icore';
532
487
 
533
- type Schema = MergeOptionsSchemas<[typeof sdkOptions, typeof formatOptions]>;
488
+ type Schema = MergeOptionsSchemas<[typeof nameOptions, typeof greetingOptions]>;
534
489
  ```
535
490
 
536
491
  Use `CommandName` when you need the inferred command name type explicitly.
@@ -538,55 +493,49 @@ Use `CommandName` when you need the inferred command name type explicitly.
538
493
  ```ts
539
494
  import type { CommandName } from 'icore';
540
495
 
541
- type Name = CommandName<typeof usersGetAccountsCommand>;
496
+ type Name = CommandName<typeof helloFormalCommand>;
542
497
  ```
543
498
 
544
499
  `Name` is equivalent to:
545
500
 
546
501
  ```ts
547
- type Name = 'users get-accounts';
502
+ type Name = 'hello formal';
548
503
  ```
549
504
 
550
505
  ### Facade of arguments
551
506
 
552
507
  The table below provides examples of how to specify the syntax.
553
508
 
554
- | Syntax | Supported | Notes |
555
- |---|---:|---|
556
- | `--name value` | yes | string and number options |
557
- | `--name=value` | yes | string and number options |
558
- | `--flag` | yes | boolean options |
559
- | `--flag=true` | no | boolean options are flag-only |
560
- | `-f` | no | short aliases are not supported |
561
- | `--no-cache` | no | negative boolean flags are not supported |
562
- | repeated options | no | duplicates are rejected |
563
- | multiple values | no | arrays are not supported |
509
+ | Syntax | Supported |
510
+ |---|---:|
511
+ | `--name value` | yes |
512
+ | `--name=value` | yes |
513
+ | `--flag` | yes |
514
+ | `--flag=true` | no |
515
+ | `-f` | no |
516
+ | `--no-cache` | no |
564
517
 
565
518
  ### Error Messages
566
519
 
567
520
  `icore` throws regular `Error` objects with predictable user-facing messages.
568
- Applications should treat these messages as display text, not as a
569
- machine-readable API.
521
+ Applications should treat these messages as **display text**, not as a
522
+ **machine-readable API**.
570
523
 
571
- Examples:
524
+ Applications can catch these errors and decide how to print them. For example,
525
+ after printing `error.message`, terminal output can look like this:
572
526
 
573
- ```txt
527
+ ```console
528
+ $ node cli.js hello --unknown
574
529
  Unexpected argument '--unknown'
575
- Unexpected duplicate argument '--format'
576
- Unexpected duplicate command 'users get-accounts'
577
- Unknown command: users get-unknown
578
- Expected required argument '--token'
579
- Expected '--format' as one of: json, table
580
- Expected '--depth' as integer
581
- Expected '--depth' to be greater than or equal to 1
582
- ```
583
530
 
584
- Applications can catch these errors and decide how to print them.
531
+ $ node cli.js hello --upper=true
532
+ Expected '--upper' as boolean flag
533
+ ```
585
534
 
586
535
  ### Project Boundary
587
536
 
588
- `icore` is intended to be a small CLI mechanics module. It should not grow into a
589
- domain-specific framework for a particular SDK or API.
537
+ `icore` is intended to be a **small CLI mechanics module**. It should **not**
538
+ grow into a domain-specific framework for a particular SDK or API.
590
539
 
591
540
  Good responsibilities for `icore`:
592
541