cli-forge 0.11.0 → 1.0.0

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,28 @@
1
- import { type ConfigurationFiles, OptionConfig, OptionConfigToType, ParsedArgs, EnvOptionConfig, ObjectOptionConfig, StringOptionConfig, NumberOptionConfig, BooleanOptionConfig, ArrayOptionConfig, ResolveProperties, AdditionalPropertiesType, WithOptional } from '@cli-forge/parser';
1
+ import { type ConfigurationFiles, OptionConfig, OptionConfigToType, ParsedArgs, EnvOptionConfig, ObjectOptionConfig, StringOptionConfig, NumberOptionConfig, BooleanOptionConfig, ArrayOptionConfig, ResolveProperties, WithOptional, MakeUndefinedPropertiesOptional } from '@cli-forge/parser';
2
+ import { InternalCLI } from './internal-cli';
3
+ /**
4
+ * Extracts the command name from a Command type.
5
+ * Works with both CLI instances and command config objects.
6
+ */
7
+ export type ExtractCommandName<T> = T extends CLI<any, any, any> ? T extends InternalCLI<any, any, any> ? string : string : T extends {
8
+ name: infer N;
9
+ } ? N extends string ? N : string : string;
10
+ /**
11
+ * Extracts the args type from a Command.
12
+ * Works with both CLI instances and command config objects.
13
+ */
14
+ export type ExtractCommandArgs<T> = T extends CLI<infer A, any, any> ? A : T extends CLICommandOptions<any, infer A, any, any> ? A : ParsedArgs;
15
+ /**
16
+ * Extracts the handler return type from a Command.
17
+ */
18
+ export type ExtractCommandHandlerReturn<T> = T extends CLI<any, infer R, any> ? R : T extends CLICommandOptions<any, any, infer R, any> ? R : void;
19
+ /**
20
+ * Converts a Command to its child CLI entry for TChildren tracking.
21
+ * TParentCLI is the parent CLI type that will be set as the child's TParent.
22
+ */
23
+ export type CommandToChildEntry<T, TParentCLI = undefined> = {
24
+ [K in ExtractCommandName<T>]: CLI<ExtractCommandArgs<T>, ExtractCommandHandlerReturn<T>, {}, TParentCLI>;
25
+ };
2
26
  /**
3
27
  * The interface for a CLI application or subcommands.
4
28
  *
@@ -18,37 +42,48 @@ import { type ConfigurationFiles, OptionConfig, OptionConfigToType, ParsedArgs,
18
42
  * }).forge();
19
43
  * ```
20
44
  */
