cli-kiss 0.2.1 → 0.2.3

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.
@@ -1,4 +1,4 @@
1
- import { OperationDescriptor } from "./Operation";
1
+ import { Operation } from "./Operation";
2
2
  import { OptionUsage } from "./Option";
3
3
  import { PositionalUsage } from "./Positional";
4
4
  import { ReaderArgs } from "./Reader";
@@ -11,172 +11,160 @@ import {
11
11
  } from "./Typo";
12
12
 
13
13
  /**
14
- * Describes a CLI command: how to parse its arguments from raw CLI input and how to
15
- * execute it within a given context.
14
+ * A CLI command parses arguments and executes within a given context.
15
+ * Created with {@link command}, {@link commandWithSubcommands}, or {@link commandChained}.
16
+ * Usually passed through {@link runAndExit} to run.
16
17
  *
17
- * A `CommandDescriptor` is the central building block of a `cli-kiss` CLI. You create
18
- * one with {@link command}, {@link commandWithSubcommands}, or {@link commandChained},
19
- * and pass it to {@link runAndExit} to run your CLI.
20
- *
21
- * @typeParam Context - The value passed into the command when it is executed. It flows
22
- * from {@link runAndExit}'s `context` argument down through the command chain.
23
- * @typeParam Result - The value produced by executing the command. For root commands
24
- * passed to {@link runAndExit} this is always `void`.
18
+ * @typeParam Context - Injected at execution time; forwarded to handlers. Use to inject dependencies.
19
+ * @typeParam Result - Value produced on execution; typically `void` for leaf commands.
25
20
  */
