icore 1.0.17 → 1.0.19

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,48 +9,114 @@ 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.
14
+
12
15
  ## [Unreleased]
13
16
 
14
17
  ### Added
15
18
 
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.
26
- - Added machine-readable `IcoreError` codes and details.
27
- - Added `parseOptionsSubsetDetailed` for validating known option subsets while preserving unknown raw options.
28
- - Added opt-in `strict: true` command resolution for rejecting options before command paths.
29
- - Added `strict: true` support to direct `runCommand` execution.
30
- - Added typed prepared command `payload` returned from `prepare` and passed to `handle`.
31
- - Added public `CommandPayload`, `CommandContext`, and `CommandResult` helper types.
32
- - Added `isPreparedCommandName` for narrowing prepared command unions by name.
33
- - Added this changelog.
34
-
35
- ### Changed
36
-
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.
19
+ - Added `TerminalApp.runPrepared(...)` for running prepared commands through terminal rendering and output.
20
+ - Added `TerminalApp.writePreparedOutput(...)` for rendering and writing already obtained terminal output.
43
21
 
44
- ### Removed
22
+ ### Changed
23
+
24
+ - Changed `createTerminalApp` typing to accept command registries with void and prepared payloads.
25
+ - 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`.
27
+
28
+ ## [1.0.16]
29
+
30
+ ### Added
31
+
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.
45
37
 
46
- - Removed explicit boolean values such as `--flag=true` and `--flag=false`; use `--flag` and `--no-flag`.
38
+ ### Changed
39
+
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.
43
+
44
+ ## [1.0.15]
45
+
46
+ ### Added
47
47
 
48
- ## [1.0.12] - 2026-07-02
48
+ - Added presentation primitives and renderers:
49
+ - `renderJson`
50
+ - `renderCsv`
51
+ - `renderCsvRow`
52
+ - `renderTextTable`
53
+ - `renderPresentationResult`
54
+ - `isPresentationResult`
55
+ - Added reusable output writer primitives:
56
+ - `createStdoutWriter`
57
+ - `createStderrWriter`
58
+ - `createBackpressureTextWriter`
49
59
 
50
60
  ### Changed
51
61
 
52
- - Replaced the preliminary flag-only boolean option naming with `syntax: 'flag'`.
53
- - Documented flag-only boolean option behavior.
62
+ - Extracted generic JSON/CSV/table rendering mechanics from project-specific CLI code.
63
+
64
+ ## [1.0.14]
65
+
66
+ ### Added
67
+
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`
84
+
85
+ ### Changed
86
+
87
+ - CLI command execution became explicitly two-phase: prepare without runtime context, then run with context.
88
+
89
+ ## [1.0.13]
90
+
91
+ ### Added
92
+
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.
99
+
100
+ ### Changed
101
+
102
+ - Boolean option handling was tightened around flag syntax: `--flag` and `--no-flag`.
103
+
104
+ ## [1.0.12]
105
+
106
+ ### Added
107
+
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.
54
120
 
55
121
  ## [1.0.11] - 2026-07-02
56
122
 
@@ -202,6 +268,10 @@ conservative.
202
268
  - Detailed changelog entries were not maintained for these releases.
203
269
 
204
270
  [Unreleased]: https://github.com/woodger/icore/commits/develop
271
+ [1.0.16]: https://www.npmjs.com/package/icore/v/1.0.16
272
+ [1.0.15]: https://www.npmjs.com/package/icore/v/1.0.15
273
+ [1.0.14]: https://www.npmjs.com/package/icore/v/1.0.14
274
+ [1.0.13]: https://www.npmjs.com/package/icore/v/1.0.13
205
275
  [1.0.12]: https://www.npmjs.com/package/icore/v/1.0.12
206
276
  [1.0.11]: https://www.npmjs.com/package/icore/v/1.0.11
207
277
  [1.0.10]: https://www.npmjs.com/package/icore/v/1.0.10
@@ -15,10 +15,10 @@ import { type Presentation, type PresentationResult } from '../presentation/faca
15
15
  import { type PresentationFormat } from '../presentation/format-options';
16
16
  import { type Output } from '../output/facade';