21
- export interface CLI<TArgs extends ParsedArgs = ParsedArgs> {
22
- command<TCommandArgs extends TArgs>(cmd: Command<TArgs, TCommandArgs>): CLI<TArgs>;
45
+ export interface CLI<TArgs extends ParsedArgs = ParsedArgs, THandlerReturn = void, TChildren = {}, TParent = undefined> {
46
+ command<TCommandArgs extends TArgs>(cmd: Command<TArgs, TCommandArgs>): CLI<TArgs, THandlerReturn, TChildren & (typeof cmd extends Command<TArgs, infer TCommandArgs, infer TCmdName> ? {
47
+ [key in TCmdName]: CLI<TCommandArgs, void, {}, CLI<TArgs, THandlerReturn, TChildren, TParent>>;
48
+ } : {}), TParent>;
23
49
  /**
24
50
  * Registers a new command with the CLI.
25
51
  * @param key What should the new command be called?
26
52
  * @param options Settings for the new command. See {@link CLICommandOptions}.
27
53
  * @returns Updated CLI instance with the new command registered.
28
54
  */
29
- command<TCommandArgs extends TArgs>(key: string, options: CLICommandOptions<TArgs, TCommandArgs>): CLI<TArgs>;
30
- /**
31
- * Registers multiple subcommands with the CLI.
32
- * @param commands Several commands to register. Can be the result of a call to {@link cli} or a configuration object.
33
- */
34
- commands(commands: Command[]): CLI<TArgs>;
55
+ command<TCommandArgs extends TArgs, TChildHandlerReturn, TKey extends string, TChildChildren = {}>(key: TKey, options: CLICommandOptions<TArgs, TCommandArgs, TChildHandlerReturn, TChildren, CLI<TArgs, THandlerReturn, TChildren, TParent>, TChildChildren>): CLI<TArgs, THandlerReturn, TChildren & {
56
+ [key in TKey]: CLI<TCommandArgs, TChildHandlerReturn, TChildChildren, CLI<TArgs, THandlerReturn, TChildren, TParent>>;
57
+ }, TParent>;
35
58
  /**
36
59
  * Registers multiple subcommands with the CLI.
37
60
  * @param commands Several commands to register. Can be the result of a call to {@link cli} or a configuration object.
61
+ * @returns Updated CLI instance with the commands registered and their types tracked in TChildren.
38
62
  */
39
- commands(...commands: Command[]): CLI<TArgs>;
63
+ commands<C1 extends Command>(c1: C1): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
64
+ commands<C1 extends Command, C2 extends Command>(c1: C1, c2: C2): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
65
+ commands<C1 extends Command, C2 extends Command, C3 extends Command>(c1: C1, c2: C2, c3: C3): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
66
+ commands<C1 extends Command, C2 extends Command, C3 extends Command, C4 extends Command>(c1: C1, c2: C2, c3: C3, c4: C4): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C4, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
67
+ commands<C1 extends Command, C2 extends Command, C3 extends Command, C4 extends Command, C5 extends Command>(c1: C1, c2: C2, c3: C3, c4: C4, c5: C5): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C4, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C5, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
68
+ commands<C1 extends Command, C2 extends Command, C3 extends Command, C4 extends Command, C5 extends Command, C6 extends Command>(c1: C1, c2: C2, c3: C3, c4: C4, c5: C5, c6: C6): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C4, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C5, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C6, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
69
+ commands<C1 extends Command, C2 extends Command, C3 extends Command, C4 extends Command, C5 extends Command, C6 extends Command, C7 extends Command>(c1: C1, c2: C2, c3: C3, c4: C4, c5: C5, c6: C6, c7: C7): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C4, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C5, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C6, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C7, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
70
+ commands<C1 extends Command, C2 extends Command, C3 extends Command, C4 extends Command, C5 extends Command, C6 extends Command, C7 extends Command, C8 extends Command>(c1: C1, c2: C2, c3: C3, c4: C4, c5: C5, c6: C6, c7: C7, c8: C8): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C4, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C5, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C6, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C7, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C8, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
71
+ commands<C1 extends Command, C2 extends Command, C3 extends Command, C4 extends Command, C5 extends Command, C6 extends Command, C7 extends Command, C8 extends Command, C9 extends Command>(c1: C1, c2: C2, c3: C3, c4: C4, c5: C5, c6: C6, c7: C7, c8: C8, c9: C9): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C4, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C5, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C6, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C7, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C8, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C9, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
72
+ commands<C1 extends Command, C2 extends Command, C3 extends Command, C4 extends Command, C5 extends Command, C6 extends Command, C7 extends Command, C8 extends Command, C9 extends Command, C10 extends Command>(c1: C1, c2: C2, c3: C3, c4: C4, c5: C5, c6: C6, c7: C7, c8: C8, c9: C9, c10: C10): CLI<TArgs, THandlerReturn, TChildren & CommandToChildEntry<C1, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C2, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C3, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C4, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C5, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C6, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C7, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C8, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C9, CLI<TArgs, THandlerReturn, TChildren, TParent>> & CommandToChildEntry<C10, CLI<TArgs, THandlerReturn, TChildren, TParent>>, TParent>;
73
+ commands(commands: Command[]): CLI<TArgs, THandlerReturn, TChildren, TParent>;
74
+ commands(...commands: Command[]): CLI<TArgs, THandlerReturn, TChildren, TParent>;
40
75
  /**
41
76
  * Register's a configuration provider for the CLI. See {@link ConfigurationProviders} for built-in providers.
42
77
  *
43
78
  * @param provider Provider to register.
44
79
  */
45
- config(provider: ConfigurationFiles.ConfigurationProvider<TArgs>): CLI<TArgs>;
80
+ config(provider: ConfigurationFiles.ConfigurationProvider<TArgs>): CLI<TArgs, THandlerReturn, TChildren, TParent>;
46
81
  /**
47
82
  * Enables the ability to run CLI commands that contain subcommands as an interactive shell.
48
83
  * This presents as a small shell that only knows the current command and its subcommands.
49
84
  * Any flags already consumed by the command will be passed to every subcommand invocation.
50
85
  */
51
- enableInteractiveShell(): CLI<TArgs>;
86
+ enableInteractiveShell(): CLI<TArgs, THandlerReturn, TChildren, TParent>;
52
87
  /**
53
88
  * Registers a custom global error handler for the CLI. This handler will be called when an error is thrown
54
89
  * during the execution of the CLI and not otherwise handled. Error handlers should re-throw the error if they
@@ -57,7 +92,7 @@ export interface CLI<TArgs extends ParsedArgs = ParsedArgs> {
57
92
  * @param handler Typically called with an Error object, but you should be prepared to handle any type of error.
58
93
  * @param actions Actions that can be taken by the error handler. Prefer using these over process.exit for better support of interactive shells.
59
94
  */
60
- errorHandler(handler: ErrorHandler): CLI<TArgs>;
95
+ errorHandler(handler: ErrorHandler): CLI<TArgs, THandlerReturn, TChildren, TParent>;
61
96
  /**
62
97
  * Registers a new option for the CLI command. This option will be accessible
63
98
  * within the command handler, as well as any subcommands.
@@ -68,24 +103,24 @@ export interface CLI<TArgs extends ParsedArgs = ParsedArgs> {
68
103
  */
69
104
  option<TOption extends string, TCoerce, const TProps extends Record<string, {
70
105
  type: string;
71
- }>, TAdditionalProps extends false | 'string' | 'number' | 'boolean' = false>(name: TOption, config: ObjectOptionConfig<TCoerce, TProps, TAdditionalProps>): CLI<TArgs & {
72
- [key in TOption]: WithOptional<unknown extends TCoerce ? ResolveProperties<TProps> & AdditionalPropertiesType<TAdditionalProps> : TCoerce, ObjectOptionConfig<TCoerce, TProps, TAdditionalProps>>;
73
- }>;
74
- option<TOption extends string, const TConfig extends StringOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
106
+ }>>(name: TOption, config: ObjectOptionConfig<TCoerce, TProps>): CLI<TArgs & MakeUndefinedPropertiesOptional<{
107
+ [key in TOption]: WithOptional<unknown extends TCoerce ? ResolveProperties<TProps> : TCoerce, ObjectOptionConfig<TCoerce, TProps>>;
108
+ }>, THandlerReturn, TChildren, TParent>;
109
+ option<TOption extends string, const TConfig extends StringOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
75
110
  [key in TOption]: OptionConfigToType<TConfig>;
76
- }>;
77
- option<TOption extends string, const TConfig extends NumberOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
111
+ }>, THandlerReturn, TChildren, TParent>;
112
+ option<TOption extends string, const TConfig extends NumberOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
78
113
  [key in TOption]: OptionConfigToType<TConfig>;
