icore 1.0.14 → 1.0.16
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 +17 -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} +79 -6
- package/dist/{commands.js → command/mechanics.js} +60 -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 -16
- 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 +51 -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 -3
- package/readme.md +281 -290
- package/dist/cli.d.ts +0 -14
- package/dist/cli.js +0 -31
package/CHANGELOG.md
CHANGED
|
@@ -13,16 +13,33 @@ conservative.
|
|
|
13
13
|
|
|
14
14
|
### Added
|
|
15
15
|
|
|
16
|
+
- Added `createCommand` as a semantic command mechanics facade.
|
|
17
|
+
- Added `createCommands` as a high-level command mechanics facade.
|
|
18
|
+
- Added `createPresentation` and `PresentationResult` for terminal view-model rendering.
|
|
19
|
+
- Added semantic `Presentation` view factory methods such as `presentation.records(...)`.
|
|
20
|
+
- Added `createOutput` as a stdout/stderr output boundary facade.
|
|
21
|
+
- Added semantic `Output.write()` and `Output.error()` methods.
|
|
22
|
+
- Added `createTerminalApp` to compose command, presentation, and output mechanics.
|
|
23
|
+
- Added generic terminal presentation renderers for JSON, CSV, and text tables.
|
|
24
|
+
- Added reusable stdout/stderr text writers with backpressure handling.
|
|
25
|
+
- Added shared `presentationFormatOptions` for common `--format` command options.
|
|
16
26
|
- Added machine-readable `IcoreError` codes and details.
|
|
27
|
+
- Added `parseOptionsSubsetDetailed` for validating known option subsets while preserving unknown raw options.
|
|
17
28
|
- Added opt-in `strict: true` command resolution for rejecting options before command paths.
|
|
18
29
|
- Added `strict: true` support to direct `runCommand` execution.
|
|
19
30
|
- Added typed prepared command `payload` returned from `prepare` and passed to `handle`.
|
|
20
31
|
- Added public `CommandPayload`, `CommandContext`, and `CommandResult` helper types.
|
|
32
|
+
- Added `isPreparedCommandName` for narrowing prepared command unions by name.
|
|
21
33
|
- Added this changelog.
|
|
22
34
|
|
|
23
35
|
### Changed
|
|
24
36
|
|
|
25
37
|
- Changed `PreparedCommand` typing to preserve payload correlation when narrowing registry unions by command name.
|
|
38
|
+
- Changed output writers to await promise-returning custom sinks.
|
|
39
|
+
- Reorganized internal source files by CLI framework responsibility without changing the root public API.
|
|
40
|
+
- Split option schema contracts from option value parsing internals.
|
|
41
|
+
- Changed npm package contents to include `CHANGELOG.md`.
|
|
42
|
+
- Removed the redundant `cli` barrel so `index` is the only package entrypoint.
|
|
26
43
|
|
|
27
44
|
### Removed
|
|
28
45
|
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
*
|
|
10
10
|
* This file must not contain typed option validation or command resolution.
|
|
11
11
|
*/
|
|
12
|
-
import type { OptionsSchema, RawOptionValue } from '
|
|
12
|
+
import type { OptionsSchema, RawOptionValue } from '../options/schema';
|
|
13
13
|
/**
|
|
14
14
|
* Parsed CLI arguments split into positional command path segments and raw
|
|
15
15
|
* named options.
|
|
16
16
|
*/
|
|
17
17
|
export type ParsedArgv = {
|
|
18
|
+
/** Non-option tokens. */
|
|
18
19
|
positionals: string[];
|
|
20
|
+
/** Unvalidated option values. */
|
|
19
21
|
options: Record<string, RawOptionValue>;
|
|
20
22
|
};
|
|
21
23
|
/**
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.parseArgv = parseArgv;
|
|
15
|
-
const
|
|
15
|
+
const icore_error_1 = require("../errors/icore-error");
|
|
16
16
|
/**
|
|
17
17
|
* Parses raw CLI arguments into positionals and raw long-option values.
|
|
18
18
|
*/
|
|
@@ -140,24 +140,24 @@ function isShortOptionToken(arg) {
|
|
|
140
140
|
return arg.length === 2 && arg.startsWith('-') && !arg.startsWith('--');
|
|
141
141
|
}
|
|
142
142
|
function createDuplicateArgumentError(name) {
|
|
143
|
-
return new
|
|
143
|
+
return new icore_error_1.IcoreError('DUPLICATE_ARGUMENT', `Unexpected duplicate argument '--${name}'`, {
|
|
144
144
|
argument: `--${name}`,
|
|
145
145
|
option: name
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
function createUnexpectedArgumentError(argument) {
|
|
149
|
-
return new
|
|
149
|
+
return new icore_error_1.IcoreError('UNEXPECTED_ARGUMENT', `Unexpected argument '${argument}'`, {
|
|
150
150
|
argument
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
153
|
function createDuplicateAliasError(alias) {
|
|
154
|
-
return new
|
|
154
|
+
return new icore_error_1.IcoreError('DUPLICATE_ALIAS', `Unexpected duplicate alias '-${alias}'`, {
|
|
155
155
|
alias,
|
|
156
156
|
argument: `-${alias}`
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
159
|
function createInvalidOptionAliasError(name, alias) {
|
|
160
|
-
return new
|
|
160
|
+
return new icore_error_1.IcoreError('INVALID_OPTION_ALIAS', `Expected alias for '--${name}' as single ASCII letter`, {
|
|
161
161
|
argument: `--${name}`,
|
|
162
162
|
option: name,
|
|
163
163
|
alias
|
|
@@ -10,26 +10,32 @@
|
|
|
10
10
|
* This file must not contain raw token parsing, option schema validation,
|
|
11
11
|
* domain behavior, SDK calls, or output formatting.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import type { InferOptions, InferProvidedOptions, OptionsSchema } from '../options/schema';
|
|
14
14
|
/**
|
|
15
15
|
* Input produced after command path and option validation, before runtime
|
|
16
16
|
* context is attached.
|
|
17
17
|
*/
|
|
18
18
|
export type PreparedCommandInput<TSchema extends OptionsSchema> = {
|
|
19
|
+
/** Parsed option values. */
|
|
19
20
|
options: InferOptions<TSchema>;
|
|
21
|
+
/** Explicit option presence metadata. */
|
|
20
22
|
provided: InferProvidedOptions<TSchema>;
|
|
23
|
+
/** Positionals after the command path. */
|
|
21
24
|
positionals: string[];
|
|
22
25
|
};
|
|
23
26
|
/**
|
|
24
27
|
* Input passed to a command handler after command path and option validation.
|
|
25
28
|
*/
|
|
26
29
|
export type CommandInput<TSchema extends OptionsSchema, TContext, TPayload = void> = PreparedCommandInput<TSchema> & {
|
|
30
|
+
/** Application runtime context. */
|
|
27
31
|
context: TContext;
|
|
28
32
|
} & ([
|
|
29
33
|
TPayload
|
|
30
34
|
] extends [void] ? {
|
|
35
|
+
/** Optional prepared payload. */
|
|
31
36
|
payload?: TPayload;
|
|
32
37
|
} : {
|
|
38
|
+
/** Prepared payload. */
|
|
33
39
|
payload: TPayload;
|
|
34
40
|
});
|
|
35
41
|
/**
|
|
@@ -40,11 +46,17 @@ export type CommandInput<TSchema extends OptionsSchema, TContext, TPayload = voi
|
|
|
40
46
|
* formatting.
|
|
41
47
|
*/
|
|
42
48
|
export type CommandDefinition<TSchema extends OptionsSchema, TContext, TResult, TPath extends readonly [string, ...string[]] = readonly [string, ...string[]], TMetadata = unknown, TPayload = void> = {
|
|
49
|
+
/** Command path segments. */
|
|
43
50
|
path: TPath;
|
|
51
|
+
/** Command option schema. */
|
|
44
52
|
options: TSchema;
|
|
53
|
+
/** Caller-owned static metadata. */
|
|
45
54
|
metadata?: TMetadata;
|
|
55
|
+
/** Allows extra positionals. */
|
|
46
56
|
allowExtraPositionals?: boolean;
|
|
57
|
+
/** Prepares payload before runtime context. */
|
|
47
58
|
prepare?(input: PreparedCommandInput<TSchema>): TPayload | Promise<TPayload>;
|
|
59
|
+
/** Runs application command behavior. */
|
|
48
60
|
handle(input: CommandInput<TSchema, TContext, TPayload>): TResult | Promise<TResult>;
|
|
49
61
|
};
|
|
50
62
|
type AnyCommandInput = PreparedCommandInput<OptionsSchema> & {
|
|
@@ -78,9 +90,18 @@ type CommandDefinitionParts<TCommand extends AnyCommandDefinition> = TCommand ex
|
|
|
78
90
|
metadata: TCommand['metadata'];
|
|
79
91
|
payload: unknown;
|
|
80
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Infers the runtime context type required by a command.
|
|
95
|
+
*/
|
|
81
96
|
export type CommandContext<TCommand extends AnyCommandDefinition> = CommandDefinitionParts<TCommand>['context'];
|
|
97
|
+
/**
|
|
98
|
+
* Infers the awaited command handler result type.
|
|
99
|
+
*/
|
|
82
100
|
export type CommandResult<TCommand extends AnyCommandDefinition> = Awaited<CommandDefinitionParts<TCommand>['result']>;
|
|
83
101
|
type CommandSchema<TCommand extends AnyCommandDefinition> = CommandDefinitionParts<TCommand>['schema'];
|
|
102
|
+
/**
|
|
103
|
+
* Infers the payload type produced by a command prepare hook.
|
|
104
|
+
*/
|
|
84
105
|
export type CommandPayload<TCommand extends AnyCommandDefinition> = CommandDefinitionParts<TCommand>['payload'];
|
|
85
106
|
/**
|
|
86
107
|
* Infers the public command name from a command path.
|
|
@@ -90,39 +111,77 @@ export type CommandName<TCommand extends AnyCommandDefinition> = CommandPathName
|
|
|
90
111
|
* Declarative command registry used to resolve command paths.
|
|
91
112
|
*/
|
|
92
113
|
export type CommandRegistry<TCommands extends readonly AnyCommandDefinition[]> = {
|
|
114
|
+
/** Registered command definitions. */
|
|
93
115
|
commands: TCommands;
|
|
116
|
+
/** Derived public command names. */
|
|
94
117
|
commandNames: readonly CommandName<TCommands[number]>[];
|
|
95
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* High-level command mechanics facade for terminal applications.
|
|
121
|
+
*/
|
|
122
|
+
export type Commands<TCommands extends readonly AnyCommandDefinition[]> = {
|
|
123
|
+
/** Original command definitions. */
|
|
124
|
+
definitions: TCommands;
|
|
125
|
+
/** Public command names. */
|
|
126
|
+
names: readonly CommandName<TCommands[number]>[];
|
|
127
|
+
/** Lower-level command registry. */
|
|
128
|
+
registry: CommandRegistry<TCommands>;
|
|
129
|
+
/** Resolves from positionals. */
|
|
130
|
+
resolve(positionals: readonly string[]): ResolvedCommand<TCommands[number]>;
|
|
131
|
+
/** Resolves from raw argv. */
|
|
132
|
+
resolveFromArgs(args: readonly string[]): ResolvedCommand<TCommands[number]>;
|
|
133
|
+
/** Prepares without runtime context. */
|
|
134
|
+
prepare(args: readonly string[], options?: CommandResolutionOptions): Promise<PreparedCommand<TCommands[number]>>;
|
|
135
|
+
/** Runs a prepared command. */
|
|
136
|
+
run(prepared: PreparedCommand<TCommands[number]>, context: CommandContext<TCommands[number]>): Promise<CommandResult<TCommands[number]>>;
|
|
137
|
+
/** Resolves, prepares, and runs. */
|
|
138
|
+
runFromArgs(args: readonly string[], context: CommandContext<TCommands[number]>, options?: CommandResolutionOptions): Promise<CommandResult<TCommands[number]>>;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Semantic facade for command mechanics.
|
|
142
|
+
*/
|
|
143
|
+
export type Command = {
|
|
144
|
+
define: typeof defineCommand;
|
|
145
|
+
registry: typeof createCommands;
|
|
146
|
+
run: typeof runCommand;
|
|
147
|
+
};
|
|
96
148
|
/**
|
|
97
149
|
* Options for command resolution from raw CLI arguments.
|
|
98
150
|
*/
|
|
99
151
|
export type CommandResolutionOptions = {
|
|
100
|
-
/**
|
|
101
|
-
* When enabled, command path must appear before options. Options before the
|
|
102
|
-
* command path are rejected instead of participating in legacy candidate
|
|
103
|
-
* schema parsing.
|
|
104
|
-
*/
|
|
152
|
+
/** Rejects options before command path. */
|
|
105
153
|
strict?: boolean;
|
|
106
154
|
};
|
|
107
155
|
/**
|
|
108
156
|
* Result of resolving a command from user positionals.
|
|
109
157
|
*/
|
|
110
158
|
export type ResolvedCommand<TCommand extends AnyCommandDefinition> = {
|
|
159
|
+
/** Space-joined command name. */
|
|
111
160
|
name: CommandName<TCommand>;
|
|
161
|
+
/** Literal command path. */
|
|
112
162
|
path: TCommand['path'];
|
|
163
|
+
/** Selected command definition. */
|
|
113
164
|
command: TCommand;
|
|
165
|
+
/** Positionals after the command path. */
|
|
114
166
|
positionals: string[];
|
|
115
167
|
};
|
|
116
168
|
/**
|
|
117
169
|
* Command resolved and validated without runtime context.
|
|
118
170
|
*/
|
|
119
171
|
export type PreparedCommand<TCommand extends AnyCommandDefinition> = TCommand extends AnyCommandDefinition ? {
|
|
172
|
+
/** Space-joined command name. */
|
|
120
173
|
name: CommandName<TCommand>;
|
|
174
|
+
/** Literal command path. */
|
|
121
175
|
path: TCommand['path'];
|
|
176
|
+
/** Selected command definition. */
|
|
122
177
|
command: TCommand;
|
|
178
|
+
/** Parsed option values. */
|
|
123
179
|
options: InferOptions<CommandSchema<TCommand>>;
|
|
180
|
+
/** Explicit option presence metadata. */
|
|
124
181
|
provided: InferProvidedOptions<CommandSchema<TCommand>>;
|
|
182
|
+
/** Positionals after the command path. */
|
|
125
183
|
positionals: string[];
|
|
184
|
+
/** Prepared payload. */
|
|
126
185
|
payload: CommandPayload<TCommand>;
|
|
127
186
|
} : never;
|
|
128
187
|
/**
|
|
@@ -133,10 +192,24 @@ export declare function defineCommand<const TSchema extends OptionsSchema, const
|
|
|
133
192
|
* Defines a command registry while preserving literal command path types.
|
|
134
193
|
*/
|
|
135
194
|
export declare function defineCommandRegistry<const TCommands extends readonly AnyCommandDefinition[]>(commands: TCommands): CommandRegistry<TCommands>;
|
|
195
|
+
/**
|
|
196
|
+
* Creates a command mechanics facade from declarative command definitions.
|
|
197
|
+
*/
|
|
198
|
+
export declare function createCommands<const TCommands extends readonly AnyCommandDefinition[]>(definitions: TCommands): Commands<TCommands>;
|
|
199
|
+
/**
|
|
200
|
+
* Creates a semantic command mechanics facade.
|
|
201
|
+
*/
|
|
202
|
+
export declare function createCommand(): Command;
|
|
136
203
|
/**
|
|
137
204
|
* Checks whether a value is a command name registered in the given registry.
|
|
138
205
|
*/
|
|
139
206
|
export declare function isCommandName<const TCommands extends readonly AnyCommandDefinition[]>(registry: CommandRegistry<TCommands>, value: unknown): value is CommandName<TCommands[number]>;
|
|
207
|
+
/**
|
|
208
|
+
* Checks whether a prepared command has the given command name.
|
|
209
|
+
*/
|
|
210
|
+
export declare function isPreparedCommandName<TPrepared extends PreparedCommand<AnyCommandDefinition>, TName extends TPrepared['name']>(prepared: TPrepared, name: TName): prepared is Extract<TPrepared, {
|
|
211
|
+
name: TName;
|
|
212
|
+
}>;
|
|
140
213
|
/**
|
|
141
214
|
* Resolves a command from already parsed positional arguments.
|
|
142
215
|
*/
|
|
@@ -14,16 +14,19 @@
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.defineCommand = defineCommand;
|
|
16
16
|
exports.defineCommandRegistry = defineCommandRegistry;
|
|
17
|
+
exports.createCommands = createCommands;
|
|
18
|
+
exports.createCommand = createCommand;
|
|
17
19
|
exports.isCommandName = isCommandName;
|
|
20
|
+
exports.isPreparedCommandName = isPreparedCommandName;
|
|
18
21
|
exports.resolveCommand = resolveCommand;
|
|
19
22
|
exports.resolveCommandFromArgs = resolveCommandFromArgs;
|
|
20
23
|
exports.prepareCommandFromArgs = prepareCommandFromArgs;
|
|
21
24
|
exports.runPreparedCommand = runPreparedCommand;
|
|
22
25
|
exports.runCommandFromRegistry = runCommandFromRegistry;
|
|
23
26
|
exports.runCommand = runCommand;
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
+
const parser_1 = require("../argv/parser");
|
|
28
|
+
const icore_error_1 = require("../errors/icore-error");
|
|
29
|
+
const parser_2 = require("../options/parser");
|
|
27
30
|
/**
|
|
28
31
|
* Defines a command while preserving literal option schema types.
|
|
29
32
|
*/
|
|
@@ -41,6 +44,42 @@ function defineCommandRegistry(commands) {
|
|
|
41
44
|
commandNames: commandNames
|
|
42
45
|
};
|
|
43
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates a command mechanics facade from declarative command definitions.
|
|
49
|
+
*/
|
|
50
|
+
function createCommands(definitions) {
|
|
51
|
+
const registry = defineCommandRegistry(definitions);
|
|
52
|
+
return {
|
|
53
|
+
definitions,
|
|
54
|
+
names: registry.commandNames,
|
|
55
|
+
registry,
|
|
56
|
+
resolve(positionals) {
|
|
57
|
+
return resolveCommand(registry, positionals);
|
|
58
|
+
},
|
|
59
|
+
resolveFromArgs(args) {
|
|
60
|
+
return resolveCommandFromArgs(registry, args);
|
|
61
|
+
},
|
|
62
|
+
prepare(args, options) {
|
|
63
|
+
return prepareCommandFromArgs(registry, args, options);
|
|
64
|
+
},
|
|
65
|
+
run(prepared, context) {
|
|
66
|
+
return runPreparedCommand(prepared, context);
|
|
67
|
+
},
|
|
68
|
+
runFromArgs(args, context, options) {
|
|
69
|
+
return runCommandFromRegistry(registry, args, context, options);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Creates a semantic command mechanics facade.
|
|
75
|
+
*/
|
|
76
|
+
function createCommand() {
|
|
77
|
+
return {
|
|
78
|
+
define: defineCommand,
|
|
79
|
+
registry: createCommands,
|
|
80
|
+
run: runCommand
|
|
81
|
+
};
|
|
82
|
+
}
|
|
44
83
|
/**
|
|
45
84
|
* Checks whether a value is a command name registered in the given registry.
|
|
46
85
|
*/
|
|
@@ -48,6 +87,12 @@ function isCommandName(registry, value) {
|
|
|
48
87
|
return typeof value === 'string'
|
|
49
88
|
&& registry.commandNames.includes(value);
|
|
50
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Checks whether a prepared command has the given command name.
|
|
92
|
+
*/
|
|
93
|
+
function isPreparedCommandName(prepared, name) {
|
|
94
|
+
return prepared.name === name;
|
|
95
|
+
}
|
|
51
96
|
/**
|
|
52
97
|
* Resolves a command from already parsed positional arguments.
|
|
53
98
|
*/
|
|
@@ -65,13 +110,13 @@ function resolveCommand(registry, positionals) {
|
|
|
65
110
|
*/
|
|
66
111
|
function resolveCommandFromArgs(registry, args) {
|
|
67
112
|
for (const command of commandsBySpecificity(registry.commands)) {
|
|
68
|
-
const argv = (0,
|
|
113
|
+
const argv = (0, parser_1.parseArgv)(args, command.options);
|
|
69
114
|
const resolved = resolveCommandCandidate(command, argv.positionals);
|
|
70
115
|
if (resolved !== undefined) {
|
|
71
116
|
return resolved;
|
|
72
117
|
}
|
|
73
118
|
}
|
|
74
|
-
throw createUnknownCommandError((0,
|
|
119
|
+
throw createUnknownCommandError((0, parser_1.parseArgv)(args).positionals);
|
|
75
120
|
}
|
|
76
121
|
/**
|
|
77
122
|
* Resolves and validates a command without requiring runtime context.
|
|
@@ -81,13 +126,13 @@ async function prepareCommandFromArgs(registry, args, options = {}) {
|
|
|
81
126
|
return prepareCommandFromArgsStrict(registry, args);
|
|
82
127
|
}
|
|
83
128
|
for (const command of commandsBySpecificity(registry.commands)) {
|
|
84
|
-
const argv = (0,
|
|
129
|
+
const argv = (0, parser_1.parseArgv)(args, command.options);
|
|
85
130
|
const resolved = resolveCommandCandidate(command, argv.positionals);
|
|
86
131
|
if (resolved !== undefined) {
|
|
87
132
|
return prepareResolvedCommand(resolved, argv.options);
|
|
88
133
|
}
|
|
89
134
|
}
|
|
90
|
-
throw createUnknownCommandError((0,
|
|
135
|
+
throw createUnknownCommandError((0, parser_1.parseArgv)(args).positionals);
|
|
91
136
|
}
|
|
92
137
|
/**
|
|
93
138
|
* Runs a prepared command with caller-provided runtime context.
|
|
@@ -121,7 +166,7 @@ async function prepareCommand(command, args, options = {}) {
|
|
|
121
166
|
if (options.strict === true) {
|
|
122
167
|
assertNoOptionBeforeCommand(args);
|
|
123
168
|
}
|
|
124
|
-
const argv = (0,
|
|
169
|
+
const argv = (0, parser_1.parseArgv)(args, command.options);
|
|
125
170
|
const extraPositionals = resolveCommandPositionals(command.path, argv.positionals);
|
|
126
171
|
const resolved = {
|
|
127
172
|
name: commandPathToName(command.path),
|
|
@@ -136,7 +181,7 @@ async function prepareCommandFromArgsStrict(registry, args) {
|
|
|
136
181
|
if (command === undefined) {
|
|
137
182
|
throw createUnknownCommandError(commandPositionalsBeforeOptions(args));
|
|
138
183
|
}
|
|
139
|
-
const argv = (0,
|
|
184
|
+
const argv = (0, parser_1.parseArgv)(args, command.options);
|
|
140
185
|
const resolved = resolveCommandCandidate(command, argv.positionals);
|
|
141
186
|
if (resolved === undefined) {
|
|
142
187
|
throw createUnknownCommandError(argv.positionals);
|
|
@@ -147,13 +192,13 @@ async function prepareResolvedCommand(resolved, rawOptions) {
|
|
|
147
192
|
const { command } = resolved;
|
|
148
193
|
if (resolved.positionals.length > 0 && command.allowExtraPositionals !== true) {
|
|
149
194
|
const positional = resolved.positionals[0] ?? '';
|
|
150
|
-
throw new
|
|
195
|
+
throw new icore_error_1.IcoreError('UNEXPECTED_POSITIONAL', `Unexpected positional argument for '${command.path.join(' ')}': ${positional}`, {
|
|
151
196
|
command: command.path.join(' '),
|
|
152
197
|
positional,
|
|
153
198
|
positionals: [...resolved.positionals]
|
|
154
199
|
});
|
|
155
200
|
}
|
|
156
|
-
const parsed = (0,
|
|
201
|
+
const parsed = (0, parser_2.parseOptionsDetailed)(command.options, rawOptions);
|
|
157
202
|
const input = {
|
|
158
203
|
options: parsed.options,
|
|
159
204
|
provided: parsed.provided,
|
|
@@ -174,7 +219,7 @@ function resolveCommandPositionals(path, positionals) {
|
|
|
174
219
|
for (let index = 0; index < path.length; index += 1) {
|
|
175
220
|
if (positionals[index] !== path[index]) {
|
|
176
221
|
const command = path.join(' ');
|
|
177
|
-
throw new
|
|
222
|
+
throw new icore_error_1.IcoreError('UNKNOWN_COMMAND', `Expected command '${command}'`, {
|
|
178
223
|
command,
|
|
179
224
|
path: [...path],
|
|
180
225
|
positionals: [...positionals]
|
|
@@ -259,18 +304,18 @@ function isOptionBeforeCommand(arg) {
|
|
|
259
304
|
}
|
|
260
305
|
function createUnknownCommandError(positionals) {
|
|
261
306
|
const command = formatCommandPositionals(positionals);
|
|
262
|
-
return new
|
|
307
|
+
return new icore_error_1.IcoreError('UNKNOWN_COMMAND', `Unknown command: ${command}`, {
|
|
263
308
|
command,
|
|
264
309
|
positionals: [...positionals]
|
|
265
310
|
});
|
|
266
311
|
}
|
|
267
312
|
function createUnexpectedArgumentError(argument) {
|
|
268
|
-
return new
|
|
313
|
+
return new icore_error_1.IcoreError('UNEXPECTED_ARGUMENT', `Unexpected argument '${argument}'`, {
|
|
269
314
|
argument
|
|
270
315
|
});
|
|
271
316
|
}
|
|
272
317
|
function createDuplicateCommandError(command) {
|
|
273
|
-
return new
|
|
318
|
+
return new icore_error_1.IcoreError('DUPLICATE_COMMAND', `Unexpected duplicate command '${command}'`, {
|
|
274
319
|
command
|
|
275
320
|
});
|
|
276
321
|
}
|
|
@@ -21,7 +21,9 @@ export type IcoreErrorDetails = Readonly<Record<string, unknown>>;
|
|
|
21
21
|
* resolution, and schema configuration failures.
|
|
22
22
|
*/
|
|
23
23
|
export declare class IcoreError extends Error {
|
|
24
|
+
/** Stable machine-readable code. */
|
|
24
25
|
readonly code: IcoreErrorCode;
|
|
26
|
+
/** Structured error context. */
|
|
25
27
|
readonly details: IcoreErrorDetails;
|
|
26
28
|
constructor(code: IcoreErrorCode, message: string, details?: IcoreErrorDetails);
|
|
27
29
|
}
|
|
@@ -16,7 +16,9 @@ exports.IcoreError = void 0;
|
|
|
16
16
|
* resolution, and schema configuration failures.
|
|
17
17
|
*/
|
|
18
18
|
class IcoreError extends Error {
|
|
19
|
+
/** Stable machine-readable code. */
|
|
19
20
|
code;
|
|
21
|
+
/** Structured error context. */
|
|
20
22
|
details;
|
|
21
23
|
constructor(code, message, details = {}) {
|
|
22
24
|
super(message);
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The package entrypoint exposes the supported command mechanics API.
|
|
3
|
+
*
|
|
4
|
+
* Allowed here:
|
|
5
|
+
* - re-exporting public argv parser contracts;
|
|
6
|
+
* - re-exporting public option schema contracts;
|
|
7
|
+
* - re-exporting public command registry contracts;
|
|
8
|
+
* - re-exporting public presentation and output contracts;
|
|
9
|
+
* - re-exporting public terminal app composition contracts;
|
|
10
|
+
*
|
|
11
|
+
* This file must not contain parser, validator, or command runtime logic.
|
|
12
|
+
*/
|
|
13
|
+
export { parseArgv, type ParsedArgv } from './argv/parser';
|
|
14
|
+
export { createCommand, createCommands, defineCommand, defineCommandRegistry, isCommandName, isPreparedCommandName, prepareCommandFromArgs, resolveCommand, resolveCommandFromArgs, runCommand, runPreparedCommand, runCommandFromRegistry, type Command, type CommandDefinition, type CommandContext, type CommandInput, type CommandName, type CommandPayload, type CommandRegistry, type CommandResolutionOptions, type CommandResult, type Commands, type PreparedCommand, type PreparedCommandInput, type ResolvedCommand } from './command/mechanics';
|
|
15
|
+
export { IcoreError, type IcoreErrorCode, type IcoreErrorDetails } from './errors/icore-error';
|
|
16
|
+
export { parseOptions, parseOptionsDetailed, parseOptionsSubsetDetailed, type ParseOptionsResult, type ParseOptionsSubsetResult } from './options/parser';
|
|
17
|
+
export { mergeOptionsSchema, type BooleanOption, type InferOptions, type InferProvidedOptions, type MergeOptionsSchemas, type NumberOption, type OptionDefinition, type OptionsSchema, type RawOptionValue, type StringOption } from './options/schema';
|
|
18
|
+
export { createPresentation, type Presentation, type PresentationRenderers } from './presentation/facade';
|
|
19
|
+
export { isPresentationFormat, presentationFormatOptions, presentationFormats, type PresentationFormat } from './presentation/format-options';
|
|
20
|
+
export { renderCsv, renderCsvRow } from './presentation/renderers/csv';
|
|
21
|
+
export { renderJson } from './presentation/renderers/json';
|
|
22
|
+
export { renderTextTable } from './presentation/renderers/table';
|
|
23
|
+
export { isPresentationResult, renderPresentationResult } from './presentation/result-renderer';
|
|
24
|
+
export { type CsvCell, type CsvRow, type CsvPresentationView, type EmptyPresentationView, type PresentationRecord, type PresentationResult, type PresentationView, type PresentationViewFactory, type RecordPresentationView, type RecordsPresentationView, type TablePresentationView, type TextPresentationView, type TextTableRow } from './presentation/view';
|
|
25
|
+
export { createOutput, type Output, type OutputOptions } from './output/facade';
|
|
26
|
+
export { createStderrWriter, createStdoutWriter } from './output/node-writer';
|
|
27
|
+
export { createBackpressureTextWriter, type BackpressureTextSink, type TextWriter } from './output/text-writer';
|
|
28
|
+
export { createTerminalApp, type TerminalApp, type TerminalAppOptions, type TerminalCommandOutput } from './terminal/app';
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The package entrypoint exposes the supported command mechanics API.
|
|
4
|
+
*
|
|
5
|
+
* Allowed here:
|
|
6
|
+
* - re-exporting public argv parser contracts;
|
|
7
|
+
* - re-exporting public option schema contracts;
|
|
8
|
+
* - re-exporting public command registry contracts;
|
|
9
|
+
* - re-exporting public presentation and output contracts;
|
|
10
|
+
* - re-exporting public terminal app composition contracts;
|
|
11
|
+
*
|
|
12
|
+
* This file must not contain parser, validator, or command runtime logic.
|
|
13
|
+
*/
|
|
2
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "
|
|
6
|
-
|
|
7
|
-
Object.defineProperty(exports, "
|
|
8
|
-
Object.defineProperty(exports, "
|
|
9
|
-
Object.defineProperty(exports, "
|
|
10
|
-
Object.defineProperty(exports, "
|
|
11
|
-
Object.defineProperty(exports, "
|
|
12
|
-
Object.defineProperty(exports, "
|
|
13
|
-
Object.defineProperty(exports, "prepareCommandFromArgs", { enumerable: true, get: function () { return
|
|
14
|
-
Object.defineProperty(exports, "resolveCommand", { enumerable: true, get: function () { return
|
|
15
|
-
Object.defineProperty(exports, "resolveCommandFromArgs", { enumerable: true, get: function () { return
|
|
16
|
-
Object.defineProperty(exports, "runCommand", { enumerable: true, get: function () { return
|
|
17
|
-
Object.defineProperty(exports, "runPreparedCommand", { enumerable: true, get: function () { return
|
|
18
|
-
Object.defineProperty(exports, "runCommandFromRegistry", { enumerable: true, get: function () { return
|
|
15
|
+
exports.createTerminalApp = exports.createBackpressureTextWriter = exports.createStdoutWriter = exports.createStderrWriter = exports.createOutput = exports.renderPresentationResult = exports.isPresentationResult = exports.renderTextTable = exports.renderJson = exports.renderCsvRow = exports.renderCsv = exports.presentationFormats = exports.presentationFormatOptions = exports.isPresentationFormat = exports.createPresentation = exports.mergeOptionsSchema = exports.parseOptionsSubsetDetailed = exports.parseOptionsDetailed = exports.parseOptions = exports.IcoreError = exports.runCommandFromRegistry = exports.runPreparedCommand = exports.runCommand = exports.resolveCommandFromArgs = exports.resolveCommand = exports.prepareCommandFromArgs = exports.isPreparedCommandName = exports.isCommandName = exports.defineCommandRegistry = exports.defineCommand = exports.createCommands = exports.createCommand = exports.parseArgv = void 0;
|
|
16
|
+
var parser_1 = require("./argv/parser");
|
|
17
|
+
Object.defineProperty(exports, "parseArgv", { enumerable: true, get: function () { return parser_1.parseArgv; } });
|
|
18
|
+
var mechanics_1 = require("./command/mechanics");
|
|
19
|
+
Object.defineProperty(exports, "createCommand", { enumerable: true, get: function () { return mechanics_1.createCommand; } });
|
|
20
|
+
Object.defineProperty(exports, "createCommands", { enumerable: true, get: function () { return mechanics_1.createCommands; } });
|
|
21
|
+
Object.defineProperty(exports, "defineCommand", { enumerable: true, get: function () { return mechanics_1.defineCommand; } });
|
|
22
|
+
Object.defineProperty(exports, "defineCommandRegistry", { enumerable: true, get: function () { return mechanics_1.defineCommandRegistry; } });
|
|
23
|
+
Object.defineProperty(exports, "isCommandName", { enumerable: true, get: function () { return mechanics_1.isCommandName; } });
|
|
24
|
+
Object.defineProperty(exports, "isPreparedCommandName", { enumerable: true, get: function () { return mechanics_1.isPreparedCommandName; } });
|
|
25
|
+
Object.defineProperty(exports, "prepareCommandFromArgs", { enumerable: true, get: function () { return mechanics_1.prepareCommandFromArgs; } });
|
|
26
|
+
Object.defineProperty(exports, "resolveCommand", { enumerable: true, get: function () { return mechanics_1.resolveCommand; } });
|
|
27
|
+
Object.defineProperty(exports, "resolveCommandFromArgs", { enumerable: true, get: function () { return mechanics_1.resolveCommandFromArgs; } });
|
|
28
|
+
Object.defineProperty(exports, "runCommand", { enumerable: true, get: function () { return mechanics_1.runCommand; } });
|
|
29
|
+
Object.defineProperty(exports, "runPreparedCommand", { enumerable: true, get: function () { return mechanics_1.runPreparedCommand; } });
|
|
30
|
+
Object.defineProperty(exports, "runCommandFromRegistry", { enumerable: true, get: function () { return mechanics_1.runCommandFromRegistry; } });
|
|
31
|
+
var icore_error_1 = require("./errors/icore-error");
|
|
32
|
+
Object.defineProperty(exports, "IcoreError", { enumerable: true, get: function () { return icore_error_1.IcoreError; } });
|
|
33
|
+
var parser_2 = require("./options/parser");
|
|
34
|
+
Object.defineProperty(exports, "parseOptions", { enumerable: true, get: function () { return parser_2.parseOptions; } });
|
|
35
|
+
Object.defineProperty(exports, "parseOptionsDetailed", { enumerable: true, get: function () { return parser_2.parseOptionsDetailed; } });
|
|
36
|
+
Object.defineProperty(exports, "parseOptionsSubsetDetailed", { enumerable: true, get: function () { return parser_2.parseOptionsSubsetDetailed; } });
|
|
37
|
+
var schema_1 = require("./options/schema");
|
|
38
|
+
Object.defineProperty(exports, "mergeOptionsSchema", { enumerable: true, get: function () { return schema_1.mergeOptionsSchema; } });
|
|
39
|
+
var facade_1 = require("./presentation/facade");
|
|
40
|
+
Object.defineProperty(exports, "createPresentation", { enumerable: true, get: function () { return facade_1.createPresentation; } });
|
|
41
|
+
var format_options_1 = require("./presentation/format-options");
|
|
42
|
+
Object.defineProperty(exports, "isPresentationFormat", { enumerable: true, get: function () { return format_options_1.isPresentationFormat; } });
|
|
43
|
+
Object.defineProperty(exports, "presentationFormatOptions", { enumerable: true, get: function () { return format_options_1.presentationFormatOptions; } });
|
|
44
|
+
Object.defineProperty(exports, "presentationFormats", { enumerable: true, get: function () { return format_options_1.presentationFormats; } });
|
|
45
|
+
var csv_1 = require("./presentation/renderers/csv");
|
|
46
|
+
Object.defineProperty(exports, "renderCsv", { enumerable: true, get: function () { return csv_1.renderCsv; } });
|
|
47
|
+
Object.defineProperty(exports, "renderCsvRow", { enumerable: true, get: function () { return csv_1.renderCsvRow; } });
|
|
48
|
+
var json_1 = require("./presentation/renderers/json");
|
|
49
|
+
Object.defineProperty(exports, "renderJson", { enumerable: true, get: function () { return json_1.renderJson; } });
|
|
50
|
+
var table_1 = require("./presentation/renderers/table");
|
|
51
|
+
Object.defineProperty(exports, "renderTextTable", { enumerable: true, get: function () { return table_1.renderTextTable; } });
|
|
52
|
+
var result_renderer_1 = require("./presentation/result-renderer");
|
|
53
|
+
Object.defineProperty(exports, "isPresentationResult", { enumerable: true, get: function () { return result_renderer_1.isPresentationResult; } });
|
|
54
|
+
Object.defineProperty(exports, "renderPresentationResult", { enumerable: true, get: function () { return result_renderer_1.renderPresentationResult; } });
|
|
55
|
+
var facade_2 = require("./output/facade");
|
|
56
|
+
Object.defineProperty(exports, "createOutput", { enumerable: true, get: function () { return facade_2.createOutput; } });
|
|
57
|
+
var node_writer_1 = require("./output/node-writer");
|
|
58
|
+
Object.defineProperty(exports, "createStderrWriter", { enumerable: true, get: function () { return node_writer_1.createStderrWriter; } });
|
|
59
|
+
Object.defineProperty(exports, "createStdoutWriter", { enumerable: true, get: function () { return node_writer_1.createStdoutWriter; } });
|
|
60
|
+
var text_writer_1 = require("./output/text-writer");
|
|
61
|
+
Object.defineProperty(exports, "createBackpressureTextWriter", { enumerable: true, get: function () { return text_writer_1.createBackpressureTextWriter; } });
|
|
62
|
+
var app_1 = require("./terminal/app");
|
|
63
|
+
Object.defineProperty(exports, "createTerminalApp", { enumerable: true, get: function () { return app_1.createTerminalApp; } });
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The option parser module validates raw option values against declarative
|
|
3
|
+
* primitive option contracts.
|
|
4
|
+
*
|
|
5
|
+
* Allowed here:
|
|
6
|
+
* - applying and validating default values;
|
|
7
|
+
* - producing typed option values and user-provided metadata;
|
|
8
|
+
*
|
|
9
|
+
* This file must not contain argv tokenization, command resolution, or
|
|
10
|
+
* application-specific validation rules.
|
|
11
|
+
*/
|
|
12
|
+
import type { InferOptions, InferProvidedOptions, OptionsSchema, RawOptionValue } from './schema';
|
|
13
|
+
/**
|
|
14
|
+
* Detailed option parsing result with values and user-provided metadata.
|
|
15
|
+
*/
|
|
16
|
+
export type ParseOptionsResult<TSchema extends OptionsSchema> = {
|
|
17
|
+
/** Parsed values. */
|
|
18
|
+
options: InferOptions<TSchema>;
|
|
19
|
+
/** Explicit option presence metadata. */
|
|
20
|
+
provided: InferProvidedOptions<TSchema>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Detailed option subset parsing result with untouched options for later
|
|
24
|
+
* command-layer validation.
|
|
25
|
+
*/
|
|
26
|
+
export type ParseOptionsSubsetResult<TSchema extends OptionsSchema> = ParseOptionsResult<TSchema> & {
|
|
27
|
+
/** Unparsed raw values. */
|
|
28
|
+
rest: Record<string, RawOptionValue>;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Validates raw option values against a declarative option schema.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseOptions<const TSchema extends OptionsSchema>(schema: TSchema, values: Record<string, RawOptionValue>): InferOptions<TSchema>;
|
|
34
|
+
/**
|
|
35
|
+
* Validates raw option values and returns parsed values with user-provided
|
|
36
|
+
* metadata.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseOptionsDetailed<const TSchema extends OptionsSchema>(schema: TSchema, values: Record<string, RawOptionValue>): ParseOptionsResult<TSchema>;
|
|
39
|
+
/**
|
|
40
|
+
* Validates only raw option values known by the given schema and leaves the
|
|
41
|
+
* remaining raw options untouched.
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseOptionsSubsetDetailed<const TSchema extends OptionsSchema>(schema: TSchema, values: Record<string, RawOptionValue>): ParseOptionsSubsetResult<TSchema>;
|