icore 1.0.19 → 2.0.1

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 CHANGED
@@ -9,42 +9,94 @@ Historical entries before this file was introduced were reconstructed from npm
9
9
  publish metadata and local git history. Older entries are intentionally
10
10
  conservative.
11
11
 
12
- Version boundaries between `1.0.12` and `1.0.15` are draft-level and should be
13
- verified against git tags.
12
+ Version boundaries from `1.0.12` through `1.0.19` were checked against npm
13
+ `gitHead` metadata and local git history.
14
14
 
15
15
  ## [Unreleased]
16
16
 
17
+ ### Fixed
18
+
19
+ - Built package output automatically before npm and Yarn packing.
20
+ - Preserved option names that overlap properties inherited from
21
+ `Object.prototype`.
22
+ - Rejected malformed nested records, table rows, and CSV rows in presentation
23
+ result guards.
24
+ - Quoted CSV cells containing carriage returns.
25
+ - Rejected backpressured writes when EventEmitter-compatible sinks error or
26
+ close before `drain`.
27
+ - Rejected top-level values without a JSON representation instead of emitting
28
+ invalid JSON text.
29
+
30
+ ## [2.0.0] - 2026-07-14
31
+
32
+ ### Added
33
+
34
+ - Added reusable terminal error rendering and exit-code policy with prepare,
35
+ execute, render, write, and external phase context.
36
+ - Added `TerminalApp.reportError(...)` for caller-owned lifecycle flows that
37
+ need the same stderr and exit-code behavior as `run(...)` and
38
+ `runPrepared(...)`.
39
+ - Added `IcoreError.category` to distinguish usage failures from invalid
40
+ command and option definitions.
41
+ - Added the public `IcoreErrorDetailsMap`, generic `IcoreErrorDetails<TCode>`,
42
+ and distributed `AnyIcoreError` contracts for code-specific error details.
43
+ - Added `isIcoreError(...)` overloads for narrowing any `IcoreError` or one
44
+ exact error code while preserving its details type.
45
+
46
+ ### Changed
47
+
48
+ - Clarified that extra positionals after a matched command path are rejected
49
+ during prepare unless the command declares `allowExtraPositionals: true`.
50
+ - Changed `TerminalApp.run(...)` and `TerminalApp.runPrepared(...)` to share the
51
+ configured terminal error policy while preserving the default error text and
52
+ exit code.
53
+ - **Breaking:** changed the `IcoreError` constructor to require code-specific
54
+ `details`; direct callers must now provide the third argument.
55
+ - Added explicit variant discriminators to `UNKNOWN_COMMAND`,
56
+ `UNEXPECTED_ARGUMENT`, `EXPECTED_REQUIRED_ARGUMENT`, and
57
+ `INVALID_OPTION_DEFAULT` details.
58
+
59
+ ## [1.0.19]
60
+
17
61
  ### Added
18
62
 
19
- - Added `TerminalApp.runPrepared(...)` for running prepared commands through terminal rendering and output.
20
63
  - Added `TerminalApp.writePreparedOutput(...)` for rendering and writing already obtained terminal output.
64
+ - Added `docs/roadmap.md` for directional project decisions that are not release history.
21
65
 
22
66
  ### Changed
23
67
 
24
- - Changed `createTerminalApp` typing to accept command registries with void and prepared payloads.
25
68
  - Changed `createTerminalApp` typing to allow custom command results when consumers run prepared commands themselves.
26
- - Updated testing instructions to use `yarn build` and `yarn test`.
69
+ - Documented the caller-owned flow for command execution results that may be
70
+ lifecycle handles before terminal output is written.
71
+ - Documented that terminal string output is written exactly as provided.
27
72
 
28
- ## [1.0.16]
73
+ ## [1.0.18]
29
74
 
30
75
  ### Added
31
76
 