79
- }>;
80
- option<TOption extends string, const TConfig extends BooleanOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
114
+ }>, THandlerReturn, TChildren, TParent>;
115
+ option<TOption extends string, const TConfig extends BooleanOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
81
116
  [key in TOption]: OptionConfigToType<TConfig>;
82
- }>;
83
- option<TOption extends string, const TConfig extends ArrayOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
117
+ }>, THandlerReturn, TChildren, TParent>;
118
+ option<TOption extends string, const TConfig extends ArrayOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
84
119
  [key in TOption]: OptionConfigToType<TConfig>;
85
- }>;
86
- option<TOption extends string, const TOptionConfig extends OptionConfig<any, any, any, any>>(name: TOption, config: TOptionConfig): CLI<TArgs & {
120
+ }>, THandlerReturn, TChildren, TParent>;
121
+ option<TOption extends string, const TOptionConfig extends OptionConfig<any, any, any>>(name: TOption, config: TOptionConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
87
122
  [key in TOption]: OptionConfigToType<TOptionConfig>;
88
- }>;
123
+ }>, THandlerReturn, TChildren, TParent>;
89
124
  /**
90
125
  * Registers a new positional argument for the CLI command. This argument will be accessible
91
126
  * within the command handler, as well as any subcommands.
@@ -95,63 +130,63 @@ export interface CLI<TArgs extends ParsedArgs = ParsedArgs> {
95
130
  */
96
131
  positional<TOption extends string, TCoerce, const TProps extends Record<string, {
97
132
  type: string;
98
- }>, TAdditionalProps extends false | 'string' | 'number' | 'boolean' = false>(name: TOption, config: ObjectOptionConfig<TCoerce, TProps, TAdditionalProps>): CLI<TArgs & {
99
- [key in TOption]: WithOptional<unknown extends TCoerce ? ResolveProperties<TProps> & AdditionalPropertiesType<TAdditionalProps> : TCoerce, ObjectOptionConfig<TCoerce, TProps, TAdditionalProps>>;
100
- }>;
101
- positional<TOption extends string, const TConfig extends StringOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
133
+ }>>(name: TOption, config: ObjectOptionConfig<TCoerce, TProps>): CLI<TArgs & MakeUndefinedPropertiesOptional<{
134
+ [key in TOption]: WithOptional<unknown extends TCoerce ? ResolveProperties<TProps> : TCoerce, ObjectOptionConfig<TCoerce, TProps>>;
135
+ }>, THandlerReturn, TChildren, TParent>;
136
+ positional<TOption extends string, const TConfig extends StringOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
102
137
  [key in TOption]: OptionConfigToType<TConfig>;
