@ls-stack/cli 0.1.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.
@@ -0,0 +1,409 @@
1
+ //#region src/cliInput.d.ts
2
+ /**
3
+ * Validation function for text inputs.
4
+ *
5
+ * @param value - The current input value to validate
6
+ * @returns `true` if valid, `false` if invalid, or a string error message
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const validate: ValidateFn = (value) => {
11
+ * if (value.length < 3) return 'Must be at least 3 characters';
12
+ * return true;
13
+ * };
14
+ * ```
15
+ */
16
+ type ValidateFn = (value: string) => boolean | string | Promise<boolean | string>;
17
+ /**
18
+ * Option configuration for select and autocomplete prompts.
19
+ *
20
+ * @template T - The string literal type for the option value
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const options: SelectOption<'dev' | 'prod'>[] = [
25
+ * { value: 'dev', label: 'Development', hint: 'Local environment' },
26
+ * { value: 'prod', label: 'Production' },
27
+ * ];
28
+ * ```
29
+ */
30
+ type SelectOption<T extends string> = {
31
+ /** The value returned when this option is selected */value: T; /** Display text shown to the user (defaults to value if not provided) */
32
+ label?: string; /** Additional description or help text for the option */
33
+ hint?: string;
34
+ };
35
+ /**
36
+ * Interactive CLI input utilities with ESC-to-cancel support.
37
+ *
38
+ * All prompts exit the process with code 0 when the user presses ESC or Ctrl+C.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { cliInput } from '@ls-stack/cli';
43
+ *
44
+ * const name = await cliInput.text('Enter your name');
45
+ * const confirm = await cliInput.confirm('Proceed?', { initial: true });
46
+ * ```
47
+ */
48
+ declare const cliInput: {
49
+ /**
50
+ * Single selection prompt from a list of options.
51
+ *
52
+ * @template T - String literal union type of option values
53
+ * @param title - The prompt message displayed to the user
54
+ * @param options - Configuration object containing the selectable options
55
+ * @returns The value of the selected option
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const env = await cliInput.select('Select environment', {
60
+ * options: [
61
+ * { value: 'dev', label: 'Development', hint: 'Local server' },
62
+ * { value: 'prod', label: 'Production' },
63
+ * ],
64
+ * });
65
+ * // env: 'dev' | 'prod'
66
+ * ```
67
+ */
68
+ select: <T extends string>(title: string, {
69
+ options
70
+ }: {
71
+ options: SelectOption<T>[];
72
+ }) => Promise<T>;
73
+ /**
74
+ * Text input with autocomplete suggestions.
75
+ *
76
+ * Searches across option values, labels, and hints (case-insensitive).
77
+ * Allows free-form text input with optional validation.
78
+ *
79
+ * @template T - String literal union type of option values
80
+ * @param title - The prompt message displayed to the user
81
+ * @param options - Configuration with autocomplete options and optional validation
82
+ * @returns The selected or entered value
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const framework = await cliInput.textWithAutocomplete('Select framework', {
87
+ * options: [
88
+ * { value: 'react', label: 'React', hint: 'UI library' },
89
+ * { value: 'vue', label: 'Vue', hint: 'Progressive framework' },
90
+ * ],
91
+ * validate: (v) => v.length > 0 || 'Required',
92
+ * });
93
+ * ```
94
+ */
95
+ textWithAutocomplete: <T extends string>(title: string, {
96
+ options,
97
+ validate
98
+ }: {
99
+ options: SelectOption<T>[];
100
+ validate?: ValidateFn;
101
+ }) => Promise<T>;
102
+ /**
103
+ * Text input prompt with optional default value and validation.
104
+ *
105
+ * @param title - The prompt message displayed to the user
106
+ * @param options - Configuration with optional initial value and validation
107
+ * @returns The entered text
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const name = await cliInput.text('Project name', {
112
+ * initial: 'my-project',
113
+ * validate: (v) => /^[a-z0-9-]+$/.test(v) || 'Invalid name',
114
+ * });
115
+ * ```
116
+ */
117
+ text: (title: string, {
118
+ initial,
119
+ validate
120
+ }?: {
121
+ initial?: string;
122
+ validate?: ValidateFn;
123
+ }) => Promise<string>;
124
+ /**
125
+ * Yes/No confirmation prompt.
126
+ *
127
+ * @param title - The prompt message displayed to the user
128
+ * @param options - Configuration with optional default value
129
+ * @returns `true` for yes, `false` for no
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * const proceed = await cliInput.confirm('Deploy to production?', {
134
+ * initial: false,
135
+ * });
136
+ * ```
137
+ */
138
+ confirm: (title: string, {
139
+ initial
140
+ }?: {
141
+ initial?: boolean;
142
+ }) => Promise<boolean>;
143
+ /**
144
+ * Multi-select prompt with checkboxes.
145
+ *
146
+ * Requires at least one option to be selected.
147
+ *
148
+ * @template T - String literal union type of option values
149
+ * @param title - The prompt message displayed to the user
150
+ * @param options - Configuration object containing the selectable options
151
+ * @returns Array of selected option values
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * const features = await cliInput.multipleSelect('Enable features', {
156
+ * options: [
157
+ * { value: 'typescript', label: 'TypeScript' },
158
+ * { value: 'eslint', label: 'ESLint' },
159
+ * { value: 'prettier', label: 'Prettier' },
160
+ * ],
161
+ * });
162
+ * // features: ('typescript' | 'eslint' | 'prettier')[]
163
+ * ```
164
+ */
165
+ multipleSelect: <T extends string>(title: string, {
166
+ options
167
+ }: {
168
+ options: SelectOption<T>[];
169
+ }) => Promise<T[]>;
170
+ /**
171
+ * Numeric input prompt.
172
+ *
173
+ * Returns `null` on non-cancellation errors (cancellation exits the process).
174
+ *
175
+ * @param title - The prompt message displayed to the user
176
+ * @param options - Configuration with optional default value
177
+ * @returns The entered number, or `null` on error
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * const port = await cliInput.number('Enter port', { initial: 3000 });
182
+ * if (port !== null) {
183
+ * console.log(`Using port ${port}`);
184
+ * }
185
+ * ```
186
+ */
187
+ number: (title: string, {
188
+ initial
189
+ }?: {
190
+ initial?: number;
191
+ }) => Promise<number | null>;
192
+ };
193
+ //#endregion
194
+ //#region src/createCli.d.ts
195
+ /**
196
+ * Positional argument definition for CLI commands.
197
+ *
198
+ * Positional arguments are parsed in the order they are defined in the `args` object.
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * // String positional arg (required)
203
+ * { type: 'positional-string', name: 'filename', description: 'File to process' }
204
+ *
205
+ * // Number positional arg with default (optional)
206
+ * { type: 'positional-number', name: 'port', description: 'Port number', default: 3000 }
207
+ * ```
208
+ */
209
+ type PositionalArg = {
210
+ type: 'positional-string';
211
+ name: string;
212
+ default?: string;
213
+ description: string;
214
+ } | {
215
+ type: 'positional-number';
216
+ name: string;
217
+ default?: number;
218
+ description: string;
219
+ };
220
+ /**
221
+ * Argument definition for CLI commands.
222
+ *
223
+ * Supports positional arguments, boolean flags, and value flags.
224
+ *
225
+ * **Argument Types:**
226
+ * - `positional-string` - Required string argument (optional if `default` is provided)
227
+ * - `positional-number` - Required number argument (optional if `default` is provided)
228
+ * - `flag` - Boolean flag (`--verbose`), defaults to `false`
229
+ * - `value-string-flag` - Flag with string value (`--config file.json`)
230
+ * - `value-number-flag` - Flag with number value (`--port 8080`)
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * const args = {
235
+ * name: { type: 'positional-string', name: 'name', description: 'Project name' },
236
+ * port: { type: 'value-number-flag', name: 'port', description: 'Port', default: 3000 },
237
+ * verbose: { type: 'flag', name: 'verbose', description: 'Verbose output' },
238
+ * };
239
+ * ```
240
+ */
241
+ type Arg = PositionalArg | {
242
+ type: 'flag';
243
+ name: string;
244
+ description: string;
245
+ } | {
246
+ type: 'value-string-flag';
247
+ default?: string;
248
+ name: string;
249
+ description: string;
250
+ } | {
251
+ type: 'value-number-flag';
252
+ default?: number;
253
+ name: string;
254
+ description: string;
255
+ };
256
+ type Cmd = {
257
+ short?: string;
258
+ description: string;
259
+ run: (...args: any[]) => Promise<void> | void;
260
+ args?: Record<string, Arg>;
261
+ examples?: {
262
+ args: string[];
263
+ description: string;
264
+ }[];
265
+ };
266
+ type GetArgType<T extends Arg> = T extends PositionalArg ? T['type'] extends 'positional-string' ? T['default'] extends string ? string : string | undefined : T['type'] extends 'positional-number' ? T['default'] extends number ? number : number | undefined : never : T extends {
267
+ type: 'flag';
268
+ } ? boolean : T extends {
269
+ type: 'value-string-flag';
270
+ } ? T['default'] extends string ? string : string | undefined : T extends {
271
+ type: 'value-number-flag';
272
+ } ? T['default'] extends number ? number : number | undefined : never;
273
+ /**
274
+ * Creates a type-safe command definition for use with `createCLI`.
275
+ *
276
+ * The `run` function receives fully typed arguments based on the `args` definition.
277
+ * Positional arguments are parsed in declaration order.
278
+ *
279
+ * @template Args - Record of argument definitions
280
+ * @param options - Command configuration
281
+ * @param options.description - Command description shown in help
282
+ * @param options.short - Optional single-character alias (cannot be 'i' or 'h')
283
+ * @param options.args - Typed argument definitions
284
+ * @param options.run - Handler function receiving parsed arguments
285
+ * @param options.examples - Optional usage examples for help text
286
+ * @returns Command definition object
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * const deploy = createCmd({
291
+ * short: 'd',
292
+ * description: 'Deploy the application',
293
+ * args: {
294
+ * env: {
295
+ * type: 'positional-string',
296
+ * name: 'env',
297
+ * description: 'Target environment',
298
+ * },
299
+ * port: {
300
+ * type: 'value-number-flag',
301
+ * name: 'port',
302
+ * description: 'Port number',
303
+ * default: 3000,
304
+ * },
305
+ * verbose: {
306
+ * type: 'flag',
307
+ * name: 'verbose',
308
+ * description: 'Enable verbose logging',
309
+ * },
310
+ * },
311
+ * examples: [
312
+ * { args: ['production'], description: 'Deploy to production' },
313
+ * { args: ['staging', '--port', '8080'], description: 'Deploy to staging on port 8080' },
314
+ * ],
315
+ * run: async ({ env, port, verbose }) => {
316
+ * // env: string, port: number, verbose: boolean
317
+ * console.log(`Deploying to ${env} on port ${port}`);
318
+ * },
319
+ * });
320
+ * ```
321
+ */
322
+ declare function createCmd<Args extends undefined | Record<string, Arg>>({
323
+ short,
324
+ description,
325
+ run,
326
+ args,
327
+ examples
328
+ }: {
329
+ short?: string;
330
+ description: string;
331
+ args?: Args;
332
+ run: (cmdArgs: { [K in keyof Args]: Args[K] extends Arg ? GetArgType<Args[K]> : never }) => Promise<void> | void;
333
+ examples?: {
334
+ args: string[];
335
+ description: string;
336
+ }[];
337
+ }): {
338
+ short: string | undefined;
339
+ description: string;
340
+ run: (cmdArgs: { [K in keyof Args]: Args[K] extends Arg ? GetArgType<Args[K]> : never }) => Promise<void> | void;
341
+ args: Args | undefined;
342
+ examples: {
343
+ args: string[];
344
+ description: string;
345
+ }[] | undefined;
346
+ };
347
+ /**
348
+ * Creates and runs a CLI application with the given commands.
349
+ *
350
+ * Automatically handles argument parsing, help generation, and interactive mode.
351
+ *
352
+ * **Built-in commands:**
353
+ * - `h` or `--help` - Shows help with all commands
354
+ * - `i` - Interactive mode (select command from list)
355
+ * - `<command> -h` - Shows help for a specific command
356
+ *
357
+ * @template C - String literal union of command names
358
+ * @param options - CLI configuration
359
+ * @param options.name - CLI display name shown in header
360
+ * @param options.baseCmd - Command prefix for help text (e.g., 'my-cli')
361
+ * @param options.sort - Optional array to customize command display order
362
+ * @param cmds - Record of command definitions created with `createCmd`
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * await createCLI(
367
+ * { name: 'My CLI', baseCmd: 'my-cli' },
368
+ * {
369
+ * hello: createCmd({
370
+ * short: 'hi',
371
+ * description: 'Say hello',
372
+ * run: async () => console.log('Hello!'),
373
+ * }),
374
+ * deploy: createCmd({
375
+ * short: 'd',
376
+ * description: 'Deploy the app',
377
+ * args: {
378
+ * env: { type: 'positional-string', name: 'env', description: 'Environment' },
379
+ * },
380
+ * run: async ({ env }) => console.log(`Deploying to ${env}`),
381
+ * }),
382
+ * },
383
+ * );
384
+ * ```
385
+ *
386
+ * @example
387
+ * ```bash
388
+ * # Usage examples:
389
+ * my-cli # Show interactive menu
390
+ * my-cli h # Show help
391
+ * my-cli i # Interactive mode
392
+ * my-cli hello # Run hello command
393
+ * my-cli hi # Run hello via short alias
394
+ * my-cli deploy prod # Run deploy with argument
395
+ * my-cli deploy -h # Show deploy command help
396
+ * ```
397
+ */
398
+ declare function createCLI<C extends string>({
399
+ name,
400
+ sort,
401
+ baseCmd
402
+ }: {
403
+ name: string;
404
+ sort?: NoInfer<C>[];
405
+ baseCmd: string;
406
+ }, cmds: Record<C, Cmd>): Promise<void>;
407
+ //#endregion
408
+ export { type Arg, type PositionalArg, type SelectOption, type ValidateFn, cliInput, createCLI, createCmd };
409
+ //# sourceMappingURL=main.d.mts.map