bob-core 2.0.0-beta.9 → 2.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.
Files changed (56) hide show
  1. package/README.md +57 -33
  2. package/dist/cjs/package-Blqq-jZJ.cjs +1 -0
  3. package/dist/cjs/src/Cli.d.ts +10 -11
  4. package/dist/cjs/src/Command.d.ts +11 -18
  5. package/dist/cjs/src/CommandIO.d.ts +5 -2
  6. package/dist/cjs/src/CommandParser.d.ts +13 -5
  7. package/dist/cjs/src/CommandRegistry.d.ts +13 -6
  8. package/dist/cjs/src/CommandSignatureParser.d.ts +5 -4
  9. package/dist/cjs/src/CommandWithSignature.d.ts +3 -3
  10. package/dist/cjs/src/ExceptionHandler.d.ts +1 -1
  11. package/dist/cjs/src/Logger.d.ts +1 -1
  12. package/dist/cjs/src/StringSimilarity.d.ts +26 -0
  13. package/dist/cjs/src/commands/HelpCommand.d.ts +1 -1
  14. package/dist/cjs/src/contracts/CommandOption.d.ts +3 -3
  15. package/dist/cjs/src/errors/BadCommandOption.d.ts +2 -1
  16. package/dist/cjs/src/errors/BadCommandParameter.d.ts +2 -1
  17. package/dist/cjs/src/errors/CommandNotFoundError.d.ts +2 -1
  18. package/dist/cjs/src/errors/InvalidOption.d.ts +2 -1
  19. package/dist/cjs/src/errors/MissingRequiredArgumentValue.d.ts +2 -1
  20. package/dist/cjs/src/errors/MissingRequiredOptionValue.d.ts +2 -1
  21. package/dist/cjs/src/index.d.ts +1 -0
  22. package/dist/cjs/src/index.js +17 -0
  23. package/dist/cjs/src/lib/types.d.ts +4 -2
  24. package/dist/cjs/src/options/HelpOption.d.ts +1 -1
  25. package/dist/esm/package-DbMvpGfM.js +33 -0
  26. package/dist/esm/src/Cli.d.ts +10 -11
  27. package/dist/esm/src/Command.d.ts +11 -18
  28. package/dist/esm/src/CommandIO.d.ts +5 -2
  29. package/dist/esm/src/CommandParser.d.ts +13 -5
  30. package/dist/esm/src/CommandRegistry.d.ts +13 -6
  31. package/dist/esm/src/CommandSignatureParser.d.ts +5 -4
  32. package/dist/esm/src/CommandWithSignature.d.ts +3 -3
  33. package/dist/esm/src/ExceptionHandler.d.ts +1 -1
  34. package/dist/esm/src/Logger.d.ts +1 -1
  35. package/dist/esm/src/StringSimilarity.d.ts +26 -0
  36. package/dist/esm/src/commands/HelpCommand.d.ts +1 -1
  37. package/dist/esm/src/contracts/CommandOption.d.ts +3 -3
  38. package/dist/esm/src/errors/BadCommandOption.d.ts +2 -1
  39. package/dist/esm/src/errors/BadCommandParameter.d.ts +2 -1
  40. package/dist/esm/src/errors/CommandNotFoundError.d.ts +2 -1
  41. package/dist/esm/src/errors/InvalidOption.d.ts +2 -1
  42. package/dist/esm/src/errors/MissingRequiredArgumentValue.d.ts +2 -1
  43. package/dist/esm/src/errors/MissingRequiredOptionValue.d.ts +2 -1
  44. package/dist/esm/src/index.d.ts +1 -0
  45. package/dist/esm/src/index.js +1029 -0
  46. package/dist/esm/src/lib/types.d.ts +4 -2
  47. package/dist/esm/src/options/HelpOption.d.ts +1 -1
  48. package/package.json +28 -21
  49. package/dist/cjs/index.d.ts +0 -2
  50. package/dist/cjs/index.js +0 -21
  51. package/dist/cjs/package-BpbpSdBI.cjs +0 -1
  52. package/dist/cjs/src/testFixtures.d.ts +0 -11
  53. package/dist/esm/index.d.ts +0 -2
  54. package/dist/esm/index.js +0 -1247
  55. package/dist/esm/package-CjHUTRfV.js +0 -31
  56. package/dist/esm/src/testFixtures.d.ts +0 -11
