@bunli/test 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.
package/dist/index.js ADDED
@@ -0,0 +1,1169 @@
1
+ // @bun
2
+ // ../core/dist/index.js
3
+ var __defProp = Object.defineProperty;
4
+ var __export = (target, all) => {
5
+ for (var name in all)
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true,
9
+ configurable: true,
10
+ set: (newValue) => all[name] = () => newValue
11
+ });
12
+ };
13
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
14
+ var exports_dist = {};
15
+ __export(exports_dist, {
16
+ validateFields: () => validateFields,
17
+ validate: () => validate,
18
+ utils: () => utils,
19
+ spinner: () => createSpinner,
20
+ select: () => select,
21
+ prompt: () => prompt2,
22
+ password: () => password,
23
+ getDotPath: () => getDotPath2,
24
+ confirm: () => confirm,
25
+ colors: () => colors,
26
+ SchemaError: () => SchemaError2
27
+ });
28
+ function createColorFunction(code) {
29
+ return (text) => {
30
+ if (!process.stdout.isTTY || process.env.NO_COLOR) {
31
+ return text;
32
+ }
33
+ return `\x1B[${code}m${text}\x1B[0m`;
34
+ };
35
+ }
36
+ function stripAnsi(text) {
37
+ return text.replace(/\x1b\[[0-9;]*m/g, "");
38
+ }
39
+ async function readline(prompt) {
40
+ process.stdout.write(prompt);
41
+ for await (const line of console) {
42
+ return line;
43
+ }
44
+ return "";
45
+ }
46
+ async function prompt(message, options = {}) {
47
+ const defaultHint = options.default ? ` (${options.default})` : "";
48
+ const promptText = `${message}${defaultHint} `;
49
+ while (true) {
50
+ const input = await readline(promptText);
51
+ const value = input.trim() || options.default || "";
52
+ if (options.schema) {
53
+ const result = await options.schema["~standard"].validate(value);
54
+ if (result.issues) {
55
+ console.error(colors.red("Invalid input:"));
56
+ for (const issue of result.issues) {
57
+ console.error(colors.dim(` \u2022 ${issue.message}`));
58
+ }
59
+ console.error();
60
+ continue;
61
+ }
62
+ return result.value;
63
+ }
64
+ if (options.validate) {
65
+ const result = options.validate(value);
66
+ if (result === true) {
67
+ return value;
68
+ } else if (typeof result === "string") {
69
+ console.error(`\u2717 ${result}`);
70
+ continue;
71
+ } else {
72
+ console.error("\u2717 Invalid input");
73
+ continue;
74
+ }
75
+ }
76
+ return value;
77
+ }
78
+ }
79
+ async function confirm(message, options = {}) {
80
+ const defaultHint = options.default === true ? "Y/n" : options.default === false ? "y/N" : "y/n";
81
+ const promptText = `${message} (${defaultHint}) `;
82
+ while (true) {
83
+ const input = await readline(promptText);
84
+ const value = input.trim().toLowerCase();
85
+ if (!value && options.default !== undefined) {
86
+ return options.default;
87
+ }
88
+ if (value === "y" || value === "yes") {
89
+ return true;
90
+ }
91
+ if (value === "n" || value === "no") {
92
+ return false;
93
+ }
94
+ console.error("\u2717 Please answer with y/yes or n/no");
95
+ }
96
+ }
97
+ async function select(message, options) {
98
+ const { options: choices, default: defaultValue } = options;
99
+ let selectedIndex = defaultValue ? choices.findIndex((opt) => opt.value === defaultValue) : 0;
100
+ if (selectedIndex === -1)
101
+ selectedIndex = 0;
102
+ console.log(message);
103
+ process.stdout.write(CURSOR_HIDE);
104
+ drawOptions(choices, selectedIndex);
105
+ return new Promise((resolve) => {
106
+ process.stdin.setRawMode(true);
107
+ process.stdin.resume();
108
+ const cleanup = () => {
109
+ process.stdin.setRawMode(false);
110
+ process.stdin.pause();
111
+ process.stdout.write(CURSOR_SHOW);
112
+ };
113
+ process.stdin.on("data", (data) => {
114
+ const key = data.toString();
115
+ if (key === "\x1B[A") {
116
+ selectedIndex = Math.max(0, selectedIndex - 1);
117
+ drawOptions(choices, selectedIndex);
118
+ } else if (key === "\x1B[B") {
119
+ selectedIndex = Math.min(choices.length - 1, selectedIndex + 1);
120
+ drawOptions(choices, selectedIndex);
121
+ } else if (key === "\r" || key === `
122
+ `) {
123
+ cleanup();
124
+ for (let i = 0;i < choices.length; i++) {
125
+ process.stdout.write(`${CSI}1A${CLEAR_LINE}`);
126
+ }
127
+ const selected = choices[selectedIndex];
128
+ if (selected) {
129
+ console.log(`\u2713 ${selected.label}`);
130
+ resolve(selected.value);
131
+ }
132
+ } else if (key === "\x03" || key === "\x1B") {
133
+ cleanup();
134
+ process.exit(0);
135
+ }
136
+ });
137
+ });
138
+ }
139
+ function drawOptions(options, selectedIndex) {
140
+ for (let i = 0;i < options.length; i++) {
141
+ process.stdout.write(`${CSI}1A`);
142
+ }
143
+ options.forEach((option, index) => {
144
+ process.stdout.write(CLEAR_LINE + CURSOR_START);
145
+ const prefix = index === selectedIndex ? "\u276F " : " ";
146
+ const hint = option.hint ? ` (${option.hint})` : "";
147
+ console.log(`${prefix}${option.label}${hint}`);
148
+ });
149
+ }
150
+ async function password(message, options = {}) {
151
+ process.stdout.write(message + " ");
152
+ return new Promise((resolve) => {
153
+ let input = "";
154
+ process.stdin.setRawMode(true);
155
+ process.stdin.resume();
156
+ const cleanup = () => {
157
+ process.stdin.setRawMode(false);
158
+ process.stdin.pause();
159
+ console.log();
160
+ };
161
+ process.stdin.on("data", async (data) => {
162
+ const key = data.toString();
163
+ if (key === "\r" || key === `
164
+ `) {
165
+ cleanup();
166
+ if (options.schema) {
167
+ const result = await options.schema["~standard"].validate(input);
168
+ if (result.issues) {
169
+ console.error(colors.red("Invalid input:"));
170
+ for (const issue of result.issues) {
171
+ console.error(colors.dim(` \u2022 ${issue.message}`));
172
+ }
173
+ console.error();
174
+ password(message, options).then(resolve);
175
+ return;
176
+ }
177
+ resolve(result.value);
178
+ } else if (options.validate) {
179
+ const result = options.validate(input);
180
+ if (result === true) {
181
+ resolve(input);
182
+ } else {
183
+ const errorMsg = typeof result === "string" ? result : "Invalid input";
184
+ console.error(`\u2717 ${errorMsg}`);
185
+ password(message, options).then(resolve);
186
+ }
187
+ } else {
188
+ resolve(input);
189
+ }
190
+ } else if (key === "\x03") {
191
+ cleanup();
192
+ process.exit(0);
193
+ } else if (key === "\x7F" || key === "\b") {
194
+ if (input.length > 0) {
195
+ input = input.slice(0, -1);
196
+ process.stdout.write("\b \b");
197
+ }
198
+ } else if (key.length === 1 && key >= " ") {
199
+ input += key;
200
+ process.stdout.write("*");
201
+ }
202
+ });
203
+ });
204
+ }
205
+ function createSpinner(options) {
206
+ const config = typeof options === "string" ? { text: options } : options || {};
207
+ let isSpinning = false;
208
+ let frameIndex = 0;
209
+ let intervalId = null;
210
+ let currentText = config.text || "";
211
+ const render = (symbol, text) => {
212
+ process.stdout.write(`${CLEAR_LINE2}${CURSOR_START2}${symbol} ${text}`);
213
+ };
214
+ const spinner = {
215
+ start(text) {
216
+ if (isSpinning)
217
+ return;
218
+ isSpinning = true;
219
+ if (text !== undefined) {
220
+ currentText = text;
221
+ }
222
+ process.stdout.write("\x1B[?25l");
223
+ intervalId = setInterval(() => {
224
+ const frame = SPINNER_FRAMES[frameIndex];
225
+ render(frame, currentText);
226
+ frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
227
+ }, 80);
228
+ },
229
+ stop(text) {
230
+ if (!isSpinning)
231
+ return;
232
+ isSpinning = false;
233
+ if (intervalId) {
234
+ clearInterval(intervalId);
235
+ intervalId = null;
236
+ }
237
+ process.stdout.write(CLEAR_LINE2 + CURSOR_START2);
238
+ process.stdout.write("\x1B[?25h");
239
+ if (text) {
240
+ console.log(text);
241
+ }
242
+ },
243
+ succeed(text) {
244
+ this.stop();
245
+ console.log(`\u2705 ${text || currentText}`);
246
+ },
247
+ fail(text) {
248
+ this.stop();
249
+ console.log(`\u274C ${text || currentText}`);
250
+ },
251
+ warn(text) {
252
+ this.stop();
253
+ console.log(`\u26A0\uFE0F ${text || currentText}`);
254
+ },
255
+ info(text) {
256
+ this.stop();
257
+ console.log(`\u2139\uFE0F ${text || currentText}`);
258
+ },
259
+ update(text) {
260
+ currentText = text;
261
+ if (isSpinning) {
262
+ render(SPINNER_FRAMES[frameIndex], currentText);
263
+ }
264
+ }
265
+ };
266
+ process.on("exit", () => spinner.stop());
267
+ process.on("SIGINT", () => {
268
+ spinner.stop();
269
+ process.exit(0);
270
+ });
271
+ return spinner;
272
+ }
273
+ function getDotPath2(issue) {
274
+ if (issue.path?.length) {
275
+ let dotPath = "";
276
+ for (const item of issue.path) {
277
+ const key = typeof item === "object" ? item.key : item;
278
+ if (typeof key === "string" || typeof key === "number") {
279
+ if (dotPath) {
280
+ dotPath += `.${key}`;
281
+ } else {
282
+ dotPath += key;
283
+ }
284
+ } else {
285
+ return null;
286
+ }
287
+ }
288
+ return dotPath;
289
+ }
290
+ return null;
291
+ }
292
+ async function validate(schema, value) {
293
+ const result = await schema["~standard"].validate(value);
294
+ if (result.issues) {
295
+ throw new SchemaError2(result.issues);
296
+ }
297
+ return result.value;
298
+ }
299
+ async function validateFields(schemas, values) {
300
+ const results = {};
301
+ const errors = {};
302
+ for (const [field, schema] of Object.entries(schemas)) {
303
+ const result = await schema["~standard"].validate(values[field]);
304
+ if (result.issues) {
305
+ errors[field] = result.issues.map((issue) => issue.message);
306
+ } else {
307
+ results[field] = result.value;
308
+ }
309
+ }
310
+ if (Object.keys(errors).length > 0) {
311
+ return { errors };
312
+ }
313
+ return results;
314
+ }
315
+ var colorCodes;
316
+ var colors;
317
+ var ESC = "\x1B";
318
+ var CSI;
319
+ var CLEAR_LINE;
320
+ var CURSOR_START;
321
+ var CURSOR_HIDE;
322
+ var CURSOR_SHOW;
323
+ var SPINNER_FRAMES;
324
+ var CLEAR_LINE2 = "\x1B[2K";
325
+ var CURSOR_START2 = "\x1B[G";
326
+ var SchemaError2;
327
+ var utils;
328
+ var prompt2;
329
+ var init_dist = __esm(() => {
330
+ colorCodes = {
331
+ black: 30,
332
+ red: 31,
333
+ green: 32,
334
+ yellow: 33,
335
+ blue: 34,
336
+ magenta: 35,
337
+ cyan: 36,
338
+ white: 37,
339
+ gray: 90,
340
+ brightRed: 91,
341
+ brightGreen: 92,
342
+ brightYellow: 93,
343
+ brightBlue: 94,
344
+ brightMagenta: 95,
345
+ brightCyan: 96,
346
+ brightWhite: 97,
347
+ bgRed: 41,
348
+ bgGreen: 42,
349
+ bgYellow: 43,
350
+ bgBlue: 44,
351
+ bgMagenta: 45,
352
+ bgCyan: 46,
353
+ bgWhite: 47,
354
+ bold: 1,
355
+ dim: 2,
356
+ italic: 3,
357
+ underline: 4,
358
+ strikethrough: 9,
359
+ reset: 0
360
+ };
361
+ colors = {
362
+ black: createColorFunction(colorCodes.black),
363
+ red: createColorFunction(colorCodes.red),
364
+ green: createColorFunction(colorCodes.green),
365
+ yellow: createColorFunction(colorCodes.yellow),
366
+ blue: createColorFunction(colorCodes.blue),
367
+ magenta: createColorFunction(colorCodes.magenta),
368
+ cyan: createColorFunction(colorCodes.cyan),
369
+ white: createColorFunction(colorCodes.white),
370
+ gray: createColorFunction(colorCodes.gray),
371
+ brightRed: createColorFunction(colorCodes.brightRed),
372
+ brightGreen: createColorFunction(colorCodes.brightGreen),
373
+ brightYellow: createColorFunction(colorCodes.brightYellow),
374
+ brightBlue: createColorFunction(colorCodes.brightBlue),
375
+ brightMagenta: createColorFunction(colorCodes.brightMagenta),
376
+ brightCyan: createColorFunction(colorCodes.brightCyan),
377
+ brightWhite: createColorFunction(colorCodes.brightWhite),
378
+ bgRed: createColorFunction(colorCodes.bgRed),
379
+ bgGreen: createColorFunction(colorCodes.bgGreen),
380
+ bgYellow: createColorFunction(colorCodes.bgYellow),
381
+ bgBlue: createColorFunction(colorCodes.bgBlue),
382
+ bgMagenta: createColorFunction(colorCodes.bgMagenta),
383
+ bgCyan: createColorFunction(colorCodes.bgCyan),
384
+ bgWhite: createColorFunction(colorCodes.bgWhite),
385
+ bold: createColorFunction(colorCodes.bold),
386
+ dim: createColorFunction(colorCodes.dim),
387
+ italic: createColorFunction(colorCodes.italic),
388
+ underline: createColorFunction(colorCodes.underline),
389
+ strikethrough: createColorFunction(colorCodes.strikethrough),
390
+ reset: createColorFunction(colorCodes.reset),
391
+ strip: stripAnsi
392
+ };
393
+ CSI = `${ESC}[`;
394
+ CLEAR_LINE = `${CSI}2K`;
395
+ CURSOR_START = `${CSI}G`;
396
+ CURSOR_HIDE = `${CSI}?25l`;
397
+ CURSOR_SHOW = `${CSI}?25h`;
398
+ SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
399
+ SchemaError2 = class extends Error {
400
+ issues;
401
+ constructor(issues) {
402
+ super(issues[0].message);
403
+ this.name = "SchemaError";
404
+ this.issues = issues;
405
+ }
406
+ };
407
+ utils = {
408
+ prompt: Object.assign(prompt, {
409
+ confirm,
410
+ select,
411
+ password
412
+ }),
413
+ spinner: createSpinner,
414
+ colors
415
+ };
416
+ prompt2 = Object.assign(prompt, {
417
+ confirm,
418
+ select,
419
+ password
420
+ });
421
+ });
422
+ function getDotPath(issue) {
423
+ if (issue.path?.length) {
424
+ let dotPath = "";
425
+ for (const item of issue.path) {
426
+ const key = typeof item === "object" ? item.key : item;
427
+ if (typeof key === "string" || typeof key === "number") {
428
+ if (dotPath) {
429
+ dotPath += `.${key}`;
430
+ } else {
431
+ dotPath += key;
432
+ }
433
+ } else {
434
+ return null;
435
+ }
436
+ }
437
+ return dotPath;
438
+ }
439
+ return null;
440
+ }
441
+ var SchemaError = class extends Error {
442
+ issues;
443
+ constructor(issues) {
444
+ super(issues[0].message);
445
+ this.name = "SchemaError";
446
+ this.issues = issues;
447
+ }
448
+ };
449
+ async function parseArgs(args, options) {
450
+ const flags = {};
451
+ const positional = [];
452
+ const shortToName = new Map;
453
+ for (const [name, opt] of Object.entries(options)) {
454
+ if (opt.short) {
455
+ shortToName.set(opt.short, name);
456
+ }
457
+ }
458
+ for (let i = 0;i < args.length; i++) {
459
+ const arg = args[i];
460
+ if (!arg)
461
+ continue;
462
+ if (arg.startsWith("--")) {
463
+ const eqIndex = arg.indexOf("=");
464
+ const name = eqIndex > 0 ? arg.slice(2, eqIndex) : arg.slice(2);
465
+ const inlineValue = eqIndex > 0 ? arg.slice(eqIndex + 1) : undefined;
466
+ if (!name || !options[name])
467
+ continue;
468
+ let value = inlineValue;
469
+ if (value === undefined && i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
470
+ value = args[++i];
471
+ }
472
+ flags[name] = await validateOption(name, value ?? "true", options[name].schema);
473
+ } else if (arg.startsWith("-") && arg.length > 1) {
474
+ const short = arg.slice(1);
475
+ const name = shortToName.get(short);
476
+ if (name && options[name]) {
477
+ let value;
478
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
479
+ value = args[++i];
480
+ }
481
+ flags[name] = await validateOption(name, value ?? "true", options[name].schema);
482
+ }
483
+ } else {
484
+ positional.push(arg);
485
+ }
486
+ }
487
+ for (const [name, opt] of Object.entries(options)) {
488
+ if (!(name in flags)) {
489
+ flags[name] = await validateOption(name, undefined, opt.schema);
490
+ }
491
+ }
492
+ return { flags, positional };
493
+ }
494
+ async function validateOption(name, value, schema) {
495
+ let processedValue = value;
496
+ if (typeof value === "string" && (value === "true" || value === "false")) {
497
+ const testResult = await schema["~standard"].validate(true);
498
+ if (!testResult.issues) {
499
+ processedValue = value === "true";
500
+ }
501
+ }
502
+ const result = await schema["~standard"].validate(processedValue);
503
+ if (result.issues) {
504
+ const issuesWithPath = result.issues.map((issue) => ({
505
+ ...issue,
506
+ path: [name, ...issue.path || []]
507
+ }));
508
+ throw new SchemaError(issuesWithPath);
509
+ }
510
+ return result.value;
511
+ }
512
+ function createCLI(config) {
513
+ const fullConfig = "commands" in config ? config : { ...config, commands: undefined };
514
+ const commands = new Map;
515
+ function registerCommand(cmd, path = []) {
516
+ const fullName = [...path, cmd.name].join(" ");
517
+ commands.set(fullName, cmd);
518
+ if (cmd.alias) {
519
+ const aliases = Array.isArray(cmd.alias) ? cmd.alias : [cmd.alias];
520
+ aliases.forEach((alias) => {
521
+ const aliasPath = [...path, alias].join(" ");
522
+ commands.set(aliasPath, cmd);
523
+ });
524
+ }
525
+ if (cmd.commands) {
526
+ cmd.commands.forEach((subCmd) => {
527
+ registerCommand(subCmd, [...path, cmd.name]);
528
+ });
529
+ }
530
+ }
531
+ function findCommand(args) {
532
+ for (let i = args.length;i > 0; i--) {
533
+ const cmdPath = args.slice(0, i).join(" ");
534
+ const command = commands.get(cmdPath);
535
+ if (command) {
536
+ return { command, remainingArgs: args.slice(i) };
537
+ }
538
+ }
539
+ return { command: undefined, remainingArgs: args };
540
+ }
541
+ function showHelp(cmd, path = []) {
542
+ if (!cmd) {
543
+ console.log(`${fullConfig.name} v${fullConfig.version}`);
544
+ if (fullConfig.description) {
545
+ console.log(fullConfig.description);
546
+ }
547
+ console.log(`
548
+ Commands:`);
549
+ const topLevel = new Set;
550
+ for (const [name, command] of commands) {
551
+ if (!name.includes(" ") && !command.alias?.includes(name)) {
552
+ topLevel.add(command);
553
+ }
554
+ }
555
+ for (const command of topLevel) {
556
+ console.log(` ${command.name.padEnd(20)} ${command.description}`);
557
+ }
558
+ } else {
559
+ const fullPath = [...path, cmd.name].join(" ");
560
+ console.log(`Usage: ${fullConfig.name} ${fullPath} [options]`);
561
+ console.log(`
562
+ ${cmd.description}`);
563
+ if (cmd.options && Object.keys(cmd.options).length > 0) {
564
+ console.log(`
565
+ Options:`);
566
+ for (const [name, opt] of Object.entries(cmd.options)) {
567
+ const flag = `--${name}${opt.short ? `, -${opt.short}` : ""}`;
568
+ const description = opt.description || "";
569
+ console.log(` ${flag.padEnd(20)} ${description}`);
570
+ }
571
+ }
572
+ if (cmd.commands && cmd.commands.length > 0) {
573
+ console.log(`
574
+ Subcommands:`);
575
+ for (const subCmd of cmd.commands) {
576
+ console.log(` ${subCmd.name.padEnd(20)} ${subCmd.description}`);
577
+ }
578
+ }
579
+ }
580
+ }
581
+ async function loadFromConfig() {
582
+ if (fullConfig.commands?.manifest) {
583
+ try {
584
+ const manifestPath = fullConfig.commands.manifest.startsWith(".") ? `${process.cwd()}/${fullConfig.commands.manifest}` : fullConfig.commands.manifest;
585
+ const manifestModule = await import(manifestPath);
586
+ const manifest = manifestModule.default || manifestModule;
587
+ await loadCommandsFromManifest(manifest);
588
+ } catch (error) {
589
+ console.error(`Failed to load command manifest from ${fullConfig.commands.manifest}:`, error);
590
+ }
591
+ }
592
+ }
593
+ async function loadCommandsFromManifest(manifest) {
594
+ async function loadFromManifest(obj, path = []) {
595
+ const commands2 = [];
596
+ if (typeof obj === "function") {
597
+ const { default: command } = await obj();
598
+ return [command];
599
+ }
600
+ for (const [key, value] of Object.entries(obj)) {
601
+ if (typeof value === "function") {
602
+ const { default: command } = await value();
603
+ commands2.push(command);
604
+ } else {
605
+ const subCommands = await loadFromManifest(value, [...path, key]);
606
+ if (subCommands.length > 0) {
607
+ const parentCommand = {
608
+ name: key,
609
+ description: `${key} commands`,
610
+ commands: subCommands
611
+ };
612
+ commands2.push(parentCommand);
613
+ }
614
+ }
615
+ }
616
+ return commands2;
617
+ }
618
+ const loadedCommands = await loadFromManifest(manifest);
619
+ loadedCommands.forEach((cmd) => registerCommand(cmd));
620
+ }
621
+ return {
622
+ command(cmd) {
623
+ registerCommand(cmd);
624
+ },
625
+ async load(manifest) {
626
+ await loadCommandsFromManifest(manifest);
627
+ },
628
+ async init() {
629
+ await loadFromConfig();
630
+ },
631
+ async run(argv = process.argv.slice(2)) {
632
+ if (argv.length === 0) {
633
+ showHelp();
634
+ return;
635
+ }
636
+ if (argv.includes("--help") || argv.includes("-h")) {
637
+ const helpIndex = Math.max(argv.indexOf("--help"), argv.indexOf("-h"));
638
+ const cmdArgs = argv.slice(0, helpIndex);
639
+ if (cmdArgs.length === 0) {
640
+ showHelp();
641
+ } else {
642
+ const { command: command2 } = findCommand(cmdArgs);
643
+ if (command2) {
644
+ showHelp(command2, cmdArgs.slice(0, -1));
645
+ } else {
646
+ console.error(`Unknown command: ${cmdArgs.join(" ")}`);
647
+ process.exit(1);
648
+ }
649
+ }
650
+ return;
651
+ }
652
+ const { command, remainingArgs } = findCommand(argv);
653
+ if (!command) {
654
+ console.error(`Unknown command: ${argv[0]}`);
655
+ process.exit(1);
656
+ }
657
+ if (!command.handler && command.commands) {
658
+ showHelp(command, argv.slice(0, argv.length - remainingArgs.length - 1));
659
+ return;
660
+ }
661
+ if (command.handler) {
662
+ try {
663
+ const parsed = await parseArgs(remainingArgs, command.options || {});
664
+ const { prompt: prompt3, spinner, colors: colors2 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
665
+ await command.handler({
666
+ flags: parsed.flags,
667
+ positional: parsed.positional,
668
+ shell: Bun.$,
669
+ env: process.env,
670
+ cwd: process.cwd(),
671
+ prompt: prompt3,
672
+ spinner,
673
+ colors: colors2
674
+ });
675
+ } catch (error) {
676
+ const { colors: colors2 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
677
+ if (error instanceof SchemaError) {
678
+ console.error(colors2.red("Validation errors:"));
679
+ const fieldErrors = {};
680
+ const generalErrors = [];
681
+ for (const issue of error.issues) {
682
+ const dotPath = getDotPath(issue);
683
+ if (dotPath) {
684
+ fieldErrors[dotPath] ??= [];
685
+ fieldErrors[dotPath].push(issue.message);
686
+ } else {
687
+ generalErrors.push(issue.message);
688
+ }
689
+ }
690
+ for (const [field, messages] of Object.entries(fieldErrors)) {
691
+ console.error(colors2.yellow(` --${field}:`));
692
+ for (const message of messages) {
693
+ console.error(colors2.dim(` \u2022 ${message}`));
694
+ }
695
+ }
696
+ for (const message of generalErrors) {
697
+ console.error(colors2.dim(` \u2022 ${message}`));
698
+ }
699
+ process.exit(1);
700
+ } else if (error instanceof Error) {
701
+ console.error(colors2.red(`Error: ${error.message}`));
702
+ process.exit(1);
703
+ }
704
+ throw error;
705
+ }
706
+ }
707
+ }
708
+ };
709
+ }
710
+
711
+ // src/test-command.ts
712
+ async function testCommand(command, options = {}) {
713
+ const startTime = performance.now();
714
+ const stdout = [];
715
+ const stderr = [];
716
+ let exitCode = 0;
717
+ let error;
718
+ const stdinLines = Array.isArray(options.stdin) ? [...options.stdin] : options.stdin ? [options.stdin] : [];
719
+ const mockPromptsMap = options.mockPrompts || {};
720
+ const promptResponsesUsed = new Map;
721
+ const mockPrompt = Object.assign(async (message, options2) => {
722
+ stdout.push(message);
723
+ let response;
724
+ if (mockPromptsMap[message]) {
725
+ const responses = mockPromptsMap[message];
726
+ const usedCount = promptResponsesUsed.get(message) || 0;
727
+ if (Array.isArray(responses)) {
728
+ response = responses[usedCount] ?? responses[responses.length - 1] ?? "";
729
+ promptResponsesUsed.set(message, usedCount + 1);
730
+ } else {
731
+ response = responses ?? "";
732
+ }
733
+ } else {
734
+ response = stdinLines.shift() || "";
735
+ }
736
+ stdout.push(response);
737
+ if (options2?.schema) {
738
+ const result = await options2.schema["~standard"].validate(response);
739
+ if (result.issues) {
740
+ stderr.push("[red]Invalid input:[/red]");
741
+ for (const issue of result.issues) {
742
+ stderr.push(`[dim] \u2022 ${issue.message}[/dim]`);
743
+ }
744
+ const hasMoreMockResponses = mockPromptsMap[message] && Array.isArray(mockPromptsMap[message]) && (promptResponsesUsed.get(message) || 0) < mockPromptsMap[message].length;
745
+ const hasMoreStdin = stdinLines.length > 0;
746
+ if (hasMoreMockResponses || hasMoreStdin) {
747
+ return mockPrompt(message, options2);
748
+ }
749
+ return;
750
+ }
751
+ return result.value;
752
+ }
753
+ if (options2?.validate) {
754
+ const validationResult = options2.validate(response);
755
+ if (validationResult !== true) {
756
+ const errorMsg = typeof validationResult === "string" ? validationResult : "Invalid input";
757
+ stderr.push(`\u2717 ${errorMsg}`);
758
+ if (stdinLines.length > 0) {
759
+ return mockPrompt(message, options2);
760
+ }
761
+ }
762
+ }
763
+ return response;
764
+ }, {
765
+ confirm: async (message, opts) => {
766
+ stdout.push(message);
767
+ let response;
768
+ if (mockPromptsMap[message]) {
769
+ const responses = mockPromptsMap[message];
770
+ const usedCount = promptResponsesUsed.get(message) || 0;
771
+ if (Array.isArray(responses)) {
772
+ response = responses[usedCount] ?? responses[responses.length - 1] ?? "";
773
+ promptResponsesUsed.set(message, usedCount + 1);
774
+ } else {
775
+ response = responses ?? "";
776
+ }
777
+ } else {
778
+ response = stdinLines.shift() || "";
779
+ }
780
+ stdout.push(response);
781
+ const normalized = response.toLowerCase().trim();
782
+ return normalized === "y" || normalized === "yes" || opts?.default && normalized === "";
783
+ },
784
+ select: async (message, selectOptions) => {
785
+ stdout.push(message);
786
+ selectOptions.options.forEach((choice2, i) => {
787
+ const label = typeof choice2 === "object" ? choice2.label : choice2;
788
+ stdout.push(` ${i + 1}. ${label}`);
789
+ });
790
+ let response;
791
+ if (mockPromptsMap[message]) {
792
+ const responses = mockPromptsMap[message];
793
+ const usedCount = promptResponsesUsed.get(message) || 0;
794
+ if (Array.isArray(responses)) {
795
+ response = responses[usedCount] ?? responses[responses.length - 1] ?? "";
796
+ promptResponsesUsed.set(message, usedCount + 1);
797
+ } else {
798
+ response = responses ?? "";
799
+ }
800
+ } else {
801
+ response = stdinLines.shift() || "1";
802
+ }
803
+ stdout.push(`> ${response}`);
804
+ const index = parseInt(response) - 1;
805
+ const choice = selectOptions.options[index] || selectOptions.options[0];
806
+ return typeof choice === "object" ? choice.value : choice;
807
+ },
808
+ password: async (message, options2) => {
809
+ stdout.push(message);
810
+ let response;
811
+ if (mockPromptsMap[message]) {
812
+ const responses = mockPromptsMap[message];
813
+ const usedCount = promptResponsesUsed.get(message) || 0;
814
+ if (Array.isArray(responses)) {
815
+ response = responses[usedCount] ?? responses[responses.length - 1] ?? "";
816
+ promptResponsesUsed.set(message, usedCount + 1);
817
+ } else {
818
+ response = responses ?? "";
819
+ }
820
+ } else {
821
+ response = stdinLines.shift() || "";
822
+ }
823
+ stdout.push("*".repeat(response.length));
824
+ if (options2?.schema) {
825
+ const result = await options2.schema["~standard"].validate(response);
826
+ if (result.issues) {
827
+ stderr.push("[red]Invalid input:[/red]");
828
+ for (const issue of result.issues) {
829
+ stderr.push(`[dim] \u2022 ${issue.message}[/dim]`);
830
+ }
831
+ const hasMoreMockResponses = mockPromptsMap[message] && Array.isArray(mockPromptsMap[message]) && (promptResponsesUsed.get(message) || 0) < mockPromptsMap[message].length;
832
+ const hasMoreStdin = stdinLines.length > 0;
833
+ if (hasMoreMockResponses || hasMoreStdin) {
834
+ return mockPrompt.password(message, options2);
835
+ }
836
+ return;
837
+ }
838
+ return result.value;
839
+ }
840
+ return response;
841
+ },
842
+ multiselect: async (message, selectOptions) => {
843
+ stdout.push(message);
844
+ selectOptions.options.forEach((choice, i) => {
845
+ const label = typeof choice === "object" ? choice.label : choice;
846
+ stdout.push(` [ ] ${i + 1}. ${label}`);
847
+ });
848
+ let response;
849
+ if (mockPromptsMap[message]) {
850
+ const responses = mockPromptsMap[message];
851
+ const usedCount = promptResponsesUsed.get(message) || 0;
852
+ if (Array.isArray(responses)) {
853
+ response = responses[usedCount] ?? responses[responses.length - 1] ?? "";
854
+ promptResponsesUsed.set(message, usedCount + 1);
855
+ } else {
856
+ response = responses ?? "";
857
+ }
858
+ } else {
859
+ response = stdinLines.shift() || "";
860
+ }
861
+ stdout.push(`> ${response}`);
862
+ const indices = response.split(",").map((s) => parseInt(s.trim()) - 1);
863
+ return indices.filter((i) => i >= 0 && i < selectOptions.options.length).map((i) => {
864
+ const choice = selectOptions.options[i];
865
+ return typeof choice === "object" ? choice.value : choice;
866
+ });
867
+ }
868
+ });
869
+ const mockSpinner = (text) => {
870
+ if (text)
871
+ stdout.push(`\u280B ${text}`);
872
+ return {
873
+ start: (text2) => {
874
+ if (text2)
875
+ stdout.push(`\u280B ${text2}`);
876
+ },
877
+ stop: (text2) => {
878
+ if (text2)
879
+ stdout.push(text2);
880
+ },
881
+ succeed: (text2) => {
882
+ stdout.push(`\u2705 ${text2 || "Done"}`);
883
+ },
884
+ fail: (text2) => {
885
+ stdout.push(`\u274C ${text2 || "Failed"}`);
886
+ },
887
+ warn: (text2) => {
888
+ stdout.push(`\u26A0\uFE0F ${text2 || "Warning"}`);
889
+ },
890
+ info: (text2) => {
891
+ stdout.push(`\u2139\uFE0F ${text2 || "Info"}`);
892
+ },
893
+ update: (text2) => {
894
+ stdout.push(`\u280B ${text2}`);
895
+ }
896
+ };
897
+ };
898
+ const mockShellCommands = options.mockShellCommands || {};
899
+ const mockShell = (strings, ...values) => {
900
+ const command2 = strings.reduce((acc, str, i) => {
901
+ return acc + str + (values[i] || "");
902
+ }, "").trim();
903
+ stdout.push(`$ ${command2}`);
904
+ const promise = Promise.resolve();
905
+ promise.text = async () => {
906
+ if (mockShellCommands[command2]) {
907
+ return mockShellCommands[command2];
908
+ }
909
+ if (command2.includes("git branch --show-current")) {
910
+ return `main
911
+ `;
912
+ }
913
+ if (command2.includes("git status")) {
914
+ return `nothing to commit, working tree clean
915
+ `;
916
+ }
917
+ return "";
918
+ };
919
+ promise.json = async () => {
920
+ if (mockShellCommands[command2]) {
921
+ try {
922
+ return JSON.parse(mockShellCommands[command2]);
923
+ } catch {
924
+ return {};
925
+ }
926
+ }
927
+ return {};
928
+ };
929
+ promise.quiet = () => promise;
930
+ return promise;
931
+ };
932
+ const mockColors = {
933
+ red: (text) => `[red]${text}[/red]`,
934
+ green: (text) => `[green]${text}[/green]`,
935
+ blue: (text) => `[blue]${text}[/blue]`,
936
+ yellow: (text) => `[yellow]${text}[/yellow]`,
937
+ cyan: (text) => `[cyan]${text}[/cyan]`,
938
+ magenta: (text) => `[magenta]${text}[/magenta]`,
939
+ gray: (text) => `[gray]${text}[/gray]`,
940
+ dim: (text) => `[dim]${text}[/dim]`,
941
+ bold: (text) => `[bold]${text}[/bold]`,
942
+ italic: (text) => `[italic]${text}[/italic]`,
943
+ underline: (text) => `[underline]${text}[/underline]`,
944
+ strikethrough: (text) => `[strikethrough]${text}[/strikethrough]`,
945
+ bgRed: (text) => `[bgRed]${text}[/bgRed]`,
946
+ bgGreen: (text) => `[bgGreen]${text}[/bgGreen]`,
947
+ bgBlue: (text) => `[bgBlue]${text}[/bgBlue]`,
948
+ bgYellow: (text) => `[bgYellow]${text}[/bgYellow]`,
949
+ bgCyan: (text) => `[bgCyan]${text}[/bgCyan]`,
950
+ bgMagenta: (text) => `[bgMagenta]${text}[/bgMagenta]`,
951
+ bgGray: (text) => `[bgGray]${text}[/bgGray]`,
952
+ black: (text) => `[black]${text}[/black]`,
953
+ white: (text) => `[white]${text}[/white]`,
954
+ bgBlack: (text) => `[bgBlack]${text}[/bgBlack]`,
955
+ bgWhite: (text) => `[bgWhite]${text}[/bgWhite]`,
956
+ brightRed: (text) => `[brightRed]${text}[/brightRed]`,
957
+ brightGreen: (text) => `[brightGreen]${text}[/brightGreen]`,
958
+ brightYellow: (text) => `[brightYellow]${text}[/brightYellow]`,
959
+ brightBlue: (text) => `[brightBlue]${text}[/brightBlue]`,
960
+ brightCyan: (text) => `[brightCyan]${text}[/brightCyan]`,
961
+ brightMagenta: (text) => `[brightMagenta]${text}[/brightMagenta]`,
962
+ brightWhite: (text) => `[brightWhite]${text}[/brightWhite]`,
963
+ reset: (text) => `[reset]${text}[/reset]`,
964
+ strip: (text) => text.replace(/\[[^\]]+\]/g, "")
965
+ };
966
+ const originalLog = console.log;
967
+ const originalError = console.error;
968
+ console.log = (...args) => {
969
+ stdout.push(args.join(" "));
970
+ };
971
+ console.error = (...args) => {
972
+ stderr.push(args.join(" "));
973
+ };
974
+ try {
975
+ const handlerArgs = {
976
+ flags: options.flags || {},
977
+ positional: options.args || [],
978
+ env: { ...process.env, ...options.env || {} },
979
+ cwd: options.cwd || process.cwd(),
980
+ prompt: mockPrompt,
981
+ spinner: mockSpinner,
982
+ shell: mockShell,
983
+ colors: mockColors
984
+ };
985
+ if (command.handler) {
986
+ await command.handler(handlerArgs);
987
+ }
988
+ exitCode = options.exitCode || 0;
989
+ } catch (err) {
990
+ error = err;
991
+ exitCode = 1;
992
+ stderr.push(error.message);
993
+ } finally {
994
+ console.log = originalLog;
995
+ console.error = originalError;
996
+ }
997
+ const duration = performance.now() - startTime;
998
+ return {
999
+ stdout: stdout.join(`
1000
+ `),
1001
+ stderr: stderr.join(`
1002
+ `),
1003
+ exitCode,
1004
+ duration,
1005
+ error
1006
+ };
1007
+ }
1008
+ async function testCLI(setupCLI, argv, options = {}) {
1009
+ const startTime = performance.now();
1010
+ const stdout = [];
1011
+ const stderr = [];
1012
+ let exitCode = 0;
1013
+ let error;
1014
+ const originalLog = console.log;
1015
+ const originalError = console.error;
1016
+ const originalExit = process.exit;
1017
+ console.log = (...args) => {
1018
+ stdout.push(args.join(" "));
1019
+ };
1020
+ console.error = (...args) => {
1021
+ stderr.push(args.join(" "));
1022
+ };
1023
+ process.exit = (code) => {
1024
+ exitCode = code || 0;
1025
+ throw new Error(`Process exited with code ${exitCode}`);
1026
+ };
1027
+ try {
1028
+ const cli = createCLI({
1029
+ name: "test-cli",
1030
+ version: "1.0.0",
1031
+ description: "Test CLI"
1032
+ });
1033
+ setupCLI(cli);
1034
+ await cli.run(argv);
1035
+ } catch (err) {
1036
+ if (!err.message.startsWith("Process exited with code")) {
1037
+ error = err;
1038
+ exitCode = 1;
1039
+ stderr.push(error?.message || "Unknown error");
1040
+ }
1041
+ } finally {
1042
+ console.log = originalLog;
1043
+ console.error = originalError;
1044
+ process.exit = originalExit;
1045
+ }
1046
+ const duration = performance.now() - startTime;
1047
+ return {
1048
+ stdout: stdout.join(`
1049
+ `),
1050
+ stderr: stderr.join(`
1051
+ `),
1052
+ exitCode,
1053
+ duration,
1054
+ error
1055
+ };
1056
+ }
1057
+ // src/matchers.ts
1058
+ function createMatchers(result) {
1059
+ return {
1060
+ toHaveExitCode(code) {
1061
+ if (result.exitCode !== code) {
1062
+ throw new Error(`Expected exit code ${code}, but got ${result.exitCode}
1063
+ ` + `stdout: ${result.stdout}
1064
+ ` + `stderr: ${result.stderr}`);
1065
+ }
1066
+ },
1067
+ toHaveSucceeded() {
1068
+ if (result.exitCode !== 0) {
1069
+ throw new Error(`Expected command to succeed (exit code 0), but got ${result.exitCode}
1070
+ ` + `stdout: ${result.stdout}
1071
+ ` + `stderr: ${result.stderr}
1072
+ ` + (result.error ? `error: ${result.error.message}` : ""));
1073
+ }
1074
+ },
1075
+ toHaveFailed() {
1076
+ if (result.exitCode === 0) {
1077
+ throw new Error(`Expected command to fail (non-zero exit code), but it succeeded
1078
+ ` + `stdout: ${result.stdout}`);
1079
+ }
1080
+ },
1081
+ toContainInStdout(text) {
1082
+ if (!result.stdout.includes(text)) {
1083
+ throw new Error(`Expected stdout to contain "${text}"
1084
+ ` + `stdout: ${result.stdout}`);
1085
+ }
1086
+ },
1087
+ toContainInStderr(text) {
1088
+ if (!result.stderr.includes(text)) {
1089
+ throw new Error(`Expected stderr to contain "${text}"
1090
+ ` + `stderr: ${result.stderr}`);
1091
+ }
1092
+ },
1093
+ toMatchStdout(pattern) {
1094
+ if (!pattern.test(result.stdout)) {
1095
+ throw new Error(`Expected stdout to match ${pattern}
1096
+ ` + `stdout: ${result.stdout}`);
1097
+ }
1098
+ },
1099
+ toMatchStderr(pattern) {
1100
+ if (!pattern.test(result.stderr)) {
1101
+ throw new Error(`Expected stderr to match ${pattern}
1102
+ ` + `stderr: ${result.stderr}`);
1103
+ }
1104
+ }
1105
+ };
1106
+ }
1107
+ function expectCommand(result) {
1108
+ const matchers = createMatchers(result);
1109
+ return {
1110
+ ...result,
1111
+ ...matchers
1112
+ };
1113
+ }
1114
+ // src/helpers.ts
1115
+ function mockPromptResponses(responses) {
1116
+ return { mockPrompts: responses };
1117
+ }
1118
+ function mockShellCommands(commands) {
1119
+ return { mockShellCommands: commands };
1120
+ }
1121
+ function mockInteractive(prompts, commands) {
1122
+ return {
1123
+ ...mockPromptResponses(prompts),
1124
+ ...commands ? mockShellCommands(commands) : {}
1125
+ };
1126
+ }
1127
+ function mockValidationAttempts(attempts) {
1128
+ return { stdin: attempts };
1129
+ }
1130
+ function mergeTestOptions(...options) {
1131
+ const merged = {};
1132
+ for (const opt of options) {
1133
+ if (opt.stdin || opt.mockPrompts) {
1134
+ const stdinArray = [];
1135
+ if (merged.stdin) {
1136
+ if (Array.isArray(merged.stdin)) {
1137
+ stdinArray.push(...merged.stdin);
1138
+ } else {
1139
+ stdinArray.push(merged.stdin);
1140
+ }
1141
+ }
1142
+ if (opt.stdin) {
1143
+ if (Array.isArray(opt.stdin)) {
1144
+ stdinArray.push(...opt.stdin);
1145
+ } else {
1146
+ stdinArray.push(opt.stdin);
1147
+ }
1148
+ }
1149
+ if (stdinArray.length > 0) {
1150
+ merged.stdin = stdinArray;
1151
+ }
1152
+ }
1153
+ Object.assign(merged, opt);
1154
+ }
1155
+ return merged;
1156
+ }
1157
+ export {
1158
+ testCommand,
1159
+ testCLI,
1160
+ mockValidationAttempts,
1161
+ mockShellCommands,
1162
+ mockPromptResponses,
1163
+ mockInteractive,
1164
+ mergeTestOptions,
1165
+ expectCommand,
1166
+ createMatchers
1167
+ };
1168
+
1169
+ //# debugId=CC322262A7E5B8FD64756E2164756E21