103
- }>;
104
- positional<TOption extends string, const TConfig extends NumberOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
138
+ }>, THandlerReturn, TChildren, TParent>;
139
+ positional<TOption extends string, const TConfig extends NumberOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
105
140
  [key in TOption]: OptionConfigToType<TConfig>;
106
- }>;
107
- positional<TOption extends string, const TConfig extends BooleanOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
141
+ }>, THandlerReturn, TChildren, TParent>;
142
+ positional<TOption extends string, const TConfig extends BooleanOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
108
143
  [key in TOption]: OptionConfigToType<TConfig>;
109
- }>;
110
- positional<TOption extends string, const TConfig extends ArrayOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & {
144
+ }>, THandlerReturn, TChildren, TParent>;
145
+ positional<TOption extends string, const TConfig extends ArrayOptionConfig<any, any>>(name: TOption, config: TConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
111
146
  [key in TOption]: OptionConfigToType<TConfig>;
112
- }>;
113
- positional<TOption extends string, const TOptionConfig extends OptionConfig<any, any, any, any>>(name: TOption, config: TOptionConfig): CLI<TArgs & {
147
+ }>, THandlerReturn, TChildren, TParent>;
148
+ positional<TOption extends string, const TOptionConfig extends OptionConfig<any, any, any>>(name: TOption, config: TOptionConfig): CLI<TArgs & MakeUndefinedPropertiesOptional<{
114
149
  [key in TOption]: OptionConfigToType<TOptionConfig>;
115
- }>;
150
+ }>, THandlerReturn, TChildren, TParent>;
116
151
  /**
117
152
  * Adds support for reading CLI options from environment variables.
118
153
  * @param prefix The prefix to use when looking up environment variables. Defaults to the command name.
119
154
  */
120
- env(prefix?: string): CLI<TArgs>;
121
- env(options: EnvOptionConfig): CLI<TArgs>;
155
+ env(prefix?: string): CLI<TArgs, THandlerReturn, TChildren, TParent>;
156
+ env(options: EnvOptionConfig): CLI<TArgs, THandlerReturn, TChildren, TParent>;
122
157
  /**
123
158
  * Sets a group of options as mutually exclusive. If more than one option is provided, there will be a validation error.
124
159
  * @param options The options that should be mutually exclusive.
125
160
  */
126
- conflicts(...options: [string, string, ...string[]]): CLI<TArgs>;
161
+ conflicts(...options: [string, string, ...string[]]): CLI<TArgs, THandlerReturn, TChildren, TParent>;
127
162
  /**
128
163
  * Sets a group of options as mutually inclusive. If one option is provided, all other options must also be provided.
129
164
  * @param option The option that implies the other options.
130
165
  * @param impliedOptions The options which become required when the option is provided.
131
166
  */
132
- implies(option: string, ...impliedOptions: string[]): CLI<TArgs>;
167
+ implies(option: string, ...impliedOptions: string[]): CLI<TArgs, THandlerReturn, TChildren, TParent>;
133
168
  /**
134
169
  * Requires a command to be provided when executing the CLI. Useful if your parent command
135
170
  * cannot be executed on its own.
136
171
  * @returns Updated CLI instance.
137
172
  */
138
- demandCommand(): CLI<TArgs>;
173
+ demandCommand(): CLI<TArgs, THandlerReturn, TChildren, TParent>;
139
174
  /**
140
175
  * Sets the usage text for the CLI. This text will be displayed in place of the default usage text
141
176
  * @param usageText Text displayed in place of the default usage text for `--help` and in generated docs.
142
177
  */
143
- usage(usageText: string): CLI<TArgs>;
178
+ usage(usageText: string): CLI<TArgs, THandlerReturn, TChildren, TParent>;
144
179
  /**
145
180
  * Sets the description for the CLI. This text will be displayed in the help text and generated docs.
146
181
  * @param examples Examples to display in the help text and generated docs.
147
182
  */
148
- examples(...examples: string[]): CLI<TArgs>;
183
+ examples(...examples: string[]): CLI<TArgs, THandlerReturn, TChildren, TParent>;
149
184
  /**
150
185
  * Allows overriding the version displayed when passing `--version`. Defaults to crawling
151
186
  * the file system to get the package.json of the currently executing command.
152
187
  * @param override
153
188
  */
154
- version(override?: string): CLI<TArgs>;
189
+ version(override?: string): CLI<TArgs, THandlerReturn, TChildren, TParent>;
155
190
  /**
156
191
  * Prints help text to stdout.
157
192
  */
