icore 1.0.3 → 1.0.4

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/readme.md CHANGED
@@ -5,59 +5,28 @@
5
5
  [![types](https://img.shields.io/npm/types/icore.svg)](https://www.npmjs.com/package/icore)
6
6
  [![license](https://img.shields.io/npm/l/icore.svg)](LICENSE)
7
7
 
8
- Small dependency-free command line interface mechanics for [Node.js®](https://nodejs.org) applications.
8
+ Small dependency-free command line interface mechanics for Node.js applications.
9
9
 
10
- [API](#api) | [Option Schemas](#option-schemas) | [Type Inference](#type-inference)
10
+ `icore` describes CLI commands with typed option schemas, resolves command
11
+ registries, validates primitive options, and passes typed input to handlers. It
12
+ stops at CLI mechanics: your application still owns business rules, SDK calls,
13
+ process lifecycle, and output formatting.
11
14
 
12
- ### How it works?
15
+ ## How It Works
13
16
 
14
17
  ![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
18
 
16
- Use `icore` when you need more than raw argument parsing:
19
+ [API Reference](docs/api.md) | [Examples](docs/examples.md)
17
20
 
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.
21
+ ## Install
25
22
 
26
- Use [`node:util.parseArgs`](https://nodejs.org/api/util.html#utilparseargsconfig)
27
- directly when you only need low-level argument parsing.
28
-
29
- ### Installation
30
-
31
- To use `icores` in your project, run:
23
+ To use `icore` in your project, run:
32
24
 
33
25
  ```sh
34
26
  npm install icore
35
27
  ```
36
28
 
37
- ### Table of Contents
38
-
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
29
+ ## Quick Start
61
30
 
62
31
  ```ts
63
32
  import { defineCommand, runCommand } from 'icore';
@@ -89,467 +58,22 @@ const output = await runCommand(
89
58
  console.log(output);
90
59
  ```
91
60
 
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.
170
-
171
- ### API
172
-
173
- ### `parseArgv(args, schema?)`
174
-
175
- Parses raw CLI arguments into positionals and raw option values.
176
-
177
- ```ts
178
- import { parseArgv } from 'icore';
179
-
180
- const argv = parseArgv([
181
- 'users',
182
- 'get-accounts',
183
- '--format=json',
184
- '--insecure'
185
- ]);
186
- ```
187
-
188
- Result:
189
-
190
- ```ts
191
- {
192
- positionals: ['users', 'get-accounts'],
193
- options: {
194
- format: 'json',
195
- insecure: true
196
- }
197
- }
198
- ```
199
-
200
- When an option schema is provided, boolean options are parsed as flag-only
201
- options without consuming the following positional argument:
202
-
203
- ```ts
204
- const argv = parseArgv([
205
- 'users',
206
- 'get-accounts',
207
- '--insecure',
208
- 'extra'
209
- ], {
210
- insecure: {
211
- type: 'boolean'
212
- }
213
- });
214
- ```
215
-
216
- Result:
217
-
218
- ```ts
219
- {
220
- positionals: ['users', 'get-accounts', 'extra'],
221
- options: {
222
- insecure: true
223
- }
224
- }
225
- ```
226
-
227
- ### `parseOptions(schema, values)`
228
-
229
- Validates raw option values using an option schema and returns typed options.
230
-
231
- ```ts
232
- import { parseOptions } from 'icore';
233
-
234
- const options = parseOptions({
235
- format: {
236
- type: 'string',
237
- choices: ['json', 'table'],
238
- default: 'table'
239
- },
240
- depth: {
241
- type: 'number',
242
- integer: true,
243
- min: 1,
244
- required: true
245
- }
246
- } as const, {
247
- depth: '10'
248
- });
249
- ```
250
-
251
- Result:
252
-
253
- ```ts
254
- {
255
- format: 'table',
256
- depth: 10
257
- }
258
- ```
61
+ ## Why icore?
259
62
 
260
- ### `parseOptionsDetailed(schema, values)`
261
-
262
- Validates raw option values and returns parsed options together with
263
- user-provided metadata.
264
-
265
- ```ts
266
- import { parseOptionsDetailed } from 'icore';
267
-
268
- const result = parseOptionsDetailed({
269
- token: {
270
- type: 'string',
271
- required: true
272
- },
273
- format: {
274
- type: 'string',
275
- choices: ['json', 'table'],
276
- default: 'table'
277
- }
278
- } as const, {
279
- token: 'secret'
280
- });
281
- ```
282
-
283
- Result:
284
-
285
- ```ts
286
- {
287
- options: {
288
- token: 'secret',
289
- format: 'table'
290
- },
291
- provided: {
292
- token: true,
293
- format: false
294
- }
295
- }
296
- ```
297
-
298
- `provided` is useful when a default value and an omitted option have different
299
- application-level meaning.
300
-
301
- ### `defineCommand(command)`
302
-
303
- Defines a command while preserving its option schema types.
304
-
305
- ```ts
306
- import { defineCommand } from 'icore';
307
-
308
- const command = defineCommand({
309
- path: ['marketdata', 'get-order-book'],
310
- options: {
311
- 'instrument-id': {
312
- type: 'string',
313
- required: true
314
- },
315
- depth: {
316
- type: 'number',
317
- integer: true,
318
- min: 1,
319
- required: true
320
- },
321
- format: {
322
- type: 'string',
323
- choices: ['json', 'table'],
324
- default: 'table'
325
- }
326
- },
327
- async handle({ options, context }) {
328
- const response = await context.sdk.marketdata.getOrderBook({
329
- instrumentId: options['instrument-id'],
330
- depth: options.depth
331
- });
332
-
333
- return context.formatOrderBook(response, options.format);
334
- }
335
- });
336
- ```
337
-
338
- ### `defineCommandRegistry(commands)`
339
-
340
- Defines a command registry while preserving literal command path types.
341
-
342
- ```ts
343
- import { defineCommandRegistry } from 'icore';
344
-
345
- const registry = defineCommandRegistry([
346
- usersGetAccountsCommand,
347
- marketdataGetOrderBookCommand
348
- ] as const);
349
- ```
350
-
351
- The registry exposes ordered commands and derived command names:
352
-
353
- ```ts
354
- registry.commandNames;
355
- ```
356
-
357
- Result:
358
-
359
- ```ts
360
- [
361
- 'users get-accounts',
362
- 'marketdata get-order-book'
363
- ]
364
- ```
365
-
366
- Duplicate command paths are rejected.
367
-
368
- ### `isCommandName(registry, value)`
369
-
370
- Checks whether an unknown value is a command name registered in the registry.
371
-
372
- ```ts
373
- if (isCommandName(registry, value)) {
374
- // value is narrowed to the registry command name union.
375
- }
376
- ```
377
-
378
- ### `resolveCommand(registry, positionals)`
379
-
380
- Resolves a command from already parsed positional arguments.
381
-
382
- ```ts
383
- import { resolveCommand } from 'icore';
384
-
385
- const resolved = resolveCommand(registry, [
386
- 'users',
387
- 'get-accounts',
388
- 'extra'
389
- ]);
390
- ```
391
-
392
- Result:
393
-
394
- ```ts
395
- {
396
- name: 'users get-accounts',
397
- path: ['users', 'get-accounts'],
398
- command: usersGetAccountsCommand,
399
- positionals: ['extra']
400
- }
401
- ```
402
-
403
- When several command paths match, the most specific command wins. For example,
404
- `users get-accounts` is preferred over `users`.
405
-
406
- ### `resolveCommandFromArgs(registry, args)`
407
-
408
- Resolves a command from raw CLI arguments. Each candidate command is parsed with
409
- its own option schema, so boolean flags do not accidentally consume command path
410
- segments.
411
-
412
- ```ts
413
- const resolved = resolveCommandFromArgs(registry, [
414
- '--verbose',
415
- 'users',
416
- 'get-accounts'
417
- ]);
418
- ```
419
-
420
- ### `runCommandFromRegistry(registry, args, context)`
421
-
422
- Resolves a command from a registry and runs its handler.
423
-
424
- ```ts
425
- const output = await runCommandFromRegistry(
426
- registry,
427
- ['users', 'get-accounts', '--format', 'json'],
428
- context
429
- );
430
- ```
431
-
432
- This is registry-level mechanics only. Application-specific setup, API request
433
- building, and output formatting still belong outside `icore`.
434
-
435
- ### `mergeOptionsSchema(...schemas)`
436
-
437
- Merges multiple option schemas while preserving literal option definition types.
438
- Later schemas override earlier schemas with the same option name.
439
-
440
- ```ts
441
- import { mergeOptionsSchema } from 'icore';
442
-
443
- const sdkOptions = {
444
- token: {
445
- type: 'string',
446
- required: true
447
- }
448
- } as const;
449
-
450
- const formatOptions = {
451
- format: {
452
- type: 'string',
453
- choices: ['json', 'table'],
454
- default: 'table'
455
- }
456
- } as const;
457
-
458
- const options = mergeOptionsSchema(sdkOptions, formatOptions);
459
- ```
460
-
461
- ### `runCommand(command, args, context)`
462
-
463
- Parses arguments, validates options, checks command positionals, and runs the
464
- handler.
465
-
466
- ```ts
467
- const output = await runCommand(
468
- command,
469
- [
470
- 'marketdata',
471
- 'get-order-book',
472
- '--instrument-id',
473
- 'instrument-id',
474
- '--depth',
475
- '10'
476
- ],
477
- context
478
- );
479
- ```
480
-
481
- By default, extra positionals are rejected. A command can opt in to extra
482
- positionals with `allowExtraPositionals: true`.
483
-
484
- ### Type Inference
485
-
486
- Use `InferOptions` when you need the parsed option type explicitly.
487
-
488
- ```ts
489
- import type { InferOptions } from 'icore';
490
-
491
- const schema = {
492
- format: {
493
- type: 'string',
494
- choices: ['json', 'table'],
495
- default: 'table'
496
- },
497
- dryRun: {
498
- type: 'boolean'
499
- }
500
- } as const;
501
-
502
- type Options = InferOptions<typeof schema>;
503
- ```
504
-
505
- `Options` is equivalent to:
506
-
507
- ```ts
508
- type Options = {
509
- format: 'json' | 'table';
510
- dryRun: boolean | undefined;
511
- };
512
- ```
513
-
514
- Required options and options with defaults are always present. Optional options
515
- without defaults are returned as `T | undefined`.
516
-
517
- Use `InferProvidedOptions` when you need the option presence type explicitly.
518
-
519
- ```ts
520
- import type { InferProvidedOptions } from 'icore';
521
-
522
- type Provided = InferProvidedOptions<typeof schema>;
523
- ```
524
-
525
- `Provided` maps every schema option to `boolean`. `true` means the user
526
- specified that option explicitly; defaults keep the flag `false`.
527
-
528
- Use `MergeOptionsSchemas` when you need the merged schema type explicitly.
529
-
530
- ```ts
531
- import type { MergeOptionsSchemas } from 'icore';
532
-
533
- type Schema = MergeOptionsSchemas<[typeof sdkOptions, typeof formatOptions]>;
534
- ```
535
-
536
- Use `CommandName` when you need the inferred command name type explicitly.
537
-
538
- ```ts
539
- import type { CommandName } from 'icore';
540
-
541
- type Name = CommandName<typeof usersGetAccountsCommand>;
542
- ```
543
-
544
- `Name` is equivalent to:
63
+ Use `icore` when you need more than raw argument parsing:
545
64
 
546
- ```ts
547
- type Name = 'users get-accounts';
548
- ```
65
+ - typed command definitions;
66
+ - command registry resolution;
67
+ - required options;
68
+ - string choices;
69
+ - number parsing with integer, minimum, and maximum constraints;
70
+ - option presence metadata;
71
+ - typed command handler input.
549
72
 
550
- ### Facade of arguments
73
+ Use [`node:util.parseArgs`](https://nodejs.org/api/util.html#utilparseargsconfig)
74
+ directly when you only need low-level argument parsing.
551
75
 
552
- The table below provides examples of how to specify the syntax.
76
+ ## Supported Syntax
553
77
 
554
78
  | Syntax | Supported | Notes |
555
79
  |---|---:|---|
@@ -562,35 +86,17 @@ The table below provides examples of how to specify the syntax.
562
86
  | repeated options | no | duplicates are rejected |
563
87
  | multiple values | no | arrays are not supported |
564
88
 
565
- ### Error Messages
89
+ ## Documentation
566
90
 
567
- `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.
91
+ - [Design notes](docs/design.md)
92
+ - [Project policies](docs/policy/index.md)
570
93
 
571
- Examples:
572
-
573
- ```txt
574
- 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
- ```
94
+ ## Requirements
583
95
 
584
- Applications can catch these errors and decide how to print them.
96
+ - Node.js `>=16.9.0`
97
+ - TypeScript declarations are included.
98
+ - Runtime dependencies: none.
585
99
 
586
- ### Project Boundary
100
+ ## License
587
101
 
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.
590
-
591
- Good responsibilities for `icore`:
592
-
593
- - option schema evaluation;
594
- - command path checking;
595
- - common argument errors;
596
- - typed command handler input.
102
+ [MIT](LICENSE)