cli-api 0.1.1 → 0.2.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 (43) hide show
  1. package/README.md +22 -25
  2. package/dist/index.d.mts +394 -0
  3. package/dist/index.mjs +3 -0
  4. package/dist/interfaces-COq24bNI.mjs +391 -0
  5. package/dist/run-C903J5ca.mjs +1137 -0
  6. package/package.json +37 -37
  7. package/.hgignore +0 -12
  8. package/.idea/$CACHE_FILE$ +0 -6
  9. package/.idea/clap.iml +0 -8
  10. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  11. package/.idea/deployment.xml +0 -63
  12. package/.idea/inspectionProfiles/Project_Default.xml +0 -10
  13. package/.idea/misc.xml +0 -6
  14. package/.idea/modules.xml +0 -8
  15. package/.idea/vcs.xml +0 -6
  16. package/Makefile +0 -14
  17. package/babel.config.json +0 -33
  18. package/dist/cjs/index.js +0 -588
  19. package/dist/cjs/index.js.map +0 -1
  20. package/dist/es/index.mjs +0 -578
  21. package/dist/es/index.mjs.map +0 -1
  22. package/dist/types/app-help.d.ts +0 -3
  23. package/dist/types/commands/command-help.d.ts +0 -2
  24. package/dist/types/commands/version.d.ts +0 -2
  25. package/dist/types/constants.d.ts +0 -4
  26. package/dist/types/index.d.ts +0 -3
  27. package/dist/types/interfaces.d.ts +0 -79
  28. package/dist/types/options.d.ts +0 -7
  29. package/dist/types/print-command-help.d.ts +0 -2
  30. package/dist/types/run.d.ts +0 -2
  31. package/dist/types/utils.d.ts +0 -17
  32. package/rollup.config.js +0 -44
  33. package/src/app-help.ts +0 -34
  34. package/src/commands/command-help.ts +0 -28
  35. package/src/commands/version.ts +0 -11
  36. package/src/constants.ts +0 -4
  37. package/src/index.ts +0 -3
  38. package/src/interfaces.ts +0 -89
  39. package/src/options.ts +0 -266
  40. package/src/print-command-help.ts +0 -78
  41. package/src/run.ts +0 -45
  42. package/src/utils.ts +0 -86
  43. package/tsconfig.json +0 -32