32
- - Added `createTerminalApp()` as the top-level terminal application composition API.
33
- - Added semantic presentation facade: `createPresentation()`.
34
- - Added semantic output facade: `createOutput()` with `output.write(...)` and `output.error(...)`.
35
- - Added terminal-ready command output support: text, async text streams, presentation results, and empty output.
36
- - Added guide-style examples for terminal app, command flow, option schemas, presentation, output, and lower-level mechanics.
77
+ - Added `TerminalApp.runPrepared(...)` for running prepared commands through terminal rendering and output.
37
78
 
38
79
  ### Changed
39
80
 
40
- - Documentation was reorganized around `command`, `presentation`, and `output`.
41
- - Examples were grouped by usage level: Terminal Application, Layer Toolkit, Primitive Mechanics.
42
- - Public README now positions `icore` as terminal application mechanics, not only argv parsing.
81
+ - Documented the `app.prepare(...)` and `app.runPrepared(...)` flow for applications
82
+ that create and clean up runtime context themselves.
43
83
 
44
- ## [1.0.15]
84
+ ## [1.0.17]
85
+
86
+ ### Changed
87
+
88
+ - Relaxed `createTerminalApp` typing so command registries with commands
89
+ without `prepare()` hooks and with void payloads compile without adapter casts.
90
+
91
+ ## [1.0.16]
45
92
 
46
93
  ### Added
47
94
 
95
+ - Added `createTerminalApp()` as the top-level terminal application composition API.
96
+ - Added command mechanics facade: `createCommand()`.
97
+ - Added semantic presentation facade: `createPresentation()`.
98
+ - Added semantic output facade: `createOutput()` with `output.write(...)` and `output.error(...)`.
99
+ - Added terminal-ready command output support: text, async text streams, presentation results, and empty output.
48
100
  - Added presentation primitives and renderers:
49
101
  - `renderJson`
50
102
  - `renderCsv`
@@ -56,67 +108,62 @@ verified against git tags.
56
108
  - `createStdoutWriter`
57
109
  - `createStderrWriter`
58
110
  - `createBackpressureTextWriter`
111
+ - Added typed option subset parsing with `parseOptionsSubsetDetailed`.
112
+ - Added guide-style examples for terminal app, command flow, option schemas, presentation, output, and lower-level mechanics.
59
113
 
60
114
  ### Changed
61
115
 
62
- - Extracted generic JSON/CSV/table rendering mechanics from project-specific CLI code.
116
+ - Documentation was reorganized around `command`, `presentation`, and `output`.
117
+ - Examples were grouped by usage level: Terminal Application, Layer Toolkit, Primitive Mechanics.
118
+ - Public README now positions `icore` as terminal application mechanics, not only argv parsing.
119
+ - Source files were reorganized by framework area: argv, command, options,
120
+ presentation, output, and terminal app.
121
+ - Updated tests to run with `fwa --prune`.
122
+ - Restored `CHANGELOG.md` to published package contents.
63
123
 
64
- ## [1.0.14]
124
+ ### Removed
125
+
126
+ - Removed the redundant CLI barrel module.
127
+ - Removed the legacy `git-flow.md` document.
128
+ - Removed the `prepare` package script.
129
+
130
+ ## [1.0.15]
65
131
 
66
132
  ### Added
67
133
 
68
- - Added command mechanics facade: `createCommand()`.
69
- - Added command registry flow:
70
- - `command.define(...)`
71
- - `command.registry(...)`
72
- - `commands.prepare(...)`
73
- - `commands.run(...)`
74
- - `commands.runFromArgs(...)`
75
- - Added lower-level command primitives:
76
- - `defineCommand`
77
- - `defineCommandRegistry`
78
- - `createCommands`
79
- - `resolveCommand`
80
- - `prepareCommandFromArgs`
81
- - `runPreparedCommand`
82
- - `runCommandFromRegistry`
83
- - `runCommand`
134
+ - Added `isPreparedCommandName` for narrowing prepared command unions by command name.
135
+
136
+ ### Changed
137
+
138
+ - Temporarily excluded `CHANGELOG.md` from published package contents.
139
+
140
+ ## [1.0.14]
84
141
 