17
17
  /**
18
- * Supported handler result shapes for the terminal app boundary.
18
+ * Supported terminal output shapes for the terminal app boundary.
19
19
  *
20
- * Domain commands may return ready text, streaming text, a presentation view,
21
- * or no output. Other result shapes are rejected before writing to stdout.
20
+ * Terminal output may be ready text, streaming text, a presentation view, or
21
+ * no output. Other result shapes are rejected before writing to stdout.
22
22
  */
23
23
  export type TerminalCommandOutput = string | AsyncIterable<string> | PresentationResult | undefined;
24
24
  type BivariantCallback<TInput, TOutput> = {
@@ -30,7 +30,7 @@ type TerminalCommandDefinition = {
30
30
  metadata?: unknown;
31
31
  allowExtraPositionals?: boolean;
32
32
  prepare?: BivariantCallback<PreparedCommandInput, unknown | Promise<unknown>>;
33
- handle: BivariantCallback<TerminalCommandInput, TerminalCommandOutput | Promise<TerminalCommandOutput>>;
33
+ handle: BivariantCallback<TerminalCommandInput, unknown | Promise<unknown>>;
34
34
  };
35
35
  type PreparedCommandInput = {
36
36
  options: Record<string, unknown>;
@@ -56,6 +56,15 @@ export type TerminalApp<TCommands extends readonly TerminalCommandDefinition[]>
56
56
  output: Output;
57
57
  /** No application context required. */
58
58
  prepare(args: readonly string[], options?: CommandResolutionOptions): Promise<PreparedCommand<TCommands[number]>>;
59
+ /** Runs an already prepared command through terminal rendering and output. */
60
+ runPrepared(prepared: PreparedCommand<TCommands[number]>, context: CommandContext<TCommands[number]>): Promise<number>;
61
+ /**
62
+ * Renders and writes already obtained terminal output.
63
+ *
64
+ * String output is written exactly as provided; add a trailing newline in the
65
+ * command result when line output is desired.
66
+ */
67
+ writePreparedOutput(prepared: PreparedCommand<TCommands[number]>, output: TerminalCommandOutput): Promise<void>;
59
68
  /** Returns a process-style exit code. */
60
69
  run(args: readonly string[], context: CommandContext<TCommands[number]>, options?: CommandResolutionOptions): Promise<number>;
61
70
  };
@@ -27,6 +27,21 @@ function createTerminalApp(options) {
27
27
  const presentation = options.presentation ?? (0, facade_1.createPresentation)();
28
28
  const output = options.output ?? (0, facade_2.createOutput)();
29
29
  const resolveFormat = options.resolveFormat ?? resolvePreparedFormat;
30
+ async function writePreparedOutput(prepared, terminalOutput) {
31
+ const format = resolveFormat(prepared);
32
+ await writeTerminalOutput(terminalOutput, format, presentation, output);
33
+ }
34
+ async function runPrepared(prepared, context) {
35
+ try {
36
+ const result = await options.commands.run(prepared, context);
37
+ await writeTerminalOutput(result, resolveFormat(prepared), presentation, output);
38
+ return 0;
39
+ }
40
+ catch (error) {
41
+ await output.error(renderTerminalError(error));
42
+ return 1;
43
+ }
44
+ }
30
45
  return {
31
46
  commands: options.commands,
32
47
  presentation,
@@ -34,13 +49,12 @@ function createTerminalApp(options) {
34
49
  prepare(args, commandOptions) {
35
50
  return options.commands.prepare(args, commandOptions);
36
51
  },
52
+ runPrepared,
53
+ writePreparedOutput,
37
54
  async run(args, context, commandOptions) {
38
55
  try {
39
56
  const prepared = await options.commands.prepare(args, commandOptions);
40
- const result = await options.commands.run(prepared, context);
41
- const format = resolveFormat(prepared);
42
- await writeTerminalOutput(result, format, presentation, output);
43
- return 0;
57
+ return runPrepared(prepared, context);
44
58
  }
45
59
  catch (error) {
46
60
  await output.error(renderTerminalError(error));
@@ -75,6 +75,72 @@ This is useful when the selected command decides which runtime resources to
75
75
  create. If preparation fails, the application can print an argument error
76
76
  without opening connections or starting background work.
77
77
 
78
+ ## Run Prepared Through A Terminal App
79
+
80
+ `app.runPrepared(...)` keeps terminal rendering, stdout/stderr writing, and exit
81
+ code handling in `createTerminalApp()` after the application has created its
82
+ own runtime context.
83
+
84
+ ```ts
85
+ const prepared = await app.prepare([
86
+ 'jobs',
87
+ 'run',
88
+ '--job-id',
89
+ 'job-42'
90
+ ], {
91
+ strict: true
92
+ });
93
+
94
+ const context = await createContext(prepared);
95
+
96
+ try {
97
+ process.exitCode = await app.runPrepared(prepared, context);
98
+ }
99
+ finally {
100
+ await cleanup(context);
101
+ }
102
+ ```
103
+
104
+ Resource creation and cleanup stay application-owned. The terminal app only
105
+ runs the prepared command and applies the same output behavior as `app.run(...)`.
106
+
107
+ ## Write Prepared Output
108
+
109
+ Use `app.writePreparedOutput(...)` when the application needs to inspect the raw
110
+ command result before terminal output is written.
111
+
112
+ ```ts
113
+ const prepared = await app.prepare([
114
+ 'jobs',
115
+ 'run',
116
+ '--job-id',
117
+ 'job-42'
118
+ ], {
119
+ strict: true
120
+ });
121
+
122
+ const context = await createContext(prepared);
123
+
124
+ try {
125
+ const result = await app.commands.run(prepared, context);
126
+
127
+ if (isShutdownHandle(result)) {
128
+ installShutdownHooks(result);
129
+
130
+ return;
131
+ }
132
+
133
+ await app.writePreparedOutput(prepared, result);
134
+ }
135
+ finally {
136
+ await cleanup(context);
137
+ }
138
+ ```
139
+
140
+ Only terminal output belongs here: ready text, streaming text, presentation
141
+ results, or no output. Strings are written exactly as provided; include `\n`
142
+ when line output is desired.
143
+
78
144
  ## Prepare From A Registry
79
145
 
80
146
  `prepareCommandFromArgs(...)` is the standalone primitive behind
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icore",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Declarative command line interface and terminal presentation mechanics for Node.js",
5
5
  "keywords": [
6
6
  "command-line",
package/readme.md CHANGED
@@ -80,7 +80,8 @@ without taking ownership of application behavior.
80
80
 
81
81
  The checked TypeScript contract lives in [`src/terminal/app.ts`](src/terminal/app.ts).
82
82
 
83
- The method returns an application object with `prepare(args)` and
83
+ The method returns an application object with `prepare(args)`,
84
+ `writePreparedOutput(prepared, output)`, `runPrepared(prepared, context)`, and
84
85
  `run(args, context)`.
85
86
 
86
87
  Construction inputs:
@@ -97,6 +98,10 @@ Returned app methods:
97
98
 
98
99
  - `app.prepare(args, options?)` delegates to
99
100
  [`commands.prepare(args, options?)`](#commandsprepareargs-options);
101
+ - `app.writePreparedOutput(prepared, output)` renders and writes already
102
+ obtained terminal output without running the command;
103
+ - `app.runPrepared(prepared, context)` runs an already prepared command, then
104
+ renders and writes terminal output;
100
105
  - `app.run(args, context, options?)` prepares the command, delegates command
101
106
  execution to [`commands.run(prepared, context)`](#commandsrunprepared-context),
102
107
  then renders and writes terminal output.
@@ -116,10 +121,15 @@ const exitCode = await app.run(args, context, {
116
121
  });
117
122
  ```
118
123
 
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.
124
+ Command handlers keep ownership of application work. Terminal output methods
125
+ accept terminal-ready results: text, streaming text, presentation results, or no
126
+ output. If a command returns an application lifecycle object, run it with
127
+ `app.commands.run(...)`, inspect or map the result, then pass only terminal
128
+ output to `app.writePreparedOutput(...)`. Application DTO mapping, config
129
+ loading, network clients, and domain behavior stay in the consuming application.
130
+
131
+ String output is written exactly as provided. Return `\n` from the command when
132
+ line output is desired.
123
133
 
124
134
  ### `createCommand()`
125
135