var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // node_modules/.pnpm/clone@2.1.2/node_modules/clone/clone.js var require_clone = __commonJS({ "node_modules/.pnpm/clone@2.1.2/node_modules/clone/clone.js"(exports, module) { "use strict"; var clone2 = function() { "use strict"; function _instanceof(obj, type) { return type != null && obj instanceof type; } var nativeMap; try { nativeMap = Map; } catch (_) { nativeMap = function() { }; } var nativeSet; try { nativeSet = Set; } catch (_) { nativeSet = function() { }; } var nativePromise; try { nativePromise = Promise; } catch (_) { nativePromise = function() { }; } function clone3(parent, circular, depth, prototype, includeNonEnumerable) { if (typeof circular === "object") { depth = circular.depth; prototype = circular.prototype; includeNonEnumerable = circular.includeNonEnumerable; circular = circular.circular; } var allParents = []; var allChildren = []; var useBuffer = typeof Buffer != "undefined"; if (typeof circular == "undefined") circular = true; if (typeof depth == "undefined") depth = Infinity; function _clone(parent2, depth2) { if (parent2 === null) return null; if (depth2 === 0) return parent2; var child; var proto; if (typeof parent2 != "object") { return parent2; } if (_instanceof(parent2, nativeMap)) { child = new nativeMap(); } else if (_instanceof(parent2, nativeSet)) { child = new nativeSet(); } else if (_instanceof(parent2, nativePromise)) { child = new nativePromise(function(resolve, reject) { parent2.then(function(value) { resolve(_clone(value, depth2 - 1)); }, function(err) { reject(_clone(err, depth2 - 1)); }); }); } else if (clone3.__isArray(parent2)) { child = []; } else if (clone3.__isRegExp(parent2)) { child = new RegExp(parent2.source, __getRegExpFlags(parent2)); if (parent2.lastIndex) child.lastIndex = parent2.lastIndex; } else if (clone3.__isDate(parent2)) { child = new Date(parent2.getTime()); } else if (useBuffer && Buffer.isBuffer(parent2)) { if (Buffer.allocUnsafe) { child = Buffer.allocUnsafe(parent2.length); } else { child = new Buffer(parent2.length); } parent2.copy(child); return child; } else if (_instanceof(parent2, Error)) { child = Object.create(parent2); } else { if (typeof prototype == "undefined") { proto = Object.getPrototypeOf(parent2); child = Object.create(proto); } else { child = Object.create(prototype); proto = prototype; } } if (circular) { var index = allParents.indexOf(parent2); if (index != -1) { return allChildren[index]; } allParents.push(parent2); allChildren.push(child); } if (_instanceof(parent2, nativeMap)) { parent2.forEach(function(value, key) { var keyChild = _clone(key, depth2 - 1); var valueChild = _clone(value, depth2 - 1); child.set(keyChild, valueChild); }); } if (_instanceof(parent2, nativeSet)) { parent2.forEach(function(value) { var entryChild = _clone(value, depth2 - 1); child.add(entryChild); }); } for (var i in parent2) { var attrs; if (proto) { attrs = Object.getOwnPropertyDescriptor(proto, i); } if (attrs && attrs.set == null) { continue; } child[i] = _clone(parent2[i], depth2 - 1); } if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(parent2); for (var i = 0; i < symbols.length; i++) { var symbol = symbols[i]; var descriptor = Object.getOwnPropertyDescriptor(parent2, symbol); if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { continue; } child[symbol] = _clone(parent2[symbol], depth2 - 1); if (!descriptor.enumerable) { Object.defineProperty(child, symbol, { enumerable: false }); } } } if (includeNonEnumerable) { var allPropertyNames = Object.getOwnPropertyNames(parent2); for (var i = 0; i < allPropertyNames.length; i++) { var propertyName = allPropertyNames[i]; var descriptor = Object.getOwnPropertyDescriptor(parent2, propertyName); if (descriptor && descriptor.enumerable) { continue; } child[propertyName] = _clone(parent2[propertyName], depth2 - 1); Object.defineProperty(child, propertyName, { enumerable: false }); } } return child; } return _clone(parent, depth); } clone3.clonePrototype = function clonePrototype(parent) { if (parent === null) return null; var c = function() { }; c.prototype = parent; return new c(); }; function __objToStr(o) { return Object.prototype.toString.call(o); } clone3.__objToStr = __objToStr; function __isDate(o) { return typeof o === "object" && __objToStr(o) === "[object Date]"; } clone3.__isDate = __isDate; function __isArray(o) { return typeof o === "object" && __objToStr(o) === "[object Array]"; } clone3.__isArray = __isArray; function __isRegExp(o) { return typeof o === "object" && __objToStr(o) === "[object RegExp]"; } clone3.__isRegExp = __isRegExp; function __getRegExpFlags(re) { var flags = ""; if (re.global) flags += "g"; if (re.ignoreCase) flags += "i"; if (re.multiline) flags += "m"; return flags; } clone3.__getRegExpFlags = __getRegExpFlags; return clone3; }(); if (typeof module === "object" && module.exports) { module.exports = clone2; } } }); // node_modules/.pnpm/shell-quote@1.8.1/node_modules/shell-quote/quote.js var require_quote = __commonJS({ "node_modules/.pnpm/shell-quote@1.8.1/node_modules/shell-quote/quote.js"(exports, module) { "use strict"; module.exports = function quote(xs) { return xs.map(function(s) { if (s && typeof s === "object") { return s.op.replace(/(.)/g, "\\$1"); } if (/["\s]/.test(s) && !/'/.test(s)) { return "'" + s.replace(/(['\\])/g, "\\$1") + "'"; } if (/["'\s]/.test(s)) { return '"' + s.replace(/(["\\$`!])/g, "\\$1") + '"'; } return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, "$1\\$2"); }).join(" "); }; } }); // node_modules/.pnpm/shell-quote@1.8.1/node_modules/shell-quote/parse.js var require_parse = __commonJS({ "node_modules/.pnpm/shell-quote@1.8.1/node_modules/shell-quote/parse.js"(exports, module) { "use strict"; var CONTROL = "(?:" + [ "\\|\\|", "\\&\\&", ";;", "\\|\\&", "\\<\\(", "\\<\\<\\<", ">>", ">\\&", "<\\&", "[&;()|<>]" ].join("|") + ")"; var controlRE = new RegExp("^" + CONTROL + "$"); var META = "|&;()<> \\t"; var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"'; var DOUBLE_QUOTE = "'((\\\\'|[^'])*?)'"; var hash = /^#$/; var SQ = "'"; var DQ = '"'; var DS = "$"; var TOKEN = ""; var mult = 4294967296; for (i = 0; i < 4; i++) { TOKEN += (mult * Math.random()).toString(16); } var i; var startsWithToken = new RegExp("^" + TOKEN); function matchAll(s, r) { var origIndex = r.lastIndex; var matches = []; var matchObj; while (matchObj = r.exec(s)) { matches.push(matchObj); if (r.lastIndex === matchObj.index) { r.lastIndex += 1; } } r.lastIndex = origIndex; return matches; } function getVar(env, pre, key) { var r = typeof env === "function" ? env(key) : env[key]; if (typeof r === "undefined" && key != "") { r = ""; } else if (typeof r === "undefined") { r = "$"; } if (typeof r === "object") { return pre + TOKEN + JSON.stringify(r) + TOKEN; } return pre + r; } function parseInternal(string2, env, opts) { if (!opts) { opts = {}; } var BS = opts.escape || "\\"; var BAREWORD = "(\\" + BS + `['"` + META + `]|[^\\s'"` + META + "])+"; var chunker = new RegExp([ "(" + CONTROL + ")", // control chars "(" + BAREWORD + "|" + SINGLE_QUOTE + "|" + DOUBLE_QUOTE + ")+" ].join("|"), "g"); var matches = matchAll(string2, chunker); if (matches.length === 0) { return []; } if (!env) { env = {}; } var commented = false; return matches.map(function(match) { var s = match[0]; if (!s || commented) { return void 0; } if (controlRE.test(s)) { return { op: s }; } var quote = false; var esc = false; var out = ""; var isGlob = false; var i2; function parseEnvVar() { i2 += 1; var varend; var varname; var char = s.charAt(i2); if (char === "{") { i2 += 1; if (s.charAt(i2) === "}") { throw new Error("Bad substitution: " + s.slice(i2 - 2, i2 + 1)); } varend = s.indexOf("}", i2); if (varend < 0) { throw new Error("Bad substitution: " + s.slice(i2)); } varname = s.slice(i2, varend); i2 = varend; } else if (/[*@#?$!_-]/.test(char)) { varname = char; i2 += 1; } else { var slicedFromI = s.slice(i2); varend = slicedFromI.match(/[^\w\d_]/); if (!varend) { varname = slicedFromI; i2 = s.length; } else { varname = slicedFromI.slice(0, varend.index); i2 += varend.index - 1; } } return getVar(env, "", varname); } for (i2 = 0; i2 < s.length; i2++) { var c = s.charAt(i2); isGlob = isGlob || !quote && (c === "*" || c === "?"); if (esc) { out += c; esc = false; } else if (quote) { if (c === quote) { quote = false; } else if (quote == SQ) { out += c; } else { if (c === BS) { i2 += 1; c = s.charAt(i2); if (c === DQ || c === BS || c === DS) { out += c; } else { out += BS + c; } } else if (c === DS) { out += parseEnvVar(); } else { out += c; } } } else if (c === DQ || c === SQ) { quote = c; } else if (controlRE.test(c)) { return { op: s }; } else if (hash.test(c)) { commented = true; var commentObj = { comment: string2.slice(match.index + i2 + 1) }; if (out.length) { return [out, commentObj]; } return [commentObj]; } else if (c === BS) { esc = true; } else if (c === DS) { out += parseEnvVar(); } else { out += c; } } if (isGlob) { return { op: "glob", pattern: out }; } return out; }).reduce(function(prev, arg) { return typeof arg === "undefined" ? prev : prev.concat(arg); }, []); } module.exports = function parse(s, env, opts) { var mapped = parseInternal(s, env, opts); if (typeof env !== "function") { return mapped; } return mapped.reduce(function(acc, s2) { if (typeof s2 === "object") { return acc.concat(s2); } var xs = s2.split(RegExp("(" + TOKEN + ".*?" + TOKEN + ")", "g")); if (xs.length === 1) { return acc.concat(xs[0]); } return acc.concat(xs.filter(Boolean).map(function(x) { if (startsWithToken.test(x)) { return JSON.parse(x.split(TOKEN)[1]); } return x; })); }, []); }; } }); // node_modules/.pnpm/shell-quote@1.8.1/node_modules/shell-quote/index.js var require_shell_quote = __commonJS({ "node_modules/.pnpm/shell-quote@1.8.1/node_modules/shell-quote/index.js"(exports) { "use strict"; exports.quote = require_quote(); exports.parse = require_parse(); } }); // src/brocli-error.ts var BroCliError = class extends Error { constructor(message, event) { const errPrefix = "BroCli error: "; super(message === void 0 ? message : `${errPrefix}${message}`); this.event = event; } }; // src/command-core.ts var import_clone = __toESM(require_clone(), 1); // src/event-handler.ts var getOptionTypeText = (option) => { let result = ""; switch (option.type) { case "boolean": result = ""; break; case "number": { if ((option.minVal ?? option.maxVal) !== void 0) { let text = ""; if (option.isInt) text = text + `integer `; if (option.minVal !== void 0) text = text + `[${option.minVal};`; else text = text + `(\u221E;`; if (option.maxVal !== void 0) text = text + `${option.maxVal}]`; else text = text + `\u221E)`; result = text; break; } if (option.isInt) { result = "integer"; break; } result = "number"; break; } case "string": { if (option.enumVals) { result = "[ " + option.enumVals.join(" | ") + " ]"; break; } result = "string"; break; } case "positional": { result = `${option.isRequired ? "<" : "["}${option.enumVals ? option.enumVals.join("|") : option.name}${option.isRequired ? ">" : "]"}`; break; } } if (option.isRequired && option.type !== "positional") result = "!" + result.length ? " " : "" + result; return result; }; var defaultEventHandler = async (event) => { switch (event.type) { case "command_help": { const command3 = event.command; const commandName = getCommandNameWithParents(command3); const cliName = event.name; const desc = command3.desc ?? command3.shortDesc; const subs = command3.subcommands?.filter((s) => !s.hidden); const subcommands = subs && subs.length ? subs : void 0; if (desc !== void 0) { console.log(` ${desc}`); } const opts = Object.values(command3.options ?? {}).filter( (opt) => !opt.config.isHidden ); const positionals = opts.filter((opt) => opt.config.type === "positional"); const options = opts.filter((opt) => opt.config.type !== "positional"); console.log("\nUsage:"); if (command3.handler) { console.log( ` ${cliName ? cliName + " " : ""}${commandName}${positionals.length ? " " + positionals.map(({ config: p }) => getOptionTypeText(p)).join(" ") : ""} [flags]` ); } else console.log(` ${cliName ? cliName + " " : ""}${commandName} [command]`); if (command3.aliases) { console.log(` Aliases:`); console.log(` ${[command3.name, ...command3.aliases].join(", ")}`); } if (subcommands) { console.log("\nAvailable Commands:"); const padding = 3; const maxLength = subcommands.reduce((p, e) => e.name.length > p ? e.name.length : p, 0); const paddedLength = maxLength + padding; const preDescPad = 2 + paddedLength; const data = subcommands.map( (s) => ` ${s.name.padEnd(paddedLength)}${(() => { const description = s.shortDesc ?? s.desc; if (!description?.length) return ""; const split = description.split("\n"); const first = split.shift(); const final = [first, ...split.map((s2) => "".padEnd(preDescPad) + s2)].join("\n"); return final; })()}` ).join("\n"); console.log(data); } if (options.length) { const aliasLength = options.reduce((p, e) => { const currentLength = e.config.aliases.reduce((pa, a) => pa + a.length, 0) + (e.config.aliases.length - 1) * 2 + 1; return currentLength > p ? currentLength : p; }, 0); const paddedAliasLength = aliasLength > 0 ? aliasLength + 1 : 0; const nameLength = options.reduce((p, e) => { const typeLen = getOptionTypeText(e.config).length; const length = typeLen > 0 ? e.config.name.length + 1 + typeLen : e.config.name.length; return length > p ? length : p; }, 0) + 3; const preDescPad = paddedAliasLength + nameLength + 2; const data = options.map( ({ config: opt }) => ` ${`${opt.aliases.length ? opt.aliases.join(", ") + "," : ""}`.padEnd(paddedAliasLength)}${`${opt.name}${(() => { const typeText = getOptionTypeText(opt); return typeText.length ? " " + typeText : ""; })()}`.padEnd(nameLength)}${(() => { if (!opt.description?.length) { return opt.default !== void 0 ? `default: ${JSON.stringify(opt.default)}` : ""; } const split = opt.description.split("\n"); const first = split.shift(); const def = opt.default !== void 0 ? ` (default: ${JSON.stringify(opt.default)})` : ""; const final = [first, ...split.map((s) => "".padEnd(preDescPad) + s)].join("\n") + def; return final; })()}` ).join("\n"); console.log("\nFlags:"); console.log(data); } console.log("\nGlobal flags:"); console.log(` -h, --help help for ${commandName}`); console.log(` -v, --version version${cliName ? ` for ${cliName}` : ""}`); if (subcommands) { console.log( ` Use "${cliName ? cliName + " " : ""}${commandName} [command] --help" for more information about a command. ` ); } return true; } case "global_help": { const cliName = event.name; const desc = event.description; const commands = event.commands.filter((c) => !c.hidden); if (desc !== void 0) { console.log(`${desc} `); } console.log("Usage:"); console.log(` ${cliName ? cliName + " " : ""}[command]`); if (commands.length) { console.log("\nAvailable Commands:"); const padding = 3; const maxLength = commands.reduce((p, e) => e.name.length > p ? e.name.length : p, 0); const paddedLength = maxLength + padding; const data = commands.map( (c) => ` ${c.name.padEnd(paddedLength)}${(() => { const desc2 = c.shortDesc ?? c.desc; if (!desc2?.length) return ""; const split = desc2.split("\n"); const first = split.shift(); const final = [first, ...split.map((s) => "".padEnd(paddedLength + 2) + s)].join("\n"); return final; })()}` ).join("\n"); console.log(data); } else { console.log("\nNo available commands."); } console.log("\nFlags:"); console.log(` -h, --help help${cliName ? ` for ${cliName}` : ""}`); console.log(` -v, --version version${cliName ? ` for ${cliName}` : ""}`); console.log("\n"); return true; } case "version": { return true; } case "error": { let msg; switch (event.violation) { case "above_max": { const matchedName = event.offender.namePart; const data = event.offender.dataPart; const option = event.option; const max = option.maxVal; msg = `Invalid value: number type argument '${matchedName}' expects maximal value of ${max} as an input, got: ${data}`; break; } case "below_min": { const matchedName = event.offender.namePart; const data = event.offender.dataPart; const option = event.option; const min = option.minVal; msg = `Invalid value: number type argument '${matchedName}' expects minimal value of ${min} as an input, got: ${data}`; break; } case "expected_int": { const matchedName = event.offender.namePart; const data = event.offender.dataPart; msg = `Invalid value: number type argument '${matchedName}' expects an integer as an input, got: ${data}`; break; } case "invalid_boolean_syntax": { const matchedName = event.offender.namePart; const data = event.offender.dataPart; msg = `Invalid syntax: boolean type argument '${matchedName}' must have it's value passed in the following formats: ${matchedName}= | ${matchedName} | ${matchedName}. Allowed values: true, false, 0, 1`; break; } case "invalid_string_syntax": { const matchedName = event.offender.namePart; msg = `Invalid syntax: string type argument '${matchedName}' must have it's value passed in the following formats: ${matchedName}= | ${matchedName} `; break; } case "invalid_number_syntax": { const matchedName = event.offender.namePart; msg = `Invalid syntax: number type argument '${matchedName}' must have it's value passed in the following formats: ${matchedName}= | ${matchedName} `; break; } case "invalid_number_value": { const matchedName = event.offender.namePart; const data = event.offender.dataPart; msg = `Invalid value: number type argument '${matchedName}' expects a number as an input, got: ${data}`; break; } case "enum_violation": { const matchedName = event.offender.namePart; const data = event.offender.dataPart; const option = event.option; const values = option.enumVals; msg = option.type === "positional" ? `Invalid value: value for the positional argument '${option.name}' must be either one of the following: ${values.join(", ")}; Received: ${data}` : `Invalid value: value for the argument '${matchedName}' must be either one of the following: ${values.join(", ")}; Received: ${data}`; break; } case "unknown_command_error": { const msg2 = `Unknown command: '${event.offender}'. Type '--help' to get help on the cli.`; console.error(msg2); return true; } case "unknown_subcommand_error": { const cName = getCommandNameWithParents(event.command); const msg2 = `Unknown command: ${cName} ${event.offender}. Type '${cName} --help' to get the help on command.`; console.error(msg2); return true; } case "missing_args_error": { const { missing: missingOpts, command: command3 } = event; msg = `Command '${command3.name}' is missing following required options: ${missingOpts.map((opt) => { const name = opt.shift(); const aliases = opt; if (aliases.length) return `${name} [${aliases.join(", ")}]`; return name; }).join(", ")}`; break; } case "unrecognized_args_error": { const { command: command3, unrecognized } = event; msg = `Unrecognized options for command '${command3.name}': ${unrecognized.join(", ")}`; break; } case "unknown_error": { const e = event.error; console.error(typeof e === "object" && e !== null && "message" in e ? e.message : e); return true; } } console.error(msg); return true; } } return false; }; var eventHandlerWrapper = (customEventHandler) => async (event) => await customEventHandler(event) ? true : await defaultEventHandler(event); // src/util.ts var import_shell_quote = __toESM(require_shell_quote(), 1); function isInt(value) { return value === Math.floor(value); } var shellArgs = (str) => (0, import_shell_quote.parse)(str).map((e) => e.toString()); var executeOrLog = async (target) => typeof target === "string" ? console.log(target) : target ? await target() : void 0; // src/command-core.ts var generatePrefix = (name) => name.startsWith("-") ? name : name.length > 1 ? `--${name}` : `-${name}`; var validateOptions = (config) => { const cloned = (0, import_clone.default)(config); const entries = []; const storedNames = []; const cfgEntries = Object.entries(cloned); for (const [key, value] of cfgEntries) { const cfg = value._.config; if (cfg.name === void 0) cfg.name = key; if (cfg.type === "positional") continue; if (cfg.name.includes("=")) { throw new BroCliError( `Can't define option '${generatePrefix(cfg.name)}' - option names and aliases cannot contain '='!` ); } for (const alias of cfg.aliases) { if (alias.includes("=")) { throw new BroCliError( `Can't define option '${generatePrefix(cfg.name)}' - option names and aliases cannot contain '='!` ); } } cfg.name = generatePrefix(cfg.name); cfg.aliases = cfg.aliases.map((a) => generatePrefix(a)); } for (const [key, value] of cfgEntries) { const cfg = value._.config; if (cfg.type === "positional") { entries.push([key, { config: cfg, $output: void 0 }]); continue; } const reservedNames = ["--help", "-h", "--version", "-v"]; const allNames = [cfg.name, ...cfg.aliases]; for (const name of allNames) { const match = reservedNames.find((n) => n === name); if (match) throw new BroCliError(`Can't define option '${cfg.name}' - name '${match}' is reserved!`); } for (const storage of storedNames) { const nameOccupier = storage.find((e) => e === cfg.name); if (!nameOccupier) continue; throw new BroCliError( `Can't define option '${cfg.name}' - name is already in use by option '${storage[0]}'!` ); } for (const alias of cfg.aliases) { for (const storage of storedNames) { const nameOccupier = storage.find((e) => e === alias); if (!nameOccupier) continue; throw new BroCliError( `Can't define option '${cfg.name}' - alias '${alias}' is already in use by option '${storage[0]}'!` ); } } const currentNames = [cfg.name, ...cfg.aliases]; storedNames.push(currentNames); currentNames.forEach((name, idx) => { if (currentNames.findIndex((e) => e === name) === idx) return; throw new BroCliError( `Can't define option '${cfg.name}' - duplicate alias '${name}'!` ); }); entries.push([key, { config: cfg, $output: void 0 }]); } return Object.fromEntries(entries); }; var assignParent = (parent, subcommands) => subcommands.forEach((e) => { e.parent = parent; if (e.subcommands) assignParent(e, e.subcommands); }); var command2 = (command3) => { const allNames = command3.aliases ? [command3.name, ...command3.aliases] : [command3.name]; const cmd = (0, import_clone.default)(command3); if (command3.subcommands && command3.options && Object.values(command3.options).find((opt) => opt._.config.type === "positional")) { throw new BroCliError( `Can't define command '${cmd.name}' - command can't have subcommands and positional args at the same time!` ); } if (!command3.handler && !command3.subcommands) { throw new BroCliError( `Can't define command '${cmd.name}' - command without subcommands must have a handler present!` ); } const processedOptions = command3.options ? validateOptions(command3.options) : void 0; cmd.options = processedOptions; cmd.name = cmd.name ?? cmd.aliases?.shift(); if (!cmd.name) throw new BroCliError(`Can't define command without name!`); cmd.aliases = cmd.aliases?.length ? cmd.aliases : void 0; if (cmd.name.startsWith("-")) { throw new BroCliError(`Can't define command '${cmd.name}' - command name can't start with '-'!`); } cmd.aliases?.forEach((a) => { if (a.startsWith("-")) { throw new BroCliError(`Can't define command '${cmd.name}' - command aliases can't start with '-'!`); } }); allNames.forEach((n, i) => { if (n === "help") { throw new BroCliError( `Can't define command '${cmd.name}' - 'help' is a reserved name. If you want to redefine help message - do so in runCli's config.` ); } const lCaseName = n?.toLowerCase(); if (lCaseName === "0" || lCaseName === "1" || lCaseName === "true" || lCaseName === "false") { throw new BroCliError( `Can't define command '${cmd.name}' - '${n}' is a reserved for boolean values name!` ); } const idx = allNames.findIndex((an) => an === n); if (idx !== i) throw new BroCliError(`Can't define command '${cmd.name}' - duplicate alias '${n}'!`); }); if (cmd.subcommands) { assignParent(cmd, cmd.subcommands); } return cmd; }; var getCommandInner = (commands, candidates, args, cliName, cliDescription) => { const { data: arg, originalIndex: index } = candidates.shift(); const command3 = commands.find((c) => { const names = c.aliases ? [c.name, ...c.aliases] : [c.name]; const res = names.find((name) => name === arg); return res; }); if (!command3) { return { command: command3, args }; } const newArgs = removeByIndex(args, index); if (!candidates.length || !command3.subcommands) { return { command: command3, args: newArgs }; } const newCandidates = candidates.map((c) => ({ data: c.data, originalIndex: c.originalIndex - 1 })); const subcommand = getCommandInner(command3.subcommands, newCandidates, newArgs, cliName, cliDescription); if (!subcommand.command) { throw new BroCliError(void 0, { type: "error", violation: "unknown_subcommand_error", name: cliName, description: cliDescription, command: command3, offender: candidates[0].data }); } return subcommand; }; var getCommand = (commands, args, cliName, cliDescription) => { const candidates = []; for (let i = 0; i < args.length; ++i) { const arg = args[i]; if (arg === "--help" || arg === "-h" || arg === "--version" || arg === "-v") { const lCaseNext = args[i + 1]?.toLowerCase(); if (lCaseNext === "0" || lCaseNext === "1" || lCaseNext === "true" || lCaseNext === "false") ++i; continue; } if (arg?.startsWith("-")) { if (!arg.includes("=")) ++i; continue; } candidates.push({ data: arg, originalIndex: i }); } if (!candidates.length) { return { command: void 0, args }; } const firstCandidate = candidates[0]; if (firstCandidate.data === "help") { return { command: "help", args: removeByIndex(args, firstCandidate.originalIndex) }; } const { command: command3, args: argsRes } = getCommandInner(commands, candidates, args, cliName, cliDescription); if (!command3) { throw new BroCliError(void 0, { type: "error", violation: "unknown_command_error", commands, name: cliName, description: cliDescription, offender: firstCandidate.data }); } return { command: command3, args: argsRes }; }; var parseArg = (command3, options, positionals, arg, nextArg, cliName, cliDescription) => { let data = void 0; const argSplit = arg.split("="); const hasEq = arg.includes("="); const namePart = argSplit.shift(); const dataPart = hasEq ? argSplit.join("=") : nextArg; let skipNext = !hasEq; if (namePart === "--help" || namePart === "-h") { return { isHelp: true }; } if (namePart === "--version" || namePart === "-v") { return { isVersion: true }; } if (!arg.startsWith("-")) { if (!positionals.length) return {}; const pos = positionals.shift(); if (pos[1].enumVals && !pos[1].enumVals.find((val) => val === arg)) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "enum_violation", command: command3, option: pos[1], offender: { dataPart: arg } }); } data = arg; return { data, skipNext: false, name: pos[0], option: pos[1] }; } const option = options.find(([optKey, opt]) => { const names = [opt.name, ...opt.aliases]; if (opt.type === "boolean") { const match = names.find((name) => name === namePart); if (!match) return false; let lcaseData = dataPart?.toLowerCase(); if (!hasEq && nextArg?.startsWith("-")) { data = true; skipNext = false; return true; } if (lcaseData === void 0 || lcaseData === "" || lcaseData === "true" || lcaseData === "1") { data = true; return true; } if (lcaseData === "false" || lcaseData === "0") { data = false; return true; } if (!hasEq) { data = true; skipNext = false; return true; } throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "invalid_boolean_syntax", option: opt, command: command3, offender: { namePart, dataPart } }); } else { const match = names.find((name) => name === namePart); if (!match) return false; if (opt.type === "string") { if (!hasEq && nextArg === void 0) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "invalid_string_syntax", option: opt, command: command3, offender: { namePart, dataPart } }); } if (opt.enumVals && !opt.enumVals.find((val) => val === dataPart)) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "enum_violation", option: opt, command: command3, offender: { namePart, dataPart } }); } data = dataPart; return true; } if (!hasEq && nextArg === void 0) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "invalid_number_syntax", option: opt, command: command3, offender: { namePart, dataPart } }); } const numData = Number(dataPart); if (isNaN(numData)) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "invalid_number_value", option: opt, command: command3, offender: { namePart, dataPart } }); } if (opt.isInt && !isInt(numData)) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "expected_int", option: opt, command: command3, offender: { namePart, dataPart } }); } if (opt.minVal !== void 0 && numData < opt.minVal) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "below_min", option: opt, command: command3, offender: { namePart, dataPart } }); } if (opt.maxVal !== void 0 && numData > opt.maxVal) { throw new BroCliError(void 0, { type: "error", name: cliName, description: cliDescription, violation: "above_max", option: opt, command: command3, offender: { namePart, dataPart } }); } data = numData; return true; } }); return { data, skipNext, name: option?.[0], option: option?.[1] }; }; var parseOptions = (command3, args, cliName, cliDescription, omitKeysOfUndefinedOptions) => { const options = command3.options; const optEntries = Object.entries(options ?? {}).map( (opt) => [opt[0], opt[1].config] ); const nonPositionalEntries = optEntries.filter(([key, opt]) => opt.type !== "positional"); const positionalEntries = optEntries.filter(([key, opt]) => opt.type === "positional"); const result = {}; const missingRequiredArr = []; const unrecognizedArgsArr = []; for (let i = 0; i < args.length; ++i) { const arg = args[i]; const nextArg = args[i + 1]; const { data, name, option, skipNext, isHelp, isVersion } = parseArg(command3, nonPositionalEntries, positionalEntries, arg, nextArg, cliName, cliDescription); if (!option) unrecognizedArgsArr.push(arg.split("=")[0]); if (skipNext) ++i; if (isHelp) return "help"; if (isVersion) return "version"; result[name] = data; } for (const [optKey, option] of optEntries) { const data = result[optKey] ?? option.default; if (!omitKeysOfUndefinedOptions) { result[optKey] = data; } else { if (data !== void 0) result[optKey] = data; } if (option.isRequired && result[optKey] === void 0) missingRequiredArr.push([option.name, ...option.aliases]); } if (missingRequiredArr.length) { throw new BroCliError(void 0, { type: "error", violation: "missing_args_error", name: cliName, description: cliDescription, command: command3, missing: missingRequiredArr }); } if (unrecognizedArgsArr.length) { throw new BroCliError(void 0, { type: "error", violation: "unrecognized_args_error", name: cliName, description: cliDescription, command: command3, unrecognized: unrecognizedArgsArr }); } return Object.keys(result).length ? result : void 0; }; var getCommandNameWithParents = (command3) => command3.parent ? `${getCommandNameWithParents(command3.parent)} ${command3.name}` : command3.name; var validateCommands = (commands, parent) => { const storedNames = {}; for (const cmd of commands) { const storageVals = Object.values(storedNames); for (const storage of storageVals) { const nameOccupier = storage.find((e) => e === cmd.name); if (!nameOccupier) continue; throw new BroCliError( `Can't define command '${getCommandNameWithParents(cmd)}': name is already in use by command '${parent ? `${getCommandNameWithParents(parent)} ` : ""}${storage[0]}'!` ); } if (cmd.aliases) { for (const alias of cmd.aliases) { for (const storage of storageVals) { const nameOccupier = storage.find((e) => e === alias); if (!nameOccupier) continue; throw new BroCliError( `Can't define command '${getCommandNameWithParents(cmd)}': alias '${alias}' is already in use by command '${parent ? `${getCommandNameWithParents(parent)} ` : ""}${storage[0]}'!` ); } } } storedNames[cmd.name] = cmd.aliases ? [cmd.name, ...cmd.aliases] : [cmd.name]; if (cmd.subcommands) cmd.subcommands = validateCommands(cmd.subcommands, cmd); } return commands; }; var removeByIndex = (arr, idx) => [...arr.slice(0, idx), ...arr.slice(idx + 1, arr.length)]; var run = async (commands, config) => { const eventHandler = config?.theme ? eventHandlerWrapper(config.theme) : defaultEventHandler; const argSource = config?.argSource ?? process.argv; const version = config?.version; const help = config?.help; const omitKeysOfUndefinedOptions = config?.omitKeysOfUndefinedOptions ?? false; const cliName = config?.name; const cliDescription = config?.description; try { const processedCmds = validateCommands(commands); let args = argSource.slice(2, argSource.length); if (!args.length) { return help !== void 0 ? await executeOrLog(help) : await eventHandler({ type: "global_help", description: cliDescription, name: cliName, commands: processedCmds }); } const helpIndex = args.findIndex((arg) => arg === "--help" || arg === "-h"); if (helpIndex !== -1 && (helpIndex > 0 ? args[helpIndex - 1]?.startsWith("-") && !args[helpIndex - 1].includes("=") ? false : true : true)) { const command4 = getCommand(processedCmds, args, cliName, cliDescription).command; if (typeof command4 === "object") { return command4.help !== void 0 ? await executeOrLog(command4.help) : await eventHandler({ type: "command_help", description: cliDescription, name: cliName, command: command4 }); } else { return help !== void 0 ? await executeOrLog(help) : await eventHandler({ type: "global_help", description: cliDescription, name: cliName, commands: processedCmds }); } } const versionIndex = args.findIndex((arg) => arg === "--version" || arg === "-v"); if (versionIndex !== -1 && (versionIndex > 0 ? args[versionIndex - 1]?.startsWith("-") ? false : true : true)) { return version !== void 0 ? await executeOrLog(version) : await eventHandler({ type: "version", name: cliName, description: cliDescription }); } const { command: command3, args: newArgs } = getCommand(processedCmds, args, cliName, cliDescription); if (!command3) { return help !== void 0 ? await executeOrLog(help) : await eventHandler({ type: "global_help", description: cliDescription, name: cliName, commands: processedCmds }); } if (command3 === "help") { let helpCommand; let newestArgs = newArgs; do { const res = getCommand(processedCmds, newestArgs, cliName, cliDescription); helpCommand = res.command; newestArgs = res.args; } while (helpCommand === "help"); return helpCommand ? helpCommand.help !== void 0 ? await executeOrLog(helpCommand.help) : await eventHandler({ type: "command_help", description: cliDescription, name: cliName, command: helpCommand }) : help !== void 0 ? await executeOrLog(help) : await eventHandler({ type: "global_help", description: cliDescription, name: cliName, commands: processedCmds }); } const optionResult = parseOptions(command3, newArgs, cliName, cliDescription, omitKeysOfUndefinedOptions); if (optionResult === "help") { return command3.help !== void 0 ? await executeOrLog(command3.help) : await eventHandler({ type: "command_help", description: cliDescription, name: cliName, command: command3 }); } if (optionResult === "version") { return version !== void 0 ? await executeOrLog(version) : await eventHandler({ type: "version", name: cliName, description: cliDescription }); } if (command3.handler) { if (config?.hook) await config.hook("before", command3); await command3.handler(command3.transform ? await command3.transform(optionResult) : optionResult); if (config?.hook) await config.hook("after", command3); return; } else { return command3.help !== void 0 ? await executeOrLog(command3.help) : await eventHandler({ type: "command_help", description: cliDescription, name: cliName, command: command3 }); } } catch (e) { if (e instanceof BroCliError) { if (e.event) await eventHandler(e.event); else { if (!config?.noExit) console.error(e.message); else return e.message; } } else { await eventHandler({ type: "error", violation: "unknown_error", name: cliName, description: cliDescription, error: e }); } if (!config?.noExit) process.exit(1); return; } }; var handler = (options, handler2) => handler2; var test = async (command3, args) => { try { const cliParsedArgs = shellArgs(args); const options = parseOptions(command3, cliParsedArgs, void 0, void 0); if (options === "help" || options === "version") { return { type: options }; } return { options: command3.transform ? await command3.transform(options) : options, type: "handler" }; } catch (e) { return { type: "error", error: e }; } }; var commandsInfo = (commands) => { const validated = validateCommands(commands); return Object.fromEntries(validated.map((c) => [c.name, { name: c.name, aliases: (0, import_clone.default)(c.aliases), desc: c.desc, shortDesc: c.shortDesc, isHidden: c.hidden, options: c.options ? Object.fromEntries(Object.entries(c.options).map(([key, opt]) => [key, (0, import_clone.default)(opt.config)])) : void 0, metadata: (0, import_clone.default)(c.metadata), subcommands: c.subcommands ? commandsInfo(c.subcommands) : void 0 }])); }; // src/option-builder.ts var OptionBuilderBase = class _OptionBuilderBase { _; config = () => this._.config; constructor(config) { this._ = { config: config ?? { aliases: [], type: "string" }, $output: void 0 }; } string(name) { const config = this.config(); return new _OptionBuilderBase({ ...config, type: "string", name }); } number(name) { const config = this.config(); return new _OptionBuilderBase({ ...config, type: "number", name }); } boolean(name) { const config = this.config(); return new _OptionBuilderBase({ ...config, type: "boolean", name }); } positional(displayName) { const config = this.config(); return new _OptionBuilderBase({ ...config, type: "positional", name: displayName }); } alias(...aliases) { const config = this.config(); return new _OptionBuilderBase({ ...config, aliases }); } desc(description) { const config = this.config(); return new _OptionBuilderBase({ ...config, description }); } hidden() { const config = this.config(); return new _OptionBuilderBase({ ...config, isHidden: true }); } required() { const config = this.config(); return new _OptionBuilderBase({ ...config, isRequired: true }); } default(value) { const config = this.config(); const enums = config.enumVals; if (enums && !enums.find((v) => value === v)) { throw new Error( `Option enums [ ${enums.join(", ")} ] are incompatible with default value ${value}` ); } return new _OptionBuilderBase({ ...config, default: value }); } enum(...values) { const config = this.config(); const defaultVal = config.default; if (defaultVal !== void 0 && !values.find((v) => defaultVal === v)) { throw new Error( `Option enums [ ${values.join(", ")} ] are incompatible with default value ${defaultVal}` ); } return new _OptionBuilderBase({ ...config, enumVals: values }); } min(value) { const config = this.config(); const maxVal = config.maxVal; if (maxVal !== void 0 && maxVal < value) { throw new BroCliError("Unable to define option's min value to be higher than max value!"); } return new _OptionBuilderBase({ ...config, minVal: value }); } max(value) { const config = this.config(); const minVal = config.minVal; if (minVal !== void 0 && minVal > value) { throw new BroCliError("Unable to define option's max value to be lower than min value!"); } return new _OptionBuilderBase({ ...config, maxVal: value }); } int() { const config = this.config(); return new _OptionBuilderBase({ ...config, isInt: true }); } }; function string(name) { return typeof name === "string" ? new OptionBuilderBase().string(name) : new OptionBuilderBase().string(); } function number(name) { return typeof name === "string" ? new OptionBuilderBase().number(name) : new OptionBuilderBase().number(); } function boolean(name) { return typeof name === "string" ? new OptionBuilderBase().boolean(name) : new OptionBuilderBase().boolean(); } function positional(displayName) { return typeof displayName === "string" ? new OptionBuilderBase().positional(displayName) : new OptionBuilderBase().positional(); } export { BroCliError, boolean, command2 as command, commandsInfo, getCommandNameWithParents, handler, number, positional, run, string, test }; //# sourceMappingURL=index.js.map