@@ -160,19 +195,100 @@ export interface CLI<TArgs extends ParsedArgs = ParsedArgs> {
160
195
  label: string;
161
196
  keys: (keyof TArgs)[];
162
197
  sortOrder: number;
163
- }): CLI<TArgs>;
164
- group(label: string, keys: (keyof TArgs)[]): CLI<TArgs>;
165
- middleware<TArgs2>(callback: MiddlewareFunction<TArgs, TArgs2>): CLI<TArgs2 extends void ? TArgs : TArgs & TArgs2>;
198
+ }): CLI<TArgs, THandlerReturn, TChildren, TParent>;
199
+ group(label: string, keys: (keyof TArgs)[]): CLI<TArgs, THandlerReturn, TChildren, TParent>;
200
+ middleware<TArgs2>(callback: MiddlewareFunction<TArgs, TArgs2>): CLI<TArgs2 extends void ? TArgs : TArgs & TArgs2, THandlerReturn, TChildren, TParent>;
166
201
  /**
167
202
  * Parses argv and executes the CLI
168
203
  * @param args argv. Defaults to process.argv.slice(2)
169
204
  * @returns Promise that resolves when the handler completes.
170
205
  */
171
206
  forge(args?: string[]): Promise<TArgs>;
207
+ /**
208
+ * Returns the typed children commands registered with this CLI.
209
+ * The return type is determined by the commands registered via `command()` or `commands()`.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * const app = cli('app')
214
+ * .command('init', { ... })
215
+ * .command('build', { ... });
216
+ *
217
+ * const children = app.getChildren();
218
+ * // children.init and children.build are typed CLI instances
219
+ * const initHandler = children.init.getHandler();
220
+ * ```
221
+ */
222
+ getChildren(): TChildren;
223
+ /**
224
+ * Returns the parent CLI instance, if this command was registered as a subcommand.
225
+ * Returns undefined for root-level CLI instances.
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * const build = cli('build', {
230
+ * handler: (args, ctx) => {
231
+ * const parent = ctx.command.getParent();
232
+ * const siblings = parent?.getChildren();
233
+ * // Access sibling commands
234
+ * }
235
+ * });
236
+ * ```
237
+ */
238
+ getParent(): TParent;
239
+ /**
240
+ * Returns a programmatic SDK for invoking this CLI and its subcommands.
241
+ * The SDK provides typed function calls instead of argv parsing.
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * const myCLI = cli('my-app')
246
+ * .option('verbose', { type: 'boolean' })
247
+ * .command('build', {
248
+ * builder: (cmd) => cmd.option('watch', { type: 'boolean' }),
249
+ * handler: (args) => ({ success: true, files: ['a.js'] })
250
+ * });
251
+ *
252
+ * const sdk = myCLI.sdk();
253
+ *
254
+ * // Invoke root command (if it has a handler)
255
+ * await sdk({ verbose: true });
256
+ *
257
+ * // Invoke subcommand with typed args
258
+ * const result = await sdk.build({ watch: true });
259
+ * console.log(result.files); // ['a.js']
260
+ * console.log(result.$args.watch); // true
261
+ *
262
+ * // Use CLI-style args for -- support
263
+ * await sdk.build(['--watch', '--', 'extra-arg']);
264
+ * ```
265
+ *
266
+ * @returns An SDK object that is callable (if this command has a handler)
267
+ * and has properties for each subcommand.
268
+ */
269
+ sdk(): SDKCommand<TArgs, THandlerReturn, TChildren>;
270
+ /**
271
+ * Returns the builder function for this command as a composable builder.
272
+ * The returned function can be used with `chain` to compose multiple builders.
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * const siblings = args.getParent().getChildren();
277
+ * const withBuildArgs = siblings.build.getBuilder()!;
278
+ * const withServeArgs = siblings.serve.getBuilder()!;
279
+ * return chain(args, withBuildArgs, withServeArgs);
280
+ * ```
281
+ */
282
+ getBuilder(): (<TInit extends ParsedArgs, TInitHandlerReturn, TInitChildren, TInitParent>(parser: CLI<TInit, TInitHandlerReturn, TInitChildren, TInitParent>) => CLI<TInit & TArgs, TInitHandlerReturn, TInitChildren & TChildren, TInitParent>) | undefined;
283
+ getHandler(): ((args: Omit<TArgs, keyof ParsedArgs>) => THandlerReturn) | undefined;
172
284
  }
