icore 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/package.json +2 -2
- package/readme.md +100 -97
package/LICENSE
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "icore",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Declarative command line interface mechanics for Node.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"command-line",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"author": {
|
|
17
|
-
"name": "Stanislav
|
|
17
|
+
"name": "Stanislav Potemkin",
|
|
18
18
|
"email": "woodger@ya.ru"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
package/readme.md
CHANGED
|
@@ -7,7 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
Small dependency-free command line interface mechanics for [Node.js®](https://nodejs.org) applications.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Supports a practical GNU-style option syntax:
|
|
11
|
+
|
|
12
|
+
- long options: `--name value`, `--name=value`;
|
|
13
|
+
- boolean flags: `--flag`, `--flag=true`, `--flag=false`, `--no-flag`;
|
|
14
|
+
- short aliases: `-f`, `-n value`;
|
|
15
|
+
- option terminator: `--`.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
11
18
|
|
|
12
19
|
To use `icore` in your project, run:
|
|
13
20
|
|
|
@@ -15,29 +22,32 @@ To use `icore` in your project, run:
|
|
|
15
22
|
npm install icore
|
|
16
23
|
```
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
## Table of Contents
|
|
26
|
+
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [API](#api)
|
|
29
|
+
- [`parseArgv(args, schema?)`](#parseargvargs-schema)
|
|
30
|
+
- [`parseOptions(schema, values)`](#parseoptionsschema-values)
|
|
31
|
+
- [`parseOptionsDetailed(schema, values)`](#parseoptionsdetailedschema-values)
|
|
32
|
+
- [`defineCommand(command)`](#definecommandcommand)
|
|
33
|
+
- [`defineCommandRegistry(commands)`](#definecommandregistrycommands)
|
|
34
|
+
- [`isCommandName(registry, value)`](#iscommandnameregistry-value)
|
|
35
|
+
- [`resolveCommand(registry, positionals)`](#resolvecommandregistry-positionals)
|
|
36
|
+
- [`resolveCommandFromArgs(registry, args)`](#resolvecommandfromargsregistry-args)
|
|
37
|
+
- [`runCommandFromRegistry(registry, args, context)`](#runcommandfromregistryregistry-args-context)
|
|
38
|
+
- [`mergeOptionsSchema(...schemas)`](#mergeoptionsschemaschemas)
|
|
39
|
+
- [`runCommand(command, args, context)`](#runcommandcommand-args-context)
|
|
40
|
+
- [How It Works](#how-it-works)
|
|
41
|
+
- [Example](#example)
|
|
42
|
+
- [Option Schemas](#option-schemas)
|
|
43
|
+
- [Type Inference](#type-inference)
|
|
44
|
+
- [Facade of arguments](#facade-of-arguments)
|
|
45
|
+
- [Error Messages](#error-messages)
|
|
46
|
+
- [Project Boundary](#project-boundary)
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
### `parseArgv(args, schema?)`
|
|
41
51
|
|
|
42
52
|
Parses raw CLI arguments into positionals and raw option values.
|
|
43
53
|
|
|
@@ -47,8 +57,8 @@ import { parseArgv } from 'icore';
|
|
|
47
57
|
const argv = parseArgv([
|
|
48
58
|
'hello',
|
|
49
59
|
'--name',
|
|
50
|
-
'
|
|
51
|
-
'--
|
|
60
|
+
'Alice',
|
|
61
|
+
'--uppercase'
|
|
52
62
|
]);
|
|
53
63
|
```
|
|
54
64
|
|
|
@@ -58,8 +68,8 @@ Result:
|
|
|
58
68
|
{
|
|
59
69
|
positionals: ['hello'],
|
|
60
70
|
options: {
|
|
61
|
-
name: '
|
|
62
|
-
|
|
71
|
+
name: 'Alice',
|
|
72
|
+
uppercase: true
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
```
|
|
@@ -70,10 +80,10 @@ options without consuming the following positional argument:
|
|
|
70
80
|
```ts
|
|
71
81
|
const argv = parseArgv([
|
|
72
82
|
'hello',
|
|
73
|
-
'--
|
|
74
|
-
'
|
|
83
|
+
'--uppercase',
|
|
84
|
+
'Alice'
|
|
75
85
|
], {
|
|
76
|
-
|
|
86
|
+
uppercase: {
|
|
77
87
|
type: 'boolean'
|
|
78
88
|
}
|
|
79
89
|
});
|
|
@@ -83,14 +93,14 @@ Result:
|
|
|
83
93
|
|
|
84
94
|
```ts
|
|
85
95
|
{
|
|
86
|
-
positionals: ['hello', '
|
|
96
|
+
positionals: ['hello', 'Alice'],
|
|
87
97
|
options: {
|
|
88
|
-
|
|
98
|
+
uppercase: true
|
|
89
99
|
}
|
|
90
100
|
}
|
|
91
101
|
```
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
### `parseOptions(schema, values)`
|
|
94
104
|
|
|
95
105
|
Validates raw option values using an option schema and returns typed options.
|
|
96
106
|
|
|
@@ -102,12 +112,12 @@ const options = parseOptions({
|
|
|
102
112
|
type: 'string',
|
|
103
113
|
default: 'world'
|
|
104
114
|
},
|
|
105
|
-
|
|
115
|
+
uppercase: {
|
|
106
116
|
type: 'boolean'
|
|
107
117
|
}
|
|
108
118
|
} as const, {
|
|
109
|
-
name: '
|
|
110
|
-
|
|
119
|
+
name: 'Alice',
|
|
120
|
+
uppercase: true
|
|
111
121
|
});
|
|
112
122
|
```
|
|
113
123
|
|
|
@@ -115,12 +125,12 @@ Result:
|
|
|
115
125
|
|
|
116
126
|
```ts
|
|
117
127
|
{
|
|
118
|
-
name: '
|
|
119
|
-
|
|
128
|
+
name: 'Alice',
|
|
129
|
+
uppercase: true
|
|
120
130
|
}
|
|
121
131
|
```
|
|
122
132
|
|
|
123
|
-
|
|
133
|
+
### `parseOptionsDetailed(schema, values)`
|
|
124
134
|
|
|
125
135
|
Validates raw option values and returns parsed options together with
|
|
126
136
|
user-provided metadata.
|
|
@@ -133,11 +143,11 @@ const result = parseOptionsDetailed({
|
|
|
133
143
|
type: 'string',
|
|
134
144
|
default: 'world'
|
|
135
145
|
},
|
|
136
|
-
|
|
146
|
+
uppercase: {
|
|
137
147
|
type: 'boolean'
|
|
138
148
|
}
|
|
139
149
|
} as const, {
|
|
140
|
-
|
|
150
|
+
uppercase: true
|
|
141
151
|
});
|
|
142
152
|
```
|
|
143
153
|
|
|
@@ -147,11 +157,11 @@ Result:
|
|
|
147
157
|
{
|
|
148
158
|
options: {
|
|
149
159
|
name: 'world',
|
|
150
|
-
|
|
160
|
+
uppercase: true
|
|
151
161
|
},
|
|
152
162
|
provided: {
|
|
153
163
|
name: false,
|
|
154
|
-
|
|
164
|
+
uppercase: true
|
|
155
165
|
}
|
|
156
166
|
}
|
|
157
167
|
```
|
|
@@ -159,7 +169,7 @@ Result:
|
|
|
159
169
|
`provided` is useful when a default value and an omitted option have different
|
|
160
170
|
application-level meaning.
|
|
161
171
|
|
|
162
|
-
|
|
172
|
+
### `defineCommand(command)`
|
|
163
173
|
|
|
164
174
|
Defines a command while preserving its option schema types.
|
|
165
175
|
|
|
@@ -173,19 +183,19 @@ const command = defineCommand({
|
|
|
173
183
|
type: 'string',
|
|
174
184
|
default: 'world'
|
|
175
185
|
},
|
|
176
|
-
|
|
186
|
+
uppercase: {
|
|
177
187
|
type: 'boolean'
|
|
178
188
|
}
|
|
179
189
|
},
|
|
180
190
|
async handle({ options }) {
|
|
181
191
|
const message = `Hello, ${options.name}!`;
|
|
182
192
|
|
|
183
|
-
return options.
|
|
193
|
+
return options.uppercase ? message.toUpperCase() : message;
|
|
184
194
|
}
|
|
185
195
|
});
|
|
186
196
|
```
|
|
187
197
|
|
|
188
|
-
|
|
198
|
+
### `defineCommandRegistry(commands)`
|
|
189
199
|
|
|
190
200
|
Defines a command registry while preserving literal command path types.
|
|
191
201
|
|
|
@@ -215,7 +225,7 @@ Result:
|
|
|
215
225
|
|
|
216
226
|
Duplicate command paths are rejected.
|
|
217
227
|
|
|
218
|
-
|
|
228
|
+
### `isCommandName(registry, value)`
|
|
219
229
|
|
|
220
230
|
Checks whether an unknown value is a command name registered in the registry.
|
|
221
231
|
|
|
@@ -225,7 +235,7 @@ if (isCommandName(registry, value)) {
|
|
|
225
235
|
}
|
|
226
236
|
```
|
|
227
237
|
|
|
228
|
-
|
|
238
|
+
### `resolveCommand(registry, positionals)`
|
|
229
239
|
|
|
230
240
|
Resolves a command from already parsed positional arguments.
|
|
231
241
|
|
|
@@ -235,7 +245,7 @@ import { resolveCommand } from 'icore';
|
|
|
235
245
|
const resolved = resolveCommand(registry, [
|
|
236
246
|
'hello',
|
|
237
247
|
'formal',
|
|
238
|
-
'
|
|
248
|
+
'Alice'
|
|
239
249
|
]);
|
|
240
250
|
```
|
|
241
251
|
|
|
@@ -246,14 +256,14 @@ Result:
|
|
|
246
256
|
name: 'hello formal',
|
|
247
257
|
path: ['hello', 'formal'],
|
|
248
258
|
command: helloFormalCommand,
|
|
249
|
-
positionals: ['
|
|
259
|
+
positionals: ['Alice']
|
|
250
260
|
}
|
|
251
261
|
```
|
|
252
262
|
|
|
253
263
|
When several command paths match, the most specific command wins. For example,
|
|
254
264
|
`hello formal` is preferred over `hello`.
|
|
255
265
|
|
|
256
|
-
|
|
266
|
+
### `resolveCommandFromArgs(registry, args)`
|
|
257
267
|
|
|
258
268
|
Resolves a command from raw CLI arguments. Each candidate command is parsed with
|
|
259
269
|
its own option schema, so boolean flags do not accidentally consume command path
|
|
@@ -263,19 +273,19 @@ segments.
|
|
|
263
273
|
const resolved = resolveCommandFromArgs(registry, [
|
|
264
274
|
'hello',
|
|
265
275
|
'--name',
|
|
266
|
-
'
|
|
267
|
-
'--
|
|
276
|
+
'Alice',
|
|
277
|
+
'--uppercase'
|
|
268
278
|
]);
|
|
269
279
|
```
|
|
270
280
|
|
|
271
|
-
|
|
281
|
+
### `runCommandFromRegistry(registry, args, context)`
|
|
272
282
|
|
|
273
283
|
Resolves a command from a registry and runs its handler.
|
|
274
284
|
|
|
275
285
|
```ts
|
|
276
286
|
const output = await runCommandFromRegistry(
|
|
277
287
|
registry,
|
|
278
|
-
['hello', '--name', '
|
|
288
|
+
['hello', '--name', 'Alice', '--uppercase'],
|
|
279
289
|
context
|
|
280
290
|
);
|
|
281
291
|
```
|
|
@@ -283,7 +293,7 @@ const output = await runCommandFromRegistry(
|
|
|
283
293
|
This is **registry-level mechanics only**. Application-specific setup, side
|
|
284
294
|
effects, and output formatting still belong outside `icore`.
|
|
285
295
|
|
|
286
|
-
|
|
296
|
+
### `mergeOptionsSchema(...schemas)`
|
|
287
297
|
|
|
288
298
|
Merges multiple option schemas while preserving literal option definition types.
|
|
289
299
|
Later schemas override earlier schemas with the same option name.
|
|
@@ -299,7 +309,7 @@ const nameOptions = {
|
|
|
299
309
|
} as const;
|
|
300
310
|
|
|
301
311
|
const greetingOptions = {
|
|
302
|
-
|
|
312
|
+
uppercase: {
|
|
303
313
|
type: 'boolean'
|
|
304
314
|
}
|
|
305
315
|
} as const;
|
|
@@ -307,7 +317,7 @@ const greetingOptions = {
|
|
|
307
317
|
const options = mergeOptionsSchema(nameOptions, greetingOptions);
|
|
308
318
|
```
|
|
309
319
|
|
|
310
|
-
|
|
320
|
+
### `runCommand(command, args, context)`
|
|
311
321
|
|
|
312
322
|
Parses arguments, validates options, checks command positionals, and runs the
|
|
313
323
|
handler.
|
|
@@ -315,7 +325,7 @@ handler.
|
|
|
315
325
|
```ts
|
|
316
326
|
const output = await runCommand(
|
|
317
327
|
command,
|
|
318
|
-
['hello', '--name', '
|
|
328
|
+
['hello', '--name', 'Alice', '--uppercase'],
|
|
319
329
|
context
|
|
320
330
|
);
|
|
321
331
|
```
|
|
@@ -323,12 +333,12 @@ const output = await runCommand(
|
|
|
323
333
|
**By default**, extra positionals are rejected. A command can opt in to extra
|
|
324
334
|
positionals with `allowExtraPositionals: true`.
|
|
325
335
|
|
|
326
|
-
|
|
336
|
+
## How It Works
|
|
327
337
|
|
|
328
|
-

|
|
329
339
|
|
|
330
340
|
|
|
331
|
-
|
|
341
|
+
## Example
|
|
332
342
|
|
|
333
343
|
```ts
|
|
334
344
|
import { defineCommand, runCommand } from 'icore';
|
|
@@ -340,20 +350,20 @@ const exampleCommand = defineCommand({
|
|
|
340
350
|
type: 'string',
|
|
341
351
|
default: 'world'
|
|
342
352
|
},
|
|
343
|
-
|
|
353
|
+
uppercase: {
|
|
344
354
|
type: 'boolean'
|
|
345
355
|
}
|
|
346
356
|
},
|
|
347
357
|
async handle({ options }) {
|
|
348
358
|
const message = `Hello, ${options.name}!`;
|
|
349
359
|
|
|
350
|
-
return options.
|
|
360
|
+
return options.uppercase ? message.toUpperCase() : message;
|
|
351
361
|
}
|
|
352
362
|
});
|
|
353
363
|
|
|
354
364
|
const output = await runCommand(
|
|
355
365
|
exampleCommand,
|
|
356
|
-
['hello', '--name', '
|
|
366
|
+
['hello', '--name', 'Alice', '--uppercase'],
|
|
357
367
|
{}
|
|
358
368
|
);
|
|
359
369
|
|
|
@@ -363,14 +373,14 @@ console.log(output);
|
|
|
363
373
|
Terminal output:
|
|
364
374
|
|
|
365
375
|
```console
|
|
366
|
-
$ node cli.js hello --name
|
|
367
|
-
HELLO,
|
|
376
|
+
$ node cli.js hello --name Alice --uppercase
|
|
377
|
+
HELLO, ALICE!
|
|
368
378
|
```
|
|
369
379
|
|
|
370
380
|
The command handler receives parsed options, user-provided option metadata,
|
|
371
381
|
remaining positionals, and caller provided context.
|
|
372
382
|
|
|
373
|
-
|
|
383
|
+
## Option Schemas
|
|
374
384
|
|
|
375
385
|
Options are described as plain objects.
|
|
376
386
|
|
|
@@ -386,7 +396,7 @@ const schema = {
|
|
|
386
396
|
type: 'string',
|
|
387
397
|
alias: 'n'
|
|
388
398
|
},
|
|
389
|
-
|
|
399
|
+
uppercase: {
|
|
390
400
|
type: 'boolean',
|
|
391
401
|
alias: 'u'
|
|
392
402
|
}
|
|
@@ -396,7 +406,7 @@ const schema = {
|
|
|
396
406
|
Aliases must be a single ASCII letter and unique within the schema. Parsed
|
|
397
407
|
values are always returned by long option name.
|
|
398
408
|
|
|
399
|
-
|
|
409
|
+
### `type: 'string'`
|
|
400
410
|
|
|
401
411
|
```ts
|
|
402
412
|
const schema = {
|
|
@@ -415,11 +425,11 @@ const schema = {
|
|
|
415
425
|
String options reject missing required values, blank strings such as `--name=`,
|
|
416
426
|
boolean flag form, and values outside `choices`.
|
|
417
427
|
|
|
418
|
-
|
|
428
|
+
### `type: 'boolean'`
|
|
419
429
|
|
|
420
430
|
```ts
|
|
421
431
|
const schema = {
|
|
422
|
-
|
|
432
|
+
uppercase: {
|
|
423
433
|
type: 'boolean'
|
|
424
434
|
}
|
|
425
435
|
} as const;
|
|
@@ -429,23 +439,23 @@ Boolean options accept **flag form**, explicit `true` / `false` values, and
|
|
|
429
439
|
schema-known negation:
|
|
430
440
|
|
|
431
441
|
```sh
|
|
432
|
-
--
|
|
433
|
-
--
|
|
434
|
-
--
|
|
435
|
-
--no-
|
|
442
|
+
--uppercase
|
|
443
|
+
--uppercase=true
|
|
444
|
+
--uppercase=false
|
|
445
|
+
--no-uppercase
|
|
436
446
|
```
|
|
437
447
|
|
|
438
448
|
Invalid explicit values are rejected:
|
|
439
449
|
|
|
440
450
|
```sh
|
|
441
|
-
--
|
|
442
|
-
--
|
|
451
|
+
--uppercase=yes
|
|
452
|
+
--uppercase=
|
|
443
453
|
```
|
|
444
454
|
|
|
445
|
-
`--
|
|
455
|
+
`--uppercase false` keeps `--uppercase` as `true` and leaves `false` as a positional
|
|
446
456
|
argument.
|
|
447
457
|
|
|
448
|
-
|
|
458
|
+
### `type: 'number'`
|
|
449
459
|
|
|
450
460
|
```ts
|
|
451
461
|
const schema = {
|
|
@@ -462,7 +472,7 @@ const schema = {
|
|
|
462
472
|
Number options parse decimal numeric values and can validate integer and range
|
|
463
473
|
constraints. Defaults are validated with the same rules as user-provided values.
|
|
464
474
|
|
|
465
|
-
|
|
475
|
+
## Type Inference
|
|
466
476
|
|
|
467
477
|
Use `InferOptions` when you need the parsed option type explicitly.
|
|
468
478
|
|
|
@@ -474,7 +484,7 @@ const schema = {
|
|
|
474
484
|
type: 'string',
|
|
475
485
|
default: 'world'
|
|
476
486
|
},
|
|
477
|
-
|
|
487
|
+
uppercase: {
|
|
478
488
|
type: 'boolean'
|
|
479
489
|
}
|
|
480
490
|
} as const;
|
|
@@ -487,7 +497,7 @@ type Options = InferOptions<typeof schema>;
|
|
|
487
497
|
```ts
|
|
488
498
|
type Options = {
|
|
489
499
|
name: string;
|
|
490
|
-
|
|
500
|
+
uppercase: boolean | undefined;
|
|
491
501
|
};
|
|
492
502
|
```
|
|
493
503
|
|
|
@@ -527,14 +537,7 @@ type Name = CommandName<typeof helloFormalCommand>;
|
|
|
527
537
|
type Name = 'hello formal';
|
|
528
538
|
```
|
|
529
539
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
Supports a practical GNU-style option syntax:
|
|
533
|
-
|
|
534
|
-
- long options: `--name value`, `--name=value`;
|
|
535
|
-
- boolean flags: `--flag`, `--flag=true`, `--flag=false`, `--no-flag`;
|
|
536
|
-
- short aliases: `-f`, `-n value`;
|
|
537
|
-
- option terminator: `--`.
|
|
540
|
+
## Facade of arguments
|
|
538
541
|
|
|
539
542
|
Use `--` to stop option parsing. The terminator itself is not included in
|
|
540
543
|
positionals; every following token is treated as positional, even when it starts
|
|
@@ -552,7 +555,7 @@ Negated syntax such as `--no-cache` is interpreted as `cache: false` when
|
|
|
552
555
|
`cache` is a known boolean option. Unknown negated options and negation for
|
|
553
556
|
string or number options are rejected.
|
|
554
557
|
|
|
555
|
-
|
|
558
|
+
## Error Messages
|
|
556
559
|
|
|
557
560
|
`icore` throws regular `Error` objects with predictable user-facing messages.
|
|
558
561
|
Applications should treat these messages as **display text**, not as a
|
|
@@ -565,14 +568,14 @@ after printing `error.message`, terminal output can look like this:
|
|
|
565
568
|
$ node cli.js hello --unknown
|
|
566
569
|
Unexpected argument '--unknown'
|
|
567
570
|
|
|
568
|
-
$ node cli.js hello --
|
|
569
|
-
Expected '--
|
|
571
|
+
$ node cli.js hello --uppercase=yes
|
|
572
|
+
Expected '--uppercase' as boolean flag
|
|
570
573
|
|
|
571
574
|
$ node cli.js hello --name=
|
|
572
575
|
Expected '--name' as string
|
|
573
576
|
```
|
|
574
577
|
|
|
575
|
-
|
|
578
|
+
## Project Boundary
|
|
576
579
|
|
|
577
580
|
`icore` is intended to be a **small CLI mechanics module**. It should **not**
|
|
578
581
|
grow into a domain-specific framework for a particular SDK or API.
|