@bemoje/cli 1.1.0 → 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.
package/index.mjs ADDED
@@ -0,0 +1,1352 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
4
+ var __export = (target, all) => {
5
+ for (var name in all)
6
+ __defProp(target, name, { get: all[name], enumerable: true });
7
+ };
8
+ var __decorateClass = (decorators, target, key, kind) => {
9
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
10
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
+ if (decorator = decorators[i])
12
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
13
+ if (kind && result) __defProp(target, key, result);
14
+ return result;
15
+ };
16
+
17
+ // src/lib/Command.ts
18
+ var Command_exports = {};
19
+ __export(Command_exports, {
20
+ Command: () => Command
21
+ });
22
+
23
+ // ../array/src/arrLast.ts
24
+ function arrLast(array) {
25
+ if (!array.length) throw new Error("Cannot get last element of empty array.");
26
+ return array[array.length - 1];
27
+ }
28
+ __name(arrLast, "arrLast");
29
+
30
+ // ../array/src/arrRemoveDuplicates.ts
31
+ function arrRemoveDuplicates(array) {
32
+ return Array.from(new Set(array));
33
+ }
34
+ __name(arrRemoveDuplicates, "arrRemoveDuplicates");
35
+
36
+ // ../object/src/entriesOf.ts
37
+ function entriesOf(obj) {
38
+ return Object.entries(obj);
39
+ }
40
+ __name(entriesOf, "entriesOf");
41
+
42
+ // ../object/src/keysOf.ts
43
+ function keysOf(obj) {
44
+ return Object.keys(obj);
45
+ }
46
+ __name(keysOf, "keysOf");
47
+
48
+ // ../object/src/defineValue.ts
49
+ function defineValue(obj, key, value, des = {}) {
50
+ return Object.defineProperty(obj, key, {
51
+ configurable: true,
52
+ value,
53
+ enumerable: true,
54
+ ...des
55
+ });
56
+ }
57
+ __name(defineValue, "defineValue");
58
+
59
+ // ../object/src/filterObject.ts
60
+ function filterObject(obj, predicate) {
61
+ const accum = {};
62
+ for (const key of keysOf(obj)) {
63
+ if (predicate(obj[key], key, obj)) {
64
+ accum[key] = obj[key];
65
+ }
66
+ }
67
+ return accum;
68
+ }
69
+ __name(filterObject, "filterObject");
70
+
71
+ // ../map/src/mapGetOrDefault.ts
72
+ function mapGetOrDefault(map, key, factory) {
73
+ let value = map.get(key);
74
+ if (value !== void 0 || map.has(key)) return value;
75
+ value = factory(key, map);
76
+ map.set(key, value);
77
+ return value;
78
+ }
79
+ __name(mapGetOrDefault, "mapGetOrDefault");
80
+
81
+ // ../map/src/TimeoutWeakMap.ts
82
+ var TimeoutWeakMap = class {
83
+ /**
84
+ * @param timeoutMs Default timeout in milliseconds for entries
85
+ */
86
+ constructor(timeoutMs) {
87
+ this.timeoutMs = timeoutMs;
88
+ }
89
+ static {
90
+ __name(this, "TimeoutWeakMap");
91
+ }
92
+ wmap = /* @__PURE__ */ new WeakMap();
93
+ /**
94
+ * Retrieves the value for the given key and refreshes its timeout.
95
+ *
96
+ * @param key The key to retrieve
97
+ * @returns The value if found, undefined otherwise
98
+ */
99
+ get(key) {
100
+ const vt = this.wmap.get(key);
101
+ if (!vt) return void 0;
102
+ const [value, timeout] = vt;
103
+ timeout.refresh();
104
+ return value;
105
+ }
106
+ /**
107
+ * Sets a key-value pair with an optional custom timeout.
108
+ *
109
+ * @param key The key to set
110
+ * @param value The value to associate with the key
111
+ * @param timeoutMs Optional timeout override for this entry
112
+ * @returns This instance for chaining
113
+ */
114
+ set(key, value, timeoutMs) {
115
+ this.wmap.set(key, [
116
+ value,
117
+ setTimeout(() => {
118
+ this.delete(key);
119
+ }, timeoutMs ?? this.timeoutMs).unref()
120
+ ]);
121
+ return this;
122
+ }
123
+ /**
124
+ * Removes a key-value pair and clears its timeout.
125
+ *
126
+ * @param key The key to delete
127
+ * @returns True if the key existed and was deleted, false otherwise
128
+ */
129
+ delete(key) {
130
+ const vt = this.wmap.get(key);
131
+ if (!vt) return false;
132
+ const timeout = vt[1];
133
+ clearTimeout(timeout);
134
+ return this.wmap.delete(key);
135
+ }
136
+ /**
137
+ * Checks if a key exists in the map.
138
+ * Does not refresh the timeout.
139
+ *
140
+ * @param key The key to check
141
+ * @returns True if the key exists, false otherwise
142
+ */
143
+ has(key) {
144
+ return this.wmap.has(key);
145
+ }
146
+ /**
147
+ * Loads multiple key-value pairs into the map.
148
+ *
149
+ * @param entries Iterable of key-value pairs to load
150
+ * @param timeoutMs Optional timeout override for all entries
151
+ * @returns This instance for chaining
152
+ */
153
+ load(entries, timeoutMs) {
154
+ for (const [key, value] of entries) {
155
+ this.set(key, value, timeoutMs);
156
+ }
157
+ return this;
158
+ }
159
+ /**
160
+ * Updates an existing value or creates a new one using an update function.
161
+ *
162
+ * @param key The key to update
163
+ * @param update Function that receives the current value and returns the new value
164
+ * @returns This instance for chaining
165
+ */
166
+ update(key, update) {
167
+ return this.set(key, update(this.get(key), key, this));
168
+ }
169
+ /**
170
+ * Gets a value or creates it using a factory function if it doesn't exist.
171
+ * Refreshes timeout if the value exists.
172
+ *
173
+ * @param key The key to get or create
174
+ * @param factory Function to create the value if it doesn't exist
175
+ * @param timeoutMs Optional timeout override for new entries
176
+ * @returns The existing or newly created value
177
+ */
178
+ getOrDefault(key, factory, timeoutMs) {
179
+ const vt = this.wmap.get(key);
180
+ if (vt) {
181
+ const [value, timeout] = vt;
182
+ timeout.refresh();
183
+ return value;
184
+ } else {
185
+ const value = factory(key, this);
186
+ this.set(key, value, timeoutMs);
187
+ return value;
188
+ }
189
+ }
190
+ };
191
+
192
+ // ../object/src/objSortKeys.ts
193
+ function objSortKeys(o, compare) {
194
+ const entries = Object.entries(o);
195
+ if (compare) entries.sort(compare);
196
+ else entries.sort((a, b) => a[0].localeCompare(b[0]));
197
+ return Object.fromEntries(entries);
198
+ }
199
+ __name(objSortKeys, "objSortKeys");
200
+
201
+ // ../object/src/valuesOf.ts
202
+ function valuesOf(obj) {
203
+ return Object.values(obj);
204
+ }
205
+ __name(valuesOf, "valuesOf");
206
+
207
+ // ../fn/src/setName.ts
208
+ function setName(name, target) {
209
+ return defineValue(target, "name", typeof name === "string" ? name : name.name, { enumerable: false });
210
+ }
211
+ __name(setName, "setName");
212
+
213
+ // src/lib/internal/collectVariadicOptionValues.ts
214
+ function collectVariadicOptionValues(parsed, options) {
215
+ for (let i = 0; i < parsed.tokens.length; i++) {
216
+ const token = parsed.tokens[i];
217
+ if (token.kind !== "option") continue;
218
+ const def = options.find((o) => o.name === token.name);
219
+ if (!def?.variadic || def.type !== "string") continue;
220
+ const values = [token.value];
221
+ let j = i + 1;
222
+ while (j < parsed.tokens.length && parsed.tokens[j].kind === "positional") {
223
+ const positionalToken = parsed.tokens[j];
224
+ values.push(positionalToken.value);
225
+ const posIndex = parsed.positionals.indexOf(positionalToken.value);
226
+ if (posIndex !== -1) parsed.positionals.splice(posIndex, 1);
227
+ j++;
228
+ }
229
+ Reflect.set(
230
+ parsed.values,
231
+ token.name,
232
+ values.filter((v) => v !== void 0)
233
+ );
234
+ }
235
+ }
236
+ __name(collectVariadicOptionValues, "collectVariadicOptionValues");
237
+
238
+ // src/lib/internal/mergeOptionDefaults.ts
239
+ function mergeOptionDefaults(values, options) {
240
+ for (const option of options) {
241
+ if (option.defaultValue !== void 0 && option.name in values) {
242
+ values[option.name] ??= option.defaultValue;
243
+ }
244
+ }
245
+ }
246
+ __name(mergeOptionDefaults, "mergeOptionDefaults");
247
+
248
+ // src/lib/internal/normalizeArgv.ts
249
+ function normalizeArgv(argv, options) {
250
+ for (const o of options) {
251
+ if (o.long === o.name) continue;
252
+ argv = argv.map((a) => {
253
+ if (a === `--${o.long}`) return `--${o.name}`;
254
+ if (a === `--no-${o.long}`) return `--no-${o.name}`;
255
+ return a;
256
+ });
257
+ }
258
+ return argv;
259
+ }
260
+ __name(normalizeArgv, "normalizeArgv");
261
+
262
+ // src/lib/internal/resolveArguments.ts
263
+ function resolveArguments(positionals, args) {
264
+ const result = args.map((arg, index) => {
265
+ if (arg.variadic) {
266
+ const remaining = positionals.slice(index);
267
+ return remaining.length > 0 ? remaining : arg.defaultValue;
268
+ }
269
+ return positionals[index] ?? arg.defaultValue;
270
+ });
271
+ while (result.length && arrLast(result) === void 0) result.pop();
272
+ return result;
273
+ }
274
+ __name(resolveArguments, "resolveArguments");
275
+
276
+ // src/lib/internal/validateParsed.ts
277
+ function validateParsed(args, optionValues, argDefs, optionDefs) {
278
+ return argDefs.map((def, index) => {
279
+ const value = args[index];
280
+ if (def.required) {
281
+ if (def.variadic ? Array.isArray(value) && value.length === 0 : value === void 0) {
282
+ return `Missing argument [${index}] ${def.usage}`;
283
+ }
284
+ }
285
+ if (def.choices && value !== void 0) {
286
+ if (![value].flat().every((v) => def.choices.includes(v))) {
287
+ return `Invalid argument [${index}] ${def.usage}: Got \`${value}\`. Accepted values: [${def.choices.map((c) => `\`${c}\``).join(",")}]`;
288
+ }
289
+ }
290
+ }).concat(
291
+ entriesOf(optionValues).map(([key, value]) => {
292
+ const def = optionDefs.find((o) => o.name === key);
293
+ if (!def) return `Unknown option --${key}`;
294
+ if (def.choices && value !== void 0) {
295
+ if (!(def.variadic ? value : [value]).every((v) => def.choices.includes(v))) {
296
+ return `Invalid option value ${def.flags}: Got \`${value}\`. Accepted values: [${def.choices.map((c) => `\`${c}\``).join(",")}]`;
297
+ }
298
+ }
299
+ })
300
+ ).filter((s) => s !== void 0).reduce(
301
+ (acc, curr) => {
302
+ return (acc ?? []).concat(curr);
303
+ },
304
+ void 0
305
+ );
306
+ }
307
+ __name(validateParsed, "validateParsed");
308
+
309
+ // ../string/src/lib/strFirstCharToUpperCase.ts
310
+ function strFirstCharToUpperCase(string) {
311
+ return string.charAt(0).toUpperCase() + string.substring(1);
312
+ }
313
+ __name(strFirstCharToUpperCase, "strFirstCharToUpperCase");
314
+
315
+ // src/lib/Command.ts
316
+ import colors3 from "ansi-colors";
317
+ import { inspect, parseArgs } from "node:util";
318
+
319
+ // src/lib/Help.ts
320
+ var Help_exports = {};
321
+ __export(Help_exports, {
322
+ Help: () => Help
323
+ });
324
+
325
+ // ../decorators/src/assertDescriptorValueIsFunction.ts
326
+ function assertDescriptorValueIsFunction(key, descriptor) {
327
+ if (!(typeof descriptor.value === "function" && descriptor.value !== Function.prototype)) {
328
+ throw new TypeError(`"value" not a function for ${key} with descriptor: ${JSON.stringify(descriptor)}.`);
329
+ }
330
+ }
331
+ __name(assertDescriptorValueIsFunction, "assertDescriptorValueIsFunction");
332
+
333
+ // ../decorators/src/lazyProp.ts
334
+ import { isFunction } from "es-toolkit/predicate";
335
+ import { ms as ms2 } from "enhanced-ms";
336
+
337
+ // ../decorators/src/memoizeSync.ts
338
+ import memoizee from "memoizee";
339
+ import { ms } from "enhanced-ms";
340
+ function memoizeSync(arg = {}) {
341
+ const opts = typeof arg === "object" ? arg : { maxAge: typeof arg === "number" ? arg : ms(arg) };
342
+ return /* @__PURE__ */ __name(function decorator(target, key, descriptor) {
343
+ if (!descriptor) throw new TypeError("descriptor is undefined");
344
+ const orig = descriptor.value;
345
+ assertDescriptorValueIsFunction(key, descriptor);
346
+ const options = { length: false, ...opts };
347
+ if (opts.instancesShareCache) {
348
+ Reflect.deleteProperty(options, "instancesShareCache");
349
+ descriptor.value = memoizee(orig, options);
350
+ } else {
351
+ const wmap = /* @__PURE__ */ new WeakMap();
352
+ descriptor.value = function(...args) {
353
+ const memoized = mapGetOrDefault(wmap, this, () => memoizee(orig, options));
354
+ return memoized.apply(this, args);
355
+ };
356
+ }
357
+ return descriptor;
358
+ }, "decorator");
359
+ }
360
+ __name(memoizeSync, "memoizeSync");
361
+
362
+ // ../decorators/src/lazyProp.ts
363
+ function lazyProp(targetOrTimeout, key, descriptor) {
364
+ if (typeof targetOrTimeout === "number" || typeof targetOrTimeout === "string") {
365
+ const maxAge = typeof targetOrTimeout === "number" ? targetOrTimeout : ms2(targetOrTimeout);
366
+ return createLazyPropDecorator(new TimeoutWeakMap(maxAge));
367
+ } else {
368
+ const decorator = createLazyPropDecorator(/* @__PURE__ */ new WeakMap());
369
+ return decorator(targetOrTimeout, key, descriptor);
370
+ }
371
+ }
372
+ __name(lazyProp, "lazyProp");
373
+ function createLazyPropDecorator(map) {
374
+ return function(target, key, descriptor) {
375
+ const { get, value } = descriptor;
376
+ if (isFunction(get)) {
377
+ descriptor.get = function() {
378
+ return mapGetOrDefault(map, this, () => get.call(this));
379
+ };
380
+ return descriptor;
381
+ }
382
+ if (isFunction(value)) {
383
+ return memoizeSync()(target, key, descriptor);
384
+ }
385
+ throw new TypeError('neither "get" nor "value" are functions');
386
+ };
387
+ }
388
+ __name(createLazyPropDecorator, "createLazyPropDecorator");
389
+
390
+ // src/lib/Help.ts
391
+ import C from "ansi-colors";
392
+ var Help = class {
393
+ static {
394
+ __name(this, "Help");
395
+ }
396
+ cmd;
397
+ /** output helpWidth, long lines are wrapped to fit */
398
+ helpWidth = process.stdout.isTTY ? process.stdout.columns : 80;
399
+ minWidthToWrap = 40;
400
+ sortSubcommands = true;
401
+ sortOptions = true;
402
+ usageDisplayOptionsAs = "[opts]";
403
+ usageDisplaySubcommandAs = "[cmd]";
404
+ constructor(cmd) {
405
+ this.cmd = cmd;
406
+ Object.defineProperty(this, "cmd", { enumerable: false });
407
+ }
408
+ visibleCommands() {
409
+ const res = Object.values(this.cmd.commands).filter((c) => !c.hidden);
410
+ if (this.sortSubcommands) {
411
+ res.sort((a, b) => {
412
+ return a.name.localeCompare(b.name);
413
+ });
414
+ }
415
+ return res;
416
+ }
417
+ /**
418
+ * Compare options for sort.
419
+ */
420
+ compareOptions(a, b) {
421
+ const getSortKey = /* @__PURE__ */ __name((option) => {
422
+ return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
423
+ }, "getSortKey");
424
+ return getSortKey(a).localeCompare(getSortKey(b));
425
+ }
426
+ visibleOptions() {
427
+ const res = this.cmd.options.filter((option) => !option.hidden);
428
+ if (this.sortOptions) res.sort(this.compareOptions);
429
+ return res;
430
+ }
431
+ visibleArguments() {
432
+ if (this.cmd.arguments.find((argument) => !!argument.description)) {
433
+ return [...this.cmd.arguments];
434
+ }
435
+ return [];
436
+ }
437
+ /**
438
+ * Get the command term to show in the list of subcommands.
439
+ */
440
+ subcommandTerm(sub) {
441
+ const args = sub.arguments.map((arg) => arg.usage).join(" ");
442
+ return (sub.aliases[0] ? sub.aliases[0].padEnd(this.longestSubcommandAliasLength(), " ") + " | " : "") + sub.name + (sub.options.length ? " " + this.usageDisplayOptionsAs : "") + // simplistic check for non-help option
443
+ (args ? " " + args : "");
444
+ }
445
+ /**
446
+ * Get the option term to show in the list of options.
447
+ */
448
+ optionTerm(option) {
449
+ return option.flags;
450
+ }
451
+ /**
452
+ * Get the argument term to show in the list of arguments.
453
+ */
454
+ argumentTerm(argument) {
455
+ return argument.name;
456
+ }
457
+ longestSubcommandAliasLength() {
458
+ return Math.max(0, ...this.visibleCommands().map((c) => c.aliases[0]?.length || 0));
459
+ }
460
+ longestSubcommandTermLength() {
461
+ return this.visibleCommands().reduce((max, command) => {
462
+ return Math.max(max, this.displayWidth(this.styleSubcommandTerm(this.subcommandTerm(command))));
463
+ }, 0);
464
+ }
465
+ longestOptionTermLength() {
466
+ return this.visibleOptions().reduce((max, option) => {
467
+ return Math.max(max, this.displayWidth(this.styleOptionTerm(this.optionTerm(option))));
468
+ }, 0);
469
+ }
470
+ longestArgumentTermLength() {
471
+ return this.visibleArguments().reduce((max, argument) => {
472
+ return Math.max(max, this.displayWidth(this.styleArgumentTerm(this.argumentTerm(argument))));
473
+ }, 0);
474
+ }
475
+ /**
476
+ * Get the command usage to be displayed at the top of the built-in help.
477
+ */
478
+ commandUsage() {
479
+ let path = "";
480
+ for (let ancestor = this.cmd.parent; ancestor; ancestor = ancestor.parent) {
481
+ path = ancestor.name + " " + path;
482
+ }
483
+ return (path + this.cmd.name + " " + [
484
+ ...Object.keys(this.cmd.commands).length ? [this.usageDisplaySubcommandAs] : [],
485
+ ...this.cmd.options.length ? [this.usageDisplayOptionsAs] : [],
486
+ ...this.cmd.arguments.map((arg) => {
487
+ return arg.required ? arg.variadic ? `<${arg.name}...>` : `<${arg.name}>` : arg.variadic ? `[${arg.name}...]` : `[${arg.name}]`;
488
+ })
489
+ ].join(" ")).trim();
490
+ }
491
+ /**
492
+ * Get the description for the command.
493
+ */
494
+ commandDescription() {
495
+ let res = "";
496
+ if (this.cmd.aliases.length) {
497
+ res += `Aliases: ${this.cmd.aliases.join(", ")}`;
498
+ res += "\n\n";
499
+ }
500
+ res += this.cmd.description;
501
+ return res;
502
+ }
503
+ /**
504
+ * Get the subcommand summary to show in the list of subcommands.
505
+ * (Fallback to description for backwards compatibility.)
506
+ */
507
+ subcommandDescription(sub) {
508
+ return sub.summary || (sub.description?.includes("\n") ? sub.description.trim().split("\n")[0] : sub.description.trim());
509
+ }
510
+ /**
511
+ * Get the option description to show in the list of options.
512
+ */
513
+ optionDescription(option) {
514
+ const extraInfo = [];
515
+ if (option.choices) {
516
+ extraInfo.push(
517
+ // use stringify to match the display of the default value
518
+ `choices: ${option.choices.map((choice) => String(choice)).join(", ")}`
519
+ );
520
+ }
521
+ if (option.defaultValue && !(Array.isArray(option.defaultValue) && option.defaultValue.length === 0)) {
522
+ extraInfo.push(`default: ${option.defaultValueDescription || String(option.defaultValue)}`);
523
+ }
524
+ if (option.env !== void 0) {
525
+ extraInfo.push(`env: ${option.env}`);
526
+ }
527
+ if (extraInfo.length > 0) {
528
+ const extraDescription = `(${extraInfo.join(", ")})`;
529
+ if (option.description) {
530
+ return `${option.description} ${extraDescription}`;
531
+ }
532
+ return extraDescription;
533
+ }
534
+ return option.description ?? "";
535
+ }
536
+ /**
537
+ * Get the argument description to show in the list of arguments.
538
+ */
539
+ argumentDescription(argument) {
540
+ const extraInfo = [];
541
+ if (argument.choices) {
542
+ extraInfo.push(
543
+ // use stringify to match the display of the default value
544
+ `choices: ${argument.choices.map((choice) => String(choice)).join(", ")}`
545
+ );
546
+ }
547
+ if (argument.defaultValue !== void 0) {
548
+ extraInfo.push(`default: ${argument.defaultValueDescription || String(argument.defaultValue)}`);
549
+ }
550
+ if (extraInfo.length > 0) {
551
+ const extraDescription = `(${extraInfo.join(", ")})`;
552
+ if (argument.description) {
553
+ return `${argument.description} ${extraDescription}`;
554
+ }
555
+ return extraDescription;
556
+ }
557
+ return argument.description ?? "";
558
+ }
559
+ /**
560
+ * Format a list of items, given a heading and an array of formatted items.
561
+ */
562
+ formatItemList(heading, items) {
563
+ if (items.length === 0) return [];
564
+ return [this.styleTitle(heading), ...items, ""];
565
+ }
566
+ /**
567
+ * Group items by their help group heading.
568
+ */
569
+ groupItems(unsortedItems, visibleItems, getGroup) {
570
+ const result = /* @__PURE__ */ new Map();
571
+ unsortedItems.forEach((item) => {
572
+ const group = getGroup(item);
573
+ if (!result.has(group)) result.set(group, []);
574
+ });
575
+ visibleItems.forEach((item) => {
576
+ const group = getGroup(item);
577
+ if (!result.has(group)) {
578
+ result.set(group, []);
579
+ }
580
+ result.get(group).push(item);
581
+ });
582
+ return result;
583
+ }
584
+ /**
585
+ * Return display width of string, ignoring ANSI escape sequences. Used in padding and wrapping calculations.
586
+ */
587
+ displayWidth(str) {
588
+ const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
589
+ return str.replace(sgrPattern, "").length;
590
+ }
591
+ /**
592
+ * Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
593
+ */
594
+ styleTitle(str) {
595
+ return C.yellow(str);
596
+ }
597
+ /**
598
+ * Style the usage line for displaying in the help. Applies specific styling to different parts like options, commands, and arguments.
599
+ */
600
+ styleUsage(str) {
601
+ return str.split(" ").map((word, index, arr) => {
602
+ if (word === this.usageDisplaySubcommandAs) return C.green(word);
603
+ if (word === this.usageDisplayOptionsAs) return C.blue(word);
604
+ if (word[0] === "<") return C.red(word);
605
+ if (word[0] === "[") return C.cyan(word);
606
+ if (arr[index + 1]?.startsWith("[")) return C.magenta(word);
607
+ return this.styleCommandText(word);
608
+ }).join(" ");
609
+ }
610
+ /**
611
+ * Style command descriptions for display in help output.
612
+ */
613
+ styleCommandDescription(str) {
614
+ return this.styleDescriptionText(str);
615
+ }
616
+ /**
617
+ * Style option descriptions for display in help output.
618
+ */
619
+ styleOptionDescription(str) {
620
+ return C.gray(this.styleDescriptionText(str));
621
+ }
622
+ /**
623
+ * Style subcommand descriptions for display in help output.
624
+ */
625
+ styleSubcommandDescription(str) {
626
+ return this.styleDescriptionText(str);
627
+ }
628
+ /**
629
+ * Style argument descriptions for display in help output.
630
+ */
631
+ styleArgumentDescription(str) {
632
+ return C.gray(this.styleDescriptionText(str));
633
+ }
634
+ /**
635
+ * Base style used by descriptions. Override in subclass to apply custom formatting.
636
+ */
637
+ styleDescriptionText(str) {
638
+ return C.gray(str);
639
+ }
640
+ /**
641
+ * Style option terms (flags) for display in help output.
642
+ */
643
+ styleOptionTerm(str) {
644
+ return this.styleOptionText(str);
645
+ }
646
+ /**
647
+ * Style subcommand terms for display in help output. Applies specific styling to different parts like options and arguments.
648
+ */
649
+ styleSubcommandTerm(str) {
650
+ const res = str.split(" ").map((word) => {
651
+ if (word === this.usageDisplayOptionsAs) return C.dim(word);
652
+ if (word[0] === "[" || word[0] === "<") return C.dim(word);
653
+ return this.styleSubcommandText(word);
654
+ }).join(" ");
655
+ const split = res.split("|");
656
+ if (split.length === 1) {
657
+ return res;
658
+ }
659
+ split[0] = C.green(split[0]);
660
+ return split.join("|");
661
+ }
662
+ /**
663
+ * Style argument terms for display in help output.
664
+ */
665
+ styleArgumentTerm(str) {
666
+ return this.styleArgumentText(str);
667
+ }
668
+ /**
669
+ * Base style used in terms and usage for options. Override in subclass to apply custom formatting.
670
+ */
671
+ styleOptionText(str) {
672
+ return str;
673
+ }
674
+ /**
675
+ * Base style used in terms and usage for arguments. Override in subclass to apply custom formatting.
676
+ */
677
+ styleArgumentText(str) {
678
+ return str;
679
+ }
680
+ /**
681
+ * Base style used in terms and usage for subcommands. Override in subclass to apply custom formatting.
682
+ */
683
+ styleSubcommandText(str) {
684
+ return str;
685
+ }
686
+ /**
687
+ * Base style used in terms and usage for commands. Override in subclass to apply custom formatting.
688
+ */
689
+ styleCommandText(str) {
690
+ return str;
691
+ }
692
+ padWidth() {
693
+ return Math.max(
694
+ this.longestOptionTermLength(),
695
+ this.longestSubcommandTermLength(),
696
+ this.longestArgumentTermLength()
697
+ );
698
+ }
699
+ /**
700
+ * Detect manually wrapped and indented strings by checking for line break followed by whitespace.
701
+ */
702
+ preformatted(str) {
703
+ return /\n[^\S\r\n]/.test(str);
704
+ }
705
+ /**
706
+ * Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
707
+ *
708
+ * So "TTT", 5, "DDD DDDD DD DDD" might be formatted for this.helpWidth=17 like so:
709
+ * TTT DDD DDDD
710
+ * DD DDD
711
+ */
712
+ formatItem(term, termWidth, description) {
713
+ const itemIndent = 2;
714
+ const itemIndentStr = " ".repeat(itemIndent);
715
+ if (!description) return itemIndentStr + term;
716
+ const paddedTerm = term.padEnd(termWidth + term.length - this.displayWidth(term));
717
+ const spacerWidth = 2;
718
+ const helpWidth = this.helpWidth;
719
+ const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
720
+ let formattedDescription;
721
+ if (remainingWidth < this.minWidthToWrap || this.preformatted(description)) {
722
+ formattedDescription = description;
723
+ } else {
724
+ const wrappedDescription = this.boxWrap(description, remainingWidth);
725
+ formattedDescription = wrappedDescription.replace(/\n/g, "\n" + " ".repeat(termWidth + spacerWidth));
726
+ }
727
+ return itemIndentStr + paddedTerm + " ".repeat(spacerWidth) + formattedDescription.replace(/\n/g, `
728
+ ${itemIndentStr}`);
729
+ }
730
+ /**
731
+ * Wrap a string at whitespace, preserving existing line breaks.
732
+ * Wrapping is skipped if the width is less than `minWidthToWrap`.
733
+ */
734
+ boxWrap(str, width) {
735
+ if (width < this.minWidthToWrap) return str;
736
+ const rawLines = str.split(/\r\n|\n/);
737
+ const chunkPattern = /[\s]*[^\s]+/g;
738
+ const wrappedLines = [];
739
+ rawLines.forEach((line) => {
740
+ const chunks = line.match(chunkPattern);
741
+ if (chunks === null) {
742
+ wrappedLines.push("");
743
+ return;
744
+ }
745
+ let sumChunks = [chunks.shift()];
746
+ let sumWidth = this.displayWidth(sumChunks[0]);
747
+ chunks.forEach((chunk) => {
748
+ const visibleWidth = this.displayWidth(chunk);
749
+ if (sumWidth + visibleWidth <= width) {
750
+ sumChunks.push(chunk);
751
+ sumWidth += visibleWidth;
752
+ return;
753
+ }
754
+ wrappedLines.push(sumChunks.join(""));
755
+ const nextChunk = chunk.trimStart();
756
+ sumChunks = [nextChunk];
757
+ sumWidth = this.displayWidth(nextChunk);
758
+ });
759
+ wrappedLines.push(sumChunks.join(""));
760
+ });
761
+ return wrappedLines.join("\n");
762
+ }
763
+ /**
764
+ * Generate the built-in help text.
765
+ */
766
+ render() {
767
+ let output = [`${this.styleTitle("Usage:")} ${this.styleUsage(this.commandUsage())}`, ""];
768
+ const des = this.commandDescription();
769
+ if (des.length > 0) {
770
+ output = output.concat([this.boxWrap(this.styleCommandDescription(des), this.helpWidth), ""]);
771
+ }
772
+ const argumentList = this.visibleArguments().map((argument) => {
773
+ return this.formatItem(
774
+ this.styleArgumentTerm(this.argumentTerm(argument)),
775
+ this.padWidth(),
776
+ this.styleArgumentDescription(this.argumentDescription(argument))
777
+ );
778
+ });
779
+ output = output.concat(this.formatItemList("Arguments:", argumentList));
780
+ const optionGroups = this.groupItems(
781
+ this.cmd.options,
782
+ this.visibleOptions(),
783
+ (option) => option.group ?? "Options:"
784
+ );
785
+ optionGroups.forEach((options, group) => {
786
+ const optionList = options.map((option) => {
787
+ return this.formatItem(
788
+ this.styleOptionTerm(this.optionTerm(option)),
789
+ this.padWidth(),
790
+ this.styleOptionDescription(this.optionDescription(option))
791
+ );
792
+ });
793
+ output = output.concat(this.formatItemList(group, optionList));
794
+ });
795
+ const commandGroups = this.groupItems(
796
+ Object.values(this.cmd.commands),
797
+ this.visibleCommands(),
798
+ (sub) => sub.group || "Commands:"
799
+ );
800
+ commandGroups.forEach((commands, group) => {
801
+ const commandList = commands.map((sub) => {
802
+ return this.formatItem(
803
+ this.styleSubcommandTerm(this.subcommandTerm(sub)),
804
+ this.padWidth(),
805
+ this.styleSubcommandDescription(this.subcommandDescription(sub))
806
+ );
807
+ });
808
+ output = output.concat(this.formatItemList(group, commandList));
809
+ });
810
+ return output.join("\n");
811
+ }
812
+ };
813
+ __decorateClass([
814
+ lazyProp
815
+ ], Help.prototype, "visibleCommands", 1);
816
+ __decorateClass([
817
+ lazyProp
818
+ ], Help.prototype, "visibleOptions", 1);
819
+ __decorateClass([
820
+ lazyProp
821
+ ], Help.prototype, "visibleArguments", 1);
822
+ __decorateClass([
823
+ lazyProp
824
+ ], Help.prototype, "longestSubcommandAliasLength", 1);
825
+ __decorateClass([
826
+ lazyProp
827
+ ], Help.prototype, "longestSubcommandTermLength", 1);
828
+ __decorateClass([
829
+ lazyProp
830
+ ], Help.prototype, "longestOptionTermLength", 1);
831
+ __decorateClass([
832
+ lazyProp
833
+ ], Help.prototype, "longestArgumentTermLength", 1);
834
+ __decorateClass([
835
+ lazyProp
836
+ ], Help.prototype, "padWidth", 1);
837
+
838
+ // src/lib/helpers/findCommand.ts
839
+ var findCommand_exports = {};
840
+ __export(findCommand_exports, {
841
+ findCommand: () => findCommand
842
+ });
843
+ function findCommand(cmd, nameOrAlias) {
844
+ return cmd.commands[nameOrAlias] ?? valuesOf(cmd.commands).find((c) => c.aliases.includes(nameOrAlias));
845
+ }
846
+ __name(findCommand, "findCommand");
847
+
848
+ // src/lib/helpers/getCommandAncestors.ts
849
+ var getCommandAncestors_exports = {};
850
+ __export(getCommandAncestors_exports, {
851
+ getCommandAncestors: () => getCommandAncestors
852
+ });
853
+
854
+ // src/lib/helpers/getCommandAndAncestors.ts
855
+ var getCommandAndAncestors_exports = {};
856
+ __export(getCommandAndAncestors_exports, {
857
+ getCommandAndAncestors: () => getCommandAndAncestors
858
+ });
859
+ function getCommandAndAncestors(cmd) {
860
+ const result = [];
861
+ let command = cmd;
862
+ for (; command; command = command.parent) {
863
+ result.push(command);
864
+ }
865
+ return result;
866
+ }
867
+ __name(getCommandAndAncestors, "getCommandAndAncestors");
868
+
869
+ // src/lib/helpers/getCommandAncestors.ts
870
+ function getCommandAncestors(cmd) {
871
+ return getCommandAndAncestors(cmd).slice(1);
872
+ }
873
+ __name(getCommandAncestors, "getCommandAncestors");
874
+
875
+ // ../node/src/createLogger.ts
876
+ import colors from "ansi-colors";
877
+ import { isPrimitive, isString } from "es-toolkit/predicate";
878
+ function createLogger(name) {
879
+ const NAME = name ? colors.dim.cyan(name) : name;
880
+ const START = [NAME, colors.dim.gray("[START]")].filter(Boolean);
881
+ const DEBUG = [NAME, colors.dim.magenta("[DEBUG]")].filter(Boolean);
882
+ const INFO = [NAME, colors.dim.gray("[INFO]")].filter(Boolean);
883
+ const DONE = [NAME, colors.dim.green("[DONE] ")].filter(Boolean);
884
+ const WARN = [NAME, colors.dim.yellow("[WARN] ")].filter(Boolean);
885
+ const ERROR = [NAME, colors.dim.red("[ERROR]")].filter(Boolean);
886
+ const grayArgs = createColoredArgs(colors.gray);
887
+ const cyanArgs = createColoredArgs(colors.cyan);
888
+ const yellowArgs = createColoredArgs(colors.yellow);
889
+ return {
890
+ start: /* @__PURE__ */ __name((...args) => console.info(...START, ...args), "start"),
891
+ done: /* @__PURE__ */ __name((...args) => console.info(...DONE, ...args), "done"),
892
+ info: /* @__PURE__ */ __name((...args) => console.info(...INFO, ...grayArgs(args)), "info"),
893
+ log: /* @__PURE__ */ __name((...args) => console.log(...NAME ? [NAME, ...args] : args), "log"),
894
+ warn: /* @__PURE__ */ __name((...args) => console.warn(...WARN, ...yellowArgs(args)), "warn"),
895
+ debug: /* @__PURE__ */ __name((...args) => console.debug(...DEBUG, ...cyanArgs(args)), "debug"),
896
+ error: /* @__PURE__ */ __name((...args) => args.forEach((arg) => console.error(...ERROR, arg)), "error")
897
+ };
898
+ }
899
+ __name(createLogger, "createLogger");
900
+ function createColoredArgs(colorFn) {
901
+ return (args) => {
902
+ return args.map((arg) => {
903
+ if (isString(arg)) {
904
+ if (arg === colors.stripColor(arg)) {
905
+ return colorFn(arg);
906
+ } else {
907
+ return arg;
908
+ }
909
+ } else if (isPrimitive(arg)) {
910
+ return colorFn(String(arg));
911
+ } else {
912
+ return arg;
913
+ }
914
+ });
915
+ };
916
+ }
917
+ __name(createColoredArgs, "createColoredArgs");
918
+
919
+ // ../node/src/timer.ts
920
+ import { isPromise } from "node:util/types";
921
+ import colors2 from "ansi-colors";
922
+ import humanizeDuration from "humanize-duration";
923
+ function timer(arg, task) {
924
+ const t0 = process.hrtime.bigint();
925
+ const [name, description] = Array.isArray(arg) ? arg : [arg, ""];
926
+ const log = createLogger(name);
927
+ if (name && description) {
928
+ log.start(description);
929
+ }
930
+ const retval = task(log, name);
931
+ if (!isPromise(retval)) {
932
+ return done(retval);
933
+ }
934
+ return retval.then((result) => {
935
+ return done(result);
936
+ });
937
+ function done(retval2) {
938
+ if (process.exitCode) return retval2;
939
+ const ns = process.hrtime.bigint() - t0;
940
+ const ms3 = Math.floor(Number(ns) / 1e6);
941
+ log.done(colors2.dim(humanizeDuration(ms3)));
942
+ return retval2;
943
+ }
944
+ __name(done, "done");
945
+ }
946
+ __name(timer, "timer");
947
+
948
+ // src/lib/helpers/findOption.ts
949
+ var findOption_exports = {};
950
+ __export(findOption_exports, {
951
+ findOption: () => findOption
952
+ });
953
+ function findOption(cmd, nameOrShortOrLong) {
954
+ return nameOrShortOrLong.startsWith("--") ? cmd.options.find((o) => o.long === nameOrShortOrLong.slice(2)) : nameOrShortOrLong.startsWith("-") ? cmd.options.find((o) => o.short === nameOrShortOrLong.slice(1)) : cmd.options.find((o) => o.name === nameOrShortOrLong);
955
+ }
956
+ __name(findOption, "findOption");
957
+
958
+ // src/lib/helpers/parseOptionFlags.ts
959
+ var parseOptionFlags_exports = {};
960
+ __export(parseOptionFlags_exports, {
961
+ parseOptionFlags: () => parseOptionFlags
962
+ });
963
+ function parseOptionFlags(flags) {
964
+ const match = flags.match(/^-(.+?), --([a-zA-Z][\w-]*)(?:\s*(<(.+?)>|\[(.+?)\]))?$/);
965
+ if (!match) throw new Error(`Invalid option format: ${flags}`);
966
+ const short = match[1];
967
+ if (short.length !== 1) {
968
+ throw new Error(`Expected short name to be a single character. Got: -${short}`);
969
+ }
970
+ const long = match[2];
971
+ const argName = (match[4] || match[5])?.replace(/\.\.\.$/, "") || void 0;
972
+ const name = long.split("-").reduce((str, word) => {
973
+ return str + word[0].toUpperCase() + word.slice(1);
974
+ });
975
+ return { short, long, name, argName };
976
+ }
977
+ __name(parseOptionFlags, "parseOptionFlags");
978
+
979
+ // src/lib/Command.ts
980
+ import { kebabCase } from "es-toolkit/string";
981
+ var _Command = class _Command {
982
+ static {
983
+ __name(this, "Command");
984
+ }
985
+ /** Parent command in the hierarchy, undefined for root command */
986
+ parent;
987
+ /** The command name used to invoke it */
988
+ name;
989
+ /** Semantic version string displayed by --version flag */
990
+ version;
991
+ /** Alternative names for invoking this command */
992
+ aliases;
993
+ /** Brief one-line description shown in command lists */
994
+ summary;
995
+ /** Full description displayed in help text */
996
+ description;
997
+ /** Whether to exclude from help listings */
998
+ hidden;
999
+ /** Category for organizing related commands in help output */
1000
+ group;
1001
+ /** Positional arguments this command accepts */
1002
+ arguments;
1003
+ /** CLI options (flags) this command recognizes */
1004
+ options;
1005
+ /** Subcommands registered with this command */
1006
+ commands;
1007
+ /** Main action handler executed when command is invoked */
1008
+ action;
1009
+ /** Option-driven actions (e.g., --help, --version) executed when their conditions match */
1010
+ hooks;
1011
+ constructor(name, parent) {
1012
+ this.name = name;
1013
+ this.aliases = [];
1014
+ this.description = "";
1015
+ this.arguments = [];
1016
+ this.options = [];
1017
+ this.commands = {};
1018
+ this.hooks = [];
1019
+ Object.defineProperty(this, "parent", { value: parent, enumerable: false });
1020
+ if (!parent) {
1021
+ this.addOption("-D, --debug", { description: "Display debug information" }).addOptionHook("debug", ({ cmd, ...data }) => {
1022
+ console.debug(inspect(cmd, { depth: 1, colors: true }));
1023
+ console.debug(inspect(data, { depth: 3, colors: true }));
1024
+ });
1025
+ this.addOption("-h, --help", { description: "Display help information" }).addOptionHook("help", ({ cmd }) => {
1026
+ console.log(cmd.renderHelp());
1027
+ process.exitCode = 0;
1028
+ });
1029
+ }
1030
+ }
1031
+ get help() {
1032
+ return new Help(this);
1033
+ }
1034
+ /** Configure how the help is rendered */
1035
+ helpConfiguration(cb) {
1036
+ const help = this.help;
1037
+ cb?.(help);
1038
+ return this;
1039
+ }
1040
+ /** Renders formatted help text using provided help definition */
1041
+ renderHelp(config = {}) {
1042
+ const result = this.help.render();
1043
+ return config.noColor ? colors3.stripColor(result) : result;
1044
+ }
1045
+ /** Sets the command name */
1046
+ setName(name) {
1047
+ this.name = name;
1048
+ }
1049
+ /** Sets command aliases, flattening nested arrays */
1050
+ setAliases(...aliases) {
1051
+ this.aliases = [];
1052
+ this.addAliases(...aliases);
1053
+ return this;
1054
+ }
1055
+ /** Adds aliases to existing ones */
1056
+ addAliases(...aliases) {
1057
+ const taken = this.parent ? valuesOf(this.parent.commands).flatMap((c) => [c.name, ...c.aliases]) : [];
1058
+ arrRemoveDuplicates(aliases.flat()).filter((a) => !this.aliases.includes(a) && a !== this.name).forEach((a) => {
1059
+ if (taken.includes(a)) {
1060
+ throw new Error(
1061
+ `Alias "${a}" is already used by a sibling command: ${findCommand(this.parent, a)?.name}`
1062
+ );
1063
+ }
1064
+ this.aliases.push(a);
1065
+ });
1066
+ this.aliases.sort((a, b) => a.length - b.length);
1067
+ return this;
1068
+ }
1069
+ /** Sets the command version */
1070
+ setVersion(version) {
1071
+ this.version = version;
1072
+ if (findOption(this, "version")) return this;
1073
+ return this.addOption("-V, --version", { description: "Display semver version" }).addOptionHook("version", ({ cmd }) => {
1074
+ console.log(getCommandAndAncestors(cmd).find((c) => c.version)?.version);
1075
+ process.exitCode = 0;
1076
+ });
1077
+ }
1078
+ /** Sets the command summary */
1079
+ setSummary(summary) {
1080
+ this.summary = summary;
1081
+ return this;
1082
+ }
1083
+ /** Sets command description, joining variadic lines */
1084
+ setDescription(...lines) {
1085
+ this.description = lines.join("\n");
1086
+ return this;
1087
+ }
1088
+ /** Sets whether command is hidden from help */
1089
+ setHidden(hidden = true) {
1090
+ this.hidden = hidden;
1091
+ return this;
1092
+ }
1093
+ /** Sets the command group for help organization */
1094
+ setGroup(group) {
1095
+ this.group = group;
1096
+ return this;
1097
+ }
1098
+ /** Add a subcommand and return the subcommand. All options are inherited by the subcommand. */
1099
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1100
+ command(name, cb) {
1101
+ if (this.arguments.length) {
1102
+ throw new Error(
1103
+ `Cannot add subcommand "${name}" to "${this.name}" because it already has arguments defined.`
1104
+ );
1105
+ }
1106
+ const sub = this.createSubcommand(name);
1107
+ const inherit = this.options;
1108
+ sub.options.push(...inherit);
1109
+ const inheritHooks = this.hooks.filter((t) => inherit.some((i) => i.name === t.name));
1110
+ sub.hooks.push(...inheritHooks);
1111
+ const taken = valuesOf(this.commands).flatMap((c) => [c.name, ...c.aliases]);
1112
+ if (taken.includes(name)) {
1113
+ throw new Error(
1114
+ `Command name "${getCommandAndAncestors(sub).map((c) => c.name)}" is already used by this command or its aliases: ${taken.join(", ")}`
1115
+ );
1116
+ }
1117
+ const kebab = kebabCase(name);
1118
+ const words = kebab.split("-");
1119
+ const initials = words.map((s) => s[0]).join("");
1120
+ if (!taken.includes(initials)) {
1121
+ sub.addAliases(initials);
1122
+ } else {
1123
+ const initials2 = words.map((s) => s[0] + s[1]).join("");
1124
+ if (!taken.includes(initials2)) {
1125
+ sub.addAliases(initials2);
1126
+ }
1127
+ }
1128
+ this.commands[name] = sub;
1129
+ return cb ? cb(sub, this) : sub;
1130
+ }
1131
+ /** Add a subcommand and return the subcommand. All options are inherited by the subcommand. */
1132
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1133
+ addCommand(name, cb) {
1134
+ this.command(name, cb);
1135
+ return this;
1136
+ }
1137
+ // Implementation
1138
+ addArgument(usage, options = {}) {
1139
+ if (!/^<(.*?)>$|^\[(.*?)\]$/.test(usage)) {
1140
+ throw new Error(`Invalid argument format: ${usage}`);
1141
+ }
1142
+ const name = usage.slice(1, -1).replace(/\.\.\.$/, "");
1143
+ const required = usage.startsWith("<");
1144
+ const variadic = usage.slice(0, -1).endsWith("...");
1145
+ const prevArg = this.arguments[this.arguments.length - 1];
1146
+ if (prevArg?.variadic) {
1147
+ throw new Error(`Cannot add argument ${usage} after variadic argument ${prevArg.usage}`);
1148
+ }
1149
+ if (required && prevArg && !prevArg?.required) {
1150
+ throw new Error(`Cannot add required argument ${usage} after optional argument ${prevArg?.usage}`);
1151
+ }
1152
+ if (required && prevArg?.defaultValue) {
1153
+ throw new Error(
1154
+ `Cannot add required argument ${usage} after optional argument with default value ${prevArg.usage}`
1155
+ );
1156
+ }
1157
+ const arg = {
1158
+ usage,
1159
+ name,
1160
+ required,
1161
+ variadic,
1162
+ ...options
1163
+ };
1164
+ if (variadic && !arg.defaultValue) {
1165
+ arg.defaultValue = [];
1166
+ }
1167
+ this.arguments.push(arg);
1168
+ return this;
1169
+ }
1170
+ /**
1171
+ * Adds command-line option with type inference. Parses format: `-s, --long [<value>|[value]|<value...>|[value...]]`
1172
+ */
1173
+ addOption(flags, opts = {}) {
1174
+ const ins = {};
1175
+ const { short, long, name, argName } = parseOptionFlags(flags);
1176
+ for (const opt of this.options) {
1177
+ if (opt.long === long) {
1178
+ throw new Error(`Option long name, --${long} already in use by: ${opt.flags}`);
1179
+ }
1180
+ if (opt.short === short) {
1181
+ throw new Error(`Option short name, -${short} already in use by: ${opt.flags}`);
1182
+ }
1183
+ }
1184
+ ins.flags = flags;
1185
+ ins.short = short;
1186
+ ins.long = long;
1187
+ ins.name = name;
1188
+ ins.description = "";
1189
+ if (!argName) {
1190
+ ins.type = "boolean";
1191
+ } else {
1192
+ ins.type = "string";
1193
+ ins.argName = argName;
1194
+ if (flags.endsWith(">")) {
1195
+ if (flags.endsWith("...>")) {
1196
+ ins.required = true;
1197
+ ins.variadic = true;
1198
+ } else {
1199
+ ins.required = true;
1200
+ }
1201
+ } else if (flags.endsWith("]")) {
1202
+ if (flags.endsWith("...]")) {
1203
+ ins.variadic = true;
1204
+ ins.defaultValue = opts.defaultValue ?? [];
1205
+ }
1206
+ }
1207
+ }
1208
+ for (const [key, value] of Object.entries(opts)) {
1209
+ if (value !== void 0) Reflect.set(ins, key, value);
1210
+ }
1211
+ if (ins.env && ins.defaultValue === void 0 && typeof process.env[ins.env] === "string") {
1212
+ ins.required = false;
1213
+ ins.flags = ins.flags.replace("<", "[").replace(">", "]");
1214
+ if (ins.type === "boolean") {
1215
+ ins.defaultValue = /^(t(rue)?|y(es)?|1)$/i.test(process.env[ins.env]);
1216
+ } else if (ins.variadic) {
1217
+ ins.defaultValue = process.env[ins.env].replace(/\]|\[/, "").split(",").map((v) => v.trim());
1218
+ } else {
1219
+ ins.defaultValue = process.env[ins.env];
1220
+ }
1221
+ }
1222
+ this.options.push(ins);
1223
+ return this;
1224
+ }
1225
+ /**
1226
+ * Register an action to be invoked when an option is set to true or string value.
1227
+ *
1228
+ * Hooks execute in addition to or instead of the main action handler,
1229
+ * allowing for option-driven behavior. For example, `--help` and `--version`
1230
+ * are implemented as hooks.
1231
+ */
1232
+ addOptionHook(optionName, action) {
1233
+ const def = findOption(this, optionName);
1234
+ if (!def.group && /process\.exitCode ?= ?.+;?\s*\}$/.test(action.toString())) {
1235
+ def.group = "Command Options";
1236
+ }
1237
+ this.hooks.push({
1238
+ name: optionName,
1239
+ predicate: setName("has" + strFirstCharToUpperCase(optionName), ({ opts }) => {
1240
+ return opts[optionName] !== void 0 && opts[optionName] !== false && !(Array.isArray(opts[optionName]) && opts[optionName].length === 0);
1241
+ }),
1242
+ action: setName(optionName, action)
1243
+ });
1244
+ return this;
1245
+ }
1246
+ /** Parses command-line arguments with subcommand support and type-safe validation. */
1247
+ parseArgv(argv = process.argv.slice(2)) {
1248
+ const sub = findCommand(this, argv[0]);
1249
+ if (sub) return sub.parseArgv(argv.slice(1));
1250
+ argv = normalizeArgv(argv, this.options);
1251
+ const parsed = parseArgs({
1252
+ args: argv,
1253
+ options: Object.fromEntries(
1254
+ this.options.map((o) => [
1255
+ o.name,
1256
+ { type: o.type, short: o.short, default: o.defaultValue, multiple: !!o.variadic }
1257
+ ])
1258
+ ),
1259
+ allowPositionals: true,
1260
+ tokens: true,
1261
+ strict: false,
1262
+ allowNegative: true
1263
+ });
1264
+ collectVariadicOptionValues(parsed, this.options);
1265
+ mergeOptionDefaults(parsed.values, this.options);
1266
+ parsed.values = objSortKeys(parsed.values, (a, b) => {
1267
+ return a[1] === false ? 1 : b[1] === false ? -1 : a[1] === true ? 1 : b[1] === true ? -1 : 0;
1268
+ });
1269
+ const args = resolveArguments(parsed.positionals, this.arguments);
1270
+ const opts = filterObject(parsed.values, (value) => value !== void 0);
1271
+ const errors = validateParsed(args, parsed.values, this.arguments, this.options);
1272
+ const path = getCommandAncestors(this).map((c) => c.name);
1273
+ const data = {
1274
+ path,
1275
+ name: this.name,
1276
+ argv,
1277
+ args,
1278
+ opts,
1279
+ errors,
1280
+ cmd: this
1281
+ };
1282
+ const hooks = this.hooks.filter((t) => t.predicate(data));
1283
+ const execute = /* @__PURE__ */ __name(async () => {
1284
+ for (const hook of hooks) {
1285
+ await hook.action(data);
1286
+ if (process.exitCode) {
1287
+ return;
1288
+ }
1289
+ }
1290
+ await timer([[...path, this.name].join(" "), this.description], async (logger) => {
1291
+ if (errors) {
1292
+ errors.forEach((msg) => logger.error(colors3.red(msg)));
1293
+ process.exitCode = 1;
1294
+ }
1295
+ if (this.action) {
1296
+ return await this.action(...args, opts, {
1297
+ ...data,
1298
+ args,
1299
+ opts,
1300
+ cmd: this,
1301
+ logger
1302
+ });
1303
+ }
1304
+ console.log(this.renderHelp());
1305
+ });
1306
+ }, "execute");
1307
+ return { ...data, hooks, execute };
1308
+ }
1309
+ /**
1310
+ * Sets the main action handler for this command, which is executed after any matching option hooks when the command is invoked.
1311
+ * The handler receives parsed arguments and options with correct typings.
1312
+ */
1313
+ setAction(fn) {
1314
+ this.action = setName(this.name, fn);
1315
+ return this;
1316
+ }
1317
+ /** Returns a new Command instance. Override this method in subclasses. */
1318
+ createSubcommand(name) {
1319
+ return new _Command(name, this);
1320
+ }
1321
+ };
1322
+ __decorateClass([
1323
+ lazyProp
1324
+ ], _Command.prototype, "help", 1);
1325
+ var Command = _Command;
1326
+
1327
+ // src/lib/types.ts
1328
+ var types_exports = {};
1329
+
1330
+ // src/index.ts
1331
+ var src_default = {
1332
+ ...Command_exports,
1333
+ //
1334
+ ...Help_exports,
1335
+ ...findCommand_exports,
1336
+ ...findOption_exports,
1337
+ ...getCommandAncestors_exports,
1338
+ ...getCommandAndAncestors_exports,
1339
+ ...parseOptionFlags_exports,
1340
+ ...types_exports
1341
+ };
1342
+ export {
1343
+ Command,
1344
+ Help,
1345
+ src_default as default,
1346
+ findCommand,
1347
+ findOption,
1348
+ getCommandAncestors,
1349
+ getCommandAndAncestors,
1350
+ parseOptionFlags
1351
+ };
1352
+ //# sourceMappingURL=index.mjs.map