icore 1.0.15 → 1.0.17
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/CHANGELOG.md +220 -0
- package/dist/{argv.d.ts → argv/parser.d.ts} +3 -1
- package/dist/{argv.js → argv/parser.js} +5 -5
- package/dist/{commands.d.ts → command/mechanics.d.ts} +73 -6
- package/dist/{commands.js → command/mechanics.js} +53 -15
- package/dist/{errors.d.ts → errors/icore-error.d.ts} +2 -0
- package/dist/{errors.js → errors/icore-error.js} +2 -0
- package/dist/index.d.ts +28 -1
- package/dist/index.js +61 -17
- package/dist/options/parser.d.ts +43 -0
- package/dist/{options.js → options/parser.js} +27 -17
- package/dist/{options.d.ts → options/schema.d.ts} +16 -23
- package/dist/options/schema.js +23 -0
- package/dist/output/facade.d.ts +28 -0
- package/dist/output/facade.js +32 -0
- package/dist/output/node-writer.d.ts +17 -0
- package/dist/output/node-writer.js +25 -0
- package/dist/output/text-writer.d.ts +29 -0
- package/dist/output/text-writer.js +49 -0
- package/dist/presentation/facade.d.ts +39 -0
- package/dist/presentation/facade.js +47 -0
- package/dist/presentation/format-options.d.ts +31 -0
- package/dist/presentation/format-options.js +37 -0
- package/dist/presentation/renderers/csv.d.ts +18 -0
- package/dist/presentation/renderers/csv.js +35 -0
- package/dist/presentation/renderers/json.d.ts +13 -0
- package/dist/presentation/renderers/json.js +18 -0
- package/dist/presentation/renderers/table.d.ts +14 -0
- package/dist/presentation/renderers/table.js +29 -0
- package/dist/presentation/result-renderer.d.ts +25 -0
- package/dist/presentation/result-renderer.js +184 -0
- package/dist/presentation/view.d.ts +58 -0
- package/dist/presentation/view.js +56 -0
- package/dist/terminal/app.d.ts +70 -0
- package/dist/terminal/app.js +100 -0
- package/examples/cli-argument-syntax.md +218 -0
- package/examples/command-resolution.md +174 -0
- package/examples/custom-command-flow.md +109 -0
- package/examples/option-schemas.md +206 -0
- package/examples/output-writers.md +128 -0
- package/examples/practical-cli-patterns.md +385 -0
- package/examples/presentation-output.md +66 -0
- package/examples/presentation-primitives.md +190 -0
- package/examples/readme.md +48 -0
- package/examples/terminal-app.md +118 -0
- package/examples/two-phase-primitives.md +282 -0
- package/package.json +9 -4
- package/readme.md +278 -299
- package/dist/cli.d.ts +0 -14
- package/dist/cli.js +0 -32
package/readme.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/icore)
|
|
6
6
|
[](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 and terminal presentation mechanics for [Node.js®](https://nodejs.org) applications.
|
|
9
9
|
|
|
10
10
|
Supports a practical GNU-style option syntax:
|
|
11
11
|
|
|
@@ -24,21 +24,40 @@ npm install icore
|
|
|
24
24
|
|
|
25
25
|
## Table of Contents
|
|
26
26
|
|
|
27
|
-
- [
|
|
28
|
-
- [
|
|
29
|
-
- [`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- [`
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
- [API Reference](#api-reference)
|
|
28
|
+
- [`createTerminalApp()`](#createterminalapp)
|
|
29
|
+
- [`createCommand()`](#createcommand)
|
|
30
|
+
- [`command.define(command)`](#commanddefinecommand)
|
|
31
|
+
- [`command.registry(commands)`](#commandregistrycommands)
|
|
32
|
+
- [`command.run(command, args, context)`](#commandruncommand-args-context)
|
|
33
|
+
- [`createCommands(commands)`](#createcommandscommands)
|
|
34
|
+
- [`commands.prepare(args, options?)`](#commandsprepareargs-options)
|
|
35
|
+
- [`commands.run(prepared, context)`](#commandsrunprepared-context)
|
|
36
|
+
- [`commands.runFromArgs(args, context, options?)`](#commandsrunfromargsargs-context-options)
|
|
37
|
+
- [`createPresentation()`](#createpresentation)
|
|
38
|
+
- [`presentation.empty()`](#presentationempty)
|
|
39
|
+
- [`presentation.text(value)`](#presentationtextvalue)
|
|
40
|
+
- [`presentation.record(value)`](#presentationrecordvalue)
|
|
41
|
+
- [`presentation.records(values)`](#presentationrecordsvalues)
|
|
42
|
+
- [`presentation.table(rows)`](#presentationtablerows)
|
|
43
|
+
- [`presentation.csv(rows)`](#presentationcsvrows)
|
|
44
|
+
- [`presentation.render(result, format?)`](#presentationrenderresult-format)
|
|
45
|
+
- [`presentation.renderers.*`](#presentationrenderers)
|
|
46
|
+
- [`createOutput()`](#createoutput)
|
|
47
|
+
- [`output.write(chunk)`](#outputwritechunk)
|
|
48
|
+
- [`output.error(chunk)`](#outputerrorchunk)
|
|
49
|
+
- [`output.stdout.write(chunk)`](#outputstdoutwritechunk)
|
|
50
|
+
- [`output.stderr.write(chunk)`](#outputstderrwritechunk)
|
|
51
|
+
- [Lower-Level Mechanics](#lower-level-mechanics)
|
|
52
|
+
- [`parseArgv(args, schema?)`](#parseargv)
|
|
53
|
+
- [`parseOptionsDetailed(schema, values)`](#parseoptionsdetailed)
|
|
54
|
+
- [`resolveCommand(registry, positionals)`](#resolvecommand)
|
|
55
|
+
- [`resolveCommandFromArgs(registry, args)`](#resolvecommandfromargs)
|
|
56
|
+
- [`prepareCommandFromArgs(registry, args, options?)`](#preparecommandfromargs)
|
|
57
|
+
- [`runPreparedCommand(prepared, context)`](#runpreparedcommand)
|
|
58
|
+
- [`runCommandFromRegistry(registry, args, context, options?)`](#runcommandfromregistry)
|
|
59
|
+
- [`runCommand(command, args, context, options?)`](#runcommand)
|
|
60
|
+
- [Internal Source Layout](#internal-source-layout)
|
|
42
61
|
- [How It Works](#how-it-works)
|
|
43
62
|
- [Example](#example)
|
|
44
63
|
- [Option Schemas](#option-schemas)
|
|
@@ -47,385 +66,340 @@ npm install icore
|
|
|
47
66
|
- [Error Messages](#error-messages)
|
|
48
67
|
- [Project Boundary](#project-boundary)
|
|
49
68
|
|
|
50
|
-
## API
|
|
69
|
+
## API Reference
|
|
51
70
|
|
|
52
|
-
|
|
71
|
+
Start at `createTerminalApp()` for regular terminal applications, then move to
|
|
72
|
+
the construction methods only when the app needs explicit command,
|
|
73
|
+
presentation, or output wiring.
|
|
53
74
|
|
|
54
|
-
|
|
75
|
+
### `createTerminalApp()`
|
|
55
76
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const argv = parseArgv([
|
|
60
|
-
'hello',
|
|
61
|
-
'--name',
|
|
62
|
-
'Alice',
|
|
63
|
-
'--uppercase'
|
|
64
|
-
]);
|
|
65
|
-
```
|
|
77
|
+
`createTerminalApp()` is the top-level terminal application composition point.
|
|
78
|
+
It wires command execution, presentation rendering, and stdout/stderr delivery
|
|
79
|
+
without taking ownership of application behavior.
|
|
66
80
|
|
|
67
|
-
|
|
81
|
+
The checked TypeScript contract lives in [`src/terminal/app.ts`](src/terminal/app.ts).
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
The method returns an application object with `prepare(args)` and
|
|
84
|
+
`run(args, context)`.
|
|
85
|
+
|
|
86
|
+
Construction inputs:
|
|
87
|
+
|
|
88
|
+
- `commands` is required; build it with [`createCommand()`](#createcommand) and
|
|
89
|
+
[`command.registry(commands)`](#commandregistrycommands), or directly with
|
|
90
|
+
[`createCommands(commands)`](#createcommandscommands);
|
|
91
|
+
- `presentation` is optional; omit it to use [`createPresentation()`](#createpresentation);
|
|
92
|
+
- `output` is optional; omit it to use [`createOutput()`](#createoutput);
|
|
93
|
+
- `resolveFormat` is optional and customizes how a prepared command selects a
|
|
94
|
+
presentation format.
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
Returned app methods:
|
|
97
|
+
|
|
98
|
+
- `app.prepare(args, options?)` delegates to
|
|
99
|
+
[`commands.prepare(args, options?)`](#commandsprepareargs-options);
|
|
100
|
+
- `app.run(args, context, options?)` prepares the command, delegates command
|
|
101
|
+
execution to [`commands.run(prepared, context)`](#commandsrunprepared-context),
|
|
102
|
+
then renders and writes terminal output.
|
|
103
|
+
|
|
104
|
+
Minimal shape:
|
|
81
105
|
|
|
82
106
|
```ts
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
const app = createTerminalApp({
|
|
108
|
+
commands,
|
|
109
|
+
presentation,
|
|
110
|
+
output
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const args = process.argv.slice(2);
|
|
114
|
+
const exitCode = await app.run(args, context, {
|
|
115
|
+
strict: true
|
|
91
116
|
});
|
|
92
117
|
```
|
|
93
118
|
|
|
94
|
-
|
|
119
|
+
Command handlers keep ownership of application work. The terminal app only
|
|
120
|
+
accepts terminal-ready results: text, streaming text, presentation results, or
|
|
121
|
+
no output. Application DTO mapping, config loading, network clients, and domain
|
|
122
|
+
behavior stay in the consuming application.
|
|
95
123
|
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
positionals: ['hello', 'Alice'],
|
|
99
|
-
options: {
|
|
100
|
-
uppercase: true
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
```
|
|
124
|
+
### `createCommand()`
|
|
104
125
|
|
|
105
|
-
|
|
126
|
+
`createCommand()` returns the command mechanics entrypoint used to build
|
|
127
|
+
`commands` for `createTerminalApp()`.
|
|
106
128
|
|
|
107
|
-
|
|
129
|
+
Simplified shape:
|
|
108
130
|
|
|
109
131
|
```ts
|
|
110
|
-
|
|
132
|
+
const command = createCommand();
|
|
111
133
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
default: 'world'
|
|
116
|
-
},
|
|
117
|
-
uppercase: {
|
|
118
|
-
type: 'boolean'
|
|
119
|
-
}
|
|
120
|
-
} as const, {
|
|
121
|
-
name: 'Alice',
|
|
122
|
-
uppercase: true
|
|
123
|
-
});
|
|
134
|
+
command.define(commandDefinition);
|
|
135
|
+
command.registry(commandDefinitions);
|
|
136
|
+
command.run(commandDefinition, args, context);
|
|
124
137
|
```
|
|
125
138
|
|
|
126
|
-
|
|
139
|
+
Build `commands` before creating the terminal app:
|
|
127
140
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
```
|
|
141
|
+
- `createCommand()` creates the command mechanics entrypoint;
|
|
142
|
+
- `command.define(...)` declares one command;
|
|
143
|
+
- `command.registry([...])` returns the `commands` object required by
|
|
144
|
+
`createTerminalApp()`.
|
|
134
145
|
|
|
135
|
-
|
|
146
|
+
The command definition keeps the application-specific parts: command path,
|
|
147
|
+
option schema, and handler behavior. The terminal app only needs the resulting
|
|
148
|
+
registry object.
|
|
136
149
|
|
|
137
|
-
|
|
138
|
-
user-provided metadata.
|
|
150
|
+
#### `command.define(command)`
|
|
139
151
|
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
Declares one command and preserves its literal path and option schema types.
|
|
153
|
+
Use it when defining commands inline before adding them to a registry.
|
|
142
154
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
} as const, {
|
|
152
|
-
uppercase: true
|
|
153
|
-
});
|
|
154
|
-
```
|
|
155
|
+
#### `command.registry(commands)`
|
|
156
|
+
|
|
157
|
+
Builds the `commands` object required by `createTerminalApp()`. It keeps the
|
|
158
|
+
registered command definitions, derived command names, and command flow methods
|
|
159
|
+
together.
|
|
160
|
+
|
|
161
|
+
#### `command.run(command, args, context)`
|
|
155
162
|
|
|
156
|
-
|
|
163
|
+
Runs a single command without a registry. Use it for focused command execution,
|
|
164
|
+
small tests, or custom flows where command path resolution is not needed.
|
|
165
|
+
|
|
166
|
+
#### `createCommands(commands)`
|
|
167
|
+
|
|
168
|
+
Use `createCommands(commands)` when the application already has command
|
|
169
|
+
definitions and does not need the `createCommand()` object form.
|
|
170
|
+
|
|
171
|
+
The returned object is the same `commands` contract consumed by
|
|
172
|
+
`createTerminalApp()`: it can resolve, prepare, and run registered commands.
|
|
173
|
+
|
|
174
|
+
Simplified shape:
|
|
157
175
|
|
|
158
176
|
```ts
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
177
|
+
const commands = createCommands(commandDefinitions);
|
|
178
|
+
|
|
179
|
+
commands.names;
|
|
180
|
+
commands.registry;
|
|
181
|
+
commands.resolve(positionals);
|
|
182
|
+
commands.resolveFromArgs(args);
|
|
183
|
+
commands.prepare(args, options);
|
|
184
|
+
commands.run(prepared, context);
|
|
185
|
+
commands.runFromArgs(args, context, options);
|
|
169
186
|
```
|
|
170
187
|
|
|
171
|
-
`
|
|
172
|
-
|
|
188
|
+
Use `commands.prepare(...)` and `commands.run(...)` when application code needs
|
|
189
|
+
the same two-phase flow used by `createTerminalApp()`. Use
|
|
190
|
+
`commands.runFromArgs(...)` for custom terminal boundaries that still want the
|
|
191
|
+
registry-level command flow.
|
|
173
192
|
|
|
174
|
-
|
|
193
|
+
#### `commands.prepare(args, options?)`
|
|
175
194
|
|
|
176
|
-
|
|
195
|
+
Resolves and validates a registered command without runtime context. This is
|
|
196
|
+
the same preparation step used by `app.prepare(...)` and `app.run(...)`.
|
|
177
197
|
|
|
178
|
-
|
|
179
|
-
import { defineCommand } from 'icore';
|
|
198
|
+
#### `commands.run(prepared, context)`
|
|
180
199
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
options: {
|
|
184
|
-
name: {
|
|
185
|
-
type: 'string',
|
|
186
|
-
default: 'world'
|
|
187
|
-
},
|
|
188
|
-
uppercase: {
|
|
189
|
-
type: 'boolean'
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
async handle({ options }) {
|
|
193
|
-
const message = `Hello, ${options.name}!`;
|
|
200
|
+
Runs an already prepared command with application context. Use it after
|
|
201
|
+
`commands.prepare(...)` when the application needs a custom two-phase flow.
|
|
194
202
|
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
```
|
|
203
|
+
#### `commands.runFromArgs(args, context, options?)`
|
|
199
204
|
|
|
200
|
-
|
|
205
|
+
Resolves, prepares, and runs a registered command without the terminal
|
|
206
|
+
application boundary. Use it when a custom boundary owns presentation or output.
|
|
201
207
|
|
|
202
|
-
|
|
208
|
+
### `createPresentation()`
|
|
203
209
|
|
|
204
|
-
|
|
205
|
-
|
|
210
|
+
`createPresentation()` creates the presentation object used by
|
|
211
|
+
`createTerminalApp()` to render terminal-ready results.
|
|
206
212
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
213
|
+
Source of truth:
|
|
214
|
+
|
|
215
|
+
- [src/presentation/facade.ts](src/presentation/facade.ts) for the presentation
|
|
216
|
+
object;
|
|
217
|
+
- [src/presentation/view.ts](src/presentation/view.ts) for presentation view
|
|
218
|
+
factories;
|
|
219
|
+
- [src/presentation/result-renderer.ts](src/presentation/result-renderer.ts)
|
|
220
|
+
for rendering presentation results.
|
|
212
221
|
|
|
213
|
-
|
|
222
|
+
Simplified shape:
|
|
214
223
|
|
|
215
224
|
```ts
|
|
216
|
-
|
|
225
|
+
const presentation = createPresentation();
|
|
226
|
+
|
|
227
|
+
presentation.empty();
|
|
228
|
+
presentation.text(value);
|
|
229
|
+
presentation.record(value);
|
|
230
|
+
presentation.records(values);
|
|
231
|
+
presentation.table(rows);
|
|
232
|
+
presentation.csv(rows);
|
|
233
|
+
presentation.render(result, format);
|
|
217
234
|
```
|
|
218
235
|
|
|
219
|
-
|
|
236
|
+
The presentation object owns generic JSON, CSV, and table rendering mechanics.
|
|
237
|
+
Application code still maps domain objects to presentation-ready values.
|
|
220
238
|
|
|
221
|
-
|
|
222
|
-
[
|
|
223
|
-
'hello',
|
|
224
|
-
'hello formal'
|
|
225
|
-
]
|
|
226
|
-
```
|
|
239
|
+
#### `presentation.empty()`
|
|
227
240
|
|
|
228
|
-
|
|
241
|
+
Creates a presentation result with no terminal output.
|
|
229
242
|
|
|
230
|
-
|
|
243
|
+
#### `presentation.text(value)`
|
|
231
244
|
|
|
232
|
-
|
|
245
|
+
Wraps ready terminal text. Use it when the command already owns the final text.
|
|
233
246
|
|
|
234
|
-
|
|
235
|
-
if (isCommandName(registry, value)) {
|
|
236
|
-
// value is narrowed to the registry command name union.
|
|
237
|
-
}
|
|
238
|
-
```
|
|
247
|
+
#### `presentation.record(value)`
|
|
239
248
|
|
|
240
|
-
|
|
249
|
+
Creates a generic one-record view. The renderer can print it as table, CSV, or
|
|
250
|
+
JSON depending on the selected format.
|
|
241
251
|
|
|
242
|
-
|
|
243
|
-
prepared command union.
|
|
252
|
+
#### `presentation.records(values)`
|
|
244
253
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// prepared.payload is narrowed to the `hello` command payload.
|
|
248
|
-
}
|
|
249
|
-
```
|
|
254
|
+
Creates a generic multi-record view. Application code still decides which fields
|
|
255
|
+
belong in each record.
|
|
250
256
|
|
|
251
|
-
|
|
257
|
+
#### `presentation.table(rows)`
|
|
252
258
|
|
|
253
|
-
|
|
259
|
+
Creates an explicit table view from prepared text rows.
|
|
254
260
|
|
|
255
|
-
|
|
256
|
-
import { resolveCommand } from 'icore';
|
|
261
|
+
#### `presentation.csv(rows)`
|
|
257
262
|
|
|
258
|
-
|
|
259
|
-
'hello',
|
|
260
|
-
'formal',
|
|
261
|
-
'Alice'
|
|
262
|
-
]);
|
|
263
|
-
```
|
|
263
|
+
Creates an explicit CSV view from scalar rows.
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
#### `presentation.render(result, format?)`
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
{
|
|
269
|
-
name: 'hello formal',
|
|
270
|
-
path: ['hello', 'formal'],
|
|
271
|
-
command: helloFormalCommand,
|
|
272
|
-
positionals: ['Alice']
|
|
273
|
-
}
|
|
274
|
-
```
|
|
267
|
+
Renders a presentation result to terminal text. The default format is `table`.
|
|
275
268
|
|
|
276
|
-
|
|
277
|
-
`hello formal` is preferred over `hello`.
|
|
269
|
+
#### `presentation.renderers.*`
|
|
278
270
|
|
|
279
|
-
|
|
271
|
+
Exposes lower-level JSON, table, and CSV renderers for custom presentation
|
|
272
|
+
composition. Prefer `presentation.render(...)` for regular terminal commands.
|
|
280
273
|
|
|
281
|
-
|
|
282
|
-
its own option schema, so boolean flags do not accidentally consume command path
|
|
283
|
-
segments.
|
|
274
|
+
### `createOutput()`
|
|
284
275
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
'hello',
|
|
288
|
-
'--name',
|
|
289
|
-
'Alice',
|
|
290
|
-
'--uppercase'
|
|
291
|
-
]);
|
|
292
|
-
```
|
|
276
|
+
`createOutput()` creates the output object used by `createTerminalApp()` to
|
|
277
|
+
write rendered text.
|
|
293
278
|
|
|
294
|
-
|
|
295
|
-
`prepareCommandFromArgs(..., { strict: true })` when command path diagnostics
|
|
296
|
-
should reject options before the command path.
|
|
279
|
+
Source of truth:
|
|
297
280
|
|
|
298
|
-
|
|
281
|
+
- [src/output/facade.ts](src/output/facade.ts) for the output object;
|
|
282
|
+
- [src/output/node-writer.ts](src/output/node-writer.ts) for stdout and stderr
|
|
283
|
+
writers;
|
|
284
|
+
- [src/output/text-writer.ts](src/output/text-writer.ts) for backpressure-aware
|
|
285
|
+
text writing.
|
|
299
286
|
|
|
300
|
-
|
|
301
|
-
the command handler.
|
|
287
|
+
Simplified shape:
|
|
302
288
|
|
|
303
289
|
```ts
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
);
|
|
290
|
+
const output = createOutput();
|
|
291
|
+
|
|
292
|
+
await output.write(chunk);
|
|
293
|
+
await output.error(chunk);
|
|
294
|
+
await output.stdout.write(chunk);
|
|
295
|
+
await output.stderr.write(chunk);
|
|
311
296
|
```
|
|
312
297
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
handler:
|
|
298
|
+
The default output writes regular text to `stdout` and diagnostic text to
|
|
299
|
+
`stderr`. Pass custom sinks when tests or applications need controlled output.
|
|
316
300
|
|
|
317
|
-
|
|
318
|
-
const helloCommand = defineCommand({
|
|
319
|
-
path: ['hello'],
|
|
320
|
-
options: {
|
|
321
|
-
name: {
|
|
322
|
-
type: 'string',
|
|
323
|
-
required: true
|
|
324
|
-
}
|
|
325
|
-
},
|
|
326
|
-
prepare({ options }) {
|
|
327
|
-
return {
|
|
328
|
-
normalizedName: options.name.trim().toLowerCase()
|
|
329
|
-
};
|
|
330
|
-
},
|
|
331
|
-
handle({ payload, context }) {
|
|
332
|
-
return context.greeter.greet(payload.normalizedName);
|
|
333
|
-
}
|
|
334
|
-
});
|
|
301
|
+
#### `output.write(chunk)`
|
|
335
302
|
|
|
336
|
-
|
|
337
|
-
'hello',
|
|
338
|
-
'--name',
|
|
339
|
-
' Alice '
|
|
340
|
-
]);
|
|
303
|
+
Writes regular terminal output through stdout.
|
|
341
304
|
|
|
342
|
-
|
|
343
|
-
```
|
|
305
|
+
#### `output.error(chunk)`
|
|
344
306
|
|
|
345
|
-
|
|
346
|
-
or SDK clients should still be created outside `icore` and passed as `context`.
|
|
307
|
+
Writes diagnostic terminal output through stderr.
|
|
347
308
|
|
|
348
|
-
|
|
309
|
+
#### `output.stdout.write(chunk)`
|
|
349
310
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
Unexpected argument '--unknown'
|
|
353
|
-
```
|
|
311
|
+
Writes directly to the stdout channel. Use this only when a specific channel
|
|
312
|
+
must be passed around.
|
|
354
313
|
|
|
355
|
-
|
|
356
|
-
backward compatibility.
|
|
314
|
+
#### `output.stderr.write(chunk)`
|
|
357
315
|
|
|
358
|
-
|
|
316
|
+
Writes directly to the stderr channel. Use this only when a specific channel
|
|
317
|
+
must be passed around.
|
|
359
318
|
|
|
360
|
-
|
|
319
|
+
### Lower-Level Mechanics
|
|
361
320
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
registry,
|
|
365
|
-
['hello', '--name', 'Alice', '--uppercase'],
|
|
366
|
-
context,
|
|
367
|
-
{
|
|
368
|
-
strict: true
|
|
369
|
-
}
|
|
370
|
-
);
|
|
371
|
-
```
|
|
321
|
+
Lower-level mechanics sit behind the construction methods. They are kept out of
|
|
322
|
+
the main navigation to keep the application construction path first.
|
|
372
323
|
|
|
373
|
-
|
|
374
|
-
|
|
324
|
+
For application code, prefer `createTerminalApp()` first. Drop to this level
|
|
325
|
+
only when the terminal application flow or construction methods are too coarse.
|
|
375
326
|
|
|
376
|
-
|
|
327
|
+
Source of truth:
|
|
377
328
|
|
|
378
|
-
|
|
379
|
-
|
|
329
|
+
- [src/argv/parser.ts](src/argv/parser.ts) for raw argv parsing;
|
|
330
|
+
- [src/options/parser.ts](src/options/parser.ts) for option value validation;
|
|
331
|
+
- [src/command/mechanics.ts](src/command/mechanics.ts) for command resolution,
|
|
332
|
+
preparation, and execution.
|
|
380
333
|
|
|
381
|
-
|
|
382
|
-
import { mergeOptionsSchema } from 'icore';
|
|
334
|
+
#### Parsing And Resolution
|
|
383
335
|
|
|
384
|
-
|
|
385
|
-
name: {
|
|
386
|
-
type: 'string',
|
|
387
|
-
default: 'world'
|
|
388
|
-
}
|
|
389
|
-
} as const;
|
|
336
|
+
This group turns raw arguments or positionals into a selected command.
|
|
390
337
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
338
|
+
- <a id="parseargv"></a>`parseArgv(args, schema?)` parses raw CLI arguments into positionals and raw
|
|
339
|
+
option values;
|
|
340
|
+
- <a id="resolvecommand"></a>`resolveCommand(registry, positionals)` resolves from already parsed
|
|
341
|
+
positionals;
|
|
342
|
+
- <a id="resolvecommandfromargs"></a>`resolveCommandFromArgs(registry, args)` resolves from raw CLI arguments by
|
|
343
|
+
using registered command schemas.
|
|
396
344
|
|
|
397
|
-
|
|
398
|
-
|
|
345
|
+
Use these methods when command selection is needed without validation or
|
|
346
|
+
execution.
|
|
399
347
|
|
|
400
|
-
|
|
348
|
+
#### Preparation
|
|
401
349
|
|
|
402
|
-
|
|
403
|
-
handler.
|
|
350
|
+
This group validates command input without runtime context.
|
|
404
351
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
352
|
+
- <a id="parseoptionsdetailed"></a>`parseOptionsDetailed(schema, values)` validates raw option values and keeps
|
|
353
|
+
option presence metadata;
|
|
354
|
+
- <a id="preparecommandfromargs"></a>`prepareCommandFromArgs(registry, args, options?)` resolves and validates a
|
|
355
|
+
registered command;
|
|
356
|
+
- `options.strict` rejects options before the command path.
|
|
357
|
+
|
|
358
|
+
Use preparation when runtime resources should be created only after command
|
|
359
|
+
selection is known.
|
|
360
|
+
|
|
361
|
+
#### Execution
|
|
362
|
+
|
|
363
|
+
This group calls command handlers after command input is prepared.
|
|
415
364
|
|
|
416
|
-
|
|
417
|
-
|
|
365
|
+
- <a id="runpreparedcommand"></a>`runPreparedCommand(prepared, context)` runs an already prepared command;
|
|
366
|
+
- <a id="runcommandfromregistry"></a>`runCommandFromRegistry(registry, args, context, options?)` prepares and runs
|
|
367
|
+
a command from a registry;
|
|
368
|
+
- <a id="runcommand"></a>`runCommand(command, args, context, options?)` runs a single command without a
|
|
369
|
+
registry.
|
|
418
370
|
|
|
419
|
-
|
|
420
|
-
|
|
371
|
+
Use execution primitives for custom boundaries. Regular CLI applications should
|
|
372
|
+
usually stay at `createTerminalApp()`.
|
|
373
|
+
|
|
374
|
+
## Internal Source Layout
|
|
375
|
+
|
|
376
|
+
The public package entrypoint remains `src/index.ts`. Internal source files are
|
|
377
|
+
grouped by CLI framework responsibility:
|
|
378
|
+
|
|
379
|
+
```text
|
|
380
|
+
src/
|
|
381
|
+
argv/ raw argv token parsing
|
|
382
|
+
options/ option schemas and option value validation
|
|
383
|
+
command/ command resolution and execution mechanics
|
|
384
|
+
presentation/ terminal view models and JSON/CSV/table renderers
|
|
385
|
+
output/ stdout/stderr writer boundaries
|
|
386
|
+
terminal/ command + presentation + output app composition
|
|
387
|
+
errors/ machine-readable icore errors
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Consumers should continue importing from `icore`; deep imports are an internal
|
|
391
|
+
source layout detail.
|
|
421
392
|
|
|
422
393
|
## How It Works
|
|
423
394
|
|
|
424
395
|

|
|
425
396
|
|
|
426
|
-
|
|
427
397
|
## Example
|
|
428
398
|
|
|
399
|
+
More examples live in [examples/readme.md](examples/readme.md), including
|
|
400
|
+
option schemas, CLI argument syntax, practical CLI patterns, and lower-level
|
|
401
|
+
primitives.
|
|
402
|
+
|
|
429
403
|
```ts
|
|
430
404
|
import { defineCommand, runCommand } from 'icore';
|
|
431
405
|
|
|
@@ -725,4 +699,9 @@ Good responsibilities for `icore`:
|
|
|
725
699
|
- option schema evaluation;
|
|
726
700
|
- command path checking;
|
|
727
701
|
- common argument errors;
|
|
728
|
-
- typed command handler input
|
|
702
|
+
- typed command handler input;
|
|
703
|
+
- generic JSON, CSV, and text table rendering;
|
|
704
|
+
- stdout/stderr text writer mechanics.
|
|
705
|
+
|
|
706
|
+
Application-specific report mapping, scalar formatting, API calls, config
|
|
707
|
+
loading, and domain behavior stay outside `icore`.
|