@@ -0,0 +1,1029 @@
1
+ import y from "prompts";
2
+ import a from "chalk";
3
+ import q from "minimist";
4
+ import I from "node:fs";
5
+ import R from "node:path";
6
+ class S {
7
+ logger;
8
+ constructor(t) {
9
+ this.logger = t.logger;
10
+ }
11
+ /**
12
+ * Logger methods
13
+ */
14
+ log(...t) {
15
+ this.logger.log(...t);
16
+ }
17
+ info(...t) {
18
+ this.logger.info(...t);
19
+ }
20
+ warn(...t) {
21
+ this.logger.warn(...t);
22
+ }
23
+ error(...t) {
24
+ this.logger.error(...t);
25
+ }
26
+ debug(...t) {
27
+ this.logger.debug(...t);
28
+ }
29
+ /**
30
+ * Prompt utils
31
+ */
32
+ async askForConfirmation(t = "Do you want to continue?", e) {
33
+ return (await y({
34
+ type: "confirm",
35
+ name: "value",
36
+ message: t,
37
+ initial: e ?? !1
38
+ })).value;
39
+ }
40
+ async askForInput(t, e, i) {
41
+ return (await y({
42
+ type: "text",
43
+ name: "value",
44
+ message: t,
45
+ initial: e,
46
+ ...i
47
+ }))?.value ?? null;
48
+ }
49
+ async askForDate(t, e, i) {
50
+ return (await y({
51
+ type: "date",
52
+ name: "value",
53
+ message: t,
54
+ initial: e,
55
+ ...i
56
+ }))?.value ?? null;
57
+ }
58
+ async askForList(t, e, i) {
59
+ return (await y({
60
+ type: "list",
61
+ name: "value",
62
+ message: t,
63
+ initial: e,
64
+ ...i
65
+ }))?.value ?? null;
66
+ }
67
+ async askForToggle(t, e, i) {
68
+ return (await y({
69
+ type: "toggle",
70
+ name: "value",
71
+ message: t,
72
+ initial: e,
73
+ ...i
74
+ }))?.value ?? null;
75
+ }
76
+ async askForSelect(t, e, i) {
77
+ if (e.length === 0)
78
+ throw new Error("No options provided");
79
+ const n = [];
80
+ for (const o of e)
81
+ typeof o == "string" ? n.push({ title: o, value: o }) : n.push(o);
82
+ return (await y({
83
+ type: "select",
84
+ name: "value",
85
+ message: t,
86
+ choices: n,
87
+ ...i
88
+ }))?.value ?? null;
89
+ }
90
+ newLoader(t = "", e = ["⠙", "⠘", "⠰", "⠴", "⠤", "⠦", "⠆", "⠃", "⠋", "⠉"], i = 100) {
91
+ let n = t, s = null, o = 0;
92
+ const u = setInterval(function() {
93
+ s && (process.stdout.write(new TextEncoder().encode("\r" + " ".repeat(s.length + 5) + "\r")), s = null), process.stdout.write(new TextEncoder().encode("\r" + e[o++] + " " + n)), o = o % e.length;
94
+ }, i), m = () => {
95
+ clearInterval(u), process.stdout.write(new TextEncoder().encode("\r" + " ".repeat(n.length + 5) + "\r"));
96
+ };
97
+ return {
98
+ [Symbol.dispose]: m,
99
+ [Symbol.asyncDispose]: m,
100
+ updateText: (h) => {
101
+ s = n, n = h;
102
+ },
103
+ stop: m
104
+ };
105
+ }
106
+ }
107
+ class f extends Error {
108
+ }
109
+ function x(r) {
110
+ if (r === "string" || r === "number") return null;
111
+ if (r === "boolean") return !1;
112
+ if (Array.isArray(r) && r.length === 1) {
113
+ if (r[0] === "string") return [];
114
+ if (r[0] === "number") return [];
115
+ }
116
+ throw new Error("Invalid option type: " + r);
117
+ }
118
+ function E(r) {
119
+ return typeof r == "string" || Array.isArray(r) ? x(r) : r.default !== void 0 ? r.default : x(r.type);
120
+ }
121
+ function g(r) {
122
+ return typeof r == "string" || Array.isArray(r) ? {
123
+ alias: [],
124
+ default: E(r),
125
+ description: "",
126
+ required: !1,
127
+ secret: !1,
128
+ type: r,
129
+ variadic: !1
130
+ } : {
131
+ alias: r.alias ? Array.isArray(r.alias) ? r.alias : [r.alias] : [],
132
+ default: r.default ?? E(r.type),
133
+ description: r.description ?? "",
134
+ required: r.required ?? !1,
135
+ secret: r.secret ?? !1,
136
+ type: r.type,
137
+ variadic: r.variadic ?? !1
138
+ };
139
+ }
140
+ class b extends f {
141
+ constructor(t, e = {}) {
142
+ super(`Invalid option ${t} in not recognized`), this.option = t, this.optionsSchema = e;
143
+ }
144
+ pretty(t) {
145
+ const e = Object.entries(this.optionsSchema);
146
+ if (e.length > 0) {
147
+ t.log(`
148
+ ${a.yellow("Available options")}:`);
149
+ for (const [i, n] of e) {
150
+ const s = g(n), o = s.alias ? typeof s.alias == "string" ? [s.alias] : s.alias : [], u = Array.isArray(s.type) ? `[${s.type[0]}]` : s.type, m = `--${i}${o.length > 0 ? o.map((l) => `, -${l}`).join("") : ""}`, h = " ".repeat(30 - m.length);
151
+ t.log(` ${a.green(m)} ${h} ${s.description || "\b"} ${a.white(`(${u})`)}`);
152
+ }
153
+ t.log("");
154
+ }
155
+ t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is not recognized.`);
156
+ }
157
+ }
158
+ class F extends f {
159
+ constructor(t) {
160
+ super(`Argument "${t}" is required.`), this.argument = t;
161
+ }
162
+ pretty(t) {
163
+ t.log(`${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.argument)} is required.`);
164
+ }
165
+ }
166
+ class V extends f {
167
+ constructor(t) {
168
+ super(`Argument "${t}" is required.`), this.option = t;
169
+ }
170
+ pretty(t) {
171
+ t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is required.`);
172
+ }
173
+ }
174
+ class z extends f {
175
+ constructor(t) {
176
+ let e = `Argument "${t.param}" value is invalid.`;
177
+ t.reason ? e += ` Reason: ${t.reason}` : e += ` Value: "${t.value}"`, super(e), this.param = t;
178
+ }
179
+ pretty(t) {
180
+ t.log(` ${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.param.param)} value is invalid. `), (this.param.value || this.param.reason) && t.log(""), this.param.value && t.log(` ${a.blue("Value")}: ${this.param.value}`), this.param.reason && t.log(` ${a.yellow("Reason")}: ${this.param.reason}`);
181
+ }
182
+ }
183
+ class C extends f {
184
+ constructor(t) {
185
+ let e = `Option "${t.option}" value is invalid.`;
186
+ t.reason ? e += ` Reason: ${t.reason}` : e += ` Value: "${t.value}"`, super(e), this.param = t;
187
+ }
188
+ pretty(t) {
189
+ t.log(` ${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.param.option)} value is invalid. `), (this.param.value || this.param.reason) && t.log(""), this.param.value && t.log(` ${a.blue("Value")}: ${this.param.value}`), this.param.reason && t.log(` ${a.yellow("Reason")}: ${this.param.reason}`);
190
+ }
191
+ }
192
+ class P extends f {
193
+ constructor(t) {
194
+ super(`Command "${t}" not found.`), this.command = t;
195
+ }
196
+ pretty(t) {
197
+ t.log(`${a.bgRed(" ERROR ")} Command ${a.yellow(this.command)} not found.`);
198
+ }
199
+ }
200
+ function w(r, t, e, i) {
201
+ if (r == null)
202
+ return i ?? null;
203
+ if (t === "string")
204
+ return String(r);
205
+ if (t === "number") {
206
+ const n = Number(r);
207
+ if (isNaN(n))
208
+ throw new C({
209
+ option: e,
210
+ reason: `Expected a number, got "${r}"`
211
+ });
212
+ return n;
213
+ }
214
+ if (t === "boolean")
215
+ return typeof r == "boolean" ? r : r === "true" || r === "1" ? !0 : r === "false" || r === "0" ? !1 : !!r;
216
+ if (Array.isArray(t)) {
217
+ const n = t[0], s = Array.isArray(r) ? r : [r];
218
+ if (n === "string")
219
+ return s.map((o) => String(o));
220
+ if (n === "number")
221
+ return s.map((o) => {
222
+ const u = Number(o);
223
+ if (isNaN(u))
224
+ throw new C({
225
+ option: e,
226
+ reason: `Expected array of numbers, got "${o}" in array`
227
+ });
228
+ return u;
229
+ });
230
+ }
231
+ return r;
232
+ }
233
+ class k {
234
+ options;
235
+ parsedOptions = null;
236
+ arguments;
237
+ parsedArguments = null;
238
+ io;
239
+ shouldPromptForMissingOptions = !0;
240
+ constructor(t) {
241
+ this.options = t.options, this.arguments = t.arguments, this.io = t.io;
242
+ }
243
+ // === PUBLIC METHODS ===
244
+ /**
245
+ * Parses raw command-line arguments into structured options and arguments
246
+ * @param args - Raw command line arguments (typically from process.argv.slice(2))
247
+ * @returns Object containing parsed options and arguments
248
+ * @throws {InvalidOption} If an naan option is provided
249
+ * @throws {BadCommandOption} If a value cannot be converted to the expected type
250
+ */
251
+ init(t) {
252
+ const { _: e, ...i } = q(t);
253
+ return this.validateUnknownOptions(i), this.parsedOptions = this.handleOptions(i), this.parsedArguments = this.handleArguments(e), {
254
+ options: this.parsedOptions,
255
+ arguments: this.parsedArguments
256
+ };
257
+ }
258
+ /**
259
+ * Validates the parsed options and arguments
260
+ * @throws {Error} If validation fails
261
+ */
262
+ async validate() {
263
+ for (const t in this.options)
264
+ if (g(this.options[t]).required && (this.parsedOptions?.[t] === void 0 || this.parsedOptions?.[t] === null))
265
+ throw new V(t);
266
+ for (const t in this.arguments) {
267
+ const e = g(this.arguments[t]), i = this.parsedArguments?.[t];
268
+ if (e.required && i == null) {
269
+ if (this.shouldPromptForMissingOptions) {
270
+ const n = await this.promptForArgument(t, e);
271
+ if (n && this.parsedArguments) {
272
+ this.parsedArguments[t] = w(n, e.type, t);
273
+ continue;
274
+ }
275
+ }
276
+ throw new F(t);
277
+ }
278
+ if (e.required && e.variadic && Array.isArray(i) && i.length === 0) {
279
+ if (this.shouldPromptForMissingOptions) {
280
+ const n = await this.promptForArgument(t, e);
281
+ if (n && this.parsedArguments) {
282
+ this.parsedArguments[t] = w(n, e.type, t);
283
+ continue;
284
+ }
285
+ }
286
+ throw new F(t);
287
+ }
288
+ }
289
+ }
290
+ /**
291
+ * Retrieves a parsed option value by name
292
+ * @param name - The option name
293
+ * @param defaultValue - Optional default value if option is not set
294
+ * @returns The typed option value
295
+ * @throws {Error} If init() has not been called yet
296
+ */
297
+ option(t, e) {
298
+ if (!this.parsedOptions)
299
+ throw new Error("Options have not been parsed yet. Call init() first.");
300
+ return this.isEmptyValue(this.parsedOptions[t]) && e !== void 0 ? e : this.parsedOptions[t];
301
+ }
302
+ setOption(t, e) {
303
+ if (!this.parsedOptions)
304
+ throw new Error("Options have not been parsed yet. Call init() first.");
305
+ if (!(t in this.options))
306
+ throw new b(t, this.options);
307
+ this.parsedOptions[t] = e;
308
+ }
309
+ /**
310
+ * Retrieves a parsed argument value by name
311
+ * @param name - The argument name
312
+ * @param defaultValue - Optional default value if argument is not set
313
+ * @returns The typed argument value
314
+ * @throws {Error} If init() has not been called yet
315
+ */
316
+ argument(t, e) {
317
+ if (!this.parsedArguments)
318
+ throw new Error("Arguments have not been parsed yet. Call init() first.");
319
+ return this.isEmptyValue(this.parsedArguments[t]) && e !== void 0 ? e : this.parsedArguments[t];
320
+ }
321
+ setArgument(t, e) {
322
+ if (!this.parsedArguments)
323
+ throw new Error("Arguments have not been parsed yet. Call init() first.");
324
+ if (!(t in this.arguments))
325
+ throw new b(t, this.arguments);
326
+ this.parsedArguments[t] = e;
327
+ }
328
+ // === PRIVATE HELPERS ===
329
+ /**
330
+ * Checks if a value should be considered "empty" for default value purposes
331
+ * @param value - The value to check
332
+ * @returns true if the value is null, undefined, or an empty array
333
+ */
334
+ isEmptyValue(t) {
335
+ return t == null || Array.isArray(t) && t.length === 0;
336
+ }
337
+ /**
338
+ * Validates that all provided options are recognized
339
+ * @throws {InvalidOption} If an unknown option is found
340
+ */
341
+ validateUnknownOptions(t) {
342
+ const e = /* @__PURE__ */ new Set();
343
+ for (const i in this.options) {
344
+ e.add(i);
345
+ const n = g(this.options[i]);
346
+ for (const s of n.alias)
347
+ e.add(s);
348
+ }
349
+ for (const i in t)
350
+ if (!e.has(i))
351
+ throw new b(i, this.options);
352
+ }
353
+ /**
354
+ * Processes named options from minimist output
355
+ */
356
+ handleOptions(t) {
357
+ const e = {};
358
+ for (const i in this.options) {
359
+ const n = g(this.options[i]);
360
+ e[i] = this.resolveOptionValue(i, n, t);
361
+ }
362
+ return e;
363
+ }
364
+ /**
365
+ * Processes positional arguments from minimist output
366
+ */
367
+ handleArguments(t) {
368
+ const e = {}, i = [...t];
369
+ for (const n in this.arguments) {
370
+ const s = g(this.arguments[n]);
371
+ if (s.variadic) {
372
+ e[n] = this.handleVariadicArgument(n, s, i);
373
+ continue;
374
+ }
375
+ e[n] = this.resolveArgumentValue(n, s, i.shift());
376
+ }
377
+ return e;
378
+ }
379
+ /**
380
+ * Handles variadic arguments that consume all remaining positional values
381
+ */
382
+ handleVariadicArgument(t, e, i) {
383
+ return i.length ? w(i, e.type, t, e.default) : e.default;
384
+ }
385
+ /**
386
+ * Resolves a single positional argument value with defaults and type conversion
387
+ * Note: Does not validate required arguments - validation happens in subclass validate() methods
388
+ */
389
+ resolveArgumentValue(t, e, i) {
390
+ return i === void 0 ? e.default : w(i, e.type, t, e.default);
391
+ }
392
+ /**
393
+ * Resolves an option value from the parsed option values object
394
+ * Handles alias resolution, defaults, and type conversion
395
+ */
396
+ resolveOptionValue(t, e, i) {
397
+ let n;
398
+ const s = [t, ...e.alias];
399
+ for (const o of s)
400
+ if (o in i) {
401
+ n = i[o];
402
+ break;
403
+ }
404
+ if (n === void 0) {
405
+ if (e.required)
406
+ throw new C({
407
+ option: t,
408
+ reason: "Required option is missing"
409
+ });
410
+ return e.default;
411
+ }
412
+ return w(n, e.type, t, e.default);
413
+ }
414
+ optionDefinitions() {
415
+ const t = {};
416
+ for (const e in this.options)
417
+ t[e] = g(this.options[e]);
418
+ return t;
419
+ }
420
+ argumentDefinitions() {
421
+ const t = {};
422
+ for (const e in this.arguments)
423
+ t[e] = g(this.arguments[e]);
424
+ return t;
425
+ }
426
+ availableOptions() {
427
+ return Object.keys(this.options);
428
+ }
429
+ availableArguments() {
430
+ return Object.keys(this.arguments);
431
+ }
432
+ /**
433
+ * Disables prompting for missing argument values
434
+ * Useful for non-interactive environments
435
+ */
436
+ disablePrompting() {
437
+ return this.shouldPromptForMissingOptions = !1, this;
438
+ }
439
+ /**
440
+ * Prompts the user to provide a missing argument value via CommandIO
441
+ * Used by validate() when shouldPromptForMissingArgs is enabled
442
+ * @param argumentName - The name of the missing argument
443
+ * @param argDef - The argument's definition for type and description
444
+ * @returns The user-provided value, or null if none given
445
+ */
446
+ async promptForArgument(t, e) {
447
+ if (!Array.isArray(e.type) && !["string", "number", "secret"].includes(e.type))
448
+ return null;
449
+ let i = `${a.yellow.bold(t)} is required`;
450
+ return e.description && (i += `: ${a.gray(`(${e.description})`)}`), i += ` ${a.green(`(${e.type}${e.variadic == !0 ? "[]" : ""})`)}
451
+ `, Array.isArray(e.type) ? (i += `Please provide one or more values, separated by commas:
452
+ `, await this.io.askForList(i, void 0, {
453
+ separator: ",",
454
+ validate: (n) => {
455
+ if (!n.length)
456
+ return "Please enter at least one value";
457
+ if (e.type[0] === "number") {
458
+ for (const s of n.split(","))
459
+ if (isNaN(Number(s)))
460
+ return "Please enter only valid numbers";
461
+ }
462
+ return !0;
463
+ }
464
+ })) : await this.io.askForInput(i, void 0, {
465
+ type: e.type === "number" ? "number" : e.secret ? "password" : "text",
466
+ validate: (n) => {
467
+ if (n == null || typeof n == "string" && !n.length)
468
+ return "This value is required";
469
+ if (e.type === "number") {
470
+ const s = Number(n);
471
+ if (isNaN(s))
472
+ return "Please enter a valid number";
473
+ } else if (e.type === "string" && (typeof n != "string" || !n.length))
474
+ return "Please enter a valid text";
475
+ return !0;
476
+ }
477
+ });
478
+ }
479
+ }
480
+ function A(r) {
481
+ return new Array(r + 5).join(" ");
482
+ }
483
+ class H {
484
+ type = "boolean";
485
+ option = "help";
486
+ alias = ["h"];
487
+ default = !1;
488
+ description = `Display help for the given command. When no command is given display help for the ${a.green("list")} command`;
489
+ async handler() {
490
+ const t = this.parser.argumentDefinitions(), e = this.parser.optionDefinitions(), i = Object.entries(t), n = Object.entries(e), s = n.map(([l, d]) => {
491
+ const p = Array.isArray(d.alias) ? d.alias : d.alias ? [d.alias] : [];
492
+ return {
493
+ name: l,
494
+ ...d,
495
+ optionWithAlias: `--${l}${p.map((c) => `, -${c}`).join("")}`
496
+ };
497
+ }), o = i.filter(([, l]) => l.required);
498
+ this.io.log(a.yellow("Description:")), this.io.log(` ${this.description}
499
+ `), this.io.log(a.yellow("Usage:")), this.io.log(` ${this.command} ${o.length > 0 ? o.map(([l]) => `<${l}>`).join(" ") : "\b"} [options]`);
500
+ const u = Math.max(...s.map((l) => l.optionWithAlias.length), 0), m = Math.max(...i.map(([l]) => l.length), 0), h = m > u ? m : u;
501
+ if (i.length > 0) {
502
+ this.io.log(`
503
+ ${a.yellow("Arguments")}:`);
504
+ for (const [l, d] of i) {
505
+ const p = A(h - l.length);
506
+ let c = ` ${a.green(l)} ${p} ${d.description ?? "\b"}`;
507
+ if (d.default !== void 0 && !d.required) {
508
+ const L = (Array.isArray(d.type) ? `[${d.type[0]}]` : d.type) === "array" || Array.isArray(d.type) ? JSON.stringify(d.default) : d.default;
509
+ c += ` ${a.yellow(`[default: ${L}]`)}`;
510
+ }
511
+ d.variadic && (c += ` ${a.white("(variadic)")}`), this.io.log(c);
512
+ }
513
+ }
514
+ if (n.length > 0) {
515
+ this.io.log(`
516
+ ${a.yellow("Options")}:`);
517
+ for (const l of s) {
518
+ const d = A(h - l.optionWithAlias.length);
519
+ let p = `${a.green(l.optionWithAlias)} ${d} ${l.description ?? "\b"}`;
520
+ if (l.type) {
521
+ const c = Array.isArray(l.type) ? `[${l.type[0]}]` : l.type;
522
+ p += ` ${a.white(`(${c})`)}`;
523
+ }
524
+ if (l.default !== void 0 && !l.required) {
525
+ const O = (Array.isArray(l.type) ? `[${l.type[0]}]` : l.type) === "array" || Array.isArray(l.type) ? JSON.stringify(l.default) : l.default;
526
+ p += ` ${a.yellow(`[default: ${O}]`)}`;
527
+ }
528
+ this.io.log(p);
529
+ }
530
+ }
531
+ if (this.commandsExamples.length > 0) {
532
+ this.io.log(`
533
+ ${a.yellow("Examples")}:`);
534
+ let l = process.argv[0].split("/").pop();
535
+ l === "node" && (l += " " + process.argv[1].split("/").pop());
536
+ for (const [d, p] of this.commandsExamples.entries())
537
+ d > 0 && this.io.log(""), this.io.log(` ${p.description}
538
+ `), this.io.log(` ${a.green(`${l} ${p.command}`)}`);
539
+ }
540
+ return -1;
541
+ }
542
+ }
543
+ class v {
544
+ _command;
545
+ description;
546
+ group;
547
+ commandsExamples = [];
548
+ get command() {
549
+ return this._command;
550
+ }
551
+ ctx;
552
+ io;
553
+ parser;
554
+ disablePromptingFlag = !1;
555
+ _preHandler;
556
+ _handler;
557
+ tmp;
558
+ defaultOptions() {
559
+ return [new H()];
560
+ }
561
+ newCommandParser(t) {
562
+ return new k({
563
+ io: t.io,
564
+ options: t.options,
565
+ arguments: t.arguments
566
+ });
567
+ }
568
+ newCommandIO(t) {
569
+ return new S(t);
570
+ }
571
+ constructor(t, e) {
572
+ this._command = t, this.description = e?.description ?? "", this.group = e?.group, this.tmp = {
573
+ options: e?.options ?? {},
574
+ arguments: e?.arguments ?? {}
575
+ };
576
+ const i = this.defaultOptions();
577
+ if (i.length > 0)
578
+ for (const n of i)
579
+ this.tmp.options[n.option] = n;
580
+ }
581
+ disablePrompting() {
582
+ return this.disablePromptingFlag = !0, this;
583
+ }
584
+ preHandler(t) {
585
+ return this._preHandler = t, this;
586
+ }
587
+ handler(t) {
588
+ return this._handler = t, this;
589
+ }
590
+ options(t) {
591
+ return this.tmp = {
592
+ options: {
593
+ ...this.tmp?.options ?? {},
594
+ ...t
595
+ },
596
+ arguments: this.tmp?.arguments ?? {}
597
+ }, this;
598
+ }
599
+ arguments(t) {
600
+ return this.tmp = {
601
+ options: this.tmp?.options ?? {},
602
+ arguments: {
603
+ ...this.tmp?.arguments ?? {},
604
+ ...t
605
+ }
606
+ }, this;
607
+ }
608
+ async run(t) {
609
+ if (!this._handler && !this.handle)
610
+ throw new Error(`No handler defined for command ${this.command || "(unknown)"}`);
611
+ let e;
612
+ if (this.ctx = t.ctx, this.io = this.newCommandIO({
613
+ logger: t.logger
614
+ }), t && "args" in t) {
615
+ const n = this.tmp?.options ?? {};
616
+ for (const s of this.defaultOptions())
617
+ s.option in n || (n[s.option] = s);
618
+ this.parser = this.newCommandParser({
619
+ io: this.io,
620
+ options: n,
621
+ arguments: this.tmp?.arguments ?? {}
622
+ }), e = this.parser.init(t.args);
623
+ for (const s of this.defaultOptions())
624
+ if (e.options[s.option] === !0) {
625
+ const o = await s.handler.call(this);
626
+ if (o && o !== 0)
627
+ return o;
628
+ }
629
+ this.disablePromptingFlag && this.parser.disablePrompting(), await this.parser.validate();
630
+ } else
631
+ e = {
632
+ options: t.options,
633
+ arguments: t.arguments
634
+ };
635
+ if (!this._preHandler && this.preHandle && (this._preHandler = this.preHandle.bind(this)), this._preHandler) {
636
+ const n = await this._preHandler(t.ctx, e);
637
+ if (n && n !== 0)
638
+ return n;
639
+ }
640
+ if (!this._handler && this.handle)
641
+ this._handler = this.handle.bind(this);
642
+ else if (!this._handler)
643
+ throw new Error(`No handler defined for command ${this.command || "(unknown)"}`);
644
+ return await this._handler(t.ctx, e) ?? 0;
645
+ }
646
+ }
647
+ class $ extends k {
648
+ command;
649
+ constructor(t) {
650
+ const e = $.parseSignature(t.signature, t.helperDefinitions, t.defaultOptions);
651
+ super({
652
+ io: t.io,
653
+ options: e.options,
654
+ arguments: e.arguments
655
+ }), this.command = e.command;
656
+ }
657
+ /**
658
+ * Parses command signature string into command name and parameter schemas
659
+ * Example: "migrate {name} {--force}" -> { command: "migrate", arguments: {name: ...}, options: {force: ...} }
660
+ */
661
+ static parseSignature(t, e, i) {
662
+ const [n, ...s] = t.split(/\{(.*?)\}/g).map((m) => m.trim()).filter(Boolean), o = {}, u = {};
663
+ for (const m of s) {
664
+ const { name: h, isOption: l, definition: d } = $.parseParamSignature(m, e);
665
+ l ? o[h] = d : u[h] = d;
666
+ }
667
+ for (const m of i)
668
+ o[m.option] = {
669
+ type: m.type,
670
+ required: m.required,
671
+ alias: m.alias,
672
+ variadic: m.variadic ?? !1,
673
+ description: m.description,
674
+ default: m.default ?? null
675
+ };
676
+ return {
677
+ command: n,
678
+ options: o,
679
+ arguments: u
680
+ };
681
+ }
682
+ /**
683
+ * Parses a single parameter signature like "{name}" or "{--force}" or "{files*}"
684
+ * Extracts name, type, default value, aliases, description, etc.
685
+ *
686
+ * Signature syntax:
687
+ * - {arg} -> required string argument
688
+ * - {arg?} -> optional argument
689
+ * - {arg=default} -> argument with default value
690
+ * - {arg*} -> variadic argument (array)
691
+ * - {arg:desc} -> argument with description
692
+ * - {--opt} -> boolean option
693
+ * - {--opt=} -> string option
694
+ * - {--opt|o} -> option with alias
695
+ */
696
+ static parseParamSignature(t, e) {
697
+ let i = t, n = !1;
698
+ const s = {
699
+ required: !0,
700
+ type: "string",
701
+ description: void 0,
702
+ default: null,
703
+ variadic: !1
704
+ };
705
+ if (i.includes(":")) {
706
+ const [o, u] = i.split(":");
707
+ i = o.trim(), s.description = u.trim();
708
+ }
709
+ if (i.includes("=")) {
710
+ const [o, u] = i.split("=");
711
+ i = o.trim(), s.default = u.trim(), s.required = !1, typeof s.default == "string" && !s.default.length ? s.default = null : s.default === "true" ? (s.default = !0, s.type = "boolean") : s.default === "false" && (s.default = !1, s.type = "boolean");
712
+ } else i.startsWith("--") && (s.required = !1, s.default = !1, s.type = "boolean");
713
+ if (i.includes("|")) {
714
+ const [o, ...u] = i.split("|");
715
+ i = o.trim(), s.alias = u.map((m) => m.trim());
716
+ }
717
+ return i.startsWith("--") && (n = !0, i = i.slice(2)), s.default === "*" && (s.default = [], s.type = ["string"]), i.endsWith("?") && (s.required = !1, i = i.slice(0, -1)), i.endsWith("*") && (s.type = ["string"], s.variadic = !0, s.default = [], i = i.slice(0, -1)), s.description = s.description ?? e[i] ?? e[`--${i}`], { name: i, isOption: n, definition: s };
718
+ }
719
+ }
720
+ class J extends v {
721
+ helperDefinitions = {};
722
+ get command() {
723
+ return this.parser ? this.parser.command : this.signature.split(" ")[0];
724
+ }
725
+ newCommandParser(t) {
726
+ return new $({
727
+ io: t.io,
728
+ signature: this.signature,
729
+ helperDefinitions: this.helperDefinitions,
730
+ defaultOptions: this.defaultOptions()
731
+ });
732
+ }
733
+ constructor() {
734
+ super("");
735
+ }
736
+ option(t, e = null) {
737
+ return this.parser.option(t, e);
738
+ }
739
+ argument(t, e = null) {
740
+ return this.parser.argument(t, e);
741
+ }
742
+ // Prompt utils
743
+ async askForConfirmation(...t) {
744
+ return this.io.askForConfirmation(...t);
745
+ }
746
+ async askForInput(...t) {
747
+ return this.io.askForInput(...t);
748
+ }
749
+ async askForSelect(...t) {
750
+ return this.io.askForSelect(...t);
751
+ }
752
+ newLoader(...t) {
753
+ return this.io.newLoader(...t);
754
+ }
755
+ }
756
+ class N {
757
+ level;
758
+ constructor(t = {}) {
759
+ this.level = t.level ?? "info";
760
+ }
761
+ shouldLog(t) {
762
+ const e = ["debug", "info", "warn", "error"], i = e.indexOf(this.level);
763
+ return e.indexOf(t) >= i;
764
+ }
765
+ setLevel(t) {
766
+ this.level = t;
767
+ }
768
+ getLevel() {
769
+ return this.level;
770
+ }
771
+ log(...t) {
772
+ console.log(...t);
773
+ }
774
+ info(...t) {
775
+ this.shouldLog("info") && console.log(...t);
776
+ }
777
+ warn(...t) {
778
+ this.shouldLog("warn") && console.warn(...t);
779
+ }
780
+ error(...t) {
781
+ this.shouldLog("error") && console.error(...t);
782
+ }
783
+ debug(...t) {
784
+ this.shouldLog("debug") && console.log(...t);
785
+ }
786
+ }
787
+ class D {
788
+ /**
789
+ * Generate bigrams (character pairs) from a string
790
+ */
791
+ getBigrams(t) {
792
+ const e = [], i = t.toLowerCase();
793
+ for (let n = 0; n < i.length - 1; n++)
794
+ e.push(i.slice(n, n + 2));
795
+ return e;
796
+ }
797
+ /**
798
+ * Calculate Dice's Coefficient similarity between two strings (0-1 scale)
799
+ */
800
+ calculateSimilarity(t, e) {
801
+ if (t === e) return 1;
802
+ if (t.length < 2 || e.length < 2) return 0;
803
+ const i = this.getBigrams(t), n = this.getBigrams(e), s = new Set(n);
804
+ let o = 0;
805
+ for (const u of i)
806
+ s.has(u) && (o++, s.delete(u));
807
+ return 2 * o / (i.length + n.length);
808
+ }
809
+ /**
810
+ * Find best matching string and ratings for all candidates
811
+ */
812
+ findBestMatch(t, e) {
813
+ const i = e.map((o) => ({
814
+ target: o,
815
+ rating: this.calculateSimilarity(t, o)
816
+ }));
817
+ let n = 0, s = i[0]?.rating ?? 0;
818
+ for (let o = 1; o < i.length; o++)
819
+ i[o].rating > s && (s = i[o].rating, n = o);
820
+ return {
821
+ ratings: i,
822
+ bestMatch: i[n],
823
+ bestMatchIndex: n
824
+ };
825
+ }
826
+ }
827
+ class j {
828
+ commands = {};
829
+ io;
830
+ logger;
831
+ stringSimilarity;
832
+ newCommandIO(t) {
833
+ return new S(t);
834
+ }
835
+ constructor(t) {
836
+ this.logger = t?.logger ?? new N(), this.io = this.newCommandIO({
837
+ logger: this.logger
838
+ }), this.stringSimilarity = t?.stringSimilarity ?? new D();
839
+ }
840
+ getAvailableCommands() {
841
+ return Object.keys(this.commands);
842
+ }
843
+ getCommands() {
844
+ return Object.values(this.commands);
845
+ }
846
+ importFile = async (t) => (await import(t)).default;
847
+ commandResolver = async (t) => {
848
+ let e = await this.importFile(t);
849
+ return e ? (e && typeof e == "object" && "default" in e && (e = e.default), typeof e == "function" ? new e() : e instanceof v ? e : null) : null;
850
+ };
851
+ withCommandResolver(t) {
852
+ return this.commandResolver = t, this;
853
+ }
854
+ withFileImporter(t) {
855
+ return this.importFile = t, this;
856
+ }
857
+ registerCommand(t, e = !1) {
858
+ const i = t.command;
859
+ if (!i)
860
+ throw new Error("Command signature is invalid, it must have a command name.");
861
+ if (!e && this.commands[i])
862
+ throw new Error(`Command ${i} already registered.`);
863
+ this.commands[i] = t;
864
+ }
865
+ async loadCommandsPath(t) {
866
+ for await (const e of this.listCommandsFiles(t))
867
+ try {
868
+ const i = await this.commandResolver(e);
869
+ i instanceof v && this.registerCommand(i);
870
+ } catch (i) {
871
+ throw new Error(`Command ${e} failed to load. ${i}`, {
872
+ cause: i
873
+ });
874
+ }
875
+ }
876
+ async runCommand(t, e, ...i) {
877
+ const n = typeof e == "string" ? this.commands[e] : e, s = typeof e == "string" ? e : n.command;
878
+ if (!n) {
879
+ const o = await this.suggestCommand(s);
880
+ return o ? await this.runCommand(t, o, ...i) : 1;
881
+ }
882
+ return await n.run({
883
+ ctx: t,
884
+ logger: this.logger,
885
+ args: i
886
+ }) ?? 0;
887
+ }
888
+ async suggestCommand(t) {
889
+ const e = this.getAvailableCommands(), { bestMatch: i, bestMatchIndex: n, ratings: s } = this.stringSimilarity.findBestMatch(t, e), o = s.filter((u) => u.rating > 0.3).map((u) => u.target);
890
+ if (i.rating > 0 && o.length <= 1 || i.rating > 0.7 && o.length > 1) {
891
+ const u = e[n];
892
+ return await this.askRunSimilarCommand(t, u) ? u : null;
893
+ }
894
+ if (o.length) {
895
+ this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
896
+ `);
897
+ const u = await this.io.askForSelect(a.green("Did you mean to run one of these commands instead?"), o);
898
+ if (u)
899
+ return u;
900
+ }
901
+ throw new P(t);
902
+ }
903
+ async askRunSimilarCommand(t, e) {
904
+ return this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
905
+ `), this.io.askForConfirmation(`${a.green(`Do you want to run ${a.yellow(e)} instead?`)} `);
906
+ }
907
+ async *listCommandsFiles(t) {
908
+ const e = I.readdirSync(t, { withFileTypes: !0 });
909
+ for (const i of e) {
910
+ const n = R.resolve(t, i.name);
911
+ if (i.isDirectory())
912
+ yield* this.listCommandsFiles(R.resolve(t, i.name));
913
+ else {
914
+ if (!n.endsWith(".ts") && !n.endsWith(".js") && !n.endsWith(".mjs") && !n.endsWith(".cjs"))
915
+ continue;
916
+ yield n;
917
+ }
918
+ }
919
+ }
920
+ }
921
+ class W {
922
+ logger;
923
+ constructor(t) {
924
+ this.logger = t;
925
+ }
926
+ handle(t) {
927
+ if (t instanceof f)
928
+ return t.pretty(this.logger), -1;
929
+ throw t;
930
+ }
931
+ }
932
+ class M extends v {
933
+ constructor(t) {
934
+ super("help", {
935
+ description: a.bold("Show help information about the CLI and its commands")
936
+ }), this.opts = t;
937
+ }
938
+ async handle() {
939
+ const t = this.opts.commandRegistry.getCommands(), e = this.opts.cliName ?? "Bob CLI", i = this.opts.cliVersion ?? "0.0.0", n = (await import("../package-DbMvpGfM.js"))?.default?.version ?? "0.0.0";
940
+ this.io.log(`${e} ${a.green(i)} (core: ${a.yellow(n)})
941
+
942
+ ${a.yellow("Usage")}:
943
+ command [options] [arguments]
944
+
945
+ ${a.yellow("Available commands")}:
946
+ `);
947
+ const s = Math.max(...t.map((m) => m.command.length)) ?? 0, o = {};
948
+ for (const m of t) {
949
+ const h = m.group ?? m.command.split(":")[0];
950
+ o[h] || (o[h] = []), o[h].push(m);
951
+ }
952
+ const u = Object.entries(o).sort(([m], [h]) => m.toLowerCase().localeCompare(h.toLowerCase())).sort(([, m], [, h]) => m.length - h.length);
953
+ for (const [m, h] of u) {
954
+ const l = h.length > 1;
955
+ l && this.io.log(a.yellow(`${m}:`));
956
+ const d = h.sort((p, c) => p.command.toLowerCase().localeCompare(c.command.toLowerCase()));
957
+ for (const p of d) {
958
+ let c = A(s - p.command.length);
959
+ l && (c = c.slice(2)), this.io.log(`${l ? " " : ""}${a.green(p.command)} ${c} ${p.description}`);
960
+ }
961
+ }
962
+ }
963
+ }
964
+ class K {
965
+ ctx;
966
+ logger;
967
+ commandRegistry;
968
+ exceptionHandler;
969
+ helpCommand;
970
+ newCommandRegistry(t) {
971
+ return new j(t);
972
+ }
973
+ newHelpCommand(t) {
974
+ return new M(t);
975
+ }
976
+ newExceptionHandler(t) {
977
+ return new W(t.logger);
978
+ }
979
+ constructor(t = {}) {
980
+ this.ctx = t.ctx, this.logger = t.logger ?? new N(), this.commandRegistry = this.newCommandRegistry({
981
+ logger: this.logger
982
+ }), this.exceptionHandler = this.newExceptionHandler({
983
+ logger: this.logger
984
+ }), this.helpCommand = this.newHelpCommand({
985
+ cliName: t.name,
986
+ cliVersion: t.version,
987
+ commandRegistry: this.commandRegistry
988
+ });
989
+ }
990
+ withCommandResolver(t) {
991
+ return this.commandRegistry.withCommandResolver(t), this;
992
+ }
993
+ withFileImporter(t) {
994
+ return this.commandRegistry.withFileImporter(t), this;
995
+ }
996
+ async withCommands(...t) {
997
+ for (const e of t)
998
+ typeof e == "string" ? await this.commandRegistry.loadCommandsPath(e) : typeof e == "function" ? this.registerCommand(new e()) : this.registerCommand(e);
999
+ }
1000
+ async runCommand(t, ...e) {
1001
+ return t ? await this.commandRegistry.runCommand(this.ctx ?? {}, t, ...e).catch(this.exceptionHandler.handle.bind(this.exceptionHandler)) : await this.runHelpCommand();
1002
+ }
1003
+ async runHelpCommand() {
1004
+ return await this.runCommand(this.helpCommand);
1005
+ }
1006
+ registerCommand(t) {
1007
+ this.commandRegistry.registerCommand(t);
1008
+ }
1009
+ }
1010
+ export {
1011
+ C as BadCommandOption,
1012
+ z as BadCommandParameter,
1013
+ f as BobError,
1014
+ K as Cli,
1015
+ v as Command,
1016
+ S as CommandIO,
1017
+ P as CommandNotFoundError,
1018
+ k as CommandParser,
1019
+ j as CommandRegistry,
1020
+ $ as CommandSignatureParser,
1021
+ J as CommandWithSignature,
1022
+ W as ExceptionHandler,
1023
+ H as HelpOption,
1024
+ b as InvalidOption,
1025
+ N as Logger,
1026
+ F as MissingRequiredArgumentValue,
1027
+ V as MissingRequiredOptionValue,
1028
+ D as StringSimilarity
1029
+ };