85
142
  ### Changed
86
143
 
87
- - CLI command execution became explicitly two-phase: prepare without runtime context, then run with context.
144
+ - Preserved prepared command payload correlation when narrowing registry unions.
145
+ - Reformatted `CHANGELOG.md` according to Keep a Changelog.
88
146
 
89
147
  ## [1.0.13]
90
148
 
91
149
  ### Added
92
150
 
93
- - Added typed option schema composition with `mergeOptionsSchema`.
94
- - Added option inference helpers for parsed values and provided metadata.
95
- - Added command-name guards:
96
- - `isCommandName`
97
- - `isPreparedCommandName`
98
- - Added detailed option parsing contracts for cases where caller needs explicit provided/not-provided metadata.
151
+ - Added machine-readable `IcoreError` codes and details.
152
+ - Added strict command resolution mode with `strict: true`.
153
+ - Added strict mode support to direct `runCommand(...)` calls.
154
+ - Added typed prepared command payloads.
155
+ - Added `CHANGELOG.md`.
99
156
 
100
157
  ### Changed
101
158
 
102
- - Boolean option handling was tightened around flag syntax: `--flag` and `--no-flag`.
159
+ - Boolean option handling was tightened to flag syntax only.
103
160
 
104
161
  ## [1.0.12]
105
162
 
106
- ### Added
163
+ ### Changed
107
164
 
108
- - Added core typed CLI option mechanics based on literal schemas:
109
- - `type: 'string'`
110
- - `type: 'boolean'`
111
- - `type: 'number'`
112
- - Added GNU-style argv parsing:
113
- - `--name value`
114
- - `--name=value`
115
- - `--flag`
116
- - short aliases
117
- - `--` terminator
118
- - Added machine-readable `IcoreError`.
119
- - Added initial TypeScript-first public API for command-line mechanics.
165
+ - Replaced the preliminary `flagOnly` boolean option setting with typed
166
+ `syntax: 'flag'`.
120
167
 
121
168
  ## [1.0.11] - 2026-07-02
122
169
 
@@ -268,6 +315,9 @@ verified against git tags.
268
315
  - Detailed changelog entries were not maintained for these releases.
269
316
 
270
317
  [Unreleased]: https://github.com/woodger/icore/commits/develop
318
+ [1.0.19]: https://www.npmjs.com/package/icore/v/1.0.19
319
+ [1.0.18]: https://www.npmjs.com/package/icore/v/1.0.18
320
+ [1.0.17]: https://www.npmjs.com/package/icore/v/1.0.17
271
321
  [1.0.16]: https://www.npmjs.com/package/icore/v/1.0.16
272
322
  [1.0.15]: https://www.npmjs.com/package/icore/v/1.0.15
273
323
  [1.0.14]: https://www.npmjs.com/package/icore/v/1.0.14
@@ -18,7 +18,7 @@ const icore_error_1 = require("../errors/icore-error");
18
18
  */