@@ -0,0 +1,1137 @@
1
+ import { a as getExecuteHandler, c as createChalk, i as OptType, n as Command, o as hasSubCommands, r as ExecutionContext, s as isExecutable } from "./interfaces-COq24bNI.mjs";
2
+ import Path from "path";
3
+ import stringWidth from "string-width";
4
+ import FileSys from "fs";
5
+
6
+ //#region src/constants.ts
7
+ const EMPTY_ARRAY = Object.freeze([]);
8
+ const EMPTY_OBJECT = Object.freeze(Object.create({ __proto__: null }));
9
+ const TRUE_VALUES = new Set([
10
+ "y",
11
+ "yes",
12
+ "t",
13
+ "true",
14
+ "1",
15
+ "on"
16
+ ]);
17
+ const FALSE_VALUES = new Set([
18
+ "n",
19
+ "no",
20
+ "f",
21
+ "false",
22
+ "0",
23
+ "off"
24
+ ]);
25
+
26
+ //#endregion
27
+ //#region src/utils.ts
28
+ function print(str = "") {
29
+ return process.stdout.write(str);
30
+ }
31
+ function printLn(...args) {
32
+ console.log(...args);
33
+ }
34
+ /**
35
+ * Semantic categories for user-facing CLI errors.
36
+ */
37
+ let ErrorCategory = /* @__PURE__ */ function(ErrorCategory$1) {
38
+ ErrorCategory$1["InvalidArg"] = "invalid-arg";
39
+ ErrorCategory$1["Misconfig"] = "misconfig";
40
+ ErrorCategory$1["Internal"] = "internal";
41
+ return ErrorCategory$1;
42
+ }({});
43
+ const ERROR_PRESENTATION = {
44
+ [ErrorCategory.InvalidArg]: {
45
+ code: 2,
46
+ color: "#D73737"
47
+ },
48
+ [ErrorCategory.Misconfig]: {
49
+ code: 254,
50
+ color: "#B854D4"
51
+ },
52
+ [ErrorCategory.Internal]: {
53
+ code: 253,
54
+ color: "#6684E1"
55
+ }
56
+ };
57
+ function blockError(str, style, chalk) {
58
+ const lines = str.split("\n");
59
+ const width = Math.max(...lines.map((l) => stringWidth(l))) + 4;
60
+ const colorize = chalk.bgHex(ERROR_PRESENTATION[style].color).hex("#FEFBEC");
61
+ printLn(colorize(space(width)));
62
+ for (const line of lines) {
63
+ const txt = ` ${line}`;
64
+ printLn(colorize(txt + space(width, txt)));
65
+ }
66
+ printLn(colorize(space(width)));
67
+ }
68
+ /**
69
+ * Creates a structured CLI error.
70
+ *
71
+ * @param message The error text that should be rendered.
72
+ * @param type The semantic error category used to determine presentation and exit code.
73
+ * @returns A structured CLI error object.
74
+ */
75
+ function createError(message, type) {
76
+ return {
77
+ message,
78
+ type
79
+ };
80
+ }
81
+ /**
82
+ * Gets the process exit code associated with a given CLI error.
83
+ *
84
+ * @param error The structured CLI error to map to a process exit code.
85
+ * @returns The default exit code used for that error type.
86
+ */
87
+ function getErrorExitCode(error) {
88
+ return ERROR_PRESENTATION[error.type].code;
89
+ }
90
+ /**
91
+ * Prints a user-facing CLI error block.
92
+ *
93
+ * @param error The structured CLI error to render.
94
+ * @returns Nothing.
95
+ */
96
+ function printError(error, chalk) {
97
+ blockError(error.message, error.type, chalk);
98
+ }
99
+ function toArray(x) {
100
+ if (!x) return EMPTY_ARRAY;
101
+ if (Array.isArray(x)) return x;
102
+ return [x];
103
+ }
104
+ function resolve(x) {
105
+ return typeof x === "function" ? x() : x;
106
+ }
107
+ function toBool(str) {
108
+ if (typeof str === "boolean") return str;
109
+ str = String(str).trim().toLowerCase();
110
+ if (TRUE_VALUES.has(str)) return true;
111
+ if (FALSE_VALUES.has(str)) return false;
112
+ throw new Error(`Could not cast "${str}" to boolean`);
113
+ }
114
+ function space(len, str) {
115
+ if (str) len -= stringWidth(str);
116
+ return len > 0 ? " ".repeat(len) : "";
117
+ }
118
+ function getTerminalWidth() {
119
+ return process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
120
+ }
121
+ function wrapText(text, width) {
122
+ if (width <= 0) return text.split("\n");
123
+ const wrappedLines = [];
124
+ for (const rawLine of text.split("\n")) {
125
+ if (rawLine.trim().length === 0) {
126
+ wrappedLines.push("");
127
+ continue;
128
+ }
129
+ const words = rawLine.trim().split(/\s+/);
130
+ let currentLine = "";
131
+ for (const word of words) {
132
+ const candidate = currentLine.length === 0 ? word : `${currentLine} ${word}`;
133
+ if (currentLine.length > 0 && stringWidth(candidate) > width) {
134
+ wrappedLines.push(currentLine);
135
+ currentLine = word;
136
+ } else currentLine = candidate;
137
+ }
138
+ if (currentLine.length > 0) wrappedLines.push(currentLine);
139
+ }
140
+ return wrappedLines;
141
+ }
142
+ function getProcName(app) {
143
+ const bin = app._bin;
144
+ if (bin != null) return bin;
145
+ const relPath = Path.relative(process.cwd(), process.argv[1]);
146
+ return `${Path.basename(process.argv[0])} ${relPath.length < process.argv[1].length ? relPath : process.argv[1]}`;
147
+ }
148
+ function includes(needle, haystack) {
149
+ if (!haystack) return false;
150
+ if (Array.isArray(haystack)) return haystack.includes(needle);
151
+ return needle === haystack;
152
+ }
153
+ function statSync(path) {
154
+ try {
155
+ return FileSys.lstatSync(path);
156
+ } catch {
157
+ return null;
158
+ }
159
+ }
160
+ function sortBy(arr, cmp) {
161
+ const collator = new Intl.Collator();
162
+ const values = arr.map(cmp);
163
+ const keys = Array.from(arr.keys());
164
+ keys.sort((a, b) => collator.compare(values[a], values[b]));
165
+ return keys.map((i) => arr[i]);
166
+ }
167
+
168
+ //#endregion
169
+ //#region src/commands/version.ts
170
+ const versionCommand = new Command("version").describe("Displays current version").run(async (_, __, context) => {
171
+ printLn(context.app._version);
172
+ });
173
+
174
+ //#endregion
175
+ //#region src/options.ts
176
+ /**
177
+ * Error thrown when argument parsing encounters an unknown option name.
178
+ *
179
+ * @param option The unrecognized option token, including its leading dashes.
180
+ * @returns A parser error whose message matches the CLI's unknown-option wording.
181
+ */
182
+ var UnknownOptionError = class extends Error {
183
+ option;
184
+ constructor(option) {
185
+ super(`option ${option} not recognized`);
186
+ this.option = option;
187
+ }
188
+ };
189
+ function getEnumValues(item) {
190
+ if (Array.isArray(item.type)) return item.type;
191
+ if (item.type === OptType.ENUM) return item.enumValues;
192
+ }
193
+ function getOptionImplicitValue(opt) {
194
+ if (opt.valueIfSet !== void 0) return resolve(opt.valueIfSet);
195
+ if (opt.type === OptType.BOOL) return true;
196
+ return !resolve(opt.defaultValue);
197
+ }
198
+ function getOptionNoPrefixValue(opt) {
199
+ if (opt.valueIfNoPrefix !== void 0) return resolve(opt.valueIfNoPrefix);
200
+ if (opt.type === OptType.BOOL) return false;
201
+ throw new Error(`\`${getOptName(opt)}\` option must define valueIfNoPrefix when noPrefix is enabled`);
202
+ }
203
+ function isRepeatable(value) {
204
+ return value === true || typeof value === "number";
205
+ }
206
+ function getMaxRepeatCount(value) {
207
+ if (value === true || value === false || value === void 0) return;
208
+ return value;
209
+ }
210
+ function getMinRequiredCount(value) {
211
+ if (value === true) return 1;
212
+ if (typeof value === "number") return value;
213
+ return 0;
214
+ }
215
+ function assertValidCount(name, value, { allowZero = false } = {}) {
216
+ if (!Number.isInteger(value) || value < 0 || !allowZero && value === 0) throw new Error(`${name} must be ${allowZero ? "a non-negative" : "a positive"} integer`);
217
+ }
218
+ function validatePositionalDefinitions(cmd) {
219
+ if (!cmd.positonals?.length) return;
220
+ let encounteredOptionalPositional = false;
221
+ for (let i = 0; i < cmd.positonals.length; ++i) {
222
+ const arg = cmd.positonals[i];
223
+ const repeatable = isRepeatable(arg.repeatable);
224
+ const minRequired = getMinRequiredCount(arg.required);
225
+ const maxRepeatCount = getMaxRepeatCount(arg.repeatable);
226
+ if (typeof arg.required === "number") {
227
+ assertValidCount(`"${arg.name}" argument required count`, arg.required, { allowZero: true });
228
+ if (!repeatable) throw new Error(`"${arg.name}" argument cannot use a numeric required count unless it is repeatable`);
229
+ }
230
+ if (typeof arg.repeatable === "number") assertValidCount(`"${arg.name}" argument repeatable count`, arg.repeatable);
231
+ if (repeatable && i < cmd.positonals.length - 1) throw new Error("Only the last argument can be repeatable");
232
+ if (maxRepeatCount !== void 0 && minRequired > maxRepeatCount) throw new Error(`"${arg.name}" argument requires at least ${minRequired} values but allows at most ${maxRepeatCount}`);
233
+ if (encounteredOptionalPositional && minRequired > 0) throw new Error("Required arguments cannot come after optional arguments");
234
+ if (minRequired === 0) encounteredOptionalPositional = true;
235
+ }
236
+ }
237
+ function pushRepeatableValue(target, value, itemName, maxCount, kind) {
238
+ if (maxCount !== void 0 && target.length >= maxCount) throw new Error(`"${itemName}" ${kind} allows at most ${maxCount} value${maxCount === 1 ? "" : "s"}`);
239
+ target.push(value);
240
+ }
241
+ /**
242
+ * Formats an option definition for display in help output.
243
+ *
244
+ * @param opt The option metadata to render.
245
+ * @returns A tuple containing the formatted flag label and its description text.
246
+ */
247
+ function formatOption(opt, chalk) {
248
+ const aliases = [];
249
+ if (opt.alias) if (Array.isArray(opt.alias)) aliases.push(...opt.alias);
250
+ else aliases.push(opt.alias);
251
+ let flags = [...aliases.map((a) => chalk.green(a.length === 1 ? `-${a}` : `--${a}`)), chalk.green(`--${opt.name}`)].join(", ");
252
+ if (!opt.alias && opt.name.length > 1) flags = ` ${flags}`;
253
+ if (opt.type !== OptType.BOOL) {
254
+ const valuePlaceholder = chalk.magenta(getValuePlaceholder(opt));
255
+ flags += opt.valueNotRequired ? `${chalk.grey("[")}=${valuePlaceholder}${chalk.grey("]")}` : `=${valuePlaceholder}`;
256
+ }
257
+ if (opt.noPrefix) flags += `, ${chalk.green(`--no-${opt.name}`)}`;
258
+ let desc = opt.description ?? "";
259
+ let defaultValueText = opt.defaultValueText;
260
+ if (defaultValueText === void 0 && opt.defaultValue !== void 0) defaultValueText = JSON.stringify(resolve(opt.defaultValue));
261
+ if (defaultValueText !== void 0) desc += chalk.yellow(` [default: ${defaultValueText}]`);
262
+ const enumValues = getEnumValues(opt);
263
+ if (enumValues?.length) desc += ` [possible values: ${enumValues.join(", ")}]`;
264
+ return [flags, desc];
265
+ }
266
+ function getValuePlaceholder(opt) {
267
+ if (opt.valuePlaceholder !== void 0) return opt.valuePlaceholder;
268
+ const enumValues = getEnumValues(opt);
269
+ if (enumValues !== void 0) return enumValues.join("|").toUpperCase();
270
+ else if (opt.type == OptType.BOOL) return JSON.stringify(!resolve(opt.defaultValue));
271
+ else if (opt.type === OptType.INT || opt.type === OptType.FLOAT) return "#";
272
+ else if (opt.type === OptType.INPUT_FILE || opt.type === OptType.OUTPUT_FILE) return "FILE";
273
+ else if (opt.type === OptType.INPUT_DIRECTORY || opt.type === OptType.OUTPUT_DIRECTORY || opt.type === OptType.EMPTY_DIRECTORY) return "DIR";
274
+ else return opt.name.toUpperCase();
275
+ }
276
+ function sortOptions(options) {
277
+ return sortBy(options, (option) => option.name);
278
+ }
279
+ function getOptions(cmd) {
280
+ return sortOptions(toArray(cmd.options));
281
+ }
282
+ /**
283
+ * Validates a command's static option and positional configuration before parsing argv.
284
+ *
285
+ * @param cmd The command definition to validate.
286
+ * @returns Nothing. Throws when the command configuration is internally inconsistent.
287
+ */
288
+ function validateCommandConfig(cmd) {
289
+ validatePositionalDefinitions(cmd);
290
+ const seenOptionTokens = /* @__PURE__ */ new Map();
291
+ for (const opt of getOptions(cmd)) {
292
+ if (typeof opt.repeatable === "number") assertValidCount(`\`${opt.name}\` option repeatable count`, opt.repeatable);
293
+ if (opt.noPrefix && !opt.valueNotRequired) throw new Error(`\`${getOptName(opt)}\` option cannot enable noPrefix unless valueNotRequired is enabled`);
294
+ if (opt.type === OptType.ENUM && !getEnumValues(opt)?.length) throw new Error(`\`${getOptName(opt)}\` option must define enumValues when using OptType.ENUM`);
295
+ if (opt.noPrefix && opt.type !== OptType.BOOL && opt.valueIfNoPrefix === void 0) throw new Error(`\`${getOptName(opt)}\` option must define valueIfNoPrefix when noPrefix is enabled`);
296
+ const tokens = new Set([opt.name]);
297
+ for (const alias of toArray(opt.alias)) tokens.add(alias);
298
+ if (opt.noPrefix) tokens.add(`no-${opt.name}`);
299
+ for (const token of tokens) {
300
+ const existing = seenOptionTokens.get(token);
301
+ if (existing !== void 0) {
302
+ const currentName = token.startsWith("no-") ? `--${token}` : getOptName(opt);
303
+ throw new Error(`Option token \`${currentName}\` collides with \`${existing}\``);
304
+ }
305
+ const displayName = token.startsWith("no-") ? `--${token}` : token.length === 1 ? `-${token}` : `--${token}`;
306
+ seenOptionTokens.set(token, displayName);
307
+ }
308
+ }
309
+ }
310
+ function parseArgs(cmd, argv) {
311
+ const args = [];
312
+ const opts = Object.create(null);
313
+ let parseFlags = true;
314
+ const allOptions = getOptions(cmd);
315
+ validateCommandConfig(cmd);
316
+ for (const opt of allOptions) if (isRepeatable(opt.repeatable)) {
317
+ const k = opt.key ?? opt.name;
318
+ if (opts[k] === void 0) opts[k] = [];
319
+ }
320
+ if (cmd.positonals?.length) for (let i = 0; i < cmd.positonals.length; ++i) {
321
+ const a = cmd.positonals[i];
322
+ if (isRepeatable(a.repeatable)) {
323
+ const k = a.key ?? a.name;
324
+ if (k && opts[k] === void 0) opts[k] = [];
325
+ }
326
+ }
327
+ const findOpt = (name) => {
328
+ const option = allOptions.find((o) => o.name === name || includes(name, o.alias));
329
+ if (option !== void 0) return {
330
+ opt: option,
331
+ negated: false
332
+ };
333
+ const negatedOption = allOptions.find((o) => o.noPrefix && `no-${o.name}` === name);
334
+ if (negatedOption !== void 0) return {
335
+ opt: negatedOption,
336
+ negated: true
337
+ };
338
+ };
339
+ const getUnknownShortOption = (cluster) => {
340
+ for (let j = 0; j < cluster.length; ++j) {
341
+ const ch = cluster[j];
342
+ if (!findOpt(ch)) return ch;
343
+ }
344
+ };
345
+ let argIdx = 0;
346
+ for (let i = 0; i < argv.length; ++i) {
347
+ let token = argv[i];
348
+ if (parseFlags && token === "--") {
349
+ parseFlags = false;
350
+ continue;
351
+ }
352
+ if (parseFlags && token.length >= 2 && token.startsWith("-")) {
353
+ let inlineValue;
354
+ if (token.startsWith("--")) {
355
+ if (token.includes("=")) {
356
+ const [left, right] = token.split("=", 2);
357
+ token = left;
358
+ inlineValue = right;
359
+ }
360
+ const name = token.slice(2);
361
+ const match = findOpt(name);
362
+ if (!match) throw new UnknownOptionError(`--${name}`);
363
+ const { opt, negated } = match;
364
+ if (negated && inlineValue !== void 0) throw new Error(`Option \`--no-${opt.name}\` does not take a value`);
365
+ let value = inlineValue;
366
+ if (negated) value = getOptionNoPrefixValue(opt);
367
+ else if (value === void 0) if (opt.valueNotRequired) value = getOptionImplicitValue(opt);
368
+ else if (i < argv.length - 1) value = argv[++i];
369
+ else throw new Error(`Missing required value for option \`${token}\``);
370
+ if (opt.type != null) value = coerceType(value, opt.type, `option \`${token}\``, getEnumValues(opt));
371
+ const k = opt.key ?? opt.name;
372
+ if (isRepeatable(opt.repeatable)) pushRepeatableValue(opts[k], value, opt.name, getMaxRepeatCount(opt.repeatable), "option");
373
+ else opts[k] = value;
374
+ } else {
375
+ const clusterText = token.slice(1);
376
+ const equalIndex = clusterText.indexOf("=");
377
+ const cluster = equalIndex === -1 ? clusterText : clusterText.slice(0, equalIndex);
378
+ const hasInlineAssignment = equalIndex !== -1;
379
+ inlineValue = equalIndex === -1 ? void 0 : clusterText.slice(equalIndex + 1);
380
+ if (hasInlineAssignment) {
381
+ const unknownOption = getUnknownShortOption(cluster);
382
+ if (unknownOption !== void 0) throw new UnknownOptionError(`-${unknownOption}`);
383
+ }
384
+ let j = 0;
385
+ while (j < cluster.length) {
386
+ const ch = cluster[j];
387
+ const match = findOpt(ch);
388
+ if (!match) throw new UnknownOptionError(`-${ch}`);
389
+ const { opt } = match;
390
+ if (opt.valueNotRequired) {
391
+ const k$1 = opt.key ?? opt.name;
392
+ const v = getOptionImplicitValue(opt);
393
+ if (isRepeatable(opt.repeatable)) pushRepeatableValue(opts[k$1], v, opt.name, getMaxRepeatCount(opt.repeatable), "option");
394
+ else opts[k$1] = v;
395
+ j += 1;
396
+ continue;
397
+ }
398
+ let value;
399
+ const remainder = cluster.slice(j + 1);
400
+ if (remainder.length && !hasInlineAssignment) value = remainder;
401
+ else if (remainder.length && hasInlineAssignment) throw new Error(`Missing required value for option \`-${ch}\``);
402
+ else if (inlineValue !== void 0 && remainder.length === 0) value = inlineValue;
403
+ else if (i < argv.length - 1) value = argv[++i];
404
+ else throw new Error(`Missing required value for option "-${ch}"`);
405
+ if (opt.type != null) value = coerceType(value, opt.type, `option \`-${ch}\``, getEnumValues(opt));
406
+ const k = opt.key ?? opt.name;
407
+ if (isRepeatable(opt.repeatable)) pushRepeatableValue(opts[k], value, opt.name, getMaxRepeatCount(opt.repeatable), "option");
408
+ else opts[k] = value;
409
+ break;
410
+ }
411
+ }
412
+ } else {
413
+ let value = token;
414
+ const def = cmd.positonals?.[argIdx];
415
+ if (def) {
416
+ if (def.type != null) value = coerceType(value, def.type, `argument \`${def.name}\``, getEnumValues(def));
417
+ const k = def.key ?? def.name;
418
+ if (isRepeatable(def.repeatable)) {
419
+ const arr = opts[k] ??= [];
420
+ pushRepeatableValue(arr, value, def.name, getMaxRepeatCount(def.repeatable), "argument");
421
+ for (i = i + 1; i < argv.length; ++i) {
422
+ let v = argv[i];
423
+ if (parseFlags && v === "--") {
424
+ parseFlags = false;
425
+ continue;
426
+ }
427
+ if (parseFlags && v.startsWith("-")) {
428
+ i -= 1;
429
+ break;
430
+ }
431
+ if (def.type != null) v = coerceType(v, def.type, `argument \`${def.name}\``, getEnumValues(def));
432
+ pushRepeatableValue(arr, v, def.name, getMaxRepeatCount(def.repeatable), "argument");
433
+ }
434
+ argIdx = isRepeatable(def.repeatable) && cmd.positonals ? cmd.positonals.length : argIdx;
435
+ args.push(...arr);
436
+ continue;
437
+ } else opts[k] = value;
438
+ }
439
+ args.push(value);
440
+ ++argIdx;
441
+ }
442
+ }
443
+ if (allOptions.length) for (const opt of allOptions) {
444
+ const k = opt.key ?? opt.name;
445
+ if (opts[k] === void 0) {
446
+ if (opt.defaultValue !== void 0) opts[k] = resolve(opt.defaultValue);
447
+ else if (opt.required) throw new Error(`\`${getOptName(opt)}\` option is required`);
448
+ }
449
+ }
450
+ if (cmd.positonals?.length) for (let i = 0; i < cmd.positonals.length; ++i) {
451
+ const a = cmd.positonals[i];
452
+ const minRequired = getMinRequiredCount(a.required);
453
+ if (minRequired > 0 && argIdx <= i && !isRepeatable(a.repeatable)) throw new Error(`\`${a.name}\` argument is required`);
454
+ const k = a.key ?? a.name;
455
+ if (isRepeatable(a.repeatable) && (opts[k]?.length ?? 0) < minRequired) throw new Error(`\`${a.name}\` argument requires at least ${minRequired} value${minRequired === 1 ? "" : "s"}`);
456
+ if (k && opts[k] === void 0 && a.defaultValue !== void 0) opts[k] = resolve(a.defaultValue);
457
+ }
458
+ return [args, opts];
459
+ }
460
+ function coerceType(value, type, itemName, enumValues) {
461
+ const normalizedEnumValue = () => {
462
+ const normalized = String(value).trim().toLowerCase();
463
+ const allowedValues = enumValues ?? (Array.isArray(type) ? type : void 0);
464
+ if (allowedValues !== void 0 && !allowedValues.includes(normalized)) {
465
+ const itemText = itemName ? ` for ${itemName}` : "";
466
+ throw new Error(`Invalid value "${value}"${itemText} (expected one of: ${allowedValues.join(", ")})`);
467
+ }
468
+ return normalized;
469
+ };
470
+ if (Array.isArray(type)) return normalizedEnumValue();
471
+ switch (type) {
472
+ case OptType.BOOL: return toBool(value);
473
+ case OptType.INT: return Math.trunc(Number(value));
474
+ case OptType.FLOAT: return Number(value);
475
+ case OptType.ENUM: return normalizedEnumValue();
476
+ case OptType.STRING: return String(value);
477
+ case OptType.INPUT_FILE: {
478
+ if (value === "-") return "/dev/stdin";
479
+ const file = Path.normalize(value);
480
+ const fullPath = Path.resolve(file);
481
+ const stat = statSync(file);
482
+ if (!stat) throw new Error(`File ${createChalk().underline(fullPath)} does not exist`);
483
+ if (!stat.isFile()) throw new Error(`${createChalk().underline(fullPath)} is not a file`);
484
+ try {
485
+ FileSys.accessSync(file, FileSys.constants.R_OK);
486
+ } catch (err) {
487
+ throw new Error(`${createChalk().underline(fullPath)} is not readable`);
488
+ }
489
+ return file;
490
+ }
491
+ case OptType.INPUT_DIRECTORY: {
492
+ const dir = Path.normalize(value);
493
+ FileSys.accessSync(dir, FileSys.constants.X_OK);
494
+ return dir;
495
+ }
496
+ case OptType.OUTPUT_FILE: {
497
+ if (value === "-") return "/dev/stdout";
498
+ const file = Path.normalize(value);
499
+ const stat = statSync(file);
500
+ if (stat) {
501
+ if (!stat.isFile()) throw new Error(`'${file}' is not a file`);
502
+ FileSys.accessSync(file, FileSys.constants.W_OK);
503
+ } else FileSys.accessSync(Path.dirname(file), FileSys.constants.W_OK);
504
+ return file;
505
+ }
506
+ case OptType.OUTPUT_DIRECTORY:
507
+ FileSys.accessSync(value, FileSys.constants.W_OK);
508
+ return Path.normalize(value);
509
+ case OptType.EMPTY_DIRECTORY: {
510
+ const dir = Path.normalize(value);
511
+ let files = [];
512
+ try {
513
+ files = FileSys.readdirSync(dir);
514
+ } catch (err) {
515
+ if (err?.code === "ENOENT") FileSys.accessSync(Path.dirname(dir), FileSys.constants.W_OK);
516
+ else throw err;
517
+ }
518
+ if (files.length) throw new Error(`${createChalk().underline(dir)} is not empty`);
519
+ return dir;
520
+ }
521
+ }
522
+ return value;
523
+ }
524
+ function getOptName(opt) {
525
+ return (opt.name.length > 1 ? "--" : "-") + opt.name;
526
+ }
527
+ function findSubCommand(name, subCommands) {
528
+ const cmdName = String(name).trim().replace(/^-{1,2}/, "").toLowerCase();
529
+ return subCommands.find((c) => c.name === cmdName || includes(cmdName, c.alias));
530
+ }
531
+ function getCommand(path, subCommands) {
532
+ if (!path.length) throw new Error("Command path is required.");
533
+ let current = subCommands;
534
+ let command;
535
+ const resolvedPath = [];
536
+ for (let i = 0; i < path.length; ++i) {
537
+ const segment = path[i];
538
+ const next = findSubCommand(segment, current);
539
+ if (next === void 0) throw new Error(`Command "${segment}" does not exist.`);
540
+ command = next;
541
+ resolvedPath.push(next.name);
542
+ if (i < path.length - 1) {
543
+ if (!hasSubCommands(next)) throw new Error(`Command "${resolvedPath.join(" ")}" does not have subCommands.`);
544
+ current = next.subCommands;
545
+ }
546
+ }
547
+ return {
548
+ command,
549
+ path: resolvedPath
550
+ };
551
+ }
552
+
553
+ //#endregion
554
+ //#region src/global-options.ts
555
+ const HELP_OPTION = {
556
+ name: "help",
557
+ alias: "h",
558
+ description: "Show help text",
559
+ type: OptType.BOOL,
560
+ valueNotRequired: true,
561
+ valueIfSet: true
562
+ };
563
+ const COLOR_OPTION = {
564
+ name: "color",
565
+ description: "Control ANSI color output.",
566
+ type: OptType.ENUM,
567
+ enumValues: [
568
+ "always",
569
+ "never",
570
+ "auto"
571
+ ],
572
+ valuePlaceholder: "WHEN",
573
+ valueNotRequired: true,
574
+ valueIfSet: "always",
575
+ noPrefix: true,
576
+ valueIfNoPrefix: "never",
577
+ defaultValue: "auto"
578
+ };
579
+ function getGlobalOptions(app) {
580
+ return sortOptions([
581
+ HELP_OPTION,
582
+ COLOR_OPTION,
583
+ ...app._globalOptions ?? []
584
+ ]);
585
+ }
586
+
587
+ //#endregion
588
+ //#region src/app-help.ts
589
+ function shouldWrapHelpEntry$1(label, description, labelWidth) {
590
+ if (!description) return false;
591
+ const terminalWidth = getTerminalWidth();
592
+ const inlineIndent = labelWidth + 4;
593
+ const inlineDescriptionLines = wrapText(description, Math.max(terminalWidth - inlineIndent, 1));
594
+ return description.includes("\n") || inlineDescriptionLines.length > 1 || labelWidth + 4 + stringWidth(description) > terminalWidth;
595
+ }
596
+ function printHelpEntry$1(label, description, labelWidth, forceWrap = false) {
597
+ print(` ${label}`);
598
+ if (!description) {
599
+ printLn();
600
+ return false;
601
+ }
602
+ if (!(forceWrap || shouldWrapHelpEntry$1(label, description, labelWidth))) {
603
+ printLn(`${space(labelWidth + 2, label)}${description}`);
604
+ return false;
605
+ }
606
+ printLn();
607
+ const terminalWidth = getTerminalWidth();
608
+ const descriptionIndent = " ".repeat(10);
609
+ const wrappedDescription = wrapText(description, Math.max(terminalWidth - descriptionIndent.length, 1));
610
+ for (const line of wrappedDescription) printLn(line.length ? `${descriptionIndent}${line}` : "");
611
+ return true;
612
+ }
613
+ function printOptionEntries$1(entries) {
614
+ const width = Math.max(...entries.map((line) => stringWidth(line[0])));
615
+ const forceWrap = entries.some(([label, description]) => shouldWrapHelpEntry$1(label, description, width));
616
+ entries.forEach(([label, description], index) => {
617
+ if (printHelpEntry$1(label, description, width, forceWrap) && index < entries.length - 1) printLn();
618
+ });
619
+ }
620
+ function printHelp(context, commands) {
621
+ const app = context.app;
622
+ const chalk = context.chalk;
623
+ print(chalk.green(app.name));
624
+ const { _author: author, _version: version } = app;
625
+ if (version) print(` ver. ${chalk.yellow(version)}`);
626
+ if (author) print(` by ${chalk.cyan(author)}`);
627
+ printLn();
628
+ if (app.description) {
629
+ printLn();
630
+ printLn(app.description);
631
+ }
632
+ printLn();
633
+ printLn(chalk.yellow("Usage:"));
634
+ print(` ${chalk.cyan(getProcName(app))}`);
635
+ if (hasSubCommands(app)) print(` ${chalk.gray("[")}--GLOBAL-OPTIONS${chalk.gray("]")} ${chalk.gray("<")}COMMAND${chalk.gray(">")}`);
636
+ printLn("\n");
637
+ if (commands.length) printAvailableCommands(commands, "Commands:", chalk);
638
+ const globalOptions = getGlobalOptions(app);
639
+ if (globalOptions.length) {
640
+ printLn();
641
+ printLn(chalk.yellow("Global Options:"));
642
+ printOptionEntries$1(globalOptions.map((option) => formatOption(option, chalk)));
643
+ }
644
+ }
645
+ function printAvailableCommands(commands, title, chalk) {
646
+ if (!commands.length) return;
647
+ printLn(chalk.yellow(title));
648
+ const width = Math.max(...commands.map((c) => stringWidth(c.name))) + 2;
649
+ for (const cmd of commands) printHelpEntry$1(chalk.green(cmd.name), cmd.description, width);
650
+ }
651
+
652
+ //#endregion
653
+ //#region src/print-command-help.ts
654
+ function shouldWrapHelpEntry(label, description, labelWidth) {
655
+ if (!description) return false;
656
+ const terminalWidth = getTerminalWidth();
657
+ const inlineIndent = labelWidth + 4;
658
+ const inlineDescriptionLines = wrapText(description, Math.max(terminalWidth - inlineIndent, 1));
659
+ return description.includes("\n") || inlineDescriptionLines.length > 1 || labelWidth + 4 + stringWidth(description) > terminalWidth;
660
+ }
661
+ function printHelpEntry(label, description, labelWidth, forceWrap = false) {
662
+ print(` ${label}`);
663
+ if (!description) {
664
+ printLn();
665
+ return false;
666
+ }
667
+ if (!(forceWrap || shouldWrapHelpEntry(label, description, labelWidth))) {
668
+ printLn(`${space(labelWidth + 2, label)}${description}`);
669
+ return false;
670
+ }
671
+ printLn();
672
+ const terminalWidth = getTerminalWidth();
673
+ const descriptionIndent = " ".repeat(10);
674
+ const wrappedDescription = wrapText(description, Math.max(terminalWidth - descriptionIndent.length, 1));
675
+ for (const line of wrappedDescription) printLn(line.length ? `${descriptionIndent}${line}` : "");
676
+ return true;
677
+ }
678
+ function printOptionEntries(entries) {
679
+ const width = Math.max(...entries.map((line) => stringWidth(line[0])));
680
+ const forceWrap = entries.some(([label, description]) => shouldWrapHelpEntry(label, description, width));
681
+ entries.forEach(([label, description], index) => {
682
+ if (printHelpEntry(label, description, width, forceWrap) && index < entries.length - 1) printLn();
683
+ });
684
+ }
685
+ function getCommandLabel(context, path) {
686
+ const proc = context.chalk.cyan(getProcName(context.app));
687
+ if (!path.length) return proc;
688
+ return `${proc} ${path.join(" ")}`;
689
+ }
690
+ function formatUsageOption(opt, chalk) {
691
+ const optionName = chalk.green(getOptName(opt));
692
+ if (opt.type === OptType.BOOL) return optionName;
693
+ const valuePlaceholder = chalk.magenta(getValuePlaceholder(opt));
694
+ if (opt.valueNotRequired) return `${optionName}${chalk.grey("[")}=${valuePlaceholder}${chalk.grey("]")}`;
695
+ return `${optionName}=${valuePlaceholder}`;
696
+ }
697
+ function formatUsageArgument(arg, chalk) {
698
+ const argumentName = chalk.magenta(arg.repeatable ? `${arg.name}...` : arg.name);
699
+ return `${chalk.grey(arg.required ? "<" : "[")}${argumentName}${chalk.grey(arg.required ? ">" : "]")}`;
700
+ }
701
+ function printCommandHelp(context, cmd, path = []) {
702
+ const app = context.app;
703
+ const chalk = context.chalk;
704
+ if (cmd.description) {
705
+ printLn(cmd.description);
706
+ printLn();
707
+ }
708
+ if (cmd.longDescription) {
709
+ printLn(cmd.longDescription);
710
+ printLn();
711
+ }
712
+ if (cmd === app) {
713
+ const author = app._author;
714
+ if (author) {
715
+ printLn(`Author: ${author}`);
716
+ printLn();
717
+ }
718
+ }
719
+ printLn(chalk.yellow("Usage:"));
720
+ print(` ${getCommandLabel(context, path)}`);
721
+ if (hasSubCommands(cmd)) print(` ${chalk.gray("<")}command${chalk.gray(">")}`);
722
+ if (isExecutable(cmd)) {
723
+ const allOptions = getOptions(cmd);
724
+ if (allOptions.length) {
725
+ let otherOptions = 0;
726
+ for (const opt of allOptions) if (opt.required) print(` ${formatUsageOption(opt, chalk)}`);
727
+ else ++otherOptions;
728
+ if (otherOptions) print(` ${chalk.gray("[")}${chalk.magenta("--options")}${chalk.gray("]")}`);
729
+ }
730
+ if (cmd.positonals?.length) {
731
+ print(` ${chalk.grey("[")}--${chalk.grey("]")}`);
732
+ for (const arg of cmd.positonals) print(` ${formatUsageArgument(arg, chalk)}`);
733
+ }
734
+ } else if (!hasSubCommands(cmd)) print(` ${chalk.gray("[options] [arguments]")}`);
735
+ printLn();
736
+ if (isExecutable(cmd)) {
737
+ const allOptions = getOptions(cmd);
738
+ if (allOptions.length) {
739
+ printLn(chalk.yellow("\nOptions:"));
740
+ printOptionEntries(allOptions.map((option) => formatOption(option, chalk)));
741
+ }
742
+ if (cmd.positonals?.length) {
743
+ printLn(chalk.yellow("\nArguments:"));
744
+ const width = Math.max(...cmd.positonals.map((arg) => stringWidth(arg.name)));
745
+ for (const arg of cmd.positonals) printHelpEntry(chalk.green(arg.name), arg.description, width);
746
+ }
747
+ }
748
+ if (hasSubCommands(cmd)) {
749
+ printLn();
750
+ printAvailableCommands(cmd.subCommands, "Sub-commands:", chalk);
751
+ }
752
+ const globalOptions = getGlobalOptions(app);
753
+ if (globalOptions.length) {
754
+ printLn();
755
+ printLn(chalk.yellow("Global Options:"));
756
+ printOptionEntries(globalOptions.map((option) => formatOption(option, chalk)));
757
+ }
758
+ if (cmd.alias) {
759
+ const aliases = toArray(cmd.alias);
760
+ printLn(chalk.yellow(`\nAlias${aliases.length !== 1 ? "es" : ""}: `) + aliases.join(chalk.gray(", ")));
761
+ }
762
+ }
763
+
764
+ //#endregion
765
+ //#region src/commands/command-help.ts
766
+ const helpCommand = new Command("help").describe("Displays help for a command").arg("command", {
767
+ description: "The command path.",
768
+ repeatable: true
769
+ }).run(async (commandPath, _, context) => {
770
+ const app = context.app;
771
+ const rootCommands = [
772
+ ...app.subCommands !== void 0 ? sortBy(app.subCommands, (c) => c.name) : [],
773
+ versionCommand,
774
+ helpCommand
775
+ ];
776
+ if (commandPath.length) {
777
+ const { command, path } = getCommand(commandPath, rootCommands);
778
+ printCommandHelp(context, command, path);
779
+ } else if (app.subCommands !== void 0) printHelp(context, rootCommands);
780
+ else printCommandHelp(context, app, []);
781
+ });
782
+
783
+ //#endregion
784
+ //#region src/run.ts
785
+ function normalizeAppAsLeafCommand(app) {
786
+ const handler = app.handler;
787
+ if (handler === void 0) throw new Error("Command is not executable.");
788
+ return {
789
+ name: app.name,
790
+ ...app.alias !== void 0 ? { alias: app.alias } : {},
791
+ ...app.description !== void 0 ? { description: app.description } : {},
792
+ ...app.longDescription !== void 0 ? { longDescription: app.longDescription } : {},
793
+ ...app.options !== void 0 ? { options: [...app.options] } : {},
794
+ ...app.positonals !== void 0 ? { positonals: [...app.positonals] } : {},
795
+ execute: handler
796
+ };
797
+ }
798
+ function normalizeLeafCommand(cmd) {
799
+ const handler = getExecuteHandler(cmd);
800
+ if (handler === void 0) throw new Error("Command is not executable.");
801
+ return {
802
+ name: cmd.name,
803
+ ...cmd.alias !== void 0 ? { alias: cmd.alias } : {},
804
+ ...cmd.description !== void 0 ? { description: cmd.description } : {},
805
+ ...cmd.longDescription !== void 0 ? { longDescription: cmd.longDescription } : {},
806
+ ...cmd.options !== void 0 ? { options: [...cmd.options] } : {},
807
+ ...cmd.positonals !== void 0 ? { positonals: [...cmd.positonals] } : {},
808
+ execute: handler
809
+ };
810
+ }
811
+ function formatConfigError(error) {
812
+ return `Config Error: ${error instanceof Error ? error.message : String(error)}`;
813
+ }
814
+ function mergeCommandOptions(app, cmd) {
815
+ const normalized = normalizeLeafCommand(cmd);
816
+ return {
817
+ ...normalized,
818
+ options: [...getGlobalOptions(app), ...normalized.options ?? []]
819
+ };
820
+ }
821
+ function isVersionFlag(arg) {
822
+ return arg === "--version";
823
+ }
824
+ function getHelpValidationCommand(cmd) {
825
+ return {
826
+ ...cmd,
827
+ options: cmd.options?.map((opt) => ({
828
+ ...opt,
829
+ required: false
830
+ })),
831
+ positonals: cmd.positonals?.map((arg) => ({
832
+ ...arg,
833
+ required: false
834
+ }))
835
+ };
836
+ }
837
+ function getRootCommands(app) {
838
+ return [
839
+ ...app.subCommands !== void 0 ? sortBy(app.subCommands, (c) => c.name) : [],
840
+ versionCommand,
841
+ helpCommand
842
+ ];
843
+ }
844
+ function createExecutionContext(app, opts, path = []) {
845
+ return new ExecutionContext(app, opts?.color ?? "auto", path);
846
+ }
847
+ function createGlobalParseCommand(app) {
848
+ return {
849
+ name: app.name,
850
+ options: getGlobalOptions(app),
851
+ positonals: [{
852
+ name: "argv",
853
+ key: "argv",
854
+ repeatable: true
855
+ }],
856
+ execute() {}
857
+ };
858
+ }
859
+ function parseGlobalOptions(app, argv) {
860
+ try {
861
+ const [, opts] = parseArgs(getHelpValidationCommand(createGlobalParseCommand(app)), argv);
862
+ return { opts };
863
+ } catch (err) {
864
+ if (err instanceof UnknownOptionError) return {};
865
+ const error = createError(err instanceof Error ? err.message : String(err), ErrorCategory.InvalidArg);
866
+ return { result: {
867
+ code: getErrorExitCode(error),
868
+ error
869
+ } };
870
+ }
871
+ }
872
+ function getOptionTokenConsumption(argv, index, rawOptions) {
873
+ const token = argv[index];
874
+ if (token === "--") return argv.length - index;
875
+ if (!token.startsWith("-") || token === "-") return 0;
876
+ const options = rawOptions;
877
+ const matchLong = (name) => options.find((opt) => opt.name === name || opt.noPrefix && `no-${opt.name}` === name || opt.alias !== void 0 && (Array.isArray(opt.alias) ? opt.alias.includes(name) : opt.alias === name));
878
+ if (token.startsWith("--")) {
879
+ const [left] = token.split("=", 1);
880
+ const option = matchLong(left.slice(2));
881
+ if (option === void 0) return 0;
882
+ if (left !== token || option.valueNotRequired) return 1;
883
+ return index < argv.length - 1 ? 2 : 1;
884
+ }
885
+ const clusterText = token.slice(1);
886
+ const equalIndex = clusterText.indexOf("=");
887
+ const cluster = equalIndex === -1 ? clusterText : clusterText.slice(0, equalIndex);
888
+ for (let i = 0; i < cluster.length; ++i) {
889
+ const option = options.find((opt) => opt.name === cluster[i] || (Array.isArray(opt.alias) ? opt.alias.includes(cluster[i]) : opt.alias === cluster[i]));
890
+ if (option === void 0) return 0;
891
+ if (!option.valueNotRequired) {
892
+ if (i < cluster.length - 1 || equalIndex !== -1) return 1;
893
+ return index < argv.length - 1 ? 2 : 1;
894
+ }
895
+ }
896
+ return 1;
897
+ }
898
+ function resolveCommandWithGlobalOptions(argv, subCommands, globalOptions) {
899
+ const path = [];
900
+ const commandIndexes = /* @__PURE__ */ new Set();
901
+ let command;
902
+ let current = subCommands;
903
+ let index = 0;
904
+ while (index < argv.length) {
905
+ const token = argv[index];
906
+ const consumed = getOptionTokenConsumption(argv, index, globalOptions);
907
+ if (consumed > 0) {
908
+ index += consumed;
909
+ continue;
910
+ }
911
+ const candidate = findSubCommand(token, current);
912
+ if (candidate === void 0) break;
913
+ command = candidate;
914
+ commandIndexes.add(index);
915
+ path.push(candidate.name);
916
+ index += 1;
917
+ if (!hasSubCommands(candidate)) break;
918
+ current = candidate.subCommands;
919
+ }
920
+ return {
921
+ command,
922
+ path,
923
+ remainingArgv: argv.filter((_, argvIndex) => !commandIndexes.has(argvIndex))
924
+ };
925
+ }
926
+ function getFirstNonGlobalToken(argv, globalOptions) {
927
+ for (let index = 0; index < argv.length;) {
928
+ const consumed = getOptionTokenConsumption(argv, index, globalOptions);
929
+ if (consumed > 0) {
930
+ index += consumed;
931
+ continue;
932
+ }
933
+ return argv[index];
934
+ }
935
+ }
936
+ function unknownCommandResult(app, commandName) {
937
+ const error = createError(`${getProcName(app)}: unknown command '${commandName}'`, ErrorCategory.InvalidArg);
938
+ return {
939
+ code: getErrorExitCode(error),
940
+ error
941
+ };
942
+ }
943
+ async function executeLeaf(app, cmd, rawArgs, path) {
944
+ try {
945
+ validateCommandConfig(mergeCommandOptions(app, cmd));
946
+ } catch (err) {
947
+ const error = createError(formatConfigError(err), ErrorCategory.Misconfig);
948
+ return {
949
+ result: {
950
+ code: getErrorExitCode(error),
951
+ error
952
+ },
953
+ context: createExecutionContext(app, void 0, path)
954
+ };
955
+ }
956
+ let args;
957
+ let opts;
958
+ try {
959
+ const parseableCommand = mergeCommandOptions(app, cmd);
960
+ const [, provisionalOpts] = parseArgs(getHelpValidationCommand(parseableCommand), rawArgs);
961
+ const provisionalContext = createExecutionContext(app, provisionalOpts, path);
962
+ if (provisionalOpts.help) {
963
+ if (cmd === app && !hasSubCommands(app)) {
964
+ printHelp(provisionalContext, getRootCommands(app));
965
+ return {
966
+ result: { code: 0 },
967
+ context: provisionalContext
968
+ };
969
+ }
970
+ printCommandHelp(provisionalContext, cmd, path);
971
+ return {
972
+ result: { code: 0 },
973
+ context: provisionalContext
974
+ };
975
+ }
976
+ [args, opts] = parseArgs(parseableCommand, rawArgs);
977
+ } catch (err) {
978
+ if (err instanceof UnknownOptionError) {
979
+ const error$1 = createError(`${getProcName(app)}: ${err.message}`, ErrorCategory.InvalidArg);
980
+ return {
981
+ result: {
982
+ code: getErrorExitCode(error$1),
983
+ error: error$1
984
+ },
985
+ context: createExecutionContext(app, void 0, path)
986
+ };
987
+ }
988
+ const error = createError(err instanceof Error ? err.message : String(err), ErrorCategory.InvalidArg);
989
+ return {
990
+ result: {
991
+ code: getErrorExitCode(error),
992
+ error
993
+ },
994
+ context: createExecutionContext(app, void 0, path)
995
+ };
996
+ }
997
+ const handler = getExecuteHandler(cmd);
998
+ if (handler === void 0) {
999
+ const error = createError("Command is not executable.", ErrorCategory.Internal);
1000
+ return {
1001
+ result: {
1002
+ code: getErrorExitCode(error),
1003
+ error
1004
+ },
1005
+ context: createExecutionContext(app, opts, path)
1006
+ };
1007
+ }
1008
+ try {
1009
+ const context = createExecutionContext(app, opts, path);
1010
+ const code = await Promise.resolve(handler(opts, args, context));
1011
+ if (code === void 0) return {
1012
+ result: { code: null },
1013
+ context
1014
+ };
1015
+ return {
1016
+ result: { code },
1017
+ context
1018
+ };
1019
+ } catch (err) {
1020
+ const error = createError(String(err?.stack ?? err), ErrorCategory.Internal);
1021
+ return {
1022
+ result: {
1023
+ code: getErrorExitCode(error),
1024
+ error
1025
+ },
1026
+ context: createExecutionContext(app, opts, path)
1027
+ };
1028
+ }
1029
+ }
1030
+ async function executeAppDetailed(app, argv = process.argv.slice(2)) {
1031
+ const rootCommands = getRootCommands(app);
1032
+ const globalOptions = getGlobalOptions(app);
1033
+ const rootContext = createExecutionContext(app);
1034
+ if (argv.length === 0) {
1035
+ if (isExecutable(app)) return executeLeaf(app, normalizeAppAsLeafCommand(app), [], []);
1036
+ printHelp(rootContext, rootCommands);
1037
+ return {
1038
+ result: { code: 0 },
1039
+ context: rootContext
1040
+ };
1041
+ }
1042
+ if (isExecutable(app) && !hasSubCommands(app)) {
1043
+ if (isVersionFlag(argv[0])) {
1044
+ printLn(app._version);
1045
+ return {
1046
+ result: { code: 0 },
1047
+ context: rootContext
1048
+ };
1049
+ }
1050
+ const globalParse = parseGlobalOptions(app, argv);
1051
+ if (globalParse.result !== void 0) return {
1052
+ result: globalParse.result,
1053
+ context: rootContext
1054
+ };
1055
+ if (globalParse.opts?.help) return executeLeaf(app, normalizeAppAsLeafCommand(app), argv, []);
1056
+ const builtin = findSubCommand(argv[0], rootCommands);
1057
+ if (builtin && isExecutable(builtin)) return executeLeaf(app, builtin, argv.slice(1), [builtin.name]);
1058
+ return executeLeaf(app, normalizeAppAsLeafCommand(app), argv, []);
1059
+ }
1060
+ const resolved = resolveCommandWithGlobalOptions(argv, rootCommands, globalOptions);
1061
+ if (!resolved.command) {
1062
+ const globalParse = parseGlobalOptions(app, argv);
1063
+ if (globalParse.result !== void 0) return {
1064
+ result: globalParse.result,
1065
+ context: rootContext
1066
+ };
1067
+ const globalOpts = globalParse.opts ?? {};
1068
+ const context = createExecutionContext(app, globalOpts);
1069
+ const firstNonGlobalToken = getFirstNonGlobalToken(argv, globalOptions);
1070
+ if (firstNonGlobalToken === void 0) {
1071
+ if (globalOpts.help || !hasSubCommands(app)) {
1072
+ if (isExecutable(app)) return executeLeaf(app, normalizeAppAsLeafCommand(app), argv, []);
1073
+ printHelp(context, rootCommands);
1074
+ return {
1075
+ result: { code: 0 },
1076
+ context
1077
+ };
1078
+ }
1079
+ printHelp(context, rootCommands);
1080
+ return {
1081
+ result: { code: 0 },
1082
+ context
1083
+ };
1084
+ }
1085
+ return {
1086
+ result: unknownCommandResult(app, firstNonGlobalToken),
1087
+ context
1088
+ };
1089
+ }
1090
+ if (hasSubCommands(resolved.command)) {
1091
+ const globalParse = parseGlobalOptions(app, resolved.remainingArgv);
1092
+ if (globalParse.result !== void 0) return {
1093
+ result: globalParse.result,
1094
+ context: rootContext
1095
+ };
1096
+ const branchOpts = globalParse.opts ?? {};
1097
+ const context = createExecutionContext(app, branchOpts, resolved.path);
1098
+ const firstNonGlobalToken = getFirstNonGlobalToken(resolved.remainingArgv, globalOptions);
1099
+ if (!resolved.remainingArgv.length || branchOpts.help && firstNonGlobalToken === void 0) {
1100
+ printCommandHelp(context, resolved.command, resolved.path);
1101
+ return {
1102
+ result: { code: 0 },
1103
+ context
1104
+ };
1105
+ }
1106
+ return {
1107
+ result: unknownCommandResult(app, firstNonGlobalToken ?? resolved.remainingArgv[0]),
1108
+ context
1109
+ };
1110
+ }
1111
+ if (!isExecutable(resolved.command)) {
1112
+ const error = createError("Command is not executable.", ErrorCategory.Internal);
1113
+ return {
1114
+ result: {
1115
+ code: getErrorExitCode(error),
1116
+ error
1117
+ },
1118
+ context: createExecutionContext(app, void 0, resolved.path)
1119
+ };
1120
+ }
1121
+ return executeLeaf(app, resolved.command, resolved.remainingArgv, resolved.path);
1122
+ }
1123
+ /**
1124
+ * Parses CLI arguments, prints any resulting error, and executes the matching command.
1125
+ *
1126
+ * @param app The app to execute.
1127
+ * @param argv The raw CLI arguments to parse. Defaults to `process.argv.slice(2)`.
1128
+ * @returns The process exit code for the CLI invocation.
1129
+ */
1130
+ async function executeApp(app, argv = process.argv.slice(2)) {
1131
+ const { result, context } = await executeAppDetailed(app, argv);
1132
+ if (result.error !== void 0) printError(result.error, context.chalk);
1133
+ return result.code ?? 0;
1134
+ }
1135
+
1136
+ //#endregion
1137
+ export { executeApp };