173
- export interface CLIHandlerContext {
174
- command: CLI<any>;
285
+ export interface CLIHandlerContext<TChildren = {}, TParent = any> {
286
+ command: CLI<any, any, TChildren, TParent>;
175
287
  }
288
+ /**
289
+ * Extracts the TChildren type parameter from a CLI type.
290
+ */
291
+ export type ExtractCLIChildren<T> = T extends CLI<any, any, infer C, any> ? C : {};
176
292
  /**
177
293
  * Represents the configuration needed to create a CLI command.
178
294
  */
@@ -184,7 +300,15 @@ TInitial extends ParsedArgs,
184
300
  /**
185
301
  * The type of the arguments that are registered after `builder` is invoked, and the type that is passed to the handler.
186
302
  */
187
- TArgs extends TInitial = TInitial> {
303
+ TArgs extends TInitial = TInitial, THandlerReturn = void,
304
+ /**
305
+ * The children commands that exist before the builder runs.
306
+ */
307
+ TInitialChildren = {}, TParent = any,
308
+ /**
309
+ * The children commands after the builder runs (includes TInitialChildren plus any added by builder).
310
+ */
311
+ TChildren = {}> {
188
312
  /**
189
313
  * If set the command will be registered under the provided name and any aliases.
190
314
  *
@@ -199,13 +323,13 @@ TArgs extends TInitial = TInitial> {
199
323
  * The command builder. This function is called before the command is executed, and is used to register options and positional parameters.
200
324
  * @param parser The parser instance to register options and positionals with.
201
325
  */
202
- builder?: (parser: CLI<TInitial>) => CLI<TArgs>;
326
+ builder?: (parser: CLI<TInitial, any, TInitialChildren, TParent>) => CLI<TArgs, any, TChildren, any>;
203
327
  /**
204
328
  * The command handler. This function is called when the command is executed.
205
329
  * @param args The parsed arguments.
206
330
  * @param context Context for the handler. Contains the command instance.
207
331
  */
208
- handler?: (args: TArgs, context: CLIHandlerContext) => void | Promise<void>;
332
+ handler?: (args: NoInfer<TArgs>, context: CLIHandlerContext<NoInfer<TChildren>, TParent>) => THandlerReturn;
209
333
  /**
210
334
  * The usage text for the command. This text will be displayed in place of the default usage text in the help text and generated docs.
211
335
  */
@@ -223,8 +347,8 @@ TArgs extends TInitial = TInitial> {
223
347
  */
224
348
  epilogue?: string;
225
349
  }
226
- export type Command<TInitial extends ParsedArgs = any, TArgs extends TInitial = TInitial> = ({
227
- name: string;
350
+ export type Command<TInitial extends ParsedArgs = any, TArgs extends TInitial = TInitial, TCommandName extends string = string> = ({
351
+ name: TCommandName;
228
352
  } & CLICommandOptions<TInitial, TArgs>) | CLI<TArgs>;
229
353
  /**
230
354
  * Error Handler for CLI applications. Error handlers should re-throw the error if they cannot handle it.
@@ -239,12 +363,51 @@ export type ErrorHandler = (e: unknown, actions: {
239
363
  */
240
364
  exit: (code?: number) => void;
241
365
  }) => void;
366
+ export type UnknownCLI = CLI<ParsedArgs, any, any, any>;
242
367
  export type MiddlewareFunction<TArgs extends ParsedArgs, TArgs2> = (args: TArgs) => TArgs2 | Promise<TArgs2>;
368
+ /**
369
+ * Result type that conditionally includes $args.
370
+ * Only attaches $args when result is an object type.
371
+ * Uses Awaited<T> to handle async handlers that return Promise<U>.
372
+ */
373
+ export type SDKResult<TArgs, THandlerReturn> = Awaited<THandlerReturn> extends object ? Awaited<THandlerReturn> & {
374
+ $args: TArgs;
375
+ } : Awaited<THandlerReturn>;
376
+ /**
377
+ * The callable signature for a command with a handler.
378
+ * Supports both object-style args (typed, skips validation) and
379
+ * string array args (CLI-style, full validation pipeline).
380
+ */
381
+ export type SDKInvokable<TArgs, THandlerReturn> = {
382
+ /**
383
+ * Invoke the command with typed object args.
384
+ * Skips validation (TypeScript handles it), applies defaults, runs middleware.
385
+ */
386
+ (args?: Partial<Omit<TArgs, 'unmatched' | '--'>>): Promise<SDKResult<TArgs, THandlerReturn>>;
387
+ /**
388
+ * Invoke the command with CLI-style string args.
389
+ * Runs full pipeline: parse → validate → middleware → handler.
390
+ * Use this when you need to pass `--` extra args.
391
+ */
392
+ (args: string[]): Promise<SDKResult<TArgs, THandlerReturn>>;
393
+ };
394
+ /**
395
+ * Recursively builds SDK type from TChildren.
396
+ * Each child command becomes a property on the SDK object.
397
+ */
398
+ export type SDKChildren<TChildren> = {
399
+ [K in keyof TChildren]: TChildren[K] extends CLI<infer A, infer R, infer C, infer _P> ? SDKCommand<A, R, C> : never;
400
+ };
401
+ /**
402
+ * A single SDK command - callable if it has a handler, with nested children as properties.
403
+ * Container commands (no handler) are not callable but still provide access to children.
404
+ */
405
+ export type SDKCommand<TArgs, THandlerReturn, TChildren> = SDKInvokable<TArgs, THandlerReturn> & SDKChildren<TChildren>;
243
406
  /**
244
407
  * Constructs a CLI instance. See {@link CLI} for more information.
245
408
  * @param name Name for the top level CLI
246
409
  * @param rootCommandConfiguration Configuration used when running the bare CLI. e.g. npx my-cli, rather than npx my-cli [cmd]
247
410
  * @returns A {@link CLI} instance.
248
411
  */
249
- export declare function cli<TArgs extends ParsedArgs>(name: string, rootCommandConfiguration?: CLICommandOptions<ParsedArgs, TArgs>): CLI<TArgs>;
412
+ export declare function cli<TArgs extends ParsedArgs, THandlerReturn = void, TChildren = {}, TName extends string = string>(name: TName, rootCommandConfiguration?: CLICommandOptions<ParsedArgs, TArgs, THandlerReturn, {}, any, TChildren>): CLI<TArgs, THandlerReturn, TChildren>;
250
413
  export default cli;
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/lib/public-api.ts"],"names":[],"mappings":";;AAybA,kBAKC;AA9aD,iDAA6C;AAma7C;;;;;GAKG;AACH,SAAgB,GAAG,CACjB,IAAY,EACZ,wBAA+D;IAE/D,OAAO,IAAI,0BAAW,CAAC,IAAI,EAAE,wBAAwB,CAAsB,CAAC;AAC9E,CAAC;AAED,kBAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/lib/public-api.ts"],"names":[],"mappings":";;AAk/BA,kBAsBC;AAv/BD,iDAA6C;AA29B7C;;;;;GAKG;AACH,SAAgB,GAAG,CAOjB,IAAW,EACX,wBAOC;IAED,OAAO,IAAI,0BAAW,CAAC,IAAI,EAAE,wBAA+B,CAI3D,CAAC;AACJ,CAAC;AAED,kBAAe,GAAG,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "cli-forge",
3
- "version": "0.11.0",
3
+ "version": "1.0.0",
4
4
  "dependencies": {
5
5
  "tslib": "^2.3.0",
6
- "@cli-forge/parser": "0.11.0"
6
+ "@cli-forge/parser": "1.0.0"
7
7
  },
8
8
  "peerDependencies": {
9
9
  "markdown-factory": "^0.2.0",
@@ -23,10 +23,14 @@
23
23
  "optional": true
24
24
  }
25
25
  },
26
+ "devDependencies": {
27
+ "vite": "^5.4.0"
28
+ },
26
29
  "type": "commonjs",
27
30
  "main": "./dist/index.js",
28
31
  "typings": "./dist/index.d.ts",
29
32
  "license": "ISC",
33
+ "homepage": "https://craigory.dev/cli-forge/",
30
34
  "repository": {
31
35
  "type": "git",
32
36
  "directory": "packages/cli-forge",
@@ -38,7 +42,8 @@
38
42
  "exports": {
39
43
  ".": {
40
44
  "require": "./dist/index.js",
41
- "types": "./dist/index.d.ts"
45
+ "types": "./dist/index.d.ts",
46
+ "import": "./dist/index.js"
42
47
  },
43
48
  "./middleware": {
44
49
  "require": "./dist/middleware.js",
@@ -47,7 +47,7 @@ export function withGenerateDocumentationArgs<T extends ParsedArgs>(
47
47
  });
48
48
  }
49
49
 
50
- export const generateDocumentationCommand: CLI = cli('generate-documentation', {
50
+ export const generateDocumentationCommand: CLI<any, any, any> = cli('generate-documentation', {
51
51
  description: 'Generate documentation for the given CLI',
52
52
  examples: [
53
53
  'cli-forge generate-documentation ./bin/my-cli',
package/src/index.ts CHANGED
@@ -3,5 +3,10 @@ export * from './lib/public-api';
3
3
  export { default } from './lib/public-api';
4
4
  export { chain } from '@cli-forge/parser';
5
5
  export { makeComposableBuilder } from './lib/composable-builder';
6
+ export type {
7
+ ComposableBuilder,
8
+ ExtractArgs,
9
+ ExtractChildren,
10
+ } from './lib/composable-builder';
6
11
  export type { ArgumentsOf } from './lib/utils';
7
12
  export { ConfigurationProviders } from './lib/configuration-providers';
@@ -1,9 +1,57 @@
1
1
  import { ParsedArgs } from '@cli-forge/parser';
2
2
  import { CLI } from './public-api';
3
3
 
4
- export function makeComposableBuilder<T2 extends ParsedArgs>(
5
- fn: (init: CLI<ParsedArgs>) => CLI<T2>
4
+ /**
5
+ * Extracts the TChildren type parameter from a CLI type.
6
+ */
7
+ export type ExtractChildren<T> = T extends CLI<any, any, infer C, any>
8
+ ? C
9
+ : never;
10
+
11
+ /**
12
+ * Extracts the TArgs type parameter from a CLI type.
13
+ */
14
+ export type ExtractArgs<T> = T extends CLI<infer A, any, any, any> ? A : never;
15
+
16
+ /**
17
+ * Type for a composable builder function that transforms a CLI.
18
+ * Used with `chain` to compose multiple builders.
19
+ */
20
+ export type ComposableBuilder<
21
+ TArgs2 extends ParsedArgs,
22
+ // eslint-disable-next-line @typescript-eslint/ban-types
23
+ TAddedChildren = {}
24
+ > = <TInit extends ParsedArgs, THandlerReturn, TChildren, TParent>(
25
+ init: CLI<TInit, THandlerReturn, TChildren, TParent>
26
+ ) => CLI<TInit & TArgs2, THandlerReturn, TChildren & TAddedChildren, TParent>;
27
+
28
+ /**
29
+ * Creates a composable builder function that can be used with `chain`.
30
+ * Can be used to add options, commands, or any other CLI modifications.
31
+ * Children added by the builder function are properly tracked in the type.
32
+ *
33
+ * @typeParam TArgs2 - The args type after the builder runs
34
+ * @typeParam TChildren2 - The children type added by the builder
35
+ */
36
+ export function makeComposableBuilder<
37
+ TArgs2 extends ParsedArgs,
38
+ // eslint-disable-next-line @typescript-eslint/ban-types
39
+ TChildren2 = {}
40
+ >(
41
+ fn: (
42
+ // eslint-disable-next-line @typescript-eslint/ban-types
43
+ init: CLI<ParsedArgs, any, {}, any>
44
+ ) => CLI<TArgs2, any, TChildren2, any>
6
45
  ) {
7
- return <TInit extends ParsedArgs>(init: CLI<TInit>) =>
8
- fn(init) as CLI<TInit & T2>;
46
+ return <TInit extends ParsedArgs, THandlerReturn, TChildren, TParent>(
47
+ init: CLI<TInit, THandlerReturn, TChildren, TParent>
48
+ ) =>
49
+ // eslint-disable-next-line @typescript-eslint/ban-types
50
+ fn(init as unknown as CLI<ParsedArgs, any, {}, any>) as unknown as CLI<
51
+ TInit & TArgs2,
52
+ THandlerReturn,
53
+ TChildren & TChildren2,
54
+ TParent
55
+ >;
9
56
  }
57
+
@@ -1,3 +1,4 @@
1
+ import { it, describe, expect } from 'vitest';
1
2
  import { InternalCLI } from './internal-cli';
2
3
  import { generateDocumentation } from './documentation';
3
4
  import cli from './public-api';
@@ -54,7 +54,7 @@ export function generateDocumentation(
54
54
  // The cli instance here is typed a bit too well
55
55
  // for the builder function, so we need to cast it to
56
56
  // a more generic form.
57
- cli.configuration.builder(cli as CLI);
57
+ cli.configuration.builder(cli as unknown as CLI);
58
58
  }
59
59
  const parser = cli.getParser();
60
60
 
@@ -1,3 +1,4 @@
1
+ import { it, describe, expect, afterEach } from 'vitest';
1
2
  import { InternalCLI } from './internal-cli';
2
3
  import { cli } from './public-api';
3
4
 
@@ -77,11 +78,11 @@ describe('cliForge', () => {
77
78
  alias: ['$0'],
78
79
  builder: (argv) => argv,
79
80
  handler: makeHandler('bar'),
80
- }) as InternalCLI;
81
- await test.clone().forge(['f']);
82
- await test.clone().forge(['foo']);
83
- await test.clone().forge(['bar']);
84
- await test.clone().forge([]);
81
+ }) as Partial<InternalCLI>;
82
+ await test.clone?.().forge(['f']);
83
+ await test.clone?.().forge(['foo']);
84
+ await test.clone?.().forge(['bar']);
85
+ await test.clone?.().forge([]);
85
86
  expect(ran).toMatchInlineSnapshot(`
86
87
  {
87
88
  "bar": 2,