26
- export type CommandDescriptor<Context, Result> = {
27
- /** Returns the static metadata (description, hint, details) for this command. */
21
+ export type Command<Context, Result> = {
22
+ /**
23
+ * Returns the command's static metadata.
24
+ */
28
25
  getInformation(): CommandInformation;
29
26
  /**
30
- * Parses `readerArgs` and returns a {@link CommandFactory} that can generate usage
31
- * information or create a ready-to-run {@link CommandInstance}.
32
- *
33
- * Parsing errors are captured and deferred: `createFactory` never throws; instead
34
- * the error surfaces when {@link CommandFactory.createInstance} is called on the
35
- * returned factory.
27
+ * Consumes args in a `readerArgs` and returns a {@link CommandDecoder}.
36
28
  */
37
- createFactory(readerArgs: ReaderArgs): CommandFactory<Context, Result>;
29
+ consumeAndMakeDecoder(
30
+ readerArgs: ReaderArgs,
31
+ ): CommandDecoder<Context, Result>;
38
32
  };
39
33
 
40
34
  /**
41
- * Produced by {@link CommandDescriptor.createFactory} after the raw CLI arguments have
42
- * been parsed. Provides two capabilities:
43
- *
44
- * 1. **Usage generation** — always available, even when parsing failed.
45
- * 2. **Instance creation** — throws a {@link TypoError} if parsing failed.
35
+ * Produced by {@link Command.consumeAndMakeDecoder}.
46
36
  *
47
- * @typeParam Context - Forwarded from the parent {@link CommandDescriptor}.
48
- * @typeParam Result - Forwarded from the parent {@link CommandDescriptor}.
37
+ * @typeParam Context - See {@link Command}.
38
+ * @typeParam Result - See {@link Command}.
49
39
  */
50
- export type CommandFactory<Context, Result> = {
40
+ export type CommandDecoder<Context, Result> = {
51
41
  /**
52
- * Builds the complete {@link CommandUsage} for the currently parsed command path.
53
- * This is called to render the `--help` output and on error when `usageOnError`
54
- * is enabled.
42
+ * Builds the {@link CommandUsage} for the current command path.
43
+ * Used for `--help` and `usageOnError`.
55
44
  */
56
45
  generateUsage(): CommandUsage;
57
46
  /**
58
- * Creates a {@link CommandInstance} that is ready to execute.
47
+ * Creates a ready-to-execute {@link CommandInterpreter}.
59
48
  *
60
- * @throws {@link TypoError} if the argument parsing that occurred during
61
- * {@link CommandDescriptor.createFactory} encountered an error (e.g. unknown
62
- * option, missing required positional, invalid type).
49
+ * @throws {@link TypoError} if parsing or decoding failed.
63
50
  */
64
- createInstance(): CommandInstance<Context, Result>;
51
+ decodeAndMakeInterpreter(): CommandInterpreter<Context, Result>;
65
52
  };
66
53
 
67
54
  /**
68
- * A fully parsed, ready-to-execute command.
55
+ * A fully parsed, decoded and ready-to-execute command.
69
56
  *
70
- * @typeParam Context - The value the caller must provide when executing the command.
71
- * @typeParam Result - The value the command produces on successful execution.
57
+ * @typeParam Context - Caller-supplied context.
58
+ * @typeParam Result - Value produced on success.
72
59
  */
73
- export type CommandInstance<Context, Result> = {
60
+ export type CommandInterpreter<Context, Result> = {
74
61
  /**
75
- * Executes the command with the provided context.
76
- *
77
- * @param context - Arbitrary value injected by the caller (see {@link runAndExit}).
78
- * @returns A promise that resolves to the command's result, or rejects if the
79
- * command handler throws.
62
+ * Executes with the provided context.
80
63
  */
81
64
  executeWithContext(context: Context): Promise<Result>;
82
65
  };
83
66
 
84
67
  /**
85
- * Static, human-readable metadata attached to a command.
86
- *
87
- * This information is displayed in the usage/help output produced by {@link usageToStyledLines}.
68
+ * Static metadata for a command, shown in `--help` output via {@link usageToStyledLines}.
88
69
  */
89
70
  export type CommandInformation = {
90
- /** Short description of what the command does. Shown prominently in the usage header. */
71
+ /**
72
+ * Short description shown in the usage header.
73
+ */
91
74
  description: string;
92
75
  /**
93
- * Optional supplementary note shown in parentheses next to the description.
94
- * Suitable for short caveats such as `"deprecated"` or `"experimental"`.
76
+ * Short note shown in parentheses (e.g. `"deprecated"`, `"experimental"`).
95
77
  */
96
78
  hint?: string;
97
79
  /**
98
- * Optional list of additional detail lines printed below the description.
99
- * Useful for multi-line explanations, examples, or caveats that don't fit in
100
- * a single sentence.
80
+ * Extra lines printed below the description.
101
81
  */
102
82
  details?: Array<string>;
103
- // TODO - printable examples ?
83
+ /**
84
+ * Examples shown in the `Examples:` section of the usage output.
85
+ */
86
+ examples?: Array<{
87
+ /**
88
+ * Explanation shown above the example.
89
+ */
90
+ explanation: string;
91
+ /**
92
+ * Command line args to show as an example of usage.
93
+ */
94
+ commandArgs: Array<
95
+ | string
96
+ | { positional: string }
97
+ | { subcommand: string }
98
+ | {
99
+ option:
100
+ | { long: string; value?: string }
101
+ | { short: string; value?: string };
102
+ }
103
+ >;
104
+ }>;
104
105
  };
105
106
 
106
107
  /**
107
- * The full usage/help model for a command as it appears after argument parsing.
108
- *
109
- * This is produced by {@link CommandFactory.generateUsage} and consumed by
110
- * {@link usageToStyledLines} to render the `--help` output.
108
+ * Full usage/help model.
109
+ * Produced by {@link CommandDecoder.generateUsage},
110
+ * Consumed by {@link usageToStyledLines}.
111
111
  */
112
112
  export type CommandUsage = {
113
113
  /**
114
- * Ordered list of breadcrumb segments that form the command's usage line, e.g.:
115
- * `Usage: my-cli <POSITIONAL> subcommand <ANOTHER_POSITIONAL>`.
116
- *
117
- * Each element is either a positional placeholder or a literal subcommand name.
114
+ * Segments forming the usage line
115
+ * (e.g. `my-cli <POSITIONAL> subcommand <ANOTHER_POSITIONAL>`).
116
+ */
117
+ segments: Array<CommandUsageSegment>;
118
+ /**
119
+ * Command's static metadata.
118
120
  */
119
- breadcrumbs: Array<CommandUsageBreadcrumb>;
120
- /** The command's static metadata (description, hint, details). */
121
121
  information: CommandInformation;
122
122
  /**
123
- * Positional arguments that belong to the current command path,
124
- * in the order they must appear on the command line.
123
+ * Positionals in declaration order.
125
124
  */
126
125
  positionals: Array<PositionalUsage>;
127
126
  /**
128
- * Subcommands available at the current level of the command hierarchy.
129
- * Non-empty only when the command is a {@link commandWithSubcommands} and the
130
- * subcommand selection could not be resolved (i.e. on error or `--help`).
127
+ * Available subcommands. Non-empty when subcommand was not specified.
131
128
  */
132
129
  subcommands: Array<CommandUsageSubcommand>;
133
130
  /**
134
- * Options (flags and valued options) accepted by the current command path,
135
- * in the order they were registered.
131
+ * Options in registration order.
136
132
  */
137
133
  options: Array<OptionUsage>;
138
134
  };
139
135
 
140
136
  /**
141
- * A single element in the usage breadcrumb trail shown at the top of the help output.
142
- *
143
- * - `{ positional: string }` — A positional placeholder such as `<NAME>` or `[FILE]`.
144
- * - `{ command: string }` — A literal subcommand token such as `deploy`.
137
+ * One element in the usage segment trail.
145
138
  */
146
- export type CommandUsageBreadcrumb =
147
- | { positional: string }
148
- | { command: string };
139
+ export type CommandUsageSegment = { positional: string } | { command: string };
149
140
 
150
141
  /**
151
- * Summary information about a single subcommand shown in the `Subcommands:` section
152
- * of the usage output.
142
+ * Subcommand entry shown in the `Subcommands:` section of the usage output.
153
143
  */
154
144
  export type CommandUsageSubcommand = {
155
- /** The literal token the user types to select this subcommand (e.g. `"deploy"`). */
145
+ /**
146
+ * Literal token the user types (e.g. `"deploy"`).
147
+ */
156
148
  name: string;
157
- /** Short description forwarded from the subcommand's {@link CommandInformation}. */
149
+ /**
150
+ * Short description from the subcommand's {@link CommandInformation}.
151
+ */
158
152
  description: string | undefined;
159
- /** Optional hint forwarded from the subcommand's {@link CommandInformation}. */
153
+ /**
154
+ * Hint from the subcommand's {@link CommandInformation}.
155
+ */
160
156
  hint: string | undefined;
161
157
  };
162
158
 
163
159
  /**
164
- * Creates a leaf command — a command that has no subcommands and directly executes
165
- * an {@link OperationDescriptor}.
160
+ * Creates a leaf command that directly executes an {@link Operation}.
166
161
  *
167
- * During parsing, `command` reads all positionals and options consumed by `operation`,
168
- * then asserts that no extra positionals remain. Any unexpected trailing positional
169
- * causes a {@link TypoError} deferred to {@link CommandFactory.createInstance}.
162
+ * @typeParam Context - Context forwarded to the handler.
163
+ * @typeParam Result - Value returned by the handler.
170
164
  *
171
- * @typeParam Context - The context value forwarded to the operation handler at
172
- * execution time.
173
- * @typeParam Result - The value returned by the operation handler.
174
- *
175
- * @param information - Static metadata (description, hint, details) for the command.
176
- * @param operation - The operation that defines options, positionals, and the execution
177
- * handler for this command.
178
- * @returns A {@link CommandDescriptor} suitable for passing to {@link runAndExit}
179
- * or composing with {@link commandWithSubcommands} / {@link commandChained}.
165
+ * @param information - Command metadata (description, hint, details).
166
+ * @param operation - Defines: options, positionals, and the handler.
167
+ * @returns A {@link Command}.
180
168
  *
181
169
  * @example
182
170
  * ```ts
@@ -191,27 +179,15 @@ export type CommandUsageSubcommand = {
191
179
  */
192
180
  export function command<Context, Result>(
193
181
  information: CommandInformation,
194
- operation: OperationDescriptor<Context, Result>,
195
- ): CommandDescriptor<Context, Result> {
182
+ operation: Operation<Context, Result>,
183
+ ): Command<Context, Result> {
196
184
  return {
197
185
  getInformation() {
198
186
  return information;
199
187
  },
200
- createFactory(readerArgs: ReaderArgs) {
201
- function generateUsage(): CommandUsage {
202
- const operationUsage = operation.generateUsage();
203
- return {
204
- breadcrumbs: operationUsage.positionals.map((positional) =>
205
- breadcrumbPositional(positional.label),
206
- ),
207
- information: information,
208
- positionals: operationUsage.positionals,
209
- subcommands: [],
210
- options: operationUsage.options,
211
- };
212
- }
188
+ consumeAndMakeDecoder(readerArgs: ReaderArgs) {
213
189
  try {
214
- const operationFactory = operation.createFactory(readerArgs);
190
+ const operationDecoder = operation.consumeAndMakeDecoder(readerArgs);
215
191
  const endPositional = readerArgs.consumePositional();
216
192
  if (endPositional !== undefined) {
217
193
  throw new TypoError(
@@ -222,20 +198,21 @@ export function command<Context, Result>(
222
198
  );
223
199
  }
224
200
  return {
225
- generateUsage,
226
- createInstance() {
227
- const operationInstance = operationFactory.createInstance();
201
+ generateUsage: () => generateUsageShallow(information, operation),
202
+ decodeAndMakeInterpreter() {
203
+ const operationInterpreter =
204
+ operationDecoder.decodeAndMakeInterpreter();
228
205
  return {
229
206
  async executeWithContext(context: Context) {
230
- return await operationInstance.executeWithContext(context);
207
+ return await operationInterpreter.executeWithContext(context);
231
208
  },
232
209
  };
233
210
  },
234
211
  };
235
212
  } catch (error) {
236
213
  return {
237
- generateUsage,
238
- createInstance() {
214
+ generateUsage: () => generateUsageShallow(information, operation),
215
+ decodeAndMakeInterpreter() {
239
216
  throw error;
240
217
  },
241
218
  };
@@ -245,34 +222,17 @@ export function command<Context, Result>(
245
222
  }
246
223
 
247
224
  /**
248
- * Creates a command that first runs an {@link OperationDescriptor} to produce an
249
- * intermediate `Payload`, then dispatches execution to one of several named subcommands
250
- * based on the next positional argument.
251
- *
252
- * **Parsing behaviour:**
253
- * 1. The `operation`'s positionals and options are parsed from `readerArgs`.
254
- * 2. The next positional token is consumed as the subcommand name.
255
- * - If no token is present, a {@link TypoError} is deferred.
256
- * - If the token does not match any key in `subcommands`, a {@link TypoError} is
257
- * deferred.
258
- * 3. The matched subcommand's factory is created with the remaining `readerArgs`.
259
- *
260
- * **Usage on error / `--help`:** when the subcommand cannot be determined, the usage
261
- * output lists all available subcommands under a `Subcommands:` section.
225
+ * Creates a command that runs an {@link Operation} to produce a `Payload`,
226
+ * then dispatches to a named subcommand based on the next positional token.
262
227
  *
263
- * @typeParam Context - The context value accepted by the root operation handler.
264
- * @typeParam Payload - The value produced by the root operation and forwarded as the
265
- * context to the selected subcommand.
266
- * @typeParam Result - The value produced by the selected subcommand.
228
+ * @typeParam Context - Context accepted by `operation`.
229
+ * @typeParam Payload - Output of `operation`; becomes the subcommand's context.
230
+ * @typeParam Result - Value produced by the selected subcommand.
267
231
  *
268
- * @param information - Static metadata shown in the top-level usage when no valid
269
- * subcommand has been selected.
270
- * @param operation - The operation that is always executed first, before the
271
- * subcommand. Its output becomes the subcommand's context.
272
- * @param subcommands - A map of lowercase subcommand names to their
273
- * {@link CommandDescriptor}s. The keys are the literal tokens the user types.
274
- * @returns A {@link CommandDescriptor} that dispatches to one of the provided
275
- * subcommands.
232
+ * @param information - Command metadata (description, hint, details).
233
+ * @param operation - Always runs first; its output becomes the subcommand's context.
234
+ * @param subcommands - Map of subcommand names to their {@link Command}s.
235
+ * @returns A {@link Command} that dispatches to one of the provided subcommands.
276
236
  *
277
237
  * @example
278
238
  * ```ts
@@ -288,18 +248,16 @@ export function command<Context, Result>(
288
248
  */
289
249
  export function commandWithSubcommands<Context, Payload, Result>(
290
250
  information: CommandInformation,
291
- operation: OperationDescriptor<Context, Payload>,
292
- subcommands: {
293
- [subcommand: Lowercase<string>]: CommandDescriptor<Payload, Result>;
294
- },
295
- ): CommandDescriptor<Context, Result> {
251
+ operation: Operation<Context, Payload>,
252
+ subcommands: { [subcommand: Lowercase<string>]: Command<Payload, Result> },
253
+ ): Command<Context, Result> {
296
254
  return {
297
255
  getInformation() {
298
256
  return information;
299
257
  },
300
- createFactory(readerArgs: ReaderArgs) {
258
+ consumeAndMakeDecoder(readerArgs: ReaderArgs) {
301
259
  try {
302
- const operationFactory = operation.createFactory(readerArgs);
260
+ const operationDecoder = operation.consumeAndMakeDecoder(readerArgs);
303
261
  const subcommandName = readerArgs.consumePositional();
304
262
  if (subcommandName === undefined) {
305
263
  throw new TypoError(
@@ -320,31 +278,29 @@ export function commandWithSubcommands<Context, Payload, Result>(
320
278
  ),
321
279
  );
322
280
  }
323
- const subcommandFactory = subcommandInput.createFactory(readerArgs);
281
+ const subcommandDecoder =
282
+ subcommandInput.consumeAndMakeDecoder(readerArgs);
324
283
  return {
325
284
  generateUsage() {
326
- const operationUsage = operation.generateUsage();
327
- const subcommandUsage = subcommandFactory.generateUsage();
328
- return {
329
- breadcrumbs: operationUsage.positionals
330
- .map((positional) => breadcrumbPositional(positional.label))
331
- .concat([breadcrumbCommand(subcommandName)])
332
- .concat(subcommandUsage.breadcrumbs),
333
- information: subcommandUsage.information,
334
- positionals: operationUsage.positionals.concat(
335
- subcommandUsage.positionals,
336
- ),
337
- subcommands: subcommandUsage.subcommands,
338
- options: operationUsage.options.concat(subcommandUsage.options),
339
- };
285
+ const subcommandUsage = subcommandDecoder.generateUsage();
286
+ const currentUsage = generateUsageShallow(information, operation);
287
+ currentUsage.segments.push(segmentCommand(subcommandName));
288
+ currentUsage.segments.push(...subcommandUsage.segments);
289
+ currentUsage.information = subcommandUsage.information;
290
+ currentUsage.positionals.push(...subcommandUsage.positionals);
291
+ currentUsage.subcommands = subcommandUsage.subcommands;
292
+ currentUsage.options.push(...subcommandUsage.options);
293
+ return currentUsage;
340
294
  },
341
- createInstance() {
342
- const operationInstance = operationFactory.createInstance();
343
- const subcommandInstance = subcommandFactory.createInstance();
295
+ decodeAndMakeInterpreter() {
296
+ const operationInterpreter =
297
+ operationDecoder.decodeAndMakeInterpreter();
298
+ const subcommandInterpreter =
299
+ subcommandDecoder.decodeAndMakeInterpreter();
344
300
  return {
345
301
  async executeWithContext(context: Context) {
346
- return await subcommandInstance.executeWithContext(
347
- await operationInstance.executeWithContext(context),
302
+ return await subcommandInterpreter.executeWithContext(
303
+ await operationInterpreter.executeWithContext(context),
348
304
  );
349
305
  },
350
306
  };
@@ -353,25 +309,15 @@ export function commandWithSubcommands<Context, Payload, Result>(
353
309
  } catch (error) {
354
310
  return {
355
311
  generateUsage() {
356
- const operationUsage = operation.generateUsage();
357
- return {
358
- breadcrumbs: operationUsage.positionals
359
- .map((positional) => breadcrumbPositional(positional.label))
360
- .concat([breadcrumbPositional("<SUBCOMMAND>")]),
361
- information: information,
362
- positionals: operationUsage.positionals,
363
- subcommands: Object.entries(subcommands).map((subcommand) => {
364
- const metadata = subcommand[1].getInformation();
365
- return {
366
- name: subcommand[0],
367
- description: metadata.description,
368
- hint: metadata.hint,
369
- };
370
- }),
371
- options: operationUsage.options,
372
- };
312
+ const currentUsage = generateUsageShallow(information, operation);
313
+ currentUsage.segments.push(segmentPositional("<SUBCOMMAND>"));
314
+ for (const [name, subcommand] of Object.entries(subcommands)) {
315
+ const { description, hint } = subcommand.getInformation();
316
+ currentUsage.subcommands.push({ name, description, hint });
317
+ }
318
+ return currentUsage;
373
319
  },
374
- createInstance() {
320
+ decodeAndMakeInterpreter() {
375
321
  throw error;
376
322
  },
377
323
  };
@@ -381,36 +327,17 @@ export function commandWithSubcommands<Context, Payload, Result>(
381
327
  }
382
328
 
383
329
  /**
384
- * Creates a command that chains two command stages by piping the output of an
385
- * {@link OperationDescriptor} directly into a {@link CommandDescriptor} as its context.
386
- *
387
- * Unlike {@link commandWithSubcommands}, there is no runtime token consumed for routing;
388
- * the `nextCommand` is always the continuation. This is useful for splitting a complex
389
- * command into reusable pieces, such as a shared authentication step followed by
390
- * different sub-actions.
330
+ * Chains an {@link Operation} and a {@link Command}: `operation` runs first, its
331
+ * output becomes `subcommand`'s context. No token is consumed for routing.
391
332
  *
392
- * **Parsing behaviour:**
393
- * 1. `operation`'s positionals and options are parsed.
394
- * 2. `nextCommand`'s factory is immediately created from the same `readerArgs` (the
395
- * remaining unparsed tokens).
396
- * 3. At execution time, `operation` runs first; its result is passed as the context to
397
- * `nextCommand`.
333
+ * @typeParam Context - Context accepted by `operation`.
334
+ * @typeParam Payload - Output of `operation`; becomes `subcommand`'s context.
335
+ * @typeParam Result - Value produced by `subcommand`.
398
336
  *
399
- * **Usage:** breadcrumbs, positionals, and options from both stages are merged into a
400
- * single flat usage description. The `information` of `nextCommand` takes precedence in
401
- * the generated usage output.
402
- *
403
- * @typeParam Context - The context value accepted by `operation`.
404
- * @typeParam Payload - The value produced by `operation` and used as the context for
405
- * `nextCommand`.
406
- * @typeParam Result - The value produced by `nextCommand`.
407
- *
408
- * @param information - Fallback metadata used in the usage output when `nextCommand`'s
409
- * factory cannot be created (i.e. on parse error in the next stage).
410
- * @param operation - The first stage operation. Its output becomes `nextCommand`'s
411
- * context.
412
- * @param nextCommand - The second stage command, executed after `operation` succeeds.
413
- * @returns A {@link CommandDescriptor} that transparently composes the two stages.
337
+ * @param information - Command metadata (description, hint, details).
338
+ * @param operation - First stage; its output is passed as `subcommand`'s context.
339
+ * @param subcommand - Second stage, executed after `operation`.
340
+ * @returns A {@link Command} transparently composing the two stages.
414
341
  *
415
342
  * @example
416
343
  * ```ts
@@ -426,40 +353,37 @@ export function commandWithSubcommands<Context, Payload, Result>(
426
353
  */
427
354
  export function commandChained<Context, Payload, Result>(
428
355
  information: CommandInformation,
429
- operation: OperationDescriptor<Context, Payload>,
430
- nextCommand: CommandDescriptor<Payload, Result>,
431
- ): CommandDescriptor<Context, Result> {
356
+ operation: Operation<Context, Payload>,
357
+ subcommand: Command<Payload, Result>,
358
+ ): Command<Context, Result> {
432
359
  return {
433
360
  getInformation() {
434
361
  return information;
435
362
  },
436
- createFactory(readerArgs: ReaderArgs) {
363
+ consumeAndMakeDecoder(readerArgs: ReaderArgs) {
437
364
  try {
438
- const operationFactory = operation.createFactory(readerArgs);
439
- const nextCommandFactory = nextCommand.createFactory(readerArgs);
365
+ const operationDecoder = operation.consumeAndMakeDecoder(readerArgs);
366
+ const subcommandDecoder = subcommand.consumeAndMakeDecoder(readerArgs);
440
367
  return {
441
368
  generateUsage() {
442
- const operationUsage = operation.generateUsage();
443
- const nextCommandUsage = nextCommandFactory.generateUsage();
444
- return {
445
- breadcrumbs: operationUsage.positionals
446
- .map((positional) => breadcrumbPositional(positional.label))
447
- .concat(nextCommandUsage.breadcrumbs),
448
- information: nextCommandUsage.information,
449
- positionals: operationUsage.positionals.concat(
450
- nextCommandUsage.positionals,
451
- ),
452
- subcommands: nextCommandUsage.subcommands,
453
- options: operationUsage.options.concat(nextCommandUsage.options),
454
- };
369
+ const subcommandUsage = subcommandDecoder.generateUsage();
370
+ const currentUsage = generateUsageShallow(information, operation);
371
+ currentUsage.segments.push(...subcommandUsage.segments);
372
+ currentUsage.information = subcommandUsage.information;
373
+ currentUsage.positionals.push(...subcommandUsage.positionals);
374
+ currentUsage.subcommands = subcommandUsage.subcommands;
375
+ currentUsage.options.push(...subcommandUsage.options);
376
+ return currentUsage;
455
377
  },
456
- createInstance() {
457
- const operationInstance = operationFactory.createInstance();
458
- const nextCommandInstance = nextCommandFactory.createInstance();
378
+ decodeAndMakeInterpreter() {
379
+ const operationInterpreter =
380
+ operationDecoder.decodeAndMakeInterpreter();
381
+ const subcommandInterpreter =
382
+ subcommandDecoder.decodeAndMakeInterpreter();
459
383
  return {
460
384
  async executeWithContext(context: Context) {
461
- return await nextCommandInstance.executeWithContext(
462
- await operationInstance.executeWithContext(context),
385
+ return await subcommandInterpreter.executeWithContext(
386
+ await operationInterpreter.executeWithContext(context),
463
387
  );
464
388
  },
465
389
  };
@@ -468,18 +392,11 @@ export function commandChained<Context, Payload, Result>(
468
392
  } catch (error) {
469
393
  return {
470
394
  generateUsage() {
471
- const operationUsage = operation.generateUsage();
472
- return {
473
- breadcrumbs: operationUsage.positionals
474
- .map((positional) => breadcrumbPositional(positional.label))
475
- .concat([breadcrumbPositional("[REST]...")]),
476
- information: information,
477
- positionals: operationUsage.positionals,
478
- subcommands: [],
479
- options: operationUsage.options,
480
- };
395
+ const currentUsage = generateUsageShallow(information, operation);
396
+ currentUsage.segments.push(segmentPositional("[REST]..."));
397
+ return currentUsage;
481
398
  },
482
- createInstance() {
399
+ decodeAndMakeInterpreter() {
483
400
  throw error;
484
401
  },
485
402
  };
@@ -488,10 +405,26 @@ export function commandChained<Context, Payload, Result>(
488
405
  };
489
406
  }
490
407
 
491
- function breadcrumbPositional(value: string): CommandUsageBreadcrumb {
408
+ function segmentPositional(value: string): CommandUsageSegment {
492
409
  return { positional: value };
493
410
  }
494
411
 
495
- function breadcrumbCommand(value: string): CommandUsageBreadcrumb {
412
+ function segmentCommand(value: string): CommandUsageSegment {
496
413
  return { command: value };
497
414
  }
415
+
416
+ function generateUsageShallow(
417
+ information: CommandInformation,
418
+ operation: Operation<any, any>,
419
+ ): CommandUsage {
420
+ const { positionals, options } = operation.generateUsage();
421
+ return {
422
+ segments: positionals.map((positional) =>
423
+ segmentPositional(positional.label),
424
+ ),
425
+ information,
426
+ positionals,
427
+ subcommands: [],
428
+ options,
429
+ };
430
+ }