padrone 1.0.0-beta.2

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/src/parse.ts ADDED
@@ -0,0 +1,222 @@
1
+ type ParseParts = {
2
+ /**
3
+ * An alphanumeric term representing a command, subcommand, or positional argument.
4
+ * Note that a term can be ambiguous until fully matched within the command hierarchy.
5
+ * We cannot fully distinguish between a nested command or a positional argument until
6
+ * the command structure is known.
7
+ */
8
+ term: {
9
+ type: 'term';
10
+ value: string;
11
+ };
12
+ /**
13
+ * A positional argument provided to the command.
14
+ * Unlike `term`, this is definitively an argument. This can be determined when
15
+ * the argument is non-alphanumeric, like a path or a number.
16
+ */
17
+ arg: {
18
+ type: 'arg';
19
+ value: string;
20
+ };
21
+ /**
22
+ * An option provided to the command, prefixed with `--`.
23
+ * If the option has an `=` sign, the value after it is used as the option's value.
24
+ * Otherwise, the value is obtained from the next part or set to `true` if no value is provided.
25
+ */
26
+ option: {
27
+ type: 'option';
28
+ key: string;
29
+ value?: string | string[];
30
+ negated?: boolean;
31
+ };
32
+ /**
33
+ * An alias option provided to the command, prefixed with `-`.
34
+ * Which option it maps to cannot be determined until the command structure is known.
35
+ */
36
+ alias: {
37
+ type: 'alias';
38
+ key: string;
39
+ value?: string | string[];
40
+ };
41
+ };
42
+
43
+ type ParsePart = ParseParts[keyof ParseParts];
44
+
45
+ /**
46
+ * Tokenizes input string respecting quoted strings and bracket arrays.
47
+ * Supports single quotes, double quotes, backticks, and square brackets.
48
+ */
49
+ function tokenizeInput(input: string): string[] {
50
+ const tokens: string[] = [];
51
+ let current = '';
52
+ let inQuote: '"' | "'" | '`' | null = null;
53
+ let bracketDepth = 0;
54
+ let i = 0;
55
+
56
+ while (i < input.length) {
57
+ const char = input[i];
58
+
59
+ if (inQuote) {
60
+ // Check for escape sequences within quotes
61
+ if (char === '\\' && i + 1 < input.length) {
62
+ const nextChar = input[i + 1];
63
+ // Handle escape sequences
64
+ if (nextChar === inQuote || nextChar === '\\') {
65
+ current += nextChar;
66
+ i += 2;
67
+ continue;
68
+ }
69
+ }
70
+
71
+ if (char === inQuote) {
72
+ // End of quoted string
73
+ inQuote = null;
74
+ } else {
75
+ current += char;
76
+ }
77
+ } else if (char === '[') {
78
+ bracketDepth++;
79
+ current += char;
80
+ } else if (char === ']') {
81
+ bracketDepth = Math.max(0, bracketDepth - 1);
82
+ current += char;
83
+ } else if (bracketDepth > 0) {
84
+ // Inside brackets - include everything including spaces
85
+ current += char;
86
+ } else if (char === '"' || char === "'" || char === '`') {
87
+ // Start of quoted string
88
+ inQuote = char;
89
+ } else if (char === ' ' || char === '\t') {
90
+ // Whitespace outside quotes and brackets - end current token
91
+ if (current) {
92
+ tokens.push(current);
93
+ current = '';
94
+ }
95
+ } else {
96
+ current += char;
97
+ }
98
+ i++;
99
+ }
100
+
101
+ // Add the last token if any
102
+ if (current) {
103
+ tokens.push(current);
104
+ }
105
+
106
+ return tokens;
107
+ }
108
+
109
+ export function parseCliInputToParts(input: string): ParsePart[] {
110
+ const parts = tokenizeInput(input.trim());
111
+ const result: ParsePart[] = [];
112
+
113
+ let pendingValue: ParseParts['option'] | ParseParts['alias'] | undefined;
114
+ let allowTerm = true;
115
+
116
+ for (const part of parts) {
117
+ if (!part) continue;
118
+ const wasPending = pendingValue;
119
+ pendingValue = undefined;
120
+
121
+ if (part.startsWith('--no-') && part.length > 5) {
122
+ // Negated boolean option (--no-verbose)
123
+ const key = part.slice(5);
124
+ const p = { type: 'option' as const, key, value: undefined, negated: true };
125
+ result.push(p);
126
+ } else if (part.startsWith('--')) {
127
+ const [key = '', value] = splitOptionValue(part.slice(2));
128
+
129
+ const p = { type: 'option' as const, key, value };
130
+ if (typeof value === 'undefined') pendingValue = p;
131
+ result.push(p);
132
+ } else if (part.startsWith('-') && part.length > 1 && !/^-\d/.test(part)) {
133
+ // Short option (but not negative numbers like -5)
134
+ const [key = '', value] = splitOptionValue(part.slice(1));
135
+
136
+ const p = { type: 'alias' as const, key, value };
137
+ if (typeof value === 'undefined') pendingValue = p;
138
+ result.push(p);
139
+ } else if (wasPending) {
140
+ wasPending.value = part;
141
+ } else if (/^[a-zA-Z0-9_-]+$/.test(part) && allowTerm) {
142
+ result.push({ type: 'term', value: part });
143
+ } else {
144
+ result.push({ type: 'arg', value: part });
145
+ allowTerm = false;
146
+ }
147
+ }
148
+ return result;
149
+ }
150
+
151
+ /**
152
+ * Split option key and value, handling quoted values after =.
153
+ */
154
+ function splitOptionValue(str: string): [string, string | string[] | undefined] {
155
+ const eqIndex = str.indexOf('=');
156
+ if (eqIndex === -1) return [str, undefined];
157
+
158
+ const key = str.slice(0, eqIndex);
159
+ let value = str.slice(eqIndex + 1);
160
+
161
+ // Remove surrounding quotes from value if present
162
+ if (
163
+ (value.startsWith('"') && value.endsWith('"')) ||
164
+ (value.startsWith("'") && value.endsWith("'")) ||
165
+ (value.startsWith('`') && value.endsWith('`'))
166
+ ) {
167
+ value = value.slice(1, -1);
168
+ return [key, value];
169
+ }
170
+
171
+ // Handle array syntax: [a,b,c] -> ['a', 'b', 'c']
172
+ if (value.startsWith('[') && value.endsWith(']')) {
173
+ const inner = value.slice(1, -1);
174
+ if (inner === '') return [key, []];
175
+ const items = parseArrayItems(inner);
176
+ return [key, items];
177
+ }
178
+
179
+ return [key, value];
180
+ }
181
+
182
+ /**
183
+ * Parse comma-separated items, respecting quotes within items.
184
+ */
185
+ function parseArrayItems(input: string): string[] {
186
+ const items: string[] = [];
187
+ let current = '';
188
+ let inQuote: '"' | "'" | '`' | null = null;
189
+ let i = 0;
190
+
191
+ while (i < input.length) {
192
+ const char = input[i];
193
+
194
+ if (inQuote) {
195
+ if (char === '\\' && i + 1 < input.length && input[i + 1] === inQuote) {
196
+ current += input[i + 1];
197
+ i += 2;
198
+ continue;
199
+ }
200
+ if (char === inQuote) {
201
+ inQuote = null;
202
+ } else {
203
+ current += char;
204
+ }
205
+ } else if (char === '"' || char === "'" || char === '`') {
206
+ inQuote = char;
207
+ } else if (char === ',') {
208
+ items.push(current.trim());
209
+ current = '';
210
+ } else {
211
+ current += char;
212
+ }
213
+ i++;
214
+ }
215
+
216
+ // Add the last item
217
+ if (current || items.length > 0) {
218
+ items.push(current.trim());
219
+ }
220
+
221
+ return items;
222
+ }
@@ -0,0 +1,99 @@
1
+ import type { AnyPadroneCommand } from './types';
2
+
3
+ /**
4
+ * Use this type instead of `any` when you intend to fix it later
5
+ * @deprecated Please replace with an actual type
6
+ */
7
+ export type TODO<TCast = any, _TReason = unknown> = TCast;
8
+
9
+ type SafeString = string & {};
10
+ export type IsUnknown<T> = unknown extends T ? true : false;
11
+ type IsAny<T> = any extends T ? true : false;
12
+ type IsNever<T> = [T] extends [never] ? true : false;
13
+
14
+ type SplitString<TName extends string, TSplitBy extends string = ' '> = TName extends `${infer FirstPart}${TSplitBy}${infer RestParts}`
15
+ ? [FirstPart, ...SplitString<RestParts, TSplitBy>]
16
+ : [TName];
17
+
18
+ type JoinString<TParts extends string[], TJoinBy extends string = ' '> = TParts extends [
19
+ infer FirstPart extends string,
20
+ ...infer RestParts extends string[],
21
+ ]
22
+ ? RestParts extends []
23
+ ? FirstPart
24
+ : `${FirstPart}${TJoinBy}${JoinString<RestParts, TJoinBy>}`
25
+ : TParts extends []
26
+ ? ''
27
+ : TParts[number];
28
+
29
+ type SplitLastSpace<S extends string> =
30
+ SplitString<S> extends [...infer Init extends string[], infer Last extends string]
31
+ ? Init extends []
32
+ ? [S, never]
33
+ : [JoinString<Init>, Last]
34
+ : [S, never];
35
+
36
+ type AnyPartExtends<U, T> = [U] extends [never] ? false : U extends any ? (U extends T ? true : never) : never extends true ? true : false;
37
+
38
+ export type FullCommandName<TName extends string, TParentName extends string = ''> = TParentName extends ''
39
+ ? TName
40
+ : `${TParentName} ${TName}`;
41
+
42
+ export type PickCommandByName<
43
+ TCommands extends AnyPadroneCommand[],
44
+ TName extends string | AnyPadroneCommand,
45
+ > = TName extends AnyPadroneCommand ? TName : Extract<FlattenCommands<TCommands>, { path: TName }>;
46
+
47
+ export type FlattenCommands<TCommands extends AnyPadroneCommand[]> = TCommands extends [infer FirstCommand, ...infer RestCommands]
48
+ ? FirstCommand extends AnyPadroneCommand
49
+ ?
50
+ | (RestCommands extends AnyPadroneCommand[] ? FlattenCommands<RestCommands> : never)
51
+ | FlattenCommands<FirstCommand['~types']['commands']>
52
+ | FirstCommand
53
+ : RestCommands extends AnyPadroneCommand[]
54
+ ? FlattenCommands<RestCommands>
55
+ : never
56
+ : TCommands[number];
57
+
58
+ export type GetCommandNames<TCommands extends AnyPadroneCommand[]> = FlattenCommands<TCommands>['path'];
59
+
60
+ /**
61
+ * Find all the commands that are prefixed with a command name.
62
+ * This is needed to avoid matching other commands when followed by a space and another word.
63
+ * For example, let's say `level1` and `level1 level2` are commands.
64
+ * Then `level1 ${string}` would also match `level1 level2`,
65
+ * and it would cause `level1 level2` to not show up in the autocomplete.
66
+ * By excluding those cases, we can ensure autocomplete works correctly.
67
+ */
68
+ type PrefixedCommands<TCommands extends AnyPadroneCommand[]> =
69
+ GetCommandNames<TCommands> extends infer CommandNames
70
+ ? CommandNames extends string
71
+ ? AnyPartExtends<GetCommandNames<TCommands>, `${CommandNames} ${string}`> extends true
72
+ ? never
73
+ : `${CommandNames} ${string}`
74
+ : never
75
+ : never;
76
+
77
+ /**
78
+ * The possible commands are the commands that can be parsed by the program.
79
+ * This includes the string that are exact matches to a command name, and strings that are prefixed with a command name.
80
+ */
81
+ export type PossibleCommands<TCommands extends AnyPadroneCommand[]> = GetCommandNames<TCommands> | PrefixedCommands<TCommands> | SafeString;
82
+
83
+ /**
84
+ * Match a string to a command by the possible commands.
85
+ * This is done by recursively splitting the string by the last space, and then checking if the prefix is a valid command name.
86
+ * This is needed to avoid matching the top-level command when there are nested commands.
87
+ */
88
+ export type PickCommandByPossibleCommands<
89
+ TCommands extends AnyPadroneCommand[],
90
+ TCommand extends PossibleCommands<TCommands>,
91
+ > = IsAny<TCommand> extends true
92
+ ? TCommands[number]
93
+ : TCommand extends GetCommandNames<TCommands>
94
+ ? PickCommandByName<TCommands, TCommand>
95
+ : SplitLastSpace<TCommand> extends [infer Prefix extends string, infer Rest]
96
+ ? IsNever<Rest> extends true
97
+ ? PickCommandByName<TCommands, Prefix>
98
+ : PickCommandByPossibleCommands<TCommands, Prefix>
99
+ : never;
package/src/types.ts ADDED
@@ -0,0 +1,313 @@
1
+ import type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { Tool } from 'ai';
3
+ import type { HelpOptions } from './help';
4
+ import type { PadroneMeta } from './options';
5
+ import type {
6
+ FlattenCommands,
7
+ FullCommandName,
8
+ GetCommandNames,
9
+ IsUnknown,
10
+ PickCommandByName,
11
+ PickCommandByPossibleCommands,
12
+ PossibleCommands,
13
+ } from './type-utils';
14
+
15
+ type UnknownRecord = Record<string, unknown>;
16
+ type EmptyRecord = Record<string, never>;
17
+ type DefaultOpts = UnknownRecord | void;
18
+
19
+ /**
20
+ * A schema that supports both validation (StandardSchemaV1) and JSON schema generation (StandardJSONSchemaV1).
21
+ * This is the type required for command arguments and options in Padrone.
22
+ */
23
+ type PadroneSchema<Input = unknown, Output = Input> = StandardSchemaV1<Input, Output> & StandardJSONSchemaV1<Input, Output>;
24
+
25
+ export type PadroneCommand<
26
+ TName extends string = string,
27
+ TParentName extends string = '',
28
+ TOpts extends PadroneSchema = PadroneSchema<DefaultOpts>,
29
+ TRes = void,
30
+ TCommands extends [...AnyPadroneCommand[]] = [],
31
+ > = {
32
+ name: TName;
33
+ path: FullCommandName<TName, TParentName>;
34
+ title?: string;
35
+ description?: string;
36
+ version?: string;
37
+ deprecated?: boolean | string;
38
+ hidden?: boolean;
39
+ needsApproval?: boolean | ((options: TOpts) => Promise<boolean> | boolean);
40
+ options?: PadroneSchema;
41
+ meta?: GetMeta<TOpts>;
42
+ handler?: (options: StandardSchemaV1.InferOutput<TOpts>) => TRes;
43
+ /** List of possible config file names to search for. */
44
+ configFiles?: string[];
45
+
46
+ parent?: AnyPadroneCommand;
47
+ commands?: TCommands;
48
+
49
+ /** @deprecated Internal use only */
50
+ '~types': {
51
+ name: TName;
52
+ parentName: TParentName;
53
+ path: FullCommandName<TName, TParentName>;
54
+ optionsInput: StandardSchemaV1.InferInput<TOpts>;
55
+ optionsOutput: StandardSchemaV1.InferOutput<TOpts>;
56
+ result: TRes;
57
+ commands: TCommands;
58
+ };
59
+ };
60
+
61
+ export type AnyPadroneCommand = PadroneCommand<string, any, any, any, [...AnyPadroneCommand[]]>;
62
+
63
+ /**
64
+ * Configuration options for a command.
65
+ */
66
+ export type PadroneCommandConfig = {
67
+ /** A short title for the command, displayed in help. */
68
+ title?: string;
69
+ /** A longer description of what the command does. */
70
+ description?: string;
71
+ /** The version of the command. */
72
+ version?: string;
73
+ /** Whether the command is deprecated, or a message explaining the deprecation. */
74
+ deprecated?: boolean | string;
75
+ /** Whether the command should be hidden from help output. */
76
+ hidden?: boolean;
77
+ /**
78
+ * List of possible config file names to search for.
79
+ * When the CLI runs, it will search for these files in the current directory
80
+ * and apply the first one found.
81
+ *
82
+ * - `undefined`: Inherit from parent command (default)
83
+ * - `['file1', 'file2']`: Use these config files
84
+ * - `[]`: Explicitly disable config file loading (no inheritance)
85
+ *
86
+ * @example ['myapp.config.json', 'myapp.config.yaml', '.myapprc']
87
+ */
88
+ configFiles?: string[];
89
+ };
90
+
91
+ export type PadroneCommandBuilder<
92
+ TName extends string = string,
93
+ TParentName extends string = '',
94
+ TOpts extends PadroneSchema = PadroneSchema<DefaultOpts>,
95
+ TRes = void,
96
+ TCommands extends [...AnyPadroneCommand[]] = [],
97
+ > = {
98
+ /**
99
+ * Configures command properties like title, description, version, deprecated, and hidden.
100
+ * @example
101
+ * ```ts
102
+ * .configure({
103
+ * title: 'Build Project',
104
+ * description: 'Compiles the project',
105
+ * deprecated: 'Use "compile" instead',
106
+ * })
107
+ * ```
108
+ */
109
+ configure: (config: PadroneCommandConfig) => PadroneCommandBuilder<TName, TParentName, TOpts, TRes, TCommands>;
110
+
111
+ /**
112
+ * Defines the options schema for the command, including positional arguments.
113
+ * Use the `positional` array in meta to specify which options are positional args.
114
+ * Use '...name' prefix for variadic (rest) arguments, matching JS/TS rest syntax.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * .options(z.object({
119
+ * source: z.string(),
120
+ * files: z.string().array(),
121
+ * dest: z.string(),
122
+ * recursive: z.boolean().default(false),
123
+ * }), {
124
+ * positional: ['source', '...files', 'dest'],
125
+ * })
126
+ * ```
127
+ */
128
+ options: <TOpts extends PadroneSchema = PadroneSchema<void>>(
129
+ options?: TOpts,
130
+ meta?: GetMeta<TOpts>,
131
+ ) => PadroneCommandBuilder<TName, TParentName, TOpts, TRes, TCommands>;
132
+
133
+ /**
134
+ * Defines the handler function to be executed when the command is run.
135
+ */
136
+ action: <TRes>(
137
+ handler?: (options: StandardSchemaV1.InferOutput<TOpts>) => TRes,
138
+ ) => PadroneCommandBuilder<TName, TParentName, TOpts, TRes, TCommands>;
139
+
140
+ /**
141
+ * Creates a nested command within the current command with the given name and builder function.
142
+ */
143
+ command: <
144
+ TNameNested extends string,
145
+ TBuilder extends PadroneCommandBuilder<TNameNested, FullCommandName<TName, TParentName>, any, any, any>,
146
+ >(
147
+ name: TNameNested,
148
+ builderFn?: (builder: PadroneCommandBuilder<TNameNested, FullCommandName<TName, TParentName>>) => TBuilder,
149
+ ) => PadroneCommandBuilder<TName, TParentName, TOpts, TRes, [...TCommands, TBuilder['~types']['command']]>;
150
+
151
+ /** @deprecated Internal use only */
152
+ '~types': {
153
+ name: TName;
154
+ parentName: TParentName;
155
+ path: FullCommandName<TName, TParentName>;
156
+ options: TOpts;
157
+ result: TRes;
158
+ commands: TCommands;
159
+ command: PadroneCommand<TName, TParentName, TOpts, TRes, TCommands>;
160
+ };
161
+ };
162
+
163
+ export type PadroneProgram<
164
+ TName extends string = string,
165
+ TOpts extends PadroneSchema = PadroneSchema<DefaultOpts>,
166
+ TRes = void,
167
+ TCommands extends [...AnyPadroneCommand[]] = [],
168
+ TCmd extends PadroneCommand<'', '', TOpts, TRes, TCommands> = PadroneCommand<'', '', TOpts, TRes, TCommands>,
169
+ > = Omit<PadroneCommandBuilder<TName, '', TOpts, TRes, TCommands>, 'command' | 'configure'> & {
170
+ /**
171
+ * Configures program properties like title, description, version, deprecated, hidden, and configFiles.
172
+ * @example
173
+ * ```ts
174
+ * .configure({
175
+ * description: 'My CLI application',
176
+ * version: '1.0.0',
177
+ * configFiles: ['myapp.config.json', '.myapprc'],
178
+ * })
179
+ * ```
180
+ */
181
+ configure: (config: PadroneCommandConfig) => PadroneProgram<TName, TOpts, TRes, TCommands>;
182
+
183
+ /**
184
+ * Creates a command within the program with the given name and builder function.
185
+ */
186
+ command: <TNameNested extends string, TBuilder extends PadroneCommandBuilder<TNameNested, '', any, any, any>>(
187
+ name: TNameNested,
188
+ builderFn?: (builder: PadroneCommandBuilder<TNameNested, ''>) => TBuilder,
189
+ ) => PadroneProgram<TName, TOpts, TRes, [...TCommands, TBuilder['~types']['command']]>;
190
+
191
+ /**
192
+ * Runs a command programmatically by name with provided options (including positional args).
193
+ */
194
+ run: <const TCommand extends GetCommandNames<[TCmd]> | FlattenCommands<[TCmd]>>(
195
+ name: TCommand,
196
+ options: NoInfer<GetOptions<'in', PickCommandByName<[TCmd], TCommand>>>,
197
+ ) => PadroneCommandResult<PickCommandByName<[TCmd], TCommand>>;
198
+
199
+ /**
200
+ * Runs the program as a CLI application, parsing `process.argv` or provided input.
201
+ */
202
+ cli: <const TCommand extends PossibleCommands<[TCmd]>>(
203
+ input?: TCommand,
204
+ options?: PadroneParseOptions,
205
+ ) => PadroneCommandResult<PickCommandByPossibleCommands<[TCmd], TCommand>>;
206
+
207
+ /**
208
+ * Parses CLI input (or the provided input string) into command, args, and options without executing anything.
209
+ */
210
+ parse: <const TCommand extends PossibleCommands<[TCmd]>>(
211
+ input?: TCommand,
212
+ options?: PadroneParseOptions,
213
+ ) => PadroneParseResult<PickCommandByPossibleCommands<[TCmd], TCommand>>;
214
+
215
+ /**
216
+ * Converts command and options back into a CLI string.
217
+ */
218
+ stringify: <const TCommand extends GetCommandNames<TCommands> = ''>(
219
+ command?: TCommand,
220
+ options?: GetOptions<'out', PickCommandByPossibleCommands<[TCmd], TCommand>>,
221
+ ) => string;
222
+
223
+ /**
224
+ * Finds a command by name, returning `undefined` if not found.
225
+ */
226
+ find: <const TName extends GetCommandNames<[TCmd]>>(
227
+ command: TName | (string & {}),
228
+ ) => IsUnknown<TName> extends false
229
+ ? TName extends string
230
+ ? PickCommandByName<[TCmd], TName>
231
+ : FlattenCommands<[TCmd]> | undefined
232
+ : FlattenCommands<[TCmd]> | undefined;
233
+
234
+ /**
235
+ * Generates a type-safe API for invoking commands programmatically.
236
+ */
237
+ api: () => PadroneAPI<TCmd>;
238
+
239
+ // TODO: implement interactive and repl methods
240
+
241
+ /**
242
+ * Starts an interactive prompt to run commands.
243
+ */
244
+ // interactive: () => Promise<PadroneCommandResult<FlattenCommands<[TCmd]>> | undefined>;
245
+
246
+ /**
247
+ * Starts a REPL (Read-Eval-Print Loop) for running commands interactively.
248
+ */
249
+ // repl: () => Promise<PadroneCommandResult<FlattenCommands<[TCmd]>>[]>;
250
+
251
+ /**
252
+ * Returns a tool definition that can be passed to AI SDK.
253
+ */
254
+ tool: () => Tool<{ command: string }>;
255
+
256
+ /**
257
+ * Returns the help information for the program or a specific command.
258
+ */
259
+ help: <const TCommand extends GetCommandNames<[TCmd]> | FlattenCommands<[TCmd]>>(command?: TCommand, options?: HelpOptions) => string;
260
+
261
+ /**
262
+ * Reflection information about the program.
263
+ * Avoid using this in application code, unless you know what you're doing.
264
+ * @deprecated Internal use only
265
+ */
266
+ '~types': {
267
+ command: TCmd;
268
+ commands: TCommands;
269
+ };
270
+ };
271
+
272
+ export type AnyPadroneProgram = PadroneProgram<string, any, any, [...AnyPadroneCommand[]]>;
273
+
274
+ export type PadroneCommandResult<TCommand extends AnyPadroneCommand = AnyPadroneCommand> = PadroneParseResult<TCommand> & {
275
+ result: GetResults<TCommand>;
276
+ };
277
+
278
+ /**
279
+ * Options for parsing CLI input.
280
+ */
281
+ export type PadroneParseOptions = {
282
+ /**
283
+ * Custom environment variables to use for env binding.
284
+ * If not provided, process.env will be used.
285
+ */
286
+ env?: Record<string, string | undefined>;
287
+ /**
288
+ * Config file data to use for config binding.
289
+ * This should be the parsed content of a config file (JSON, YAML, etc.).
290
+ */
291
+ configData?: Record<string, unknown>;
292
+ };
293
+
294
+ export type PadroneParseResult<TCommand extends AnyPadroneCommand = AnyPadroneCommand> = {
295
+ command: TCommand;
296
+ options?: GetOptions<'out', TCommand>;
297
+ optionsResult?: StandardSchemaV1.Result<GetOptions<'out', TCommand>>;
298
+ };
299
+
300
+ export type PadroneAPI<TCommand extends AnyPadroneCommand> = PadroneAPICommand<TCommand> & {
301
+ [K in TCommand['~types']['commands'][number] as K['name']]: PadroneAPI<K>;
302
+ };
303
+
304
+ type PadroneAPICommand<TCommand extends AnyPadroneCommand> = (options: GetOptions<'in', TCommand>) => GetResults<TCommand>;
305
+
306
+ type NormalizeOptions<TOptions> = IsUnknown<TOptions> extends true ? void | EmptyRecord : TOptions;
307
+ type GetOptions<TDir extends 'in' | 'out', TCommand extends AnyPadroneCommand> = TDir extends 'in'
308
+ ? NormalizeOptions<TCommand['~types']['optionsInput']>
309
+ : NormalizeOptions<TCommand['~types']['optionsOutput']>;
310
+
311
+ type GetResults<TCommand extends AnyPadroneCommand> = ReturnType<NonNullable<TCommand['handler']>>;
312
+
313
+ type GetMeta<TOpts extends PadroneSchema> = PadroneMeta<NonNullable<StandardSchemaV1.InferInput<TOpts>>>;