19
19
  function parseArgv(args, schema) {
20
20
  const positionals = [];
21
- const options = {};
21
+ const options = Object.create(null);
22
22
  const aliases = buildShortAliasMap(schema);
23
23
  let parseOptions = true;
24
24
  for (let index = 0; index < args.length; index += 1) {
@@ -68,10 +68,10 @@ function parseArgv(args, schema) {
68
68
  if (name === '') {
69
69
  throw createUnexpectedArgumentError(arg);
70
70
  }
71
- const definition = schema?.[name];
71
+ const definition = getOwnOptionDefinition(schema, name);
72
72
  if (separatorIndex === -1 && definition === undefined && name.startsWith('no-')) {
73
73
  const negatedName = name.slice(3);
74
- const negatedDefinition = schema?.[negatedName];
74
+ const negatedDefinition = getOwnOptionDefinition(schema, negatedName);
75
75
  if (negatedDefinition?.type === 'boolean') {
76
76
  if (Object.hasOwn(options, negatedName)) {
77
77
  throw createDuplicateArgumentError(negatedName);
@@ -106,9 +106,15 @@ function parseArgv(args, schema) {
106
106
  }
107
107
  return {
108
108
  positionals,
109
- options
109
+ options: { ...options }
110
110
  };
111
111
  }
112
+ function getOwnOptionDefinition(schema, name) {
113
+ if (schema === undefined || !Object.hasOwn(schema, name)) {
114
+ return undefined;
115
+ }
116
+ return schema[name];
117
+ }
112
118
  function buildShortAliasMap(schema) {
113
119
  const aliases = new Map();
114
120
  if (schema === undefined) {
@@ -147,6 +153,7 @@ function createDuplicateArgumentError(name) {
147
153
  }
148
154
  function createUnexpectedArgumentError(argument) {
149
155
  return new icore_error_1.IcoreError('UNEXPECTED_ARGUMENT', `Unexpected argument '${argument}'`, {
156
+ reason: 'malformed-option',
150
157
  argument
151
158
  });
152
159
  }
@@ -220,6 +220,7 @@ function resolveCommandPositionals(path, positionals) {
220
220
  if (positionals[index] !== path[index]) {
221
221
  const command = path.join(' ');
222
222
  throw new icore_error_1.IcoreError('UNKNOWN_COMMAND', `Expected command '${command}'`, {
223
+ reason: 'path-mismatch',
223
224
  command,
224
225
  path: [...path],
225
226
  positionals: [...positionals]
@@ -305,12 +306,14 @@ function isOptionBeforeCommand(arg) {
305
306
  function createUnknownCommandError(positionals) {
306
307
  const command = formatCommandPositionals(positionals);
307
308
  return new icore_error_1.IcoreError('UNKNOWN_COMMAND', `Unknown command: ${command}`, {
309
+ reason: 'unresolved',
308
310
  command,
309
311
  positionals: [...positionals]
310
312
  });
311
313
  }
312
314
  function createUnexpectedArgumentError(argument) {
313
315
  return new icore_error_1.IcoreError('UNEXPECTED_ARGUMENT', `Unexpected argument '${argument}'`, {
316
+ reason: 'option-before-command',
314
317
  argument
315
318
  });
316
319
  }
@@ -3,27 +3,150 @@
3
3
  *
4
4
  * Allowed here:
5
5
  * - defining stable error codes;
6
+ * - correlating error codes with structured details;
6
7
  * - preserving human-readable messages on Error instances;
7
8
  * - carrying structured details for application-level handling;
9
+ * - narrowing unknown errors by their stable codes;
8
10
  *
9
11
  * This file must not contain parser, validator, or command resolution logic.
10
12
  */
13
+ type OptionIdentityDetails = {
14
+ argument: string;
15
+ option: string;
16
+ };
17
+ type UnknownCommandDetails = {
18
+ reason: 'unresolved';
19
+ command: string;
20
+ positionals: readonly string[];
21
+ } | {
22
+ reason: 'path-mismatch';
23
+ command: string;
24
+ path: readonly string[];
25
+ positionals: readonly string[];
26
+ };
27
+ type UnexpectedArgumentDetails = {
28
+ reason: 'malformed-option';
29
+ argument: string;
30
+ } | {
31
+ reason: 'unknown-option';
32
+ argument: string;
33
+ option: string;
34
+ } | {
35
+ reason: 'option-before-command';
36
+ argument: string;
37
+ };
38
+ type ExpectedRequiredArgumentDetails = {
39
+ reason: 'option';
40
+ argument: string;
41
+ option: string;
42
+ } | {
43
+ reason: 'positional';
44
+ argument: string;
45
+ positional: string;
46
+ };
47
+ type InvalidOptionTypeDetails = OptionIdentityDetails & ({
48
+ expected: 'string' | 'boolean flag' | 'number';
49
+ value: unknown;
50
+ } | {
51
+ expected: 'integer';
52
+ value: number;
53
+ } | {
54
+ expected: 'minimum';
55
+ min: number;
56
+ value: number;
57
+ } | {
58
+ expected: 'maximum';
59
+ max: number;
60
+ value: number;
61
+ });
62
+ type InvalidOptionDefaultDetails = OptionIdentityDetails & ({
63
+ expected: 'string' | 'boolean' | 'number';
64
+ value: unknown;
65
+ } | {
66
+ expected: 'integer';
67
+ value: number;
68
+ } | {
69
+ expected: 'minimum';
70
+ min: number;
71
+ value: number;
72
+ } | {
73
+ expected: 'maximum';
74
+ max: number;
75
+ value: number;
76
+ } | {
77
+ expected: 'choice';
78
+ choices: readonly (string | number)[];
79
+ value: string | number;
80
+ });
81
+ /** Structured details protocol keyed by every stable `IcoreError` code. */
82
+ export type IcoreErrorDetailsMap = {
83
+ UNKNOWN_COMMAND: UnknownCommandDetails;
84
+ UNEXPECTED_ARGUMENT: UnexpectedArgumentDetails;
85
+ DUPLICATE_ARGUMENT: OptionIdentityDetails;
86
+ EXPECTED_REQUIRED_ARGUMENT: ExpectedRequiredArgumentDetails;
87
+ INVALID_OPTION_TYPE: InvalidOptionTypeDetails;
88
+ INVALID_OPTION_CHOICE: OptionIdentityDetails & {
89
+ choices: readonly (string | number)[];
90
+ value: string | number;
91
+ };
92
+ UNEXPECTED_POSITIONAL: {
93
+ command: string;
94
+ positional: string;
95
+ positionals: readonly string[];
96
+ };
97
+ INVALID_OPTION_ALIAS: OptionIdentityDetails & {
98
+ alias: unknown;
99
+ };
100
+ DUPLICATE_ALIAS: {
101
+ argument: string;
102
+ alias: string;
103
+ };
104
+ INVALID_OPTION_DEFAULT: InvalidOptionDefaultDetails;
105
+ DUPLICATE_COMMAND: {
106
+ command: string;
107
+ };
108
+ };
109
+ /** Stable machine-readable error code for `icore` CLI mechanics errors. */
110
+ export type IcoreErrorCode = keyof IcoreErrorDetailsMap;
111
+ /** Structured details for one exact `IcoreError` code. */
112
+ export type IcoreErrorDetails<TCode extends IcoreErrorCode> = Readonly<IcoreErrorDetailsMap[TCode]>;
11
113
  /**
12
- * Stable machine-readable error code for `icore` CLI mechanics errors.
114
+ * Stable category for distinguishing user input failures from invalid command
115
+ * or option definitions.
13
116
  */
14
- export type IcoreErrorCode = 'UNKNOWN_COMMAND' | 'UNEXPECTED_ARGUMENT' | 'DUPLICATE_ARGUMENT' | 'EXPECTED_REQUIRED_ARGUMENT' | 'INVALID_OPTION_TYPE' | 'INVALID_OPTION_CHOICE' | 'UNEXPECTED_POSITIONAL' | 'INVALID_OPTION_ALIAS' | 'DUPLICATE_ALIAS' | 'INVALID_OPTION_DEFAULT' | 'DUPLICATE_COMMAND';
15
- /**
16
- * Structured context for application-level error handling.
17
- */
18
- export type IcoreErrorDetails = Readonly<Record<string, unknown>>;
117
+ export type IcoreErrorCategory = 'usage' | 'definition';
118
+ declare const icoreErrorCategoryByCode: {
119
+ readonly UNKNOWN_COMMAND: "usage";
120
+ readonly UNEXPECTED_ARGUMENT: "usage";
121
+ readonly DUPLICATE_ARGUMENT: "usage";
122
+ readonly EXPECTED_REQUIRED_ARGUMENT: "usage";
123
+ readonly INVALID_OPTION_TYPE: "usage";
124
+ readonly INVALID_OPTION_CHOICE: "usage";
125
+ readonly UNEXPECTED_POSITIONAL: "usage";
126
+ readonly INVALID_OPTION_ALIAS: "definition";
127
+ readonly DUPLICATE_ALIAS: "definition";
128
+ readonly INVALID_OPTION_DEFAULT: "definition";
129
+ readonly DUPLICATE_COMMAND: "definition";
130
+ };
19
131
  /**
20
132
  * Error thrown by `icore` for CLI parsing, option validation, command
21
133
  * resolution, and schema configuration failures.
22
134
  */
23
- export declare class IcoreError extends Error {
135
+ export declare class IcoreError<TCode extends IcoreErrorCode = IcoreErrorCode> extends Error {
24
136
  /** Stable machine-readable code. */
25
- readonly code: IcoreErrorCode;
137
+ readonly code: TCode;
138
+ /** Stable high-level error category. */
139
+ readonly category: typeof icoreErrorCategoryByCode[TCode];
26
140
  /** Structured error context. */
27
- readonly details: IcoreErrorDetails;
28
- constructor(code: IcoreErrorCode, message: string, details?: IcoreErrorDetails);
141
+ readonly details: IcoreErrorDetails<TCode>;
142
+ constructor(code: TCode, message: string, details: IcoreErrorDetails<TCode>);
29
143
  }
144
+ /** Distributed union preserving the correlation between code and details. */
145
+ export type AnyIcoreError = {
146
+ [TCode in IcoreErrorCode]: IcoreError<TCode>;
147
+ }[IcoreErrorCode];
148
+ /** Checks whether an unknown value is any supported `IcoreError`. */
149
+ export declare function isIcoreError(error: unknown): error is AnyIcoreError;
150
+ /** Checks and narrows an unknown value to one exact `IcoreError` code. */
151
+ export declare function isIcoreError<TCode extends IcoreErrorCode>(error: unknown, code: TCode): error is IcoreError<TCode>;
152
+ export {};
@@ -4,13 +4,29 @@
4
4
  *
5
5
  * Allowed here:
6
6
  * - defining stable error codes;
7
+ * - correlating error codes with structured details;
7
8
  * - preserving human-readable messages on Error instances;
8
9
  * - carrying structured details for application-level handling;
10
+ * - narrowing unknown errors by their stable codes;
9
11
  *
10
12
  * This file must not contain parser, validator, or command resolution logic.
11
13
  */
12
14
  Object.defineProperty(exports, "__esModule", { value: true });
13
15
  exports.IcoreError = void 0;
16
+ exports.isIcoreError = isIcoreError;
17
+ const icoreErrorCategoryByCode = {
18
+ UNKNOWN_COMMAND: 'usage',
19
+ UNEXPECTED_ARGUMENT: 'usage',
20
+ DUPLICATE_ARGUMENT: 'usage',
21
+ EXPECTED_REQUIRED_ARGUMENT: 'usage',
22
+ INVALID_OPTION_TYPE: 'usage',
23
+ INVALID_OPTION_CHOICE: 'usage',
24
+ UNEXPECTED_POSITIONAL: 'usage',
25
+ INVALID_OPTION_ALIAS: 'definition',
26
+ DUPLICATE_ALIAS: 'definition',
27
+ INVALID_OPTION_DEFAULT: 'definition',
28
+ DUPLICATE_COMMAND: 'definition'
29
+ };
14
30
  /**
15
31
  * Error thrown by `icore` for CLI parsing, option validation, command
16
32
  * resolution, and schema configuration failures.
@@ -18,14 +34,21 @@ exports.IcoreError = void 0;
18
34
  class IcoreError extends Error {
19
35
  /** Stable machine-readable code. */
20
36
  code;
37
+ /** Stable high-level error category. */
38
+ category;
21
39
  /** Structured error context. */
22
40
  details;
23
- constructor(code, message, details = {}) {
41
+ constructor(code, message, details) {
24
42
  super(message);
25
43
  this.name = 'IcoreError';
26
44
  this.code = code;
45
+ this.category = icoreErrorCategoryByCode[code];
27
46
  this.details = details;
28
47
  Object.setPrototypeOf(this, new.target.prototype);
29
48
  }
30
49
  }
31
50
  exports.IcoreError = IcoreError;
51
+ function isIcoreError(error, code) {
52
+ return error instanceof IcoreError
53
+ && (code === undefined || error.code === code);
54
+ }
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  */
13
13
  export { parseArgv, type ParsedArgv } from './argv/parser';
14
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';
15
+ export { IcoreError, isIcoreError, type AnyIcoreError, type IcoreErrorCategory, type IcoreErrorCode, type IcoreErrorDetails, type IcoreErrorDetailsMap } from './errors/icore-error';
16
16
  export { parseOptions, parseOptionsDetailed, parseOptionsSubsetDetailed, type ParseOptionsResult, type ParseOptionsSubsetResult } from './options/parser';
17
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
18
  export { createPresentation, type Presentation, type PresentationRenderers } from './presentation/facade';
@@ -25,4 +25,4 @@ export { type CsvCell, type CsvRow, type CsvPresentationView, type EmptyPresenta
25
25
  export { createOutput, type Output, type OutputOptions } from './output/facade';
26
26
  export { createStderrWriter, createStdoutWriter } from './output/node-writer';
27
27
  export { createBackpressureTextWriter, type BackpressureTextSink, type TextWriter } from './output/text-writer';
28
- export { createTerminalApp, type TerminalApp, type TerminalAppOptions, type TerminalCommandOutput } from './terminal/app';
28
+ export { createTerminalApp, type TerminalApp, type TerminalAppOptions, type TerminalCommandOutput, type TerminalErrorContext, type TerminalErrorPhase, type TerminalErrorPolicy } from './terminal/app';
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  * This file must not contain parser, validator, or command runtime logic.
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
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;
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.isIcoreError = 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
16
  var parser_1 = require("./argv/parser");
17
17
  Object.defineProperty(exports, "parseArgv", { enumerable: true, get: function () { return parser_1.parseArgv; } });
18
18
  var mechanics_1 = require("./command/mechanics");
@@ -30,6 +30,7 @@ Object.defineProperty(exports, "runPreparedCommand", { enumerable: true, get: fu
30
30
  Object.defineProperty(exports, "runCommandFromRegistry", { enumerable: true, get: function () { return mechanics_1.runCommandFromRegistry; } });
31
31
  var icore_error_1 = require("./errors/icore-error");
32
32
  Object.defineProperty(exports, "IcoreError", { enumerable: true, get: function () { return icore_error_1.IcoreError; } });
33
+ Object.defineProperty(exports, "isIcoreError", { enumerable: true, get: function () { return icore_error_1.isIcoreError; } });
33
34
  var parser_2 = require("./options/parser");
34
35
  Object.defineProperty(exports, "parseOptions", { enumerable: true, get: function () { return parser_2.parseOptions; } });
35
36
  Object.defineProperty(exports, "parseOptionsDetailed", { enumerable: true, get: function () { return parser_2.parseOptionsDetailed; } });
@@ -26,8 +26,8 @@ function parseOptions(schema, values) {
26
26
  * metadata.
27
27
  */
28
28
  function parseOptionsDetailed(schema, values) {
29
- const parsed = {};
30
- const provided = {};
29
+ const parsed = Object.create(null);
30
+ const provided = Object.create(null);
31
31
  for (const name of Object.keys(values)) {
32
32
  if (!Object.hasOwn(schema, name)) {
33
33
  throw createUnexpectedArgumentError(name);
@@ -35,27 +35,30 @@ function parseOptionsDetailed(schema, values) {
35
35
  }
36
36
  for (const name of Object.keys(schema)) {
37
37
  const definition = schema[name];
38
- const value = values[String(name)];
38
+ const optionName = String(name);
39
+ const value = Object.hasOwn(values, optionName)
40
+ ? values[optionName]
41
+ : undefined;
39
42
  if (definition === undefined) {
40
43
  continue;
41
44
  }
42
45
  provided[name] = value !== undefined;
43
46
  if (value === undefined) {
44
47
  if ('default' in definition) {
45
- parsed[name] = parseDefaultOptionValue(String(name), definition);
48
+ parsed[name] = parseDefaultOptionValue(optionName, definition);
46
49
  continue;
47
50
  }
48
51
  if (definition.required === true) {
49
- throw createExpectedRequiredArgumentError(String(name));
52
+ throw createExpectedRequiredArgumentError(optionName);
50
53
  }
51
54
  parsed[name] = undefined;
52
55
  continue;
53
56
  }
54
- parsed[name] = parseOptionValue(String(name), definition, value);
57
+ parsed[name] = parseOptionValue(optionName, definition, value);
55
58
  }
56
59
  return {
57
- options: parsed,
58
- provided: provided
60
+ options: { ...parsed },
61
+ provided: { ...provided }
59
62
  };
60
63
  }
61
64
  /**
@@ -63,8 +66,8 @@ function parseOptionsDetailed(schema, values) {
63
66
  * remaining raw options untouched.
64
67
  */
65
68
  function parseOptionsSubsetDetailed(schema, values) {
66
- const subsetValues = {};
67
- const rest = {};
69
+ const subsetValues = Object.create(null);
70
+ const rest = Object.create(null);
68
71
  for (const [name, value] of Object.entries(values)) {
69
72
  if (Object.hasOwn(schema, name)) {
70
73
  subsetValues[name] = value;
@@ -74,7 +77,7 @@ function parseOptionsSubsetDetailed(schema, values) {
74
77
  }
75
78
  return {
76
79
  ...parseOptionsDetailed(schema, subsetValues),
77
- rest
80
+ rest: { ...rest }
78
81
  };
79
82
  }
80
83
  function parseOptionValue(name, definition, value) {
@@ -151,7 +154,10 @@ function validateNumberConstraints(name, definition, parsed, source = 'value') {
151
154
  value: parsed
152
155
  });
153
156
  }
154
- throw createExpectedOptionTypeError(name, 'integer', parsed);
157
+ throw createInvalidOptionTypeError(name, `Expected '--${name}' as integer`, {
158
+ expected: 'integer',
159
+ value: parsed
160
+ });
155
161
  }
156
162
  if (definition.min !== undefined && parsed < definition.min) {
157
163
  const message = `Expected '--${name}' to be greater than or equal to ${String(definition.min)}`;
@@ -192,6 +198,7 @@ function assertChoice(name, choices, value, source = 'value') {
192
198
  const message = `Expected '--${name}' as one of: ${choices.join(', ')}`;
193
199
  if (source === 'default') {
194
200
  throw createInvalidOptionDefaultError(name, message, {
201
+ expected: 'choice',
195
202
  choices: [...choices],
196
203
  value
197
204
  });
@@ -200,12 +207,14 @@ function assertChoice(name, choices, value, source = 'value') {
200
207
  }
201
208
  function createUnexpectedArgumentError(name) {
202
209
  return new icore_error_1.IcoreError('UNEXPECTED_ARGUMENT', `Unexpected argument '--${name}'`, {
210
+ reason: 'unknown-option',
203
211
  argument: `--${name}`,
204
212
  option: name
205
213
  });
206
214
  }
207
215
  function createExpectedRequiredArgumentError(name) {
208
216
  return new icore_error_1.IcoreError('EXPECTED_REQUIRED_ARGUMENT', `Expected required argument '--${name}'`, {
217
+ reason: 'option',
209
218
  argument: `--${name}`,
210
219
  option: name
211
220
  });
@@ -19,5 +19,6 @@ exports.mergeOptionsSchema = mergeOptionsSchema;
19
19
  * Later schemas override earlier schemas with the same option name.
20
20
  */
21
21
  function mergeOptionsSchema(schema, ...schemas) {
22
- return Object.assign({}, schema, ...schemas);
22
+ const merged = Object.assign(Object.create(null), schema, ...schemas);
23
+ return { ...merged };
23
24
  }