@visulima/cerebro 2.1.5 → 3.0.0-alpha.10

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 (41) hide show
  1. package/CHANGELOG.md +228 -0
  2. package/LICENSE.md +1810 -98
  3. package/README.md +28 -21
  4. package/dist/cli.d.ts +22 -0
  5. package/dist/commands/completion-command.js +204 -5
  6. package/dist/commands/help-command.js +200 -1
  7. package/dist/commands/readme-command.js +326 -32
  8. package/dist/commands/version-command.js +18 -1
  9. package/dist/default-env.d.ts +1 -1
  10. package/dist/index.js +7 -1
  11. package/dist/logger/create-pail-logger.js +34 -1
  12. package/dist/packem_chunks/has-new-version.js +264 -1
  13. package/dist/packem_shared/Cerebro-bgCm5Tb1.js +3444 -0
  14. package/dist/packem_shared/VERBOSITY_QUIET-Dp46zlLW.js +10 -0
  15. package/dist/packem_shared/VisulimaError-DA7QsCxH.js +34 -0
  16. package/dist/packem_shared/cerebro-error-GmJ3jN7Q.js +16 -0
  17. package/dist/packem_shared/index-CS31xKFe.js +264 -0
  18. package/dist/packem_shared/runtime-process-B6ZplyWn.js +187 -0
  19. package/dist/plugins/error-handler-plugin.js +648 -1
  20. package/dist/plugins/runtime-version-check-plugin.js +77 -1
  21. package/dist/plugins/update-notifier/update-notifier-plugin.js +517 -1
  22. package/dist/types/cli.d.ts +11 -0
  23. package/dist/types/command-line-usage.d.ts +1 -1
  24. package/dist/types/command.d.ts +10 -10
  25. package/dist/util/command-processing/option-processor.d.ts +8 -8
  26. package/dist/util/general/compile-cache.d.ts +41 -0
  27. package/dist/util/general/compile-cache.js +16 -0
  28. package/dist/util/general/heap-tuning.d.ts +81 -0
  29. package/dist/util/general/heap-tuning.js +91 -0
  30. package/dist/util/general/register-exception-handler.d.ts +1 -1
  31. package/dist/util/process-env-variables.d.ts +1 -1
  32. package/package.json +18 -9
  33. package/dist/packem_shared/Cerebro-CQZ9sj4S.js +0 -4
  34. package/dist/packem_shared/VERBOSITY_QUIET-XPultrIA.js +0 -1
  35. package/dist/packem_shared/VisulimaError--04oA1Oy.js +0 -76
  36. package/dist/packem_shared/cerebro-error-BnJTixb2.js +0 -1
  37. package/dist/packem_shared/help-command-CIRIXN03.js +0 -1
  38. package/dist/packem_shared/index-DQ3pvLQH.js +0 -6
  39. package/dist/packem_shared/isVisulimaError-jVZgumOU-C4fgdbWg.js +0 -1
  40. package/dist/packem_shared/renderError-ZMlMvw1N-eVUSdl6c.js +0 -24
  41. package/dist/packem_shared/runtime-process-G-n-wOub.js +0 -1
@@ -0,0 +1,3444 @@
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
4
+
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
+
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
15
+ }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
19
+
20
+ import HelpCommand from '../commands/help-command.js';
21
+ import { VERBOSITY_DEBUG, POSITIONALS_KEY, VERBOSITY_QUIET, VERBOSITY_VERBOSE, VERBOSITY_NORMAL } from './VERBOSITY_QUIET-Dp46zlLW.js';
22
+ import { C as CerebroError } from './cerebro-error-GmJ3jN7Q.js';
23
+ import { d as getEnv, f as getArgv, o as onProcessEvent, e as exitProcess, g as getCwd, h as getExecPath, i as getExecArgv } from './runtime-process-B6ZplyWn.js';
24
+ import { distance } from 'fastest-levenshtein';
25
+ const {
26
+ createRequire
27
+ } = __cjs_getBuiltinModule("node:module");
28
+
29
+ const defaultOptions = [
30
+ {
31
+ description: "Turn on verbose output",
32
+ group: "global",
33
+ name: "verbose",
34
+ type: Boolean
35
+ },
36
+ {
37
+ description: "Turn on debugging output",
38
+ group: "global",
39
+ name: "debug",
40
+ type: Boolean
41
+ },
42
+ {
43
+ alias: "h",
44
+ description: "Print out helpful usage information",
45
+ group: "global",
46
+ name: "help",
47
+ type: Boolean
48
+ },
49
+ {
50
+ alias: "q",
51
+ description: "Silence output",
52
+ group: "global",
53
+ name: "quiet",
54
+ type: Boolean
55
+ },
56
+ {
57
+ alias: "V",
58
+ description: "Print version info",
59
+ group: "global",
60
+ name: "version",
61
+ type: Boolean
62
+ },
63
+ {
64
+ description: "Turn off colored output",
65
+ group: "global",
66
+ name: "no-color",
67
+ type: Boolean
68
+ },
69
+ {
70
+ description: "Force colored output",
71
+ group: "global",
72
+ name: "color",
73
+ type: Boolean
74
+ }
75
+ ];
76
+
77
+ class CommandNotFoundError extends CerebroError {
78
+ commandName;
79
+ constructor(commandName, suggestions = []) {
80
+ const message = `Command "${commandName}" not found${suggestions.length > 0 ? `. Did you mean: ${suggestions.join(", ")}?` : ""}`;
81
+ super(message, "COMMAND_NOT_FOUND", {
82
+ commandName,
83
+ suggestions
84
+ });
85
+ this.name = "CommandNotFoundError";
86
+ this.commandName = commandName;
87
+ if (suggestions.length > 0) {
88
+ this.hint = `Try one of these commands: ${suggestions.join(", ")}`;
89
+ }
90
+ }
91
+ }
92
+
93
+ class ConflictingOptionsError extends CerebroError {
94
+ option1;
95
+ option2;
96
+ constructor(option1, option2) {
97
+ super(`Options "${option1}" and "${option2}" cannot be used together`, "CONFLICTING_OPTIONS", { option1, option2 });
98
+ this.name = "ConflictingOptionsError";
99
+ this.option1 = option1;
100
+ this.option2 = option2;
101
+ this.hint = `Remove either --${option1} or --${option2}`;
102
+ }
103
+ }
104
+
105
+ class PluginError extends CerebroError {
106
+ pluginName;
107
+ constructor(pluginName, message, originalError) {
108
+ super(`Plugin "${pluginName}" error: ${message}`, "PLUGIN_ERROR", { originalError, pluginName });
109
+ this.name = "PluginError";
110
+ this.pluginName = pluginName;
111
+ if (originalError) {
112
+ this.cause = originalError;
113
+ }
114
+ }
115
+ }
116
+
117
+ class PluginManager {
118
+ logger;
119
+ plugins = /* @__PURE__ */ new Map();
120
+ initialized = false;
121
+ cachedDependencyOrder = void 0;
122
+ constructor(logger) {
123
+ this.logger = logger;
124
+ }
125
+ /**
126
+ * Checks if any plugins are registered.
127
+ * @returns True if at least one plugin is registered
128
+ */
129
+ hasPlugins() {
130
+ return this.plugins.size > 0;
131
+ }
132
+ /**
133
+ * Registers a plugin.
134
+ * @param plugin The plugin to register
135
+ * @throws {Error} If plugin name is already registered or dependencies are invalid
136
+ */
137
+ register(plugin) {
138
+ if (this.initialized) {
139
+ throw new Error(`Cannot register plugin "${plugin.name}" after initialization`);
140
+ }
141
+ if (this.plugins.has(plugin.name)) {
142
+ throw new Error(`Plugin "${plugin.name}" is already registered`);
143
+ }
144
+ const env = getEnv();
145
+ if (env.CEREBRO_OUTPUT_LEVEL === String(VERBOSITY_DEBUG)) {
146
+ this.logger.debug(`registering plugin: ${plugin.name}`);
147
+ }
148
+ this.plugins.set(plugin.name, plugin);
149
+ this.cachedDependencyOrder = void 0;
150
+ }
151
+ /**
152
+ * Initializes all registered plugins.
153
+ * @param context The plugin context for initialization
154
+ */
155
+ // eslint-disable-next-line sonarjs/cognitive-complexity
156
+ async init(context) {
157
+ if (this.initialized) {
158
+ throw new Error("PluginManager already initialized");
159
+ }
160
+ if (this.plugins.size === 0) {
161
+ this.logger.debug("no plugins registered, skipping initialization");
162
+ this.initialized = true;
163
+ return;
164
+ }
165
+ this.validateDependencies();
166
+ const orderedPlugins = this.getDependencyOrder();
167
+ this.logger.debug(`initializing ${String(orderedPlugins.length)} plugin(s)...`);
168
+ for (const plugin of orderedPlugins) {
169
+ if (typeof plugin.init === "function") {
170
+ this.logger.debug(`initializing plugin: ${plugin.name}`);
171
+ try {
172
+ await plugin.init(context);
173
+ } catch (error) {
174
+ const pluginError = new PluginError(
175
+ plugin.name,
176
+ `Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,
177
+ error instanceof Error ? error : void 0
178
+ );
179
+ this.logger.error(pluginError.message);
180
+ throw pluginError;
181
+ }
182
+ }
183
+ }
184
+ this.initialized = true;
185
+ }
186
+ /**
187
+ * Executes a specific lifecycle hook for all plugins.
188
+ * @param hook The lifecycle hook name
189
+ * @param toolbox The command toolbox (for command-specific hooks)
190
+ * @param result The command result (for afterCommand hook)
191
+ */
192
+ async executeLifecycle(hook, toolbox, result) {
193
+ if (!this.initialized) {
194
+ throw new Error("PluginManager not initialized");
195
+ }
196
+ if (this.plugins.size === 0) {
197
+ return;
198
+ }
199
+ const orderedPlugins = this.getDependencyOrder();
200
+ for (const plugin of orderedPlugins) {
201
+ const hookFunction = plugin[hook];
202
+ if (typeof hookFunction === "function") {
203
+ this.logger.debug(`executing ${hook} hook for plugin: ${plugin.name}`);
204
+ try {
205
+ await (hook === "afterCommand" ? hookFunction(toolbox, result) : hookFunction(toolbox));
206
+ } catch (error) {
207
+ this.logger.error(`Error in ${hook} hook for plugin "${plugin.name}":`, error);
208
+ throw error;
209
+ }
210
+ }
211
+ }
212
+ }
213
+ /**
214
+ * Executes error handlers for all plugins.
215
+ * @param error The error that occurred
216
+ * @param toolbox The command toolbox
217
+ */
218
+ async executeErrorHandlers(error, toolbox) {
219
+ if (!this.initialized) {
220
+ return;
221
+ }
222
+ if (this.plugins.size === 0) {
223
+ return;
224
+ }
225
+ const orderedPlugins = this.getDependencyOrder();
226
+ for (const plugin of orderedPlugins) {
227
+ if (typeof plugin.onError === "function") {
228
+ this.logger.debug(`executing error handler for plugin: ${plugin.name}`);
229
+ try {
230
+ await plugin.onError(error, toolbox);
231
+ } catch (handlerError) {
232
+ this.logger.error(`Error in error handler for plugin "${plugin.name}":`, handlerError);
233
+ }
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * Gets all registered plugins in dependency order.
239
+ * @returns Array of plugins sorted by dependencies
240
+ */
241
+ getDependencyOrder() {
242
+ if (this.cachedDependencyOrder !== void 0) {
243
+ return this.cachedDependencyOrder;
244
+ }
245
+ const ordered = [];
246
+ const visited = /* @__PURE__ */ new Set();
247
+ const visiting = /* @__PURE__ */ new Set();
248
+ const visit = (pluginName) => {
249
+ if (visited.has(pluginName)) {
250
+ return;
251
+ }
252
+ if (visiting.has(pluginName)) {
253
+ throw new Error(`Circular dependency detected involving plugin "${pluginName}"`);
254
+ }
255
+ const plugin = this.plugins.get(pluginName);
256
+ if (!plugin) {
257
+ throw new Error(`Plugin "${pluginName}" not found`);
258
+ }
259
+ visiting.add(pluginName);
260
+ if (plugin.dependencies) {
261
+ for (const dependency of plugin.dependencies) {
262
+ visit(dependency);
263
+ }
264
+ }
265
+ visiting.delete(pluginName);
266
+ visited.add(pluginName);
267
+ ordered.push(plugin);
268
+ };
269
+ for (const pluginName of this.plugins.keys()) {
270
+ visit(pluginName);
271
+ }
272
+ this.cachedDependencyOrder = ordered;
273
+ return ordered;
274
+ }
275
+ /**
276
+ * Validates that all plugin dependencies exist.
277
+ * @throws {Error} If any dependencies are missing
278
+ */
279
+ validateDependencies() {
280
+ for (const plugin of this.plugins.values()) {
281
+ if (plugin.dependencies) {
282
+ for (const dependency of plugin.dependencies) {
283
+ if (!this.plugins.has(dependency)) {
284
+ throw new Error(`Plugin "${plugin.name}" depends on "${dependency}" which is not registered`);
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ }
291
+
292
+ const optionIsBoolean = (option) => option.type?.name === "Boolean";
293
+
294
+ const getTypeLabel = (definition) => {
295
+ let typeLabel = definition.type ? definition.type.name.toLowerCase() : "string";
296
+ const multiple = definition.multiple ?? definition.lazyMultiple ? "[]" : "";
297
+ if (typeLabel) {
298
+ typeLabel = typeLabel === "boolean" ? "" : `{underline ${typeLabel}${multiple}}`;
299
+ }
300
+ return typeLabel;
301
+ };
302
+ const mapOptionTypeLabel = (definition) => {
303
+ if (optionIsBoolean(definition)) {
304
+ return definition;
305
+ }
306
+ definition.typeLabel = definition.typeLabel ?? getTypeLabel(definition);
307
+ if (definition.defaultOption) {
308
+ definition.typeLabel = `${definition.typeLabel} (D)`;
309
+ }
310
+ if (definition.required) {
311
+ definition.typeLabel = `${definition.typeLabel} (R)`;
312
+ }
313
+ return definition;
314
+ };
315
+
316
+ const isShort = new RegExp(/^-([^\d-])$/);
317
+ const isLong = new RegExp(/^--(\S+)/);
318
+ const isCombined = new RegExp(/^-([^\d-]{2,})$/);
319
+ const isOption$1 = (argument) => isShort.test(argument) || isLong.test(argument) || isCombined.test(argument);
320
+ const commandLineCommands = (commands, argv) => {
321
+ const command = argv[0] && isOption$1(argv[0]) || argv.length === 0 ? null : argv.shift() ?? null;
322
+ if (!commands.includes(command)) {
323
+ const error = new Error(`Command not recognised: ${String(command)}`);
324
+ error.command = command;
325
+ error.name = "INVALID_COMMAND";
326
+ throw error;
327
+ }
328
+ return { argv, command };
329
+ };
330
+
331
+ class VisulimaError extends Error {
332
+ loc;
333
+ title;
334
+ /**
335
+ * A message that explains to the user how they can fix the error.
336
+ */
337
+ hint;
338
+ type = "VisulimaError";
339
+ constructor({ cause, hint, location, message, name, stack, title }) {
340
+ super(message, {
341
+ cause
342
+ });
343
+ this.title = title;
344
+ this.name = name;
345
+ this.stack = stack ?? this.stack;
346
+ this.loc = location;
347
+ this.hint = hint;
348
+ }
349
+ setLocation(location) {
350
+ this.loc = location;
351
+ }
352
+ setName(name) {
353
+ this.name = name;
354
+ }
355
+ setMessage(message) {
356
+ this.message = message;
357
+ }
358
+ setHint(hint) {
359
+ this.hint = hint;
360
+ }
361
+ }
362
+
363
+ class AlreadySetError extends VisulimaError {
364
+ optionName;
365
+ /**
366
+ * Creates a new AlreadySetError instance.
367
+ * @param optionName The name of the option that was already set
368
+ */
369
+ constructor(optionName) {
370
+ super({
371
+ cause: void 0,
372
+ hint: `Remove the duplicate option '${optionName}' from your command line arguments.`,
373
+ location: void 0,
374
+ message: `Option '${optionName}' is already set`,
375
+ name: "ALREADY_SET",
376
+ stack: void 0,
377
+ title: "Option Already Set"
378
+ });
379
+ this.optionName = optionName;
380
+ Object.setPrototypeOf(this, AlreadySetError.prototype);
381
+ }
382
+ }
383
+
384
+ class UnknownOptionError extends VisulimaError {
385
+ optionName;
386
+ /**
387
+ * Creates a new UnknownOptionError instance.
388
+ * @param optionName
389
+ */
390
+ constructor(optionName) {
391
+ super({
392
+ cause: void 0,
393
+ hint: `Check your option definitions or remove the unknown option '${optionName}' from your command line arguments.`,
394
+ location: void 0,
395
+ message: `Unknown option: --${optionName}`,
396
+ name: "UNKNOWN_OPTION",
397
+ stack: void 0,
398
+ title: "Unknown Option"
399
+ });
400
+ this.optionName = `--${optionName}`;
401
+ Object.setPrototypeOf(this, UnknownOptionError.prototype);
402
+ }
403
+ }
404
+
405
+ class UnknownValueError extends VisulimaError {
406
+ value;
407
+ /**
408
+ * Creates a new UnknownValueError instance.
409
+ * @param value The unknown value encountered
410
+ */
411
+ constructor(value) {
412
+ super({
413
+ hint: "Use a defined option or add a defaultOption to capture this value.",
414
+ message: `Unknown value: ${value}`,
415
+ name: "UNKNOWN_VALUE",
416
+ title: "Unknown Value"
417
+ });
418
+ this.value = value;
419
+ Object.setPrototypeOf(this, UnknownValueError.prototype);
420
+ }
421
+ }
422
+
423
+ class InvalidDefinitionsError extends VisulimaError {
424
+ /**
425
+ * Creates a new InvalidDefinitionsError instance.
426
+ * @param message The error message describing the invalid definition
427
+ * @param hint Optional hint for resolving the error
428
+ */
429
+ constructor(message, hint) {
430
+ super({
431
+ cause: void 0,
432
+ hint,
433
+ location: void 0,
434
+ message,
435
+ name: "INVALID_DEFINITIONS",
436
+ stack: void 0,
437
+ title: "Invalid Option Definition"
438
+ });
439
+ Object.setPrototypeOf(this, InvalidDefinitionsError.prototype);
440
+ }
441
+ }
442
+
443
+ const isBooleanType$2 = (type) => type === Boolean || typeof type === "function" && type.name.startsWith("Boolean");
444
+ const isNumberType = (type) => type === Number || typeof type === "function" && type.name === "Number";
445
+ const isStringType = (type) => type === String || typeof type === "function" && type.name === "String";
446
+ const convertValue = (value, type) => {
447
+ if (Array.isArray(value)) {
448
+ if (isBooleanType$2(type)) {
449
+ return value.map(Boolean);
450
+ }
451
+ if (isNumberType(type)) {
452
+ return value.map(Number);
453
+ }
454
+ if (isStringType(type)) {
455
+ return value.map(String);
456
+ }
457
+ return value.map((item) => type(String(item)));
458
+ }
459
+ if (value === null) {
460
+ return null;
461
+ }
462
+ if (isBooleanType$2(type)) {
463
+ return Boolean(value);
464
+ }
465
+ if (isNumberType(type)) {
466
+ return Number(value);
467
+ }
468
+ if (isStringType(type)) {
469
+ return typeof value === "string" ? value : String(value);
470
+ }
471
+ return type(typeof value === "string" ? value : String(value));
472
+ };
473
+ const debug = (enable, message, namespace, ...args) => {
474
+ if (enable) {
475
+ console.debug(`[command-line-args:${namespace}] ${message}`, ...args);
476
+ }
477
+ };
478
+ const CAMEL_CASE_PATTERN = /-([a-z])/g;
479
+ const NUMERIC_PATTERN = /^\d+$/;
480
+ const isBooleanType$1 = (type) => type === Boolean || typeof type === "function" && type.name.startsWith("Boolean");
481
+ const isSpecialKey = (key) => key.codePointAt(0) === 95;
482
+ const appendToArrayMultiple = (existingValue, newValues) => {
483
+ if (Array.isArray(existingValue)) {
484
+ return [...existingValue, ...newValues];
485
+ }
486
+ return [existingValue, ...newValues];
487
+ };
488
+ const isUnsafeKey = (key) => key === "__proto__" || key === "constructor" || key === "prototype";
489
+ const createOrAppendArray = (object, key, value, isArray = false) => {
490
+ if (object[key] === void 0) {
491
+ object[key] = isArray ? [value] : value;
492
+ } else if (isArray && Array.isArray(object[key])) {
493
+ object[key].push(value);
494
+ } else {
495
+ object[key] = [object[key], value];
496
+ }
497
+ };
498
+ const getDefinition = (name, definitionMap, aliasMap, caseInsensitiveNameMap, caseInsensitiveAliasMap) => {
499
+ let definition = definitionMap.get(name) ?? aliasMap.get(name);
500
+ if (!definition && caseInsensitiveNameMap) {
501
+ const lowercasedKey = name.toLowerCase();
502
+ definition = caseInsensitiveNameMap.get(lowercasedKey) ?? caseInsensitiveAliasMap?.get(lowercasedKey);
503
+ }
504
+ return definition;
505
+ };
506
+ const resolveArgs = (tokens, definitions, options, argv) => {
507
+ const debugEnabled = options.debug ?? false;
508
+ debug(debugEnabled, `resolveArgs called with options:`, "resolver", {
509
+ partial: options.partial,
510
+ stopAtFirstUnknown: options.stopAtFirstUnknown
511
+ });
512
+ debug(debugEnabled, "Starting argument resolution", "resolver");
513
+ debug(debugEnabled, "Tokens:", "resolver", tokens);
514
+ debug(debugEnabled, "Definitions:", "resolver", definitions);
515
+ debug(debugEnabled, "Processing tokens...", "resolver");
516
+ const definitionMap = /* @__PURE__ */ new Map();
517
+ const aliasMap = /* @__PURE__ */ new Map();
518
+ const caseInsensitiveNameMap = options.caseInsensitive ? /* @__PURE__ */ new Map() : void 0;
519
+ const caseInsensitiveAliasMap = options.caseInsensitive ? /* @__PURE__ */ new Map() : void 0;
520
+ const camelCaseMap = options.camelCase ? /* @__PURE__ */ new Map() : void 0;
521
+ const camelCaseReverseMap = options.camelCase ? /* @__PURE__ */ new Map() : void 0;
522
+ for (const definition of definitions) {
523
+ definitionMap.set(definition.name, definition);
524
+ if (definition.alias) {
525
+ aliasMap.set(definition.alias, definition);
526
+ }
527
+ if (options.caseInsensitive && caseInsensitiveNameMap) {
528
+ caseInsensitiveNameMap.set(definition.name.toLowerCase(), definition);
529
+ if (definition.alias && caseInsensitiveAliasMap) {
530
+ caseInsensitiveAliasMap.set(definition.alias.toLowerCase(), definition);
531
+ }
532
+ }
533
+ if (options.camelCase && camelCaseMap && camelCaseReverseMap) {
534
+ const camelCase = definition.name.replaceAll(CAMEL_CASE_PATTERN, (_, letter) => letter.toUpperCase());
535
+ camelCaseMap.set(definition.name, camelCase);
536
+ camelCaseReverseMap.set(camelCase, definition.name);
537
+ }
538
+ }
539
+ const output = {};
540
+ const values = {};
541
+ const unknownArgs = [];
542
+ const consumedPositionalIndices = /* @__PURE__ */ new Set();
543
+ let stoppedByTerminator = false;
544
+ const defaultOptionDefinition = definitions.find((d) => d.defaultOption);
545
+ const hasGroups = definitions.some((d) => d.group);
546
+ const hasNumberType = definitions.some((d) => d.type === Number);
547
+ for (let i = 0; i < tokens.length; i++) {
548
+ const token = tokens[i];
549
+ if (token.kind === "option-terminator") {
550
+ output._unknown = argv.slice(token.index);
551
+ stoppedByTerminator = true;
552
+ break;
553
+ }
554
+ if (token.kind === "option" && token.name) {
555
+ let definition = getDefinition(token.name, definitionMap, aliasMap, caseInsensitiveNameMap, caseInsensitiveAliasMap);
556
+ if (!definition && token.value === void 0 && hasNumberType && NUMERIC_PATTERN.test(token.name)) {
557
+ const numberDefinition = definitions.find((anyDefinition) => anyDefinition.type === Number);
558
+ if (numberDefinition) {
559
+ definition = numberDefinition;
560
+ token.value = token.name;
561
+ token.name = numberDefinition.name;
562
+ }
563
+ }
564
+ const optionName = definition ? definition.name : token.name;
565
+ const isMultiple = definition?.multiple;
566
+ const isLazyMultiple = definition?.lazyMultiple;
567
+ if (values[optionName] !== void 0 && !isMultiple && !isLazyMultiple && !options.partial) {
568
+ throw new AlreadySetError(optionName);
569
+ }
570
+ if (!definition && options.partial) {
571
+ const rawArgument = token.rawName ?? `--${token.name}${token.value !== void 0 && token.inlineValue ? `=${token.value}` : ""}`;
572
+ unknownArgs.push({ index: token.index, value: rawArgument });
573
+ continue;
574
+ }
575
+ if (!definition && options.stopAtFirstUnknown) {
576
+ output._unknown = argv.slice(token.index);
577
+ break;
578
+ }
579
+ if (!definition && !options.partial) {
580
+ throw new UnknownOptionError(token.name);
581
+ }
582
+ if (token.value === void 0) {
583
+ const nextToken = tokens[i + 1];
584
+ const isValueOnlyOptionToken = nextToken?.kind === "option" && !("name" in nextToken) && nextToken.value !== void 0;
585
+ const shouldConsumeValue = nextToken && definition && !(definition.type && isBooleanType$1(definition.type)) && (nextToken.kind === "positional" || isValueOnlyOptionToken);
586
+ const isDefaultOptionNonMultiple = definition && definition.defaultOption && !definition.multiple && !definition.lazyMultiple;
587
+ if (shouldConsumeValue && (!definition?.defaultOption || isDefaultOptionNonMultiple)) {
588
+ if (isMultiple) {
589
+ let currentIndex = i + 1;
590
+ const collectedValues = [];
591
+ while (currentIndex < tokens.length && (tokens[currentIndex].kind === "positional" || tokens[currentIndex].kind === "option" && !("name" in tokens[currentIndex]) && tokens[currentIndex].value !== void 0)) {
592
+ collectedValues.push(tokens[currentIndex].value);
593
+ consumedPositionalIndices.add(tokens[currentIndex].index);
594
+ currentIndex++;
595
+ }
596
+ values[optionName] = values[optionName] === void 0 ? collectedValues : appendToArrayMultiple(values[optionName], collectedValues);
597
+ i = currentIndex - 1;
598
+ } else if (isLazyMultiple) {
599
+ createOrAppendArray(values, optionName, nextToken.value, true);
600
+ consumedPositionalIndices.add(nextToken.index);
601
+ i++;
602
+ } else {
603
+ values[optionName] = nextToken.value;
604
+ consumedPositionalIndices.add(nextToken.index);
605
+ i++;
606
+ }
607
+ } else if (definition?.type && isBooleanType$1(definition.type)) {
608
+ createOrAppendArray(values, optionName, true, isMultiple);
609
+ } else {
610
+ values[optionName] = isMultiple ? [] : null;
611
+ }
612
+ } else {
613
+ let { value } = token;
614
+ if (definition?.type && isBooleanType$1(definition.type)) {
615
+ switch (value) {
616
+ case "": {
617
+ if (options.partial) {
618
+ values._unknown ??= [];
619
+ values._unknown.push(`${token.rawName ?? `--${token.name}`}${token.value ? `=${token.value}` : ""}`);
620
+ value = true;
621
+ } else {
622
+ throw new UnknownOptionError(token.name);
623
+ }
624
+ break;
625
+ }
626
+ case "false": {
627
+ value = false;
628
+ break;
629
+ }
630
+ case "true": {
631
+ value = true;
632
+ break;
633
+ }
634
+ default: {
635
+ value = true;
636
+ }
637
+ }
638
+ }
639
+ const collectedValues = [value];
640
+ if (isMultiple) {
641
+ let currentIndex = i + 1;
642
+ while (currentIndex < tokens.length && tokens[currentIndex].kind === "positional") {
643
+ collectedValues.push(tokens[currentIndex].value);
644
+ consumedPositionalIndices.add(tokens[currentIndex].index);
645
+ currentIndex++;
646
+ }
647
+ i = currentIndex - 1;
648
+ }
649
+ if (values[optionName] === void 0) {
650
+ values[optionName] = isMultiple || isLazyMultiple ? collectedValues : value;
651
+ } else if (isMultiple || isLazyMultiple) {
652
+ values[optionName] = appendToArrayMultiple(values[optionName], collectedValues);
653
+ } else {
654
+ values[optionName] = value;
655
+ }
656
+ }
657
+ } else if (token.kind === "positional" && options.stopAtFirstUnknown && !consumedPositionalIndices.has(token.index) && !defaultOptionDefinition) {
658
+ debug(debugEnabled, `Found unconsumed positional token at index ${String(token.index)}, stopping processing`, "resolver");
659
+ output._unknown = argv.slice(token.index);
660
+ break;
661
+ }
662
+ }
663
+ for (const [key, value] of Object.entries(values)) {
664
+ const definition = definitionMap.get(key);
665
+ if (definition && (definition.multiple || definition.lazyMultiple) && !Array.isArray(value)) {
666
+ values[key] = [value];
667
+ }
668
+ }
669
+ let stopAtUnknownArgvIndex = Number.POSITIVE_INFINITY;
670
+ if (options.stopAtFirstUnknown && !stoppedByTerminator) {
671
+ for (const token of tokens) {
672
+ if (token.kind === "option" && !definitionMap.has(token.name ?? "") && !aliasMap.has(token.name ?? "") && (!options.caseInsensitive || !caseInsensitiveNameMap?.has(token.name?.toLowerCase() ?? "") && !caseInsensitiveAliasMap?.has(token.name?.toLowerCase() ?? ""))) {
673
+ stopAtUnknownArgvIndex = token.index;
674
+ break;
675
+ }
676
+ }
677
+ }
678
+ if (defaultOptionDefinition) {
679
+ const positionalValues = [];
680
+ const positionalTokens = [];
681
+ for (const token of tokens) {
682
+ if (token.kind === "positional" && !consumedPositionalIndices.has(token.index) && token.index < stopAtUnknownArgvIndex) {
683
+ positionalValues.push(token.value);
684
+ positionalTokens.push(token);
685
+ }
686
+ }
687
+ if (positionalValues.length > 0) {
688
+ const existingValue = values[defaultOptionDefinition.name];
689
+ const isMultiple = defaultOptionDefinition.multiple ?? defaultOptionDefinition.lazyMultiple;
690
+ if (existingValue === void 0) {
691
+ if (isMultiple) {
692
+ positionalTokens.forEach((token) => consumedPositionalIndices.add(token.index));
693
+ values[defaultOptionDefinition.name] = positionalValues;
694
+ } else {
695
+ consumedPositionalIndices.add(positionalTokens[0].index);
696
+ values[defaultOptionDefinition.name] = positionalValues[0];
697
+ }
698
+ } else if (isMultiple) {
699
+ positionalTokens.forEach((token) => consumedPositionalIndices.add(token.index));
700
+ values[defaultOptionDefinition.name] = Array.isArray(existingValue) ? [...positionalValues, ...existingValue] : [...positionalValues, existingValue];
701
+ }
702
+ }
703
+ }
704
+ if (!options.partial) {
705
+ for (const token of tokens) {
706
+ if (token.kind === "positional" && !consumedPositionalIndices.has(token.index)) {
707
+ throw new UnknownValueError(argv[token.index]);
708
+ }
709
+ }
710
+ }
711
+ if (options.partial && !options.stopAtFirstUnknown) {
712
+ const allUnknownItems = [...unknownArgs];
713
+ if (values._unknown) {
714
+ const valueUnknownMap = /* @__PURE__ */ new Map();
715
+ for (const [i, element] of argv.entries()) {
716
+ valueUnknownMap.set(element, i);
717
+ }
718
+ for (const argument of values._unknown) {
719
+ const index = valueUnknownMap.get(argument);
720
+ if (index !== void 0) {
721
+ allUnknownItems.push({ index, value: argument });
722
+ }
723
+ }
724
+ }
725
+ for (const token of tokens) {
726
+ if (token.kind === "positional" && !consumedPositionalIndices.has(token.index)) {
727
+ allUnknownItems.push({ index: token.index, value: argv[token.index] });
728
+ }
729
+ }
730
+ if (allUnknownItems.length > 0) {
731
+ allUnknownItems.sort((a, b) => a.index - b.index);
732
+ output._unknown = allUnknownItems.map((item) => item.value);
733
+ }
734
+ }
735
+ if (options.stopAtFirstUnknown && !stoppedByTerminator) {
736
+ const firstUnknownOptionTokenIndex = tokens.findIndex(
737
+ (token) => token.kind === "option" && !definitionMap.has(token.name ?? "") && !aliasMap.has(token.name ?? "") && (!options.caseInsensitive || !caseInsensitiveNameMap?.has(token.name?.toLowerCase() ?? "") && !caseInsensitiveAliasMap?.has(token.name?.toLowerCase() ?? ""))
738
+ );
739
+ const firstUnconsumedPositionalTokenIndex = tokens.findIndex((token) => token.kind === "positional" && !consumedPositionalIndices.has(token.index));
740
+ let firstTokenIndex = -1;
741
+ if (firstUnknownOptionTokenIndex !== -1 && firstUnconsumedPositionalTokenIndex !== -1) {
742
+ firstTokenIndex = Math.min(firstUnknownOptionTokenIndex, firstUnconsumedPositionalTokenIndex);
743
+ } else if (firstUnknownOptionTokenIndex !== -1) {
744
+ firstTokenIndex = firstUnknownOptionTokenIndex;
745
+ } else if (firstUnconsumedPositionalTokenIndex !== -1) {
746
+ firstTokenIndex = firstUnconsumedPositionalTokenIndex;
747
+ }
748
+ if (firstTokenIndex >= 0) {
749
+ const argvIndex = tokens[firstTokenIndex].index;
750
+ output._unknown = argv.slice(argvIndex);
751
+ }
752
+ } else if (unknownArgs.length > 0 && !options.partial) {
753
+ output._unknown = unknownArgs.map((item) => item.value);
754
+ }
755
+ for (const [key, value] of Object.entries(values)) {
756
+ const finalKey = options.camelCase ? camelCaseMap?.get(key) ?? key : key;
757
+ const definition = definitionMap.get(key);
758
+ output[finalKey] = definition?.type ? convertValue(value, definition.type) : value === void 0 ? null : value;
759
+ }
760
+ for (const definition of definitions) {
761
+ const key = options.camelCase ? camelCaseMap?.get(definition.name) ?? definition.name : definition.name;
762
+ if (!(key in output) && definition.defaultValue !== void 0) {
763
+ const isMultipleDefinition = definition.multiple ?? definition.lazyMultiple;
764
+ if (isMultipleDefinition) {
765
+ output[key] = Array.isArray(definition.defaultValue) ? [...definition.defaultValue] : [definition.defaultValue];
766
+ } else {
767
+ output[key] = definition.defaultValue;
768
+ }
769
+ }
770
+ }
771
+ if (hasGroups) {
772
+ const groups = {};
773
+ const allOptions = {};
774
+ const ungroupedOptions = {};
775
+ for (const definition of definitions) {
776
+ if (definition.group) {
777
+ const groupArray = Array.isArray(definition.group) ? definition.group : [definition.group];
778
+ for (const group of groupArray) {
779
+ if (isUnsafeKey(group)) {
780
+ continue;
781
+ }
782
+ groups[group] ??= {};
783
+ }
784
+ }
785
+ }
786
+ for (const key of Object.keys(output)) {
787
+ if (!isSpecialKey(key)) {
788
+ allOptions[key] = output[key];
789
+ let originalKey = key;
790
+ if (options.camelCase) {
791
+ originalKey = camelCaseReverseMap?.get(key) ?? key;
792
+ }
793
+ const definition = definitionMap.get(originalKey);
794
+ if (definition?.group) {
795
+ const groupArray = Array.isArray(definition.group) ? definition.group : [definition.group];
796
+ for (const group of groupArray) {
797
+ if (isUnsafeKey(group)) {
798
+ continue;
799
+ }
800
+ if (groups[group]) {
801
+ groups[group][key] = output[key];
802
+ }
803
+ }
804
+ } else {
805
+ ungroupedOptions[key] = output[key];
806
+ }
807
+ }
808
+ }
809
+ const groupedOutput = { _all: allOptions };
810
+ for (const [group, groupOptions] of Object.entries(groups)) {
811
+ groupedOutput[group] = groupOptions;
812
+ }
813
+ if (Object.keys(ungroupedOptions).length > 0) {
814
+ groupedOutput._none = ungroupedOptions;
815
+ }
816
+ if (output._unknown) {
817
+ groupedOutput._unknown = output._unknown;
818
+ }
819
+ Object.keys(output).forEach((key) => delete output[key]);
820
+ Object.assign(output, groupedOutput);
821
+ }
822
+ debug(debugEnabled, "Final parsed result:", "resolver", output);
823
+ return output;
824
+ };
825
+ const HYPHEN_CHAR = "-";
826
+ const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
827
+ const EQUAL_CHAR = "=";
828
+ const EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
829
+ const TERMINATOR = "--";
830
+ const SHORT_OPTION_PREFIX = HYPHEN_CHAR;
831
+ const LONG_OPTION_PREFIX = "--";
832
+ const hasLongOptionPrefix = (argument) => argument.length > 2 && argument.startsWith(LONG_OPTION_PREFIX);
833
+ const isLongOption = (argument) => hasLongOptionPrefix(argument) && !argument.includes(EQUAL_CHAR, 3);
834
+ const isLongOptionAndValue = (argument) => hasLongOptionPrefix(argument) && argument.includes(EQUAL_CHAR, 3);
835
+ const hasOptionValue = (value) => value !== void 0 && value.length > 0 && value.codePointAt(0) !== HYPHEN_CODE;
836
+ const isShortOption = (argument) => {
837
+ if (argument.length !== 2 || argument.codePointAt(0) !== HYPHEN_CODE || argument.codePointAt(1) === HYPHEN_CODE) {
838
+ return false;
839
+ }
840
+ const secondCharCode = argument.codePointAt(1);
841
+ return secondCharCode !== void 0 && (secondCharCode < 48 || secondCharCode > 57);
842
+ };
843
+ const isShortOptionGroup = (argument) => {
844
+ if (argument.length <= 2) {
845
+ return false;
846
+ }
847
+ if (argument.codePointAt(0) !== HYPHEN_CODE) {
848
+ return false;
849
+ }
850
+ if (argument.codePointAt(1) === HYPHEN_CODE) {
851
+ return false;
852
+ }
853
+ return true;
854
+ };
855
+ const parseArgsTokens = (args) => {
856
+ const tokens = [];
857
+ const remainings = [...args];
858
+ let index = -1;
859
+ let groupCount = 0;
860
+ while (remainings.length > 0) {
861
+ const argument = remainings.shift();
862
+ if (argument === void 0) {
863
+ break;
864
+ }
865
+ const nextArgument = remainings[0];
866
+ if (groupCount > 0) {
867
+ groupCount--;
868
+ } else {
869
+ index++;
870
+ }
871
+ if (argument === TERMINATOR) {
872
+ tokens.push({
873
+ index,
874
+ kind: "option-terminator"
875
+ });
876
+ const mapped = remainings.map((argument_, index_) => {
877
+ return { index: index + index_ + 1, kind: "positional", value: argument_ };
878
+ });
879
+ tokens.push(...mapped);
880
+ index += remainings.length;
881
+ break;
882
+ }
883
+ if (isShortOption(argument)) {
884
+ const shortOption = argument.charAt(1);
885
+ let value;
886
+ if (groupCount) {
887
+ tokens.push({
888
+ index,
889
+ kind: "option",
890
+ name: shortOption,
891
+ rawName: argument,
892
+ value
893
+ });
894
+ if (groupCount === 1 && hasOptionValue(nextArgument)) {
895
+ value = remainings.shift();
896
+ tokens.push({
897
+ index,
898
+ kind: "option",
899
+ value
900
+ });
901
+ }
902
+ } else {
903
+ tokens.push({
904
+ index,
905
+ kind: "option",
906
+ name: shortOption,
907
+ rawName: argument,
908
+ value
909
+ });
910
+ }
911
+ if (value !== void 0) {
912
+ ++index;
913
+ }
914
+ continue;
915
+ }
916
+ if (isShortOptionGroup(argument) && !argument.includes(EQUAL_CHAR)) {
917
+ const expanded = [];
918
+ let shortValue = "";
919
+ let localHasShortValueSeparator = false;
920
+ for (let i = 1; i < argument.length; i++) {
921
+ const shortableOption = argument.charAt(i);
922
+ if (localHasShortValueSeparator) {
923
+ shortValue += shortableOption;
924
+ } else if (shortableOption.codePointAt(0) === EQUAL_CODE) {
925
+ localHasShortValueSeparator = true;
926
+ } else {
927
+ expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
928
+ }
929
+ }
930
+ if (localHasShortValueSeparator) {
931
+ if (expanded.length > 0) {
932
+ const lastOption = expanded.pop();
933
+ expanded.push(`${lastOption}=${shortValue}`);
934
+ } else {
935
+ expanded.push(shortValue);
936
+ }
937
+ }
938
+ remainings.unshift(...expanded);
939
+ groupCount = expanded.length;
940
+ continue;
941
+ }
942
+ if (isLongOption(argument)) {
943
+ const longOption = argument.slice(2);
944
+ tokens.push({
945
+ index,
946
+ kind: "option",
947
+ name: longOption,
948
+ rawName: argument
949
+ });
950
+ continue;
951
+ }
952
+ if (isLongOptionAndValue(argument)) {
953
+ const equalIndex = argument.indexOf(EQUAL_CHAR);
954
+ const longOption = argument.slice(2, equalIndex);
955
+ const value = argument.slice(equalIndex + 1);
956
+ tokens.push({
957
+ index,
958
+ inlineValue: true,
959
+ kind: "option",
960
+ name: longOption,
961
+ rawName: argument,
962
+ value
963
+ });
964
+ continue;
965
+ }
966
+ if (argument.length > 2 && argument.codePointAt(0) === HYPHEN_CODE && argument.codePointAt(1) !== HYPHEN_CODE && argument.includes(EQUAL_CHAR)) {
967
+ const equalIndex = argument.indexOf(EQUAL_CHAR);
968
+ const shortOption = argument.charAt(1);
969
+ const value = argument.slice(equalIndex + 1);
970
+ tokens.push({
971
+ index,
972
+ inlineValue: true,
973
+ kind: "option",
974
+ name: shortOption,
975
+ rawName: argument,
976
+ value
977
+ });
978
+ continue;
979
+ }
980
+ tokens.push({
981
+ index,
982
+ kind: "positional",
983
+ value: argument
984
+ });
985
+ }
986
+ return tokens;
987
+ };
988
+ const DIGIT_PATTERN = /\d/;
989
+ const isBooleanType = (type) => type !== void 0 && type !== null && (type === Boolean || typeof type === "function" && type.name.startsWith("Boolean"));
990
+ const isValidCustomTypeFunction = (typeFunction) => (
991
+ // Accept any function as a valid type converter (Boolean, Number, String, or custom functions)
992
+ // We check only the type without invoking the function to avoid side effects
993
+ typeof typeFunction === "function"
994
+ );
995
+ const validateDefinitions = (definitions, caseInsensitive, debugOptions) => {
996
+ const debugEnabled = debugOptions?.debug ?? false;
997
+ debug(debugEnabled, "Validating definitions:", "validation", definitions, "caseInsensitive:", caseInsensitive);
998
+ const names = /* @__PURE__ */ new Set();
999
+ const aliases = /* @__PURE__ */ new Set();
1000
+ const namesLower = /* @__PURE__ */ new Set();
1001
+ const aliasesLower = /* @__PURE__ */ new Set();
1002
+ let defaultOptionCount = 0;
1003
+ for (const definition of definitions) {
1004
+ debug(debugEnabled, "Checking definition:", "validation", definition);
1005
+ if (!definition.name) {
1006
+ debug(debugEnabled, "Validation failed: name is required", "validation");
1007
+ throw new InvalidDefinitionsError("Invalid option definition: name is required");
1008
+ }
1009
+ if (typeof definition.name !== "string") {
1010
+ throw new InvalidDefinitionsError("Invalid option definition: name must be a string");
1011
+ }
1012
+ if (definition.name.trim() === "") {
1013
+ throw new InvalidDefinitionsError("Invalid option definition: name cannot be empty");
1014
+ }
1015
+ const nameLower = caseInsensitive ? definition.name.toLowerCase() : "";
1016
+ if (names.has(definition.name) || caseInsensitive && namesLower.has(nameLower)) {
1017
+ throw new InvalidDefinitionsError(`Invalid option definition: duplicate name '${definition.name}'`);
1018
+ }
1019
+ if (aliases.has(definition.name) || caseInsensitive && aliasesLower.has(nameLower)) {
1020
+ throw new InvalidDefinitionsError(`Invalid option definition: name '${definition.name}' conflicts with an existing alias`);
1021
+ }
1022
+ names.add(definition.name);
1023
+ if (caseInsensitive) {
1024
+ namesLower.add(nameLower);
1025
+ }
1026
+ if (definition.alias !== void 0) {
1027
+ if (typeof definition.alias !== "string") {
1028
+ throw new InvalidDefinitionsError("Invalid option definition: alias must be a string");
1029
+ }
1030
+ if (definition.alias.length !== 1) {
1031
+ throw new InvalidDefinitionsError("Invalid option definition: alias must be a single character");
1032
+ }
1033
+ if (DIGIT_PATTERN.test(definition.alias)) {
1034
+ throw new InvalidDefinitionsError("Invalid option definition: alias cannot be numeric");
1035
+ }
1036
+ if (definition.alias === "-") {
1037
+ throw new InvalidDefinitionsError('Invalid option definition: alias cannot be "-"');
1038
+ }
1039
+ const aliasLower = caseInsensitive ? definition.alias.toLowerCase() : "";
1040
+ if (aliases.has(definition.alias) || caseInsensitive && aliasesLower.has(aliasLower)) {
1041
+ throw new InvalidDefinitionsError(`Invalid option definition: duplicate alias '${definition.alias}'`);
1042
+ }
1043
+ if (names.has(definition.alias) || caseInsensitive && namesLower.has(aliasLower)) {
1044
+ throw new InvalidDefinitionsError(`Invalid option definition: alias '${definition.alias}' conflicts with an existing option name`);
1045
+ }
1046
+ aliases.add(definition.alias);
1047
+ if (caseInsensitive) {
1048
+ aliasesLower.add(aliasLower);
1049
+ }
1050
+ }
1051
+ if (definition.defaultOption) {
1052
+ defaultOptionCount++;
1053
+ if (definition.type !== void 0 && isBooleanType(definition.type)) {
1054
+ throw new InvalidDefinitionsError("Invalid option definition: defaultOption cannot be Boolean type");
1055
+ }
1056
+ }
1057
+ if (definition.type !== void 0) {
1058
+ const isValidType = definition.type === Boolean || definition.type === Number || definition.type === String || typeof definition.type === "function" && isValidCustomTypeFunction(definition.type);
1059
+ if (!isValidType) {
1060
+ throw new InvalidDefinitionsError("Invalid option definition: invalid type");
1061
+ }
1062
+ }
1063
+ }
1064
+ if (defaultOptionCount > 1) {
1065
+ debug(debugEnabled, "Validation failed: multiple defaultOptions not allowed", "validation");
1066
+ throw new InvalidDefinitionsError("Invalid option definition: multiple defaultOptions not allowed");
1067
+ }
1068
+ debug(debugEnabled, "Validation completed successfully", "validation");
1069
+ };
1070
+ const commandLineArgs = (optionDefinitions, options = {}) => {
1071
+ const debugEnabled = options.debug ?? false;
1072
+ debug(debugEnabled, "Starting command-line-args parsing", "index");
1073
+ debug(debugEnabled, "Options:", "index", options);
1074
+ const effectiveOptions = { ...options };
1075
+ if (effectiveOptions.stopAtFirstUnknown) {
1076
+ effectiveOptions.partial = true;
1077
+ }
1078
+ const definitions = Array.isArray(optionDefinitions) ? optionDefinitions : [optionDefinitions];
1079
+ debug(debugEnabled, "Normalized definitions:", "index", definitions);
1080
+ validateDefinitions(definitions, effectiveOptions.caseInsensitive, debugEnabled ? effectiveOptions : void 0);
1081
+ let { argv } = effectiveOptions;
1082
+ if (!argv) {
1083
+ argv = process.argv.slice(2);
1084
+ if (process.execArgv.length > 0) {
1085
+ const execArgs = new Set(process.execArgv);
1086
+ argv = argv.filter((argument) => !execArgs.has(argument));
1087
+ }
1088
+ }
1089
+ debug(debugEnabled, "Using argv:", "index", argv);
1090
+ let normalizedArgv = argv;
1091
+ if (effectiveOptions.caseInsensitive) {
1092
+ normalizedArgv = argv.map((argument) => {
1093
+ if (argument.startsWith("--")) {
1094
+ const equalsIndex = argument.indexOf("=");
1095
+ const optionName = equalsIndex === -1 ? argument.slice(2) : argument.slice(2, equalsIndex);
1096
+ const normalizedName = optionName.toLowerCase();
1097
+ return equalsIndex === -1 ? `--${normalizedName}` : `--${normalizedName}${argument.slice(equalsIndex)}`;
1098
+ }
1099
+ if (argument.startsWith("-") && !argument.startsWith("--") && argument.length > 1) {
1100
+ const flagsAndRest = argument.slice(1).split("=", 2);
1101
+ const flags = flagsAndRest[0];
1102
+ const rest = flagsAndRest[1];
1103
+ if (!flags) {
1104
+ return argument;
1105
+ }
1106
+ const lowered = flags.toLowerCase();
1107
+ return rest === void 0 ? `-${lowered}` : `-${lowered}=${rest}`;
1108
+ }
1109
+ return argument;
1110
+ });
1111
+ }
1112
+ const tokens = parseArgsTokens(normalizedArgv.map(String));
1113
+ debug(debugEnabled, "Tokenized arguments:", "index", tokens);
1114
+ const result = resolveArgs(tokens, definitions, effectiveOptions, argv);
1115
+ debug(debugEnabled, "Command-line-args parsing completed", "index");
1116
+ return result;
1117
+ };
1118
+
1119
+ class EmptyToolbox {
1120
+ result;
1121
+ argv;
1122
+ options;
1123
+ argument;
1124
+ command;
1125
+ commandName;
1126
+ env;
1127
+ logger;
1128
+ runtime;
1129
+ constructor(commandName, command) {
1130
+ this.commandName = commandName;
1131
+ this.command = command;
1132
+ }
1133
+ }
1134
+
1135
+ const argumentNameRegExp = /^-{1,2}(\w+)(=(.+))?$/;
1136
+ const getParameterOption = (argument, options, optionMapByName, optionMapByAlias) => {
1137
+ const regExpResult = argumentNameRegExp.exec(argument);
1138
+ if (regExpResult === null) {
1139
+ return {};
1140
+ }
1141
+ const nameOrAlias = regExpResult[1];
1142
+ if (!nameOrAlias) {
1143
+ return {};
1144
+ }
1145
+ const option = optionMapByName && optionMapByAlias ? optionMapByName.get(nameOrAlias) ?? optionMapByAlias.get(nameOrAlias) : options.find((o) => o.name === nameOrAlias || o.alias === nameOrAlias);
1146
+ if (option !== void 0) {
1147
+ return { argName: option.name, argValue: regExpResult[3], option };
1148
+ }
1149
+ return {};
1150
+ };
1151
+
1152
+ const convertType = (value, option) => {
1153
+ if (option.type === void 0) {
1154
+ return value;
1155
+ }
1156
+ if (option.type.name === "Boolean") {
1157
+ if (value === "true" || value === "1") {
1158
+ return option.type(true);
1159
+ }
1160
+ if (value === "false" || value === "0") {
1161
+ return option.type(false);
1162
+ }
1163
+ }
1164
+ return option.type(value);
1165
+ };
1166
+ const booleanValue$1 = /* @__PURE__ */ new Set(["0", "1", "false", "true"]);
1167
+ const getBooleanValues = (arguments_, options, optionMapByName, optionMapByAlias) => {
1168
+ if (options.length === 0 || arguments_.length === 0) {
1169
+ return {};
1170
+ }
1171
+ const getBooleanValue = (argumentsAndLastOption, argument) => {
1172
+ const { argName, argValue, option } = getParameterOption(argument, options, optionMapByName, optionMapByAlias);
1173
+ const { lastOption } = argumentsAndLastOption;
1174
+ if (option && optionIsBoolean(option) && argValue && argName) {
1175
+ argumentsAndLastOption.partial[argName] = convertType(
1176
+ argValue,
1177
+ option
1178
+ );
1179
+ } else if (argumentsAndLastOption.lastName && lastOption && optionIsBoolean(lastOption) && booleanValue$1.has(argument)) {
1180
+ argumentsAndLastOption.partial[argumentsAndLastOption.lastName] = convertType(
1181
+ argument,
1182
+ lastOption
1183
+ );
1184
+ }
1185
+ return { lastName: argName, lastOption: option, partial: argumentsAndLastOption.partial };
1186
+ };
1187
+ return arguments_.reduce(getBooleanValue, { partial: {} }).partial;
1188
+ };
1189
+
1190
+ const booleanValue = /* @__PURE__ */ new Set(["0", "1", "false", "true"]);
1191
+ const removeBooleanValues = (arguments_, options, optionMapByName, optionMapByAlias) => {
1192
+ if (options.length === 0 || arguments_.length === 0) {
1193
+ return arguments_;
1194
+ }
1195
+ const removeBooleanArguments = (argumentsAndLastValue, argument) => {
1196
+ const { argValue, option } = getParameterOption(argument, options, optionMapByName, optionMapByAlias);
1197
+ const { lastOption } = argumentsAndLastValue;
1198
+ if (lastOption && optionIsBoolean(lastOption) && booleanValue.has(argument)) {
1199
+ const { args } = argumentsAndLastValue;
1200
+ const result = args.slice(0, -1);
1201
+ return { args: result };
1202
+ }
1203
+ if (option && optionIsBoolean(option) && argValue) {
1204
+ return { args: argumentsAndLastValue.args };
1205
+ }
1206
+ const newArgs = [...argumentsAndLastValue.args, argument];
1207
+ return { args: newArgs, lastOption: option };
1208
+ };
1209
+ return arguments_.reduce(removeBooleanArguments, { args: [] }).args;
1210
+ };
1211
+
1212
+ const mergeArguments = (argumentLists) => {
1213
+ const argumentsByName = /* @__PURE__ */ new Map();
1214
+ for (const argument of argumentLists) {
1215
+ const existing = argumentsByName.get(argument.name);
1216
+ if (existing) {
1217
+ argumentsByName.set(argument.name, { ...existing, ...argument });
1218
+ } else {
1219
+ argumentsByName.set(argument.name, argument);
1220
+ }
1221
+ }
1222
+ return [...argumentsByName.values()];
1223
+ };
1224
+
1225
+ const transformBooleanEnv = (value) => {
1226
+ if (value === void 0) {
1227
+ return void 0;
1228
+ }
1229
+ const normalized = value.toLowerCase().trim();
1230
+ return normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on";
1231
+ };
1232
+ const transformEnvValue = (envDefinition, envValue) => {
1233
+ if (!envDefinition.type) {
1234
+ return envValue;
1235
+ }
1236
+ if (envValue === void 0) {
1237
+ return void 0;
1238
+ }
1239
+ const isBooleanType = envDefinition.type === Boolean || typeof envDefinition.type === "function" && envDefinition.type.name === "Boolean";
1240
+ if (isBooleanType) {
1241
+ return transformBooleanEnv(envValue);
1242
+ }
1243
+ const isNumberType = envDefinition.type === Number || typeof envDefinition.type === "function" && envDefinition.type.name === "Number";
1244
+ if (isNumberType) {
1245
+ const parsed = Number.parseFloat(envValue);
1246
+ return Number.isNaN(parsed) ? void 0 : parsed;
1247
+ }
1248
+ const isStringType = envDefinition.type === String || typeof envDefinition.type === "function" && envDefinition.type.name === "String";
1249
+ if (isStringType) {
1250
+ return envValue;
1251
+ }
1252
+ return envDefinition.type(envValue);
1253
+ };
1254
+ const UNDERSCORE_CHAR_PATTERN = /_./g;
1255
+ const LEADING_UPPERCASE_PATTERN = /^[A-Z]/;
1256
+ const toCamelCase = (name) => name.toLowerCase().replaceAll(UNDERSCORE_CHAR_PATTERN, (match) => match[1]?.toUpperCase() ?? match).replace(LEADING_UPPERCASE_PATTERN, (char) => char.toLowerCase());
1257
+ const processEnvVariables = (envDefinitions) => {
1258
+ if (!envDefinitions || envDefinitions.length === 0) {
1259
+ return {};
1260
+ }
1261
+ const result = {};
1262
+ const env = getEnv();
1263
+ for (const envDefinition of envDefinitions) {
1264
+ const envValue = env[envDefinition.name];
1265
+ const transformedValue = transformEnvValue(envDefinition, envValue);
1266
+ const finalValue = transformedValue === void 0 ? envDefinition.defaultValue : transformedValue;
1267
+ const camelCaseName = toCamelCase(envDefinition.name);
1268
+ result[camelCaseName] = finalValue;
1269
+ }
1270
+ return result;
1271
+ };
1272
+
1273
+ const buildOptionMaps = (commandOptions) => {
1274
+ const optionMapByName = /* @__PURE__ */ new Map();
1275
+ const optionMapByAlias = /* @__PURE__ */ new Map();
1276
+ for (const option of commandOptions) {
1277
+ optionMapByName.set(option.name, option);
1278
+ if (option.alias) {
1279
+ const aliases = Array.isArray(option.alias) ? option.alias : [option.alias];
1280
+ for (const alias of aliases) {
1281
+ optionMapByAlias.set(alias, option);
1282
+ }
1283
+ }
1284
+ }
1285
+ return { optionMapByAlias, optionMapByName };
1286
+ };
1287
+ const prepareToolbox = (command, parsedArgs, booleanValues, extraOptions) => {
1288
+ const toolbox = new EmptyToolbox(command.name, command);
1289
+ const { _all, positionals } = parsedArgs;
1290
+ const hasBooleanValues = Object.keys(booleanValues).length > 0;
1291
+ const mergedAll = hasBooleanValues ? { ..._all, ...booleanValues } : _all;
1292
+ if (POSITIONALS_KEY in mergedAll) {
1293
+ delete mergedAll[POSITIONALS_KEY];
1294
+ }
1295
+ toolbox.argument = positionals?.[POSITIONALS_KEY] ?? [];
1296
+ const hasExtraOptions = Object.keys(extraOptions).length > 0;
1297
+ toolbox.options = hasExtraOptions ? { ...mergedAll, ...extraOptions } : mergedAll;
1298
+ toolbox.env = processEnvVariables(command.env);
1299
+ return toolbox;
1300
+ };
1301
+ const processCommandArgs = (command, commandArguments, defaultOptions) => {
1302
+ const commandOptions = command.options ?? [];
1303
+ const hasCommandOptions = commandOptions.length > 0;
1304
+ let arguments_ = hasCommandOptions ? mergeArguments([...commandOptions, ...defaultOptions]) : mergeArguments(defaultOptions);
1305
+ if (arguments_.length > 0) {
1306
+ for (const argument of arguments_) {
1307
+ if (argument.multiple && argument.lazyMultiple) {
1308
+ throw new Error(`Argument "${argument.name}" cannot have both multiple and lazyMultiple options, please choose one.`);
1309
+ }
1310
+ }
1311
+ }
1312
+ if (command.argument) {
1313
+ arguments_ = [
1314
+ {
1315
+ defaultOption: true,
1316
+ description: command.argument.description,
1317
+ group: "positionals",
1318
+ multiple: true,
1319
+ name: POSITIONALS_KEY,
1320
+ type: command.argument.type,
1321
+ typeLabel: command.argument.typeLabel
1322
+ },
1323
+ ...arguments_
1324
+ ];
1325
+ }
1326
+ let argvForParsing;
1327
+ let booleanValues;
1328
+ if (hasCommandOptions) {
1329
+ const { optionMapByAlias, optionMapByName } = buildOptionMaps(commandOptions);
1330
+ argvForParsing = removeBooleanValues(commandArguments, commandOptions, optionMapByName, optionMapByAlias);
1331
+ booleanValues = getBooleanValues(commandArguments, commandOptions, optionMapByName, optionMapByAlias);
1332
+ } else {
1333
+ argvForParsing = commandArguments;
1334
+ booleanValues = {};
1335
+ }
1336
+ const parsedArgs = commandLineArgs(arguments_, {
1337
+ argv: argvForParsing,
1338
+ camelCase: true,
1339
+ partial: true,
1340
+ stopAtFirstUnknown: true
1341
+ });
1342
+ return { arguments_, booleanValues, parsedArgs };
1343
+ };
1344
+ const executeCommand = async (command, toolbox, _commandArgs) => command.execute(toolbox);
1345
+
1346
+ class CommandValidationError extends CerebroError {
1347
+ commandName;
1348
+ missingOptions;
1349
+ constructor(commandName, missingOptions) {
1350
+ super(`Command "${commandName}" is missing required options: ${missingOptions.join(", ")}`, "COMMAND_VALIDATION_ERROR", {
1351
+ commandName,
1352
+ missingOptions
1353
+ });
1354
+ this.name = "CommandValidationError";
1355
+ this.commandName = commandName;
1356
+ this.missingOptions = missingOptions;
1357
+ this.hint = `Provide the following required options: ${missingOptions.join(", ")}`;
1358
+ }
1359
+ }
1360
+
1361
+ const listMissingArguments = (commandLineConfig, parsedArguments, onlyRequired = false) => {
1362
+ const missing = [];
1363
+ for (const config of commandLineConfig) {
1364
+ if (!onlyRequired && !config.required) {
1365
+ continue;
1366
+ }
1367
+ if (parsedArguments[config.name] !== void 0) {
1368
+ continue;
1369
+ }
1370
+ if (config.type?.name === "Boolean") {
1371
+ parsedArguments[config.name] = false;
1372
+ continue;
1373
+ }
1374
+ missing.push(config);
1375
+ }
1376
+ return missing;
1377
+ };
1378
+
1379
+ const isSimilar = (string1, string2) => {
1380
+ if (string2.includes(string1)) {
1381
+ return true;
1382
+ }
1383
+ const lengthDiff = Math.abs(string1.length - string2.length);
1384
+ if (lengthDiff > string1.length / 2) {
1385
+ return false;
1386
+ }
1387
+ return distance(string1, string2) <= string1.length / 3;
1388
+ };
1389
+ const findAlternatives = (string, array) => {
1390
+ const id = string.toLowerCase();
1391
+ return array.filter((nextId) => isSimilar(nextId.toLowerCase(), id));
1392
+ };
1393
+
1394
+ const validateUnknownOptions = (commandArguments, command) => {
1395
+ const errors = [];
1396
+ if (commandArguments._unknown) {
1397
+ commandArguments._unknown.forEach((unknownOption) => {
1398
+ const isOption = unknownOption.startsWith("--");
1399
+ let error = `Found unknown ${isOption ? "option" : "argument"} "${unknownOption}"`;
1400
+ if (isOption) {
1401
+ const foundAlternatives = findAlternatives(
1402
+ unknownOption.replace("--", ""),
1403
+ (command.options ?? []).map((option) => option.name)
1404
+ );
1405
+ if (foundAlternatives.length > 0) {
1406
+ const [first, ...rest] = foundAlternatives.map((alternative) => `--${alternative}`);
1407
+ error += rest.length > 0 ? `, did you mean ${first ?? ""} or ${rest.join(", ")}?` : `, did you mean ${first ?? ""}?`;
1408
+ }
1409
+ }
1410
+ errors.push(error);
1411
+ });
1412
+ }
1413
+ if (errors.length > 0) {
1414
+ throw new Error(errors.join("\n"));
1415
+ }
1416
+ };
1417
+ const validateRequiredOptions = (arguments_, commandArguments, command) => {
1418
+ const requiredOptions = command.__requiredOptions__;
1419
+ const missingOptions = requiredOptions ? listMissingArguments(requiredOptions, commandArguments, true) : listMissingArguments(arguments_, commandArguments, false);
1420
+ if (missingOptions.length > 0) {
1421
+ throw new CommandValidationError(
1422
+ command.name,
1423
+ missingOptions.map((argument) => argument.name)
1424
+ );
1425
+ }
1426
+ if (commandArguments._unknown && commandArguments._unknown.length > 0 && !command.argument) {
1427
+ validateUnknownOptions(commandArguments, command);
1428
+ }
1429
+ };
1430
+ const validateConflictingOptions = (arguments_, commandArguments, command) => {
1431
+ const conflicts = command.__conflictingOptions__ ?? arguments_.filter((argument) => argument.conflicts !== void 0);
1432
+ if (conflicts.length > 0) {
1433
+ const conflict = conflicts.find((argument) => {
1434
+ if (Array.isArray(argument.conflicts)) {
1435
+ return argument.conflicts.some((c) => commandArguments[c] !== void 0) && commandArguments[argument.name] !== void 0;
1436
+ }
1437
+ return commandArguments[argument.conflicts] !== void 0 && commandArguments[argument.name] !== void 0;
1438
+ });
1439
+ if (conflict) {
1440
+ throw new ConflictingOptionsError(
1441
+ conflict.name,
1442
+ typeof conflict.conflicts === "string" ? conflict.conflicts : conflict.conflicts?.[0] ?? "unknown"
1443
+ );
1444
+ }
1445
+ }
1446
+ };
1447
+ const validateDuplicateOptions = (command) => {
1448
+ if (!Array.isArray(command.options)) {
1449
+ return;
1450
+ }
1451
+ const byName = /* @__PURE__ */ new Map();
1452
+ const byAlias = /* @__PURE__ */ new Map();
1453
+ for (const opt of command.options) {
1454
+ if (opt.name) {
1455
+ const existing = byName.get(opt.name) ?? [];
1456
+ existing.push(opt);
1457
+ byName.set(opt.name, existing);
1458
+ }
1459
+ if (typeof opt.alias === "string" && opt.alias.length > 0) {
1460
+ const existing = byAlias.get(opt.alias) ?? [];
1461
+ existing.push(opt);
1462
+ byAlias.set(opt.alias, existing);
1463
+ } else if (Array.isArray(opt.alias)) {
1464
+ for (const alias of opt.alias) {
1465
+ if (alias.length > 0) {
1466
+ const existing = byAlias.get(alias) ?? [];
1467
+ existing.push(opt);
1468
+ byAlias.set(alias, existing);
1469
+ }
1470
+ }
1471
+ }
1472
+ }
1473
+ const errors = [];
1474
+ for (const [name, group] of byName) {
1475
+ if (group.length > 1) {
1476
+ errors.push(`Duplicate option name "${name}" in command "${command.name}": ${JSON.stringify(group)}`);
1477
+ }
1478
+ }
1479
+ for (const [alias, group] of byAlias) {
1480
+ if (group.length > 1) {
1481
+ errors.push(`Duplicate option alias "-${alias}" used by options ${group.map((o) => `"${o.name}"`).join(", ")} in command "${command.name}"`);
1482
+ }
1483
+ }
1484
+ if (errors.length > 0) {
1485
+ throw new Error(errors.join("\n"));
1486
+ }
1487
+ };
1488
+
1489
+ const parseNestedCommand = (availableCommands, argv) => {
1490
+ if (argv.length === 0) {
1491
+ return { argv: [], commandPath: void 0 };
1492
+ }
1493
+ const pathKeyParts = [];
1494
+ for (let depth = 1; depth <= argv.length; depth += 1) {
1495
+ const argument = argv[depth - 1];
1496
+ if (argument === void 0) {
1497
+ break;
1498
+ }
1499
+ pathKeyParts.push(argument);
1500
+ const pathKey = pathKeyParts.join(" ");
1501
+ if (availableCommands.has(pathKey)) {
1502
+ const remainingArgv = argv.slice(depth);
1503
+ return { argv: remainingArgv, commandPath: [...pathKeyParts] };
1504
+ }
1505
+ }
1506
+ return { argv, commandPath: void 0 };
1507
+ };
1508
+ const getCommandPathKey = (commandPath) => commandPath.join(" ");
1509
+ const getFullCommandPath = (commandName, commandPath) => {
1510
+ if (commandPath && commandPath.length > 0) {
1511
+ return [...commandPath, commandName];
1512
+ }
1513
+ return [commandName];
1514
+ };
1515
+
1516
+ const r = String.raw;
1517
+ const e = r`\p{Emoji}(?:\p{EMod}|[\u{E0020}-\u{E007E}]+\u{E007F}|\uFE0F?\u20E3?)`;
1518
+ const emojiRegex = () => new RegExp(r`\p{RI}{2}|(?![#*\d](?!\uFE0F?\u20E3))${e}(?:\u200D${e})*`, "gu");
1519
+ Object.freeze(
1520
+ /* @__PURE__ */ new Map([
1521
+ [0, 0],
1522
+ // Reset all
1523
+ [1, 22],
1524
+ // Bold → Not bold
1525
+ [2, 22],
1526
+ // Dim → Not bold
1527
+ [3, 23],
1528
+ // Italic → Not italic
1529
+ [4, 24],
1530
+ // Underline → Not underline
1531
+ [7, 27],
1532
+ // Inverse → Not inverse
1533
+ [8, 28],
1534
+ // Hidden → Not hidden
1535
+ [9, 29],
1536
+ // Strikethrough → Not strikethrough
1537
+ [30, 39],
1538
+ // Foreground colors → Default foreground
1539
+ [31, 39],
1540
+ [32, 39],
1541
+ [33, 39],
1542
+ [34, 39],
1543
+ [35, 39],
1544
+ [36, 39],
1545
+ [37, 39],
1546
+ [40, 49],
1547
+ // Background colors → Default background
1548
+ [41, 49],
1549
+ [42, 49],
1550
+ [43, 49],
1551
+ [44, 49],
1552
+ [45, 49],
1553
+ [46, 49],
1554
+ [47, 49],
1555
+ [90, 39]
1556
+ // Bright foreground → Default foreground
1557
+ ])
1558
+ );
1559
+ const RE_EMOJI = emojiRegex();
1560
+ const RE_SEPARATORS = /[-_./\s]+/g;
1561
+ const RE_FAST_ANSI = /(\u001B\[[0-9;]*[a-z])/i;
1562
+ const RE_ARABIC = new RegExp("\\p{Script=Arabic}", "u");
1563
+ const RE_BENGALI = new RegExp("\\p{Script=Bengali}", "u");
1564
+ const RE_CYRILLIC = new RegExp("\\p{Script=Cyrillic}", "u");
1565
+ const RE_DEVANAGARI = new RegExp("\\p{Script=Devanagari}", "u");
1566
+ const RE_ETHIOPIC = new RegExp("\\p{Script=Ethiopic}", "u");
1567
+ const RE_GREEK = new RegExp("\\p{Script=Greek}", "u");
1568
+ const RE_GREEK_LATIN_SPLIT = new RegExp("\\p{Script=Greek}+|\\p{Script=Latin}+|[^\\p{Script=Greek}\\p{Script=Latin}]+", "gu");
1569
+ const RE_GUJARATI = new RegExp("\\p{Script=Gujarati}", "u");
1570
+ const RE_GURMUKHI = new RegExp("\\p{Script=Gurmukhi}", "u");
1571
+ const RE_HANGUL = new RegExp("\\p{Script=Hangul}", "u");
1572
+ const RE_HEBREW = new RegExp("\\p{Script=Hebrew}", "u");
1573
+ const RE_HIRAGANA = new RegExp("\\p{Script=Hiragana}", "u");
1574
+ const RE_KANJI = new RegExp("\\p{Script=Han}", "u");
1575
+ const RE_KANNADA = new RegExp("\\p{Script=Kannada}", "u");
1576
+ const RE_KATAKANA = new RegExp("\\p{Script=Katakana}", "u");
1577
+ const RE_KHMER = new RegExp("\\p{Script=Khmer}", "u");
1578
+ const RE_LAO = new RegExp("\\p{Script=Lao}", "u");
1579
+ const RE_LATIN = new RegExp("\\p{Script=Latin}", "u");
1580
+ const RE_MALAYALAM = new RegExp("\\p{Script=Malayalam}", "u");
1581
+ const RE_MYANMAR = new RegExp("\\p{Script=Myanmar}", "u");
1582
+ const RE_ORIYA = new RegExp("\\p{Script=Oriya}", "u");
1583
+ const RE_SINHALA = new RegExp("\\p{Script=Sinhala}", "u");
1584
+ const RE_TAMIL = new RegExp("\\p{Script=Tamil}", "u");
1585
+ const RE_TELUGU = new RegExp("\\p{Script=Telugu}", "u");
1586
+ const RE_THAI = new RegExp("\\p{Script=Thai}", "u");
1587
+ const RE_TIBETAN = new RegExp("\\p{Script=Tibetan}", "u");
1588
+ const RE_UZBEK_LATIN_MODIFIER = /[\u02BB\u02BC\u0027]/u;
1589
+ const stripEmoji = (stringValue) => stringValue.replace(RE_EMOJI, "");
1590
+
1591
+ class LRUCache {
1592
+ capacity;
1593
+ cache;
1594
+ keyOrder;
1595
+ constructor(capacity) {
1596
+ this.capacity = capacity;
1597
+ this.cache = /* @__PURE__ */ new Map();
1598
+ this.keyOrder = [];
1599
+ }
1600
+ get(key) {
1601
+ if (!this.cache.has(key)) {
1602
+ return void 0;
1603
+ }
1604
+ this.keyOrder = this.keyOrder.filter((k) => k !== key);
1605
+ this.keyOrder.push(key);
1606
+ return this.cache.get(key);
1607
+ }
1608
+ has(key) {
1609
+ return this.cache.has(key);
1610
+ }
1611
+ set(key, value) {
1612
+ if (this.cache.has(key)) {
1613
+ this.keyOrder = this.keyOrder.filter((k) => k !== key);
1614
+ } else if (this.cache.size >= this.capacity) {
1615
+ const lruKey = this.keyOrder.shift();
1616
+ if (lruKey !== void 0) {
1617
+ this.cache.delete(lruKey);
1618
+ }
1619
+ }
1620
+ this.cache.set(key, value);
1621
+ this.keyOrder.push(key);
1622
+ }
1623
+ delete(key) {
1624
+ this.cache.delete(key);
1625
+ this.keyOrder = this.keyOrder.filter((k) => k !== key);
1626
+ }
1627
+ clear() {
1628
+ this.cache.clear();
1629
+ this.keyOrder = [];
1630
+ }
1631
+ size() {
1632
+ return this.cache.size;
1633
+ }
1634
+ }
1635
+
1636
+ const lowerFirst = (value, options) => {
1637
+ if (typeof value !== "string" || value === "") {
1638
+ return "";
1639
+ }
1640
+ const firstChar = value[0].toLowerCase();
1641
+ return firstChar + value.slice(1);
1642
+ };
1643
+
1644
+ const {
1645
+ stripVTControlCharacters
1646
+ } = __cjs_getBuiltinModule("node:util");
1647
+ const regexCache = new LRUCache(1e3);
1648
+ const RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
1649
+ const getSeparatorsRegex = (separators) => {
1650
+ const key = separators.join("");
1651
+ if (regexCache.has(key)) {
1652
+ return regexCache.get(key);
1653
+ }
1654
+ const pattern = separators.map((s) => s.replaceAll(RE_ESCAPE_SPECIAL, String.raw`\$&`)).join("|");
1655
+ const regex = new RegExp(pattern, "g");
1656
+ regexCache.set(key, regex);
1657
+ return regex;
1658
+ };
1659
+ const splitByEmoji = (text) => {
1660
+ const segments = [];
1661
+ let lastIndex = 0;
1662
+ let match;
1663
+ RE_EMOJI.lastIndex = 0;
1664
+ while ((match = RE_EMOJI.exec(text)) !== null) {
1665
+ if (match.index > lastIndex) {
1666
+ segments.push(text.slice(lastIndex, match.index));
1667
+ }
1668
+ segments.push(match[0]);
1669
+ lastIndex = RE_EMOJI.lastIndex;
1670
+ }
1671
+ if (lastIndex < text.length) {
1672
+ segments.push(text.slice(lastIndex));
1673
+ }
1674
+ return segments.filter(Boolean);
1675
+ };
1676
+ const RE_SLOVENIAN_SPECIAL = /[ČŠŽĐ]/i;
1677
+ const isUpperCode = new Uint8Array(128);
1678
+ const isLowerCode = new Uint8Array(128);
1679
+ const isDigitCode = new Uint8Array(128);
1680
+ for (let index = 0; index < 128; index++) {
1681
+ isUpperCode[index] = index >= 65 && index <= 90 ? 1 : 0;
1682
+ isLowerCode[index] = index >= 97 && index <= 122 ? 1 : 0;
1683
+ isDigitCode[index] = index >= 48 && index <= 57 ? 1 : 0;
1684
+ }
1685
+ const isUpper = (code) => isUpperCode[code];
1686
+ const isLower = (code) => isLowerCode[code];
1687
+ const isDigit = (code) => isDigitCode[code];
1688
+ const handleScriptTransitions = (s, scriptDetectors, caseSensitive, locale, customSplitLogic) => {
1689
+ if (s.length === 0) {
1690
+ return [];
1691
+ }
1692
+ let hasDetectedScript = false;
1693
+ const detectorValues = Object.values(scriptDetectors);
1694
+ for (const detectorValue of detectorValues) {
1695
+ if (detectorValue(s[0])) {
1696
+ hasDetectedScript = true;
1697
+ break;
1698
+ }
1699
+ }
1700
+ if (!hasDetectedScript && !caseSensitive) {
1701
+ return [s];
1702
+ }
1703
+ const chars = [...s];
1704
+ const result = [];
1705
+ let currentSegment = chars[0];
1706
+ let previousType = "other";
1707
+ const scriptEntries = Object.entries(scriptDetectors);
1708
+ for (const scriptEntry of scriptEntries) {
1709
+ const [type, detector] = scriptEntry;
1710
+ if (detector(chars[0])) {
1711
+ previousType = type;
1712
+ break;
1713
+ }
1714
+ }
1715
+ let previousIsUpper = caseSensitive && locale ? chars[0] === chars[0].toLocaleUpperCase(locale) : false;
1716
+ for (let index = 1; index < chars.length; index++) {
1717
+ const char = chars[index];
1718
+ let currentType = "other";
1719
+ for (const scriptEntry of scriptEntries) {
1720
+ const [type, detector] = scriptEntry;
1721
+ if (detector(char)) {
1722
+ currentType = type;
1723
+ break;
1724
+ }
1725
+ }
1726
+ const isUpperCaseChar = caseSensitive && locale ? char === char.toLocaleUpperCase(locale) : false;
1727
+ let shouldSplit = false;
1728
+ if (customSplitLogic) {
1729
+ shouldSplit = customSplitLogic(previousType, currentType, previousIsUpper, isUpperCaseChar, char, index, chars);
1730
+ } else {
1731
+ if (previousType !== currentType && previousType !== "other" && currentType !== "other") {
1732
+ shouldSplit = true;
1733
+ }
1734
+ if (caseSensitive && currentType !== "other" && !previousIsUpper && isUpperCaseChar) {
1735
+ shouldSplit = true;
1736
+ }
1737
+ }
1738
+ if (shouldSplit) {
1739
+ result.push(currentSegment);
1740
+ currentSegment = char;
1741
+ } else {
1742
+ currentSegment += char;
1743
+ }
1744
+ previousType = currentType;
1745
+ if (caseSensitive) {
1746
+ previousIsUpper = isUpperCaseChar;
1747
+ }
1748
+ }
1749
+ if (currentSegment && currentSegment.length > 0) {
1750
+ result.push(currentSegment);
1751
+ }
1752
+ return result.length > 0 ? result : [s];
1753
+ };
1754
+ const detectAndProcessAcronym = (s, start, knownAcronyms, tokens) => {
1755
+ if (knownAcronyms.size === 0) {
1756
+ return start;
1757
+ }
1758
+ for (const acronym of knownAcronyms) {
1759
+ if (s.startsWith(acronym, start)) {
1760
+ tokens.push(acronym);
1761
+ return start + acronym.length;
1762
+ }
1763
+ }
1764
+ return start;
1765
+ };
1766
+ const splitCamelCaseFast = (s, knownAcronyms = /* @__PURE__ */ new Set()) => {
1767
+ if (s.length === 0) {
1768
+ return [];
1769
+ }
1770
+ if (s.toUpperCase() === s) {
1771
+ return [s];
1772
+ }
1773
+ let start = 0;
1774
+ const tokens = [];
1775
+ const width = s.length;
1776
+ for (let index = 1; index < width; index++) {
1777
+ const newStart = detectAndProcessAcronym(s, start, knownAcronyms, tokens);
1778
+ if (newStart !== start) {
1779
+ start = newStart;
1780
+ index = start - 1;
1781
+ continue;
1782
+ }
1783
+ const previousCode = s.codePointAt(index - 1);
1784
+ const currentCode = s.codePointAt(index);
1785
+ const previousIsUpper = previousCode && previousCode < 128 && isUpper(previousCode);
1786
+ const currentIsUpper = currentCode && currentCode < 128 && isUpper(currentCode);
1787
+ const previousIsLower = previousCode && previousCode < 128 && isLower(previousCode);
1788
+ const previousIsDigit = previousCode && previousCode < 128 && isDigit(previousCode);
1789
+ const currentIsDigit = currentCode && currentCode < 128 && isDigit(currentCode);
1790
+ if (previousIsLower && currentIsUpper) {
1791
+ tokens.push(s.slice(start, index));
1792
+ start = index;
1793
+ continue;
1794
+ }
1795
+ if (previousIsDigit && !currentIsDigit || !previousIsDigit && currentIsDigit) {
1796
+ tokens.push(s.slice(start, index));
1797
+ start = index;
1798
+ continue;
1799
+ }
1800
+ if (currentIsDigit && !previousIsDigit) {
1801
+ let isNextUpper = false;
1802
+ let isNextDigit = false;
1803
+ if (index + 1 < width) {
1804
+ const nextCode = s.codePointAt(index + 1);
1805
+ isNextUpper = nextCode && nextCode < 128 && isUpper(nextCode);
1806
+ isNextDigit = nextCode && nextCode < 128 && isDigit(nextCode);
1807
+ }
1808
+ if (!isNextDigit && isNextUpper) {
1809
+ tokens.push(s.slice(start, index), s.slice(index, index + 1));
1810
+ start = index + 1;
1811
+ continue;
1812
+ }
1813
+ }
1814
+ if (index + 1 < width) {
1815
+ const nextCode = s.codePointAt(index + 1);
1816
+ const nextIsLower = nextCode && nextCode < 128 && isLower(nextCode);
1817
+ if (previousIsUpper && currentIsUpper && nextIsLower) {
1818
+ const candidate = s.slice(start, index + 1);
1819
+ if (!knownAcronyms.has(candidate)) {
1820
+ tokens.push(s.slice(start, index));
1821
+ start = index;
1822
+ }
1823
+ }
1824
+ }
1825
+ }
1826
+ if (start < width) {
1827
+ tokens.push(s.slice(start));
1828
+ }
1829
+ return tokens.filter((token) => token !== "");
1830
+ };
1831
+ const splitCamelCaseLocale = (s, locale, knownAcronyms) => {
1832
+ if (s.length === 0) {
1833
+ return [];
1834
+ }
1835
+ const isUpperCase = s === s.toLocaleUpperCase(locale);
1836
+ if (locale.startsWith("de")) {
1837
+ if (!isUpperCase && s.replaceAll("ß", "SS") === s.toLocaleUpperCase(locale)) {
1838
+ return [s];
1839
+ }
1840
+ const chars2 = [...s];
1841
+ const width_2 = chars2.length;
1842
+ const result2 = [];
1843
+ let currentSegment2 = chars2[0];
1844
+ let previousIsUpper2 = chars2[0] === chars2[0].toLocaleUpperCase(locale);
1845
+ let isInUpperSequence = previousIsUpper2;
1846
+ let upperSequenceStart = previousIsUpper2 ? 0 : -1;
1847
+ for (let index = 1; index < width_2; index++) {
1848
+ const char = chars2[index];
1849
+ const isUpperCaseChar = char === char.toLocaleUpperCase(locale);
1850
+ if (isUpperCaseChar === previousIsUpper2) {
1851
+ currentSegment2 += char;
1852
+ } else if (isUpperCaseChar) {
1853
+ if (currentSegment2 && currentSegment2.length > 0) {
1854
+ result2.push(currentSegment2);
1855
+ currentSegment2 = char;
1856
+ }
1857
+ isInUpperSequence = true;
1858
+ upperSequenceStart = index;
1859
+ } else {
1860
+ if (isInUpperSequence && index - upperSequenceStart > 1) {
1861
+ const lastUpperChar = chars2[index - 1];
1862
+ const withoutLastUpper = currentSegment2.slice(0, -1);
1863
+ if (withoutLastUpper && withoutLastUpper.length > 0) {
1864
+ result2.push(withoutLastUpper);
1865
+ }
1866
+ currentSegment2 = lastUpperChar + char;
1867
+ } else {
1868
+ currentSegment2 += char;
1869
+ }
1870
+ isInUpperSequence = false;
1871
+ upperSequenceStart = -1;
1872
+ }
1873
+ previousIsUpper2 = isUpperCaseChar;
1874
+ }
1875
+ if (currentSegment2 && currentSegment2.length > 0) {
1876
+ result2.push(currentSegment2);
1877
+ }
1878
+ return result2;
1879
+ }
1880
+ if (locale.startsWith("uk") || locale.startsWith("ru") || locale.startsWith("bg") || locale.startsWith("sr") || locale.startsWith("mk") || locale.startsWith("be")) {
1881
+ if (!RE_CYRILLIC.test(s) && !RE_LATIN.test(s)) {
1882
+ return [s];
1883
+ }
1884
+ const chars2 = [...s];
1885
+ const width_2 = chars2.length;
1886
+ const result2 = [];
1887
+ let currentSegment2 = chars2[0];
1888
+ const firstChar = chars2[0];
1889
+ let previousType;
1890
+ if (RE_CYRILLIC.test(firstChar)) {
1891
+ previousType = 1;
1892
+ } else if (RE_LATIN.test(firstChar)) {
1893
+ previousType = 2;
1894
+ } else {
1895
+ previousType = 0;
1896
+ }
1897
+ let previousIsUpper2 = firstChar === firstChar.toLocaleUpperCase(locale);
1898
+ for (let index = 1; index < width_2; index++) {
1899
+ const char = chars2[index];
1900
+ let currentType;
1901
+ if (RE_CYRILLIC.test(char)) {
1902
+ currentType = 1;
1903
+ } else if (RE_LATIN.test(char)) {
1904
+ currentType = 2;
1905
+ } else {
1906
+ currentType = 0;
1907
+ }
1908
+ const isUpperCaseChar = char === char.toLocaleUpperCase(locale);
1909
+ if (previousType !== currentType && (previousType === 1 || previousType === 2) && (currentType === 1 || currentType === 2) || currentType === previousType && !previousIsUpper2 && isUpperCaseChar) {
1910
+ result2.push(currentSegment2);
1911
+ currentSegment2 = char;
1912
+ } else {
1913
+ currentSegment2 += char;
1914
+ }
1915
+ previousType = currentType;
1916
+ previousIsUpper2 = isUpperCaseChar;
1917
+ }
1918
+ if (currentSegment2 && currentSegment2.length > 0) {
1919
+ result2.push(currentSegment2);
1920
+ }
1921
+ const finalResult = [];
1922
+ for (let index = 0; index < result2.length; index++) {
1923
+ if (index < result2.length - 1 && result2[index].length === 1 && RE_LATIN.test(result2[index]) && RE_CYRILLIC.test(result2[index + 1][0])) {
1924
+ finalResult.push(result2[index] + result2[index + 1]);
1925
+ index += 1;
1926
+ } else {
1927
+ finalResult.push(result2[index]);
1928
+ }
1929
+ }
1930
+ return finalResult;
1931
+ }
1932
+ if (locale.startsWith("el")) {
1933
+ if (!RE_GREEK.test(s) && !RE_LATIN.test(s)) {
1934
+ return [s];
1935
+ }
1936
+ const parts = [];
1937
+ RE_GREEK_LATIN_SPLIT.lastIndex = 0;
1938
+ let greekExecMatch;
1939
+ while ((greekExecMatch = RE_GREEK_LATIN_SPLIT.exec(s)) !== null) {
1940
+ parts.push(greekExecMatch[0]);
1941
+ }
1942
+ if (parts.length === 0) {
1943
+ parts.push(s);
1944
+ }
1945
+ const result2 = [];
1946
+ const width = parts.length;
1947
+ if (width === 1) {
1948
+ const part = parts[0];
1949
+ if (!part || !RE_GREEK.test(part[0]) || part.length === 1) {
1950
+ return [part ?? s];
1951
+ }
1952
+ }
1953
+ for (const greekPart of parts) {
1954
+ if (!greekPart) {
1955
+ continue;
1956
+ }
1957
+ if (!RE_GREEK.test(greekPart[0]) || greekPart.length === 1) {
1958
+ result2.push(greekPart);
1959
+ continue;
1960
+ }
1961
+ const partLength = greekPart.length;
1962
+ let word = greekPart[0];
1963
+ let previousIsUpper2 = greekPart[0] === greekPart[0].toLocaleUpperCase(locale);
1964
+ for (let index = 1; index < partLength; index++) {
1965
+ const char = greekPart[index];
1966
+ const isUpperCaseChar = char === char.toLocaleUpperCase(locale);
1967
+ if (!previousIsUpper2 && isUpperCaseChar) {
1968
+ result2.push(word);
1969
+ word = char;
1970
+ } else {
1971
+ word += char;
1972
+ }
1973
+ previousIsUpper2 = isUpperCaseChar;
1974
+ }
1975
+ if (word) {
1976
+ result2.push(word);
1977
+ }
1978
+ }
1979
+ return result2;
1980
+ }
1981
+ if (locale.startsWith("ja") || locale.startsWith("ko")) {
1982
+ const isJapanese = locale.startsWith("ja");
1983
+ const scriptDetectors = isJapanese ? {
1984
+ hiragana: (char) => RE_HIRAGANA.test(char),
1985
+ kanji: (char) => RE_KANJI.test(char),
1986
+ katakana: (char) => RE_KATAKANA.test(char),
1987
+ latin: (char) => RE_LATIN.test(char)
1988
+ } : {
1989
+ hangul: (char) => RE_HANGUL.test(char),
1990
+ latin: (char) => RE_LATIN.test(char)
1991
+ };
1992
+ const particles = /* @__PURE__ */ new Set(["が", "で", "と", "に", "の", "は", "へ", "も", "や", "を"]);
1993
+ if (isJapanese) {
1994
+ const baseSegments = handleScriptTransitions(
1995
+ s,
1996
+ scriptDetectors,
1997
+ false,
1998
+ locale,
1999
+ (previousType, currentType) => previousType === "hiragana" && currentType === "katakana" || previousType === "katakana" && currentType === "hiragana" || previousType === "hiragana" && currentType === "latin" || previousType === "katakana" && currentType === "latin" || previousType === "kanji" && currentType === "latin" || previousType === "latin" && (currentType === "hiragana" || currentType === "katakana" || currentType === "kanji")
2000
+ // latin -> japanese
2001
+ );
2002
+ const result2 = [];
2003
+ for (const baseSegment of baseSegments) {
2004
+ const segment = baseSegment;
2005
+ if (segment.length === 1 && particles.has(segment) && result2.length > 0) {
2006
+ result2[result2.length - 1] = result2.at(-1) + segment;
2007
+ } else {
2008
+ result2.push(segment);
2009
+ }
2010
+ }
2011
+ return result2.length > 0 ? result2 : [s];
2012
+ }
2013
+ return handleScriptTransitions(
2014
+ s,
2015
+ scriptDetectors,
2016
+ false,
2017
+ locale,
2018
+ (previousType, currentType) => previousType === "hangul" && currentType === "latin" || previousType === "latin" && currentType === "hangul"
2019
+ // latin -> hangul
2020
+ );
2021
+ }
2022
+ if (locale.startsWith("sl")) {
2023
+ const chars2 = [...s];
2024
+ const width_2 = chars2.length;
2025
+ const result2 = [];
2026
+ let currentSegment2 = chars2[0];
2027
+ let previousIsUpper2 = chars2[0] === chars2[0].toLocaleUpperCase(locale);
2028
+ for (let index = 1; index < width_2; index++) {
2029
+ const char = chars2[index];
2030
+ const isUpperCaseChar = char === char.toLocaleUpperCase(locale);
2031
+ const isSpecialChar = RE_SLOVENIAN_SPECIAL.test(char);
2032
+ const nextIsUpper = index < width_2 - 1 && chars2[index + 1] === chars2[index + 1].toLocaleUpperCase(locale);
2033
+ if (!previousIsUpper2 && isUpperCaseChar || isSpecialChar && nextIsUpper) {
2034
+ result2.push(currentSegment2);
2035
+ currentSegment2 = char;
2036
+ if (isSpecialChar && nextIsUpper) {
2037
+ result2.push(currentSegment2);
2038
+ currentSegment2 = "";
2039
+ }
2040
+ } else {
2041
+ currentSegment2 += char;
2042
+ }
2043
+ previousIsUpper2 = isUpperCaseChar;
2044
+ }
2045
+ if (currentSegment2 && currentSegment2.length > 0) {
2046
+ result2.push(currentSegment2);
2047
+ }
2048
+ return result2;
2049
+ }
2050
+ if (locale.startsWith("zh")) {
2051
+ return handleScriptTransitions(
2052
+ s,
2053
+ {
2054
+ han: (char) => RE_KANJI.test(char),
2055
+ latin: (char) => RE_LATIN.test(char)
2056
+ },
2057
+ false,
2058
+ locale
2059
+ );
2060
+ }
2061
+ if (["ar", "fa", "he", "ur"].includes(locale.split("-")[0])) {
2062
+ const isRtlChar = (ch) => RE_HEBREW.test(ch) || RE_ARABIC.test(ch);
2063
+ return handleScriptTransitions(
2064
+ s,
2065
+ {
2066
+ latin: (char) => RE_LATIN.test(char),
2067
+ rtl: (char) => isRtlChar(char)
2068
+ },
2069
+ false,
2070
+ locale
2071
+ );
2072
+ }
2073
+ if ([
2074
+ "am",
2075
+ // Amharic
2076
+ "bn",
2077
+ // Bengali
2078
+ "gu",
2079
+ // Gujarati
2080
+ "hi",
2081
+ // Hindi
2082
+ "km",
2083
+ // Khmer
2084
+ "kn",
2085
+ // Kannada
2086
+ "lo",
2087
+ // Lao
2088
+ "ml",
2089
+ // Malayalam
2090
+ "mr",
2091
+ // Marathi
2092
+ "ne",
2093
+ // Nepali
2094
+ "or",
2095
+ // Oriya
2096
+ "pa",
2097
+ // Punjabi
2098
+ "si",
2099
+ // Sinhala
2100
+ "ta",
2101
+ // Tamil
2102
+ "te",
2103
+ // Telugu
2104
+ "th"
2105
+ // Thai
2106
+ ].includes(locale.split("-")[0])) {
2107
+ const isIndicChar = (ch) => RE_DEVANAGARI.test(ch) || RE_BENGALI.test(ch) || RE_GUJARATI.test(ch) || RE_GURMUKHI.test(ch) || RE_KANNADA.test(ch) || RE_TAMIL.test(ch) || RE_TELUGU.test(ch) || RE_MALAYALAM.test(ch) || RE_SINHALA.test(ch) || RE_THAI.test(ch) || RE_LAO.test(ch) || RE_TIBETAN.test(ch) || RE_MYANMAR.test(ch) || RE_ETHIOPIC.test(ch) || RE_KHMER.test(ch) || RE_ORIYA.test(ch);
2108
+ return handleScriptTransitions(
2109
+ s,
2110
+ {
2111
+ indic: (char) => isIndicChar(char),
2112
+ latin: (char) => RE_LATIN.test(char)
2113
+ },
2114
+ false,
2115
+ locale
2116
+ );
2117
+ }
2118
+ if (["be", "bg", "ru", "sr", "uk"].includes(locale)) {
2119
+ return handleScriptTransitions(
2120
+ s,
2121
+ {
2122
+ cyrillic: (char) => RE_CYRILLIC.test(char),
2123
+ latin: (char) => RE_LATIN.test(char)
2124
+ },
2125
+ true,
2126
+ // Enable case-sensitive splitting
2127
+ locale
2128
+ );
2129
+ }
2130
+ if (["ar", "fa", "he"].includes(locale)) {
2131
+ return handleScriptTransitions(
2132
+ s,
2133
+ {
2134
+ latin: (char) => RE_LATIN.test(char),
2135
+ rtl: (char) => RE_HEBREW.test(char) || RE_ARABIC.test(char)
2136
+ },
2137
+ false,
2138
+ locale
2139
+ );
2140
+ }
2141
+ if (locale.startsWith("ko")) {
2142
+ return handleScriptTransitions(
2143
+ s,
2144
+ {
2145
+ hangul: (char) => RE_HANGUL.test(char),
2146
+ latin: (char) => RE_LATIN.test(char)
2147
+ },
2148
+ false,
2149
+ locale
2150
+ );
2151
+ }
2152
+ if (locale.startsWith("uz")) {
2153
+ if (!RE_CYRILLIC.test(s) && !RE_LATIN.test(s)) {
2154
+ return [s];
2155
+ }
2156
+ const chars2 = [...s];
2157
+ const width_2 = chars2.length;
2158
+ const result2 = [];
2159
+ let currentSegment2 = chars2[0];
2160
+ let previousIsUpper2 = chars2[0] === chars2[0].toLocaleUpperCase(locale);
2161
+ for (let index = 1; index < width_2; index++) {
2162
+ const char = chars2[index];
2163
+ const isUpperCaseChar = char === char.toLocaleUpperCase(locale);
2164
+ if (RE_UZBEK_LATIN_MODIFIER.test(char) || RE_UZBEK_LATIN_MODIFIER.test(chars2[index - 1])) {
2165
+ currentSegment2 += char;
2166
+ continue;
2167
+ }
2168
+ if (!previousIsUpper2 && isUpperCaseChar) {
2169
+ result2.push(currentSegment2);
2170
+ currentSegment2 = char;
2171
+ } else {
2172
+ currentSegment2 += char;
2173
+ }
2174
+ previousIsUpper2 = isUpperCaseChar;
2175
+ }
2176
+ if (currentSegment2 && currentSegment2.length > 0) {
2177
+ result2.push(currentSegment2);
2178
+ }
2179
+ return result2;
2180
+ }
2181
+ const chars = [...s];
2182
+ const width_ = chars.length;
2183
+ const result = [];
2184
+ let currentSegment = chars[0];
2185
+ let previousIsUpper = chars[0] === chars[0].toLocaleUpperCase(locale);
2186
+ for (const acronym of knownAcronyms) {
2187
+ if (s.startsWith(acronym)) {
2188
+ result.push(acronym);
2189
+ currentSegment = chars[acronym.length];
2190
+ previousIsUpper = currentSegment === currentSegment.toLocaleUpperCase(locale);
2191
+ break;
2192
+ }
2193
+ }
2194
+ for (let index = 1; index < width_; index++) {
2195
+ const char = chars[index];
2196
+ const isUpperCaseChar = char === char.toLocaleUpperCase(locale);
2197
+ let acronymLength = 0;
2198
+ for (const acronym of knownAcronyms) {
2199
+ if (s.startsWith(acronym, index)) {
2200
+ result.push(currentSegment, acronym);
2201
+ acronymLength = acronym.length;
2202
+ currentSegment = "";
2203
+ const lastAcronymChar = acronym.at(-1);
2204
+ if (lastAcronymChar) {
2205
+ previousIsUpper = lastAcronymChar === lastAcronymChar.toLocaleUpperCase(locale);
2206
+ }
2207
+ break;
2208
+ }
2209
+ }
2210
+ if (acronymLength > 0) {
2211
+ index += acronymLength - 1;
2212
+ continue;
2213
+ }
2214
+ if (!previousIsUpper && isUpperCaseChar) {
2215
+ result.push(currentSegment);
2216
+ currentSegment = char;
2217
+ } else {
2218
+ currentSegment += char;
2219
+ }
2220
+ previousIsUpper = isUpperCaseChar;
2221
+ }
2222
+ if (currentSegment) {
2223
+ result.push(currentSegment);
2224
+ }
2225
+ return result;
2226
+ };
2227
+ const processTextWithAnsiEmoji = (text, locale, knownAcronyms) => {
2228
+ const result = [];
2229
+ const segments = RE_FAST_ANSI.test(text) ? text.split(RE_FAST_ANSI).filter(Boolean) : [text];
2230
+ for (const segment of segments) {
2231
+ const seg = segment;
2232
+ if (RE_FAST_ANSI.test(seg)) {
2233
+ result.push(seg);
2234
+ } else {
2235
+ const subs = RE_EMOJI.test(seg) ? splitByEmoji(seg).filter(Boolean) : [seg];
2236
+ for (const emojiSub of subs) {
2237
+ if (RE_EMOJI.test(emojiSub)) {
2238
+ result.push(emojiSub);
2239
+ } else {
2240
+ if (locale) {
2241
+ const normalizedLocale = locale.toLowerCase().split("-")[0];
2242
+ result.push(...splitCamelCaseLocale(emojiSub, normalizedLocale, knownAcronyms));
2243
+ } else {
2244
+ result.push(...splitCamelCaseFast(emojiSub, knownAcronyms));
2245
+ }
2246
+ }
2247
+ }
2248
+ }
2249
+ }
2250
+ return result;
2251
+ };
2252
+ const splitByCase = (input, options = {}) => {
2253
+ if (!input || typeof input !== "string") {
2254
+ return [];
2255
+ }
2256
+ const {
2257
+ handleAnsi = false,
2258
+ handleEmoji = false,
2259
+ knownAcronyms = [],
2260
+ locale,
2261
+ normalize = false,
2262
+ separators,
2263
+ stripAnsi: stripAnsiOption = false,
2264
+ stripEmoji: stripEmojiOption = false
2265
+ } = options;
2266
+ const acronymSet = new Set([...knownAcronyms].toSorted((a, b) => b.length - a.length));
2267
+ let cleanedInput = input;
2268
+ if (stripAnsiOption) {
2269
+ cleanedInput = stripVTControlCharacters(cleanedInput);
2270
+ }
2271
+ if (stripEmojiOption) {
2272
+ cleanedInput = stripEmoji(cleanedInput);
2273
+ }
2274
+ let separatorRegex;
2275
+ if (Array.isArray(separators)) {
2276
+ separatorRegex = getSeparatorsRegex(separators);
2277
+ } else if (separators instanceof RegExp) {
2278
+ separatorRegex = separators;
2279
+ } else {
2280
+ separatorRegex = RE_SEPARATORS;
2281
+ }
2282
+ const parts = [];
2283
+ let workingInput = cleanedInput;
2284
+ const regex = separatorRegex.flags.includes("g") ? separatorRegex : new RegExp(separatorRegex.source, `${separatorRegex.flags}g`);
2285
+ while (workingInput.length > 0) {
2286
+ const match = regex.exec(workingInput);
2287
+ if (!match) {
2288
+ if (workingInput === "..") {
2289
+ parts.push("..");
2290
+ } else if (workingInput === ".") {
2291
+ parts.push(".");
2292
+ } else if (workingInput.length > 0) {
2293
+ parts.push(workingInput);
2294
+ }
2295
+ break;
2296
+ }
2297
+ const matchIndex = match.index;
2298
+ const matchText = match[0];
2299
+ const matchLength = matchText.length;
2300
+ const beforeMatch = workingInput.slice(0, matchIndex);
2301
+ const afterMatch = workingInput.slice(matchIndex + matchLength);
2302
+ if (matchText.startsWith("../")) {
2303
+ parts.push("..");
2304
+ workingInput = workingInput.slice(matchIndex + 3);
2305
+ } else if (matchText.startsWith("./")) {
2306
+ parts.push(".");
2307
+ workingInput = workingInput.slice(matchIndex + 2);
2308
+ } else if (matchIndex === 0 && matchText === "..") {
2309
+ parts.push("..");
2310
+ workingInput = workingInput.slice(2);
2311
+ } else if (matchIndex === 0 && matchText === ".") {
2312
+ parts.push(".");
2313
+ workingInput = workingInput.slice(1);
2314
+ } else {
2315
+ if (beforeMatch.length > 0) {
2316
+ parts.push(beforeMatch);
2317
+ }
2318
+ let pos = 0;
2319
+ while ((pos = matchText.indexOf("../", pos)) !== -1) {
2320
+ parts.push("..");
2321
+ pos += 3;
2322
+ }
2323
+ pos = 0;
2324
+ while ((pos = matchText.indexOf("./", pos)) !== -1) {
2325
+ if (pos === 0 || matchText[pos - 1] !== ".") {
2326
+ parts.push(".");
2327
+ }
2328
+ pos += 2;
2329
+ }
2330
+ let remainingAfterMatch = afterMatch;
2331
+ while (remainingAfterMatch.startsWith("../")) {
2332
+ parts.push("..");
2333
+ remainingAfterMatch = remainingAfterMatch.slice(3);
2334
+ }
2335
+ while (remainingAfterMatch.startsWith("./")) {
2336
+ parts.push(".");
2337
+ remainingAfterMatch = remainingAfterMatch.slice(2);
2338
+ }
2339
+ if (remainingAfterMatch === "..") {
2340
+ parts.push("..");
2341
+ break;
2342
+ } else if (remainingAfterMatch === ".") {
2343
+ parts.push(".");
2344
+ break;
2345
+ } else {
2346
+ workingInput = remainingAfterMatch;
2347
+ }
2348
+ }
2349
+ regex.lastIndex = 0;
2350
+ }
2351
+ if (parts.length === 0) {
2352
+ const standardParts = cleanedInput.split(separatorRegex).filter(Boolean);
2353
+ parts.push(...standardParts);
2354
+ }
2355
+ let tokens = [];
2356
+ for (const splitPart of parts) {
2357
+ if (handleAnsi || handleEmoji) {
2358
+ tokens.push(...processTextWithAnsiEmoji(splitPart, locale, acronymSet));
2359
+ } else if (locale) {
2360
+ tokens.push(...splitCamelCaseLocale(splitPart, locale, acronymSet));
2361
+ } else {
2362
+ tokens.push(...splitCamelCaseFast(splitPart, acronymSet));
2363
+ }
2364
+ }
2365
+ if (normalize) {
2366
+ tokens = tokens.map((token) => {
2367
+ if (acronymSet.has(token)) {
2368
+ return token;
2369
+ }
2370
+ if (locale && token === token.toLocaleUpperCase(locale)) {
2371
+ return token[0] + token.slice(1).toLocaleLowerCase(locale);
2372
+ }
2373
+ if (token.toUpperCase() === token && !acronymSet.has(token)) {
2374
+ return token.slice(0, 1) + token.slice(1).toLowerCase();
2375
+ }
2376
+ return token;
2377
+ });
2378
+ }
2379
+ return tokens;
2380
+ };
2381
+
2382
+ const upperFirst = (value, options) => {
2383
+ if (typeof value !== "string" || value === "") {
2384
+ return "";
2385
+ }
2386
+ const firstChar = value[0].toUpperCase();
2387
+ return firstChar + value.slice(1);
2388
+ };
2389
+
2390
+ const joinSegments = (segments, joiner) => {
2391
+ const { length } = segments;
2392
+ if (length === 0) {
2393
+ return "";
2394
+ }
2395
+ if (length === 1) {
2396
+ return segments[0];
2397
+ }
2398
+ const result = [];
2399
+ let ansiStart = "";
2400
+ let currentContent = "";
2401
+ for (let index = 0; index < length; index++) {
2402
+ const segment = segments[index];
2403
+ if (RE_FAST_ANSI.test(segment)) {
2404
+ if (ansiStart) {
2405
+ result.push(ansiStart + currentContent + segment);
2406
+ ansiStart = "";
2407
+ currentContent = "";
2408
+ } else {
2409
+ if (result.length > 0) {
2410
+ result.push(joiner);
2411
+ }
2412
+ ansiStart = segment;
2413
+ }
2414
+ continue;
2415
+ }
2416
+ if (ansiStart) {
2417
+ if (currentContent) {
2418
+ currentContent += joiner;
2419
+ }
2420
+ currentContent += segment;
2421
+ } else {
2422
+ if (result.length > 0) {
2423
+ result.push(joiner);
2424
+ }
2425
+ result.push(segment);
2426
+ }
2427
+ }
2428
+ return result.join("");
2429
+ };
2430
+
2431
+ const camelCase = (value, options) => {
2432
+ if (typeof value !== "string" || !value) {
2433
+ return "";
2434
+ }
2435
+ let firstWord = true;
2436
+ const result = joinSegments(
2437
+ splitByCase(value, {
2438
+ handleAnsi: options?.handleAnsi,
2439
+ handleEmoji: options?.handleEmoji,
2440
+ knownAcronyms: options?.knownAcronyms,
2441
+ locale: options?.locale,
2442
+ normalize: options?.normalize,
2443
+ separators: void 0,
2444
+ stripAnsi: options?.stripAnsi,
2445
+ stripEmoji: options?.stripEmoji
2446
+ }).map((word) => {
2447
+ const normalized = word;
2448
+ const lowered = normalized.toLowerCase();
2449
+ if (firstWord) {
2450
+ firstWord = false;
2451
+ return lowerFirst(lowered);
2452
+ }
2453
+ return upperFirst(lowered);
2454
+ }),
2455
+ ""
2456
+ );
2457
+ return result;
2458
+ };
2459
+
2460
+ const processOptionNames = (command) => {
2461
+ command.options?.forEach((option) => {
2462
+ option.__camelCaseName__ = camelCase(option.name);
2463
+ });
2464
+ };
2465
+ const addNegatableOptions = (command) => {
2466
+ if (!Array.isArray(command.options) || command.options.length === 0) {
2467
+ return;
2468
+ }
2469
+ const optionNames = /* @__PURE__ */ new Set();
2470
+ for (const option of command.options) {
2471
+ optionNames.add(option.name);
2472
+ }
2473
+ const optionsToAdd = [];
2474
+ for (const option of command.options) {
2475
+ if (option.name.startsWith("no-")) {
2476
+ const nonNegatedName = option.name.replace("no-", "");
2477
+ if (!optionNames.has(nonNegatedName)) {
2478
+ if (option.type !== Boolean) {
2479
+ throw new Error(`Cannot add negated option "${option.name}" to command "${command.name}" because it is not a boolean.`);
2480
+ }
2481
+ const negatedOption = {
2482
+ ...option,
2483
+ defaultValue: option.defaultValue === void 0 ? true : !option.defaultValue,
2484
+ name: nonNegatedName
2485
+ };
2486
+ optionsToAdd.push(negatedOption);
2487
+ optionNames.add(nonNegatedName);
2488
+ }
2489
+ }
2490
+ }
2491
+ if (optionsToAdd.length > 0) {
2492
+ command.options.push(...optionsToAdd);
2493
+ }
2494
+ };
2495
+ const mapNegatableOptions = (toolbox, command) => {
2496
+ if (!command.options || command.options.length === 0) {
2497
+ return;
2498
+ }
2499
+ const { options } = toolbox;
2500
+ const negatedOptionMap = /* @__PURE__ */ new Map();
2501
+ for (const option of command.options) {
2502
+ if (option.name.startsWith("no-")) {
2503
+ const camelCaseName = camelCase(option.name);
2504
+ negatedOptionMap.set(camelCaseName, option);
2505
+ }
2506
+ }
2507
+ const negatableKeys = Object.keys(options).filter((key) => negatedOptionMap.has(key));
2508
+ if (negatableKeys.length === 0) {
2509
+ return;
2510
+ }
2511
+ for (const negatedKey of negatableKeys) {
2512
+ const thirdChar = negatedKey.charAt(2);
2513
+ if (!thirdChar) {
2514
+ continue;
2515
+ }
2516
+ const nonNegatedKey = thirdChar.toLowerCase() + negatedKey.slice(3);
2517
+ const negatedOption = negatedOptionMap.get(negatedKey);
2518
+ if (negatedOption) {
2519
+ negatedOption.__negated__ = true;
2520
+ }
2521
+ options[nonNegatedKey] = !options[negatedKey];
2522
+ Reflect.deleteProperty(options, negatedKey);
2523
+ }
2524
+ };
2525
+ const mapImpliedOptions = (toolbox, command) => {
2526
+ if (!command.options || command.options.length === 0) {
2527
+ return;
2528
+ }
2529
+ const optionMapByCamelCase = /* @__PURE__ */ new Map();
2530
+ for (const option of command.options) {
2531
+ if (option.__camelCaseName__ && option.__negated__ === void 0 && option.implies !== void 0) {
2532
+ optionMapByCamelCase.set(option.__camelCaseName__, option);
2533
+ }
2534
+ }
2535
+ if (optionMapByCamelCase.size === 0) {
2536
+ return;
2537
+ }
2538
+ const { options } = toolbox;
2539
+ for (const optionKey of Object.keys(options)) {
2540
+ const option = optionMapByCamelCase.get(optionKey);
2541
+ if (option?.implies) {
2542
+ const { implies } = option;
2543
+ for (const [key, value] of Object.entries(implies)) {
2544
+ if (options[key] === void 0) {
2545
+ options[key] = value;
2546
+ }
2547
+ }
2548
+ }
2549
+ }
2550
+ };
2551
+
2552
+ const isElectronApp = () => !!process.versions.electron;
2553
+ const isBundledElectronApp = () => isElectronApp() && !process.defaultApp;
2554
+ const getProcessArgvBinIndex = () => {
2555
+ if (isBundledElectronApp()) {
2556
+ return 0;
2557
+ }
2558
+ return 1;
2559
+ };
2560
+ const hideBin = (argv) => argv.slice(getProcessArgvBinIndex() + 1);
2561
+
2562
+ const COMMAND_DELIMITER = " ";
2563
+ const equals = (a, b) => {
2564
+ if (a === b) {
2565
+ return true;
2566
+ }
2567
+ if (a.length !== b.length) {
2568
+ return false;
2569
+ }
2570
+ return a.every((v, index) => v === b[index]);
2571
+ };
2572
+ const parseRawCommand = (commandArray) => {
2573
+ if (typeof commandArray === "string") {
2574
+ return commandArray.split(COMMAND_DELIMITER);
2575
+ }
2576
+ const argv = getArgv();
2577
+ if (equals(commandArray, argv)) {
2578
+ return hideBin(commandArray);
2579
+ }
2580
+ return commandArray;
2581
+ };
2582
+
2583
+ const registerExceptionHandler = (logger) => {
2584
+ const uncaughtExceptionHandler = (error) => {
2585
+ logger.error(`Uncaught exception: ${error.message || error}`);
2586
+ if (error.stack) {
2587
+ logger.error(error.stack);
2588
+ }
2589
+ exitProcess(1);
2590
+ };
2591
+ const unhandledRejectionHandler = (reason, _promise) => {
2592
+ if (reason instanceof Error) {
2593
+ logger.error(`Promise rejection: ${reason.message || reason}`);
2594
+ if (reason.stack) {
2595
+ logger.error(reason.stack);
2596
+ }
2597
+ } else {
2598
+ let reasonString;
2599
+ if (typeof reason === "string") {
2600
+ reasonString = reason;
2601
+ } else {
2602
+ try {
2603
+ reasonString = JSON.stringify(reason);
2604
+ } catch {
2605
+ reasonString = String(reason);
2606
+ }
2607
+ }
2608
+ logger.error(`Promise rejection: ${reasonString}`);
2609
+ }
2610
+ exitProcess(1);
2611
+ };
2612
+ const cleanupUncaughtException = onProcessEvent("uncaughtException", uncaughtExceptionHandler);
2613
+ const cleanupUnhandledRejection = onProcessEvent("unhandledRejection", unhandledRejectionHandler);
2614
+ return () => {
2615
+ cleanupUncaughtException();
2616
+ cleanupUnhandledRejection();
2617
+ };
2618
+ };
2619
+
2620
+ const MAX_NAME_LENGTH = 100;
2621
+ const NAME_PATTERN = /^[a-z][\w-]*$/i;
2622
+ const validateNonEmptyString = (value, fieldName) => {
2623
+ if (typeof value !== "string" || value.trim().length === 0) {
2624
+ throw new CerebroError(`${fieldName} must be a non-empty string`, "INVALID_INPUT", { fieldName, value });
2625
+ }
2626
+ return value.trim();
2627
+ };
2628
+ const validateStringArray = (value, fieldName) => {
2629
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
2630
+ throw new CerebroError(`${fieldName} must be an array of strings`, "INVALID_INPUT", { fieldName, value });
2631
+ }
2632
+ return value;
2633
+ };
2634
+ const validateObject = (value, fieldName) => {
2635
+ if (typeof value !== "object" || value === null) {
2636
+ throw new CerebroError(`${fieldName} must be an object`, "INVALID_INPUT", { fieldName, value });
2637
+ }
2638
+ return value;
2639
+ };
2640
+ const validateCommandName = (name) => {
2641
+ const trimmedName = validateNonEmptyString(name, "Command name");
2642
+ if (trimmedName.length > MAX_NAME_LENGTH) {
2643
+ throw new CerebroError(`Command name is too long (maximum ${String(MAX_NAME_LENGTH)} characters)`, "INVALID_COMMAND_NAME", {
2644
+ commandName: trimmedName,
2645
+ length: trimmedName.length
2646
+ });
2647
+ }
2648
+ if (trimmedName.includes("..") || trimmedName.includes("/") || trimmedName.includes("\\") || trimmedName.includes(";") || trimmedName.includes("|") || trimmedName.includes("&")) {
2649
+ throw new CerebroError(`Command name "${trimmedName}" contains invalid characters`, "INVALID_COMMAND_NAME", { commandName: trimmedName });
2650
+ }
2651
+ if (!NAME_PATTERN.test(trimmedName)) {
2652
+ throw new CerebroError(
2653
+ `Command name "${trimmedName}" must start with a letter and contain only letters, numbers, hyphens, and underscores`,
2654
+ "INVALID_COMMAND_NAME",
2655
+ { commandName: trimmedName }
2656
+ );
2657
+ }
2658
+ return trimmedName;
2659
+ };
2660
+
2661
+ const MAX_ARGUMENT_LENGTH = 1e4;
2662
+ const MAX_ARGS = 100;
2663
+ const DANGEROUS_CHARS = /* @__PURE__ */ new Set(["\n", "\r", " ", "\0", '"', "$", "&", "'", "(", ")", ";", "<", ">", "[", "\\", "]", "`", "{", "|", "}"]);
2664
+ const sanitizeArgument = (argument) => {
2665
+ if (typeof argument !== "string") {
2666
+ throw new TypeError("Argument must be a string");
2667
+ }
2668
+ if (argument.length > MAX_ARGUMENT_LENGTH) {
2669
+ throw new Error(`Argument is too long (maximum ${String(MAX_ARGUMENT_LENGTH)} characters)`);
2670
+ }
2671
+ for (const char of argument) {
2672
+ if (DANGEROUS_CHARS.has(char)) {
2673
+ throw new Error(`Argument contains dangerous character: ${char}`);
2674
+ }
2675
+ }
2676
+ return argument.trim();
2677
+ };
2678
+ const sanitizeArguments = (args) => {
2679
+ if (!Array.isArray(args)) {
2680
+ throw new TypeError("Arguments must be an array");
2681
+ }
2682
+ if (args.length > MAX_ARGS) {
2683
+ throw new Error(`Too many arguments (maximum ${String(MAX_ARGS)})`);
2684
+ }
2685
+ return args.map((argument) => sanitizeArgument(argument));
2686
+ };
2687
+
2688
+ const OPTION_REGEX_SHORT = /^-([^\d-])$/;
2689
+ const OPTION_REGEX_LONG = /^--(\S+)/;
2690
+ const OPTION_REGEX_COMBINED = /^-([^\d-]{2,})$/;
2691
+ const isOption = (argument) => OPTION_REGEX_SHORT.test(argument) || OPTION_REGEX_LONG.test(argument) || OPTION_REGEX_COMBINED.test(argument);
2692
+ class Cli {
2693
+ #logger;
2694
+ #options;
2695
+ #argv;
2696
+ #cwd;
2697
+ #cliName;
2698
+ #packageVersion;
2699
+ #packageName;
2700
+ #pluginManager;
2701
+ #commands;
2702
+ /** Map of command path keys to full command paths for nested command lookup */
2703
+ #commandPaths;
2704
+ /**
2705
+ * Map of commands keyed by their full path string (e.g., "deploy staging")
2706
+ * This allows correct resolution when different paths share the same leaf name
2707
+ */
2708
+ #commandsByPath;
2709
+ #defaultCommand;
2710
+ #commandSection;
2711
+ #pluginsInitialized = false;
2712
+ #exceptionHandlerCleanup;
2713
+ #exceptionHandlerRegistered = false;
2714
+ #cachedCommandPathKeys;
2715
+ #cachedCommandNames;
2716
+ #cachedAllCommandPaths;
2717
+ #customGlobalOptions = [];
2718
+ /**
2719
+ * Gets all command path keys (cached for performance).
2720
+ * @returns Array of command path keys
2721
+ */
2722
+ #getCommandPathKeys() {
2723
+ if (this.#cachedCommandPathKeys === void 0) {
2724
+ this.#cachedCommandPathKeys = [...this.#commandPaths.keys()];
2725
+ }
2726
+ return this.#cachedCommandPathKeys;
2727
+ }
2728
+ /**
2729
+ * Gets all command names (cached for performance).
2730
+ * @returns Array of command names
2731
+ */
2732
+ #getCommandNames() {
2733
+ if (this.#cachedCommandNames === void 0) {
2734
+ this.#cachedCommandNames = [...this.#commands.keys()];
2735
+ }
2736
+ return this.#cachedCommandNames;
2737
+ }
2738
+ /**
2739
+ * Gets all command paths combined (cached for performance).
2740
+ * @returns Array of all command paths
2741
+ */
2742
+ #getAllCommandPaths() {
2743
+ if (this.#cachedAllCommandPaths === void 0) {
2744
+ this.#cachedAllCommandPaths = [...this.#getCommandPathKeys(), ...this.#getCommandNames()];
2745
+ }
2746
+ return this.#cachedAllCommandPaths;
2747
+ }
2748
+ /**
2749
+ * Gets all global options (built-in + custom).
2750
+ */
2751
+ #getAllGlobalOptions() {
2752
+ if (this.#customGlobalOptions.length === 0) {
2753
+ return defaultOptions;
2754
+ }
2755
+ return [...defaultOptions, ...this.#customGlobalOptions];
2756
+ }
2757
+ /**
2758
+ * Invalidates cached command arrays (call when commands are added/removed).
2759
+ */
2760
+ #invalidateCommandCache() {
2761
+ this.#cachedCommandPathKeys = void 0;
2762
+ this.#cachedCommandNames = void 0;
2763
+ this.#cachedAllCommandPaths = void 0;
2764
+ }
2765
+ /**
2766
+ * Gets parsed argv.
2767
+ * @returns Parsed and sanitized argv array
2768
+ */
2769
+ #getArgv() {
2770
+ if (this.#argv === void 0) {
2771
+ const rawArgv = parseRawCommand(this.#options.argv);
2772
+ this.#argv = sanitizeArguments(rawArgv);
2773
+ this.#setVerbosityLevel();
2774
+ }
2775
+ return this.#argv;
2776
+ }
2777
+ /**
2778
+ * Sets verbosity level from argv flags.
2779
+ */
2780
+ #setVerbosityLevel() {
2781
+ if (!this.#argv) {
2782
+ return;
2783
+ }
2784
+ const env = getEnv();
2785
+ let verbositySet = false;
2786
+ for (const argument of this.#argv) {
2787
+ if (argument === "--quiet" || argument === "-q") {
2788
+ env.CEREBRO_OUTPUT_LEVEL = String(VERBOSITY_QUIET);
2789
+ verbositySet = true;
2790
+ break;
2791
+ }
2792
+ if (argument === "--verbose" || argument === "-v") {
2793
+ env.CEREBRO_OUTPUT_LEVEL = String(VERBOSITY_VERBOSE);
2794
+ verbositySet = true;
2795
+ break;
2796
+ }
2797
+ if (argument === "--debug" || argument === "-vvv") {
2798
+ env.CEREBRO_OUTPUT_LEVEL = String(VERBOSITY_DEBUG);
2799
+ verbositySet = true;
2800
+ break;
2801
+ }
2802
+ }
2803
+ if (!verbositySet) {
2804
+ env.CEREBRO_OUTPUT_LEVEL = Object.hasOwn(env, "DEBUG") ? String(VERBOSITY_DEBUG) : String(VERBOSITY_NORMAL);
2805
+ }
2806
+ }
2807
+ /**
2808
+ * Registers exception handlers.
2809
+ */
2810
+ #ensureExceptionHandlers() {
2811
+ if (!this.#exceptionHandlerRegistered) {
2812
+ this.#exceptionHandlerCleanup = registerExceptionHandler(this.#logger);
2813
+ this.#exceptionHandlerRegistered = true;
2814
+ }
2815
+ }
2816
+ /**
2817
+ * Common command execution logic shared between run() and runCommand().
2818
+ */
2819
+ #executeCommandInternal(command, commandArguments, extraOptions, pathKey) {
2820
+ this.#logger.debug(`command '${pathKey}' found, parsing command args: ${commandArguments.join(", ")}`);
2821
+ const { arguments_, booleanValues, parsedArgs } = processCommandArgs(command, commandArguments, this.#getAllGlobalOptions());
2822
+ const hasBooleanValues = Object.keys(booleanValues).length > 0;
2823
+ const commandArgs = hasBooleanValues ? { ...parsedArgs, _all: { ...parsedArgs._all, ...booleanValues } } : parsedArgs;
2824
+ validateRequiredOptions(arguments_, commandArgs, command);
2825
+ const toolbox = prepareToolbox(command, parsedArgs, booleanValues, extraOptions);
2826
+ toolbox.runtime = this;
2827
+ toolbox.argv = this.#getArgv();
2828
+ const hasOptions = command.options && command.options.length > 0;
2829
+ if (hasOptions && command.options) {
2830
+ const negatedOptions = command.options.filter((option) => option.name.startsWith("no-"));
2831
+ for (const negatedOption of negatedOptions) {
2832
+ const nonNegatedName = negatedOption.name.replace("no-", "");
2833
+ const negatedFlag = `--${negatedOption.name}`;
2834
+ const nonNegatedFlag = `--${nonNegatedName}`;
2835
+ const hasNegatedFlag = commandArguments.includes(negatedFlag);
2836
+ const hasNonNegatedFlag = commandArguments.includes(nonNegatedFlag);
2837
+ if (hasNegatedFlag && hasNonNegatedFlag) {
2838
+ throw new ConflictingOptionsError(nonNegatedName, negatedOption.name);
2839
+ }
2840
+ }
2841
+ }
2842
+ if (hasOptions) {
2843
+ mapNegatableOptions(toolbox, command);
2844
+ mapImpliedOptions(toolbox, command);
2845
+ }
2846
+ validateConflictingOptions(arguments_, toolbox.options, command);
2847
+ const env = getEnv();
2848
+ if (env.CEREBRO_OUTPUT_LEVEL === String(VERBOSITY_DEBUG)) {
2849
+ this.#logger.debug("command options parsed from options:");
2850
+ this.#logger.debug(JSON.stringify(toolbox.options, null, 2));
2851
+ this.#logger.debug("command argument parsed from argument:");
2852
+ this.#logger.debug(JSON.stringify(toolbox.argument, null, 2));
2853
+ }
2854
+ return { arguments_, booleanValues, commandArgs, parsedArgs, toolbox };
2855
+ }
2856
+ /**
2857
+ * Create a new CLI instance.
2858
+ * @param cliName
2859
+ * @param options The options for the CLI.
2860
+ * @param options.argv The command line arguments.
2861
+ * @param options.cwd The current working directory.
2862
+ * @param options.logger The logger to use.
2863
+ * @param options.packageName
2864
+ * @param options.packageVersion
2865
+ */
2866
+ // eslint-disable-next-line sonarjs/cognitive-complexity
2867
+ constructor(cliName, options = {}) {
2868
+ if (typeof cliName !== "string" || cliName.trim().length === 0) {
2869
+ throw new CerebroError("CLI name must be a non-empty string", "INVALID_INPUT", { cliName });
2870
+ }
2871
+ this.#cliName = cliName.trim();
2872
+ const argv = options.argv ?? getArgv();
2873
+ const cwd = options.cwd ?? getCwd();
2874
+ this.#options = { ...options, argv, cwd };
2875
+ if (this.#options.argv && !Array.isArray(this.#options.argv)) {
2876
+ throw new CerebroError("CLI argv option must be an array of strings", "INVALID_INPUT", { argv: this.#options.argv });
2877
+ }
2878
+ if (this.#options.cwd && typeof this.#options.cwd !== "string") {
2879
+ throw new CerebroError("CLI cwd option must be a string", "INVALID_INPUT", { cwd: this.#options.cwd });
2880
+ }
2881
+ if (this.#options.packageName && typeof this.#options.packageName !== "string") {
2882
+ throw new CerebroError("CLI packageName option must be a string", "INVALID_INPUT", { packageName: this.#options.packageName });
2883
+ }
2884
+ if (this.#options.packageVersion && typeof this.#options.packageVersion !== "string") {
2885
+ throw new CerebroError("CLI packageVersion option must be a string", "INVALID_INPUT", { packageVersion: this.#options.packageVersion });
2886
+ }
2887
+ const env = getEnv();
2888
+ env.CEREBRO_OUTPUT_LEVEL = String(VERBOSITY_NORMAL);
2889
+ if (typeof this.#options.logger === "object") {
2890
+ const requiredMethods = ["debug", "error", "info", "log", "warn"];
2891
+ const missingMethods = [];
2892
+ const logger = this.#options.logger;
2893
+ for (const method of requiredMethods) {
2894
+ if (typeof logger[method] !== "function") {
2895
+ missingMethods.push(method);
2896
+ }
2897
+ }
2898
+ if (missingMethods.length > 0) {
2899
+ throw new CerebroError(`Logger object is missing required methods: ${missingMethods.join(", ")}`, "INVALID_INPUT", {
2900
+ logger: this.#options.logger,
2901
+ missingMethods
2902
+ });
2903
+ }
2904
+ this.#logger = this.#options.logger;
2905
+ } else {
2906
+ this.#logger = {
2907
+ ...console,
2908
+ debug: (...args) => {
2909
+ if (env.CEREBRO_OUTPUT_LEVEL === String(VERBOSITY_DEBUG)) {
2910
+ console.debug(...args);
2911
+ }
2912
+ }
2913
+ };
2914
+ }
2915
+ this.#packageVersion = this.#options.packageVersion;
2916
+ this.#packageName = this.#options.packageName;
2917
+ this.#cwd = this.#options.cwd;
2918
+ this.#defaultCommand = "help";
2919
+ this.#commandSection = {};
2920
+ this.#commands = /* @__PURE__ */ new Map();
2921
+ this.#commandPaths = /* @__PURE__ */ new Map();
2922
+ this.#commandsByPath = /* @__PURE__ */ new Map();
2923
+ }
2924
+ /**
2925
+ * Sets the command section configuration for help display.
2926
+ *
2927
+ * This affects how the CLI name and version are displayed in help output.
2928
+ * @param commandSection The command section configuration
2929
+ * @returns The CLI instance for method chaining
2930
+ * @example
2931
+ * ```typescript
2932
+ * cli.setCommandSection({
2933
+ * header: 'My App v2.0.0',
2934
+ * footer: 'For more info, visit https://example.com'
2935
+ * });
2936
+ * ```
2937
+ */
2938
+ setCommandSection(commandSection) {
2939
+ this.#commandSection = commandSection;
2940
+ return this;
2941
+ }
2942
+ /**
2943
+ * Gets the current command section configuration.
2944
+ * @returns The command section configuration
2945
+ */
2946
+ getCommandSection() {
2947
+ if (!this.#commandSection.header) {
2948
+ this.#commandSection.header = `${this.#cliName}${this.#packageVersion ? ` v${this.#packageVersion}` : ""}`;
2949
+ }
2950
+ return this.#commandSection;
2951
+ }
2952
+ /**
2953
+ * Sets the default command to run when no command is specified.
2954
+ *
2955
+ * By default, this is set to 'help'. The command must already be registered
2956
+ * with the CLI instance.
2957
+ * @param commandName The command name to use as the default
2958
+ * @returns The CLI instance for method chaining
2959
+ * @example
2960
+ * ```typescript
2961
+ * cli.setDefaultCommand('start');
2962
+ * ```
2963
+ */
2964
+ setDefaultCommand(commandName) {
2965
+ this.#defaultCommand = commandName;
2966
+ return this;
2967
+ }
2968
+ /**
2969
+ * Gets the current default command.
2970
+ * @returns The name of the default command
2971
+ */
2972
+ get defaultCommand() {
2973
+ return this.#defaultCommand;
2974
+ }
2975
+ /**
2976
+ * Adds a command to the CLI.
2977
+ *
2978
+ * Commands define the available operations that users can execute.
2979
+ * Each command can have options, arguments, aliases, and custom execution logic.
2980
+ * @template OD - The option definition type for the command
2981
+ * @param command The command configuration object
2982
+ * @returns The CLI instance for method chaining
2983
+ * @throws {CerebroError} If the command name already exists or validation fails
2984
+ * @example
2985
+ * ```typescript
2986
+ * cli.addCommand({
2987
+ * name: 'build',
2988
+ * description: 'Build the project',
2989
+ * options: [
2990
+ * {
2991
+ * name: 'output',
2992
+ * alias: 'o',
2993
+ * type: String,
2994
+ * description: 'Output directory'
2995
+ * }
2996
+ * ],
2997
+ * execute: ({ options }) => {
2998
+ * console.log(`Building to ${options.output || 'dist'}`);
2999
+ * }
3000
+ * });
3001
+ * ```
3002
+ */
3003
+ // eslint-disable-next-line sonarjs/cognitive-complexity
3004
+ addCommand(command) {
3005
+ validateObject(command, "Command");
3006
+ validateCommandName(command.name);
3007
+ if (command.alias) {
3008
+ if (typeof command.alias === "string") {
3009
+ validateCommandName(command.alias);
3010
+ } else {
3011
+ validateStringArray(command.alias, "Command alias").forEach((alias) => validateCommandName(alias));
3012
+ }
3013
+ }
3014
+ if (command.argument) {
3015
+ validateObject(command.argument, "Command argument");
3016
+ }
3017
+ if (command.options) {
3018
+ validateObject(command.options, "Command options");
3019
+ }
3020
+ if (command.commandPath) {
3021
+ validateStringArray(command.commandPath, "Command commandPath");
3022
+ command.commandPath.forEach((segment) => {
3023
+ validateCommandName(segment);
3024
+ });
3025
+ }
3026
+ const fullPath = getFullCommandPath(command.name, command.commandPath);
3027
+ const pathKey = getCommandPathKey(fullPath);
3028
+ if (this.#commandPaths.has(pathKey)) {
3029
+ throw new CerebroError(`Command with path "${pathKey}" already exists`, "DUPLICATE_COMMAND", {
3030
+ commandName: command.name,
3031
+ commandPath: command.commandPath
3032
+ });
3033
+ }
3034
+ if (this.#commands.has(command.name) && !command.commandPath) {
3035
+ throw new CerebroError(`Command with name "${command.name}" already exists`, "DUPLICATE_COMMAND", { commandName: command.name });
3036
+ }
3037
+ if (command.options) {
3038
+ for (const option of command.options) {
3039
+ mapOptionTypeLabel(option);
3040
+ }
3041
+ }
3042
+ validateDuplicateOptions(command);
3043
+ addNegatableOptions(command);
3044
+ processOptionNames(command);
3045
+ if (command.options) {
3046
+ command.__conflictingOptions__ = command.options.filter((option) => option.conflicts !== void 0);
3047
+ command.__requiredOptions__ = command.options.filter((option) => option.required === true);
3048
+ }
3049
+ this.#commands.set(command.name, command);
3050
+ this.#commandPaths.set(pathKey, fullPath);
3051
+ this.#commandsByPath.set(pathKey, command);
3052
+ this.#invalidateCommandCache();
3053
+ if (command.alias !== void 0) {
3054
+ const aliases = typeof command.alias === "string" ? [command.alias] : command.alias;
3055
+ for (const alias of aliases) {
3056
+ const env = getEnv();
3057
+ if (env.CEREBRO_OUTPUT_LEVEL === String(VERBOSITY_DEBUG)) {
3058
+ this.#logger.debug("adding alias", alias);
3059
+ }
3060
+ if (this.#commands.has(alias)) {
3061
+ throw new CerebroError(`Command alias "${alias}" conflicts with existing command`, "DUPLICATE_COMMAND", {
3062
+ alias,
3063
+ commandName: command.name
3064
+ });
3065
+ }
3066
+ this.#commands.set(alias, command);
3067
+ }
3068
+ }
3069
+ return this;
3070
+ }
3071
+ /**
3072
+ * Adds a global option available to all commands.
3073
+ *
3074
+ * Global options are parsed alongside command-specific options and displayed
3075
+ * in the help output under the "Global Options" section.
3076
+ * @param option The option definition
3077
+ * @returns The CLI instance for method chaining
3078
+ * @example
3079
+ * ```typescript
3080
+ * cli.addGlobalOption({
3081
+ * name: 'cwd',
3082
+ * type: String,
3083
+ * description: 'Override working directory',
3084
+ * });
3085
+ * ```
3086
+ */
3087
+ addGlobalOption(option) {
3088
+ const optionDefinition = option;
3089
+ const builtInNames = new Set(defaultOptions.map((o) => o.name));
3090
+ const builtInAliases = new Set(defaultOptions.map((o) => o.alias).filter(Boolean));
3091
+ if (builtInNames.has(optionDefinition.name)) {
3092
+ throw new CerebroError(`Cannot add global option "--${optionDefinition.name}": it conflicts with a built-in global option`, "DUPLICATE_OPTION", {
3093
+ optionName: optionDefinition.name
3094
+ });
3095
+ }
3096
+ if (optionDefinition.alias && builtInAliases.has(optionDefinition.alias)) {
3097
+ throw new CerebroError(
3098
+ `Cannot add global option with alias "-${optionDefinition.alias}": it conflicts with a built-in global option alias`,
3099
+ "DUPLICATE_OPTION",
3100
+ { alias: optionDefinition.alias, optionName: optionDefinition.name }
3101
+ );
3102
+ }
3103
+ const existingNames = new Set(this.#customGlobalOptions.map((o) => o.name));
3104
+ if (existingNames.has(optionDefinition.name)) {
3105
+ throw new CerebroError(`Global option "--${optionDefinition.name}" has already been added`, "DUPLICATE_OPTION", { optionName: optionDefinition.name });
3106
+ }
3107
+ optionDefinition.group = "global";
3108
+ mapOptionTypeLabel(optionDefinition);
3109
+ this.#customGlobalOptions.push(optionDefinition);
3110
+ return this;
3111
+ }
3112
+ /**
3113
+ * Gets all global options (built-in + custom).
3114
+ * @returns Array of all global option definitions
3115
+ */
3116
+ getGlobalOptions() {
3117
+ return this.#getAllGlobalOptions();
3118
+ }
3119
+ /**
3120
+ * Adds a plugin to extend the CLI functionality.
3121
+ *
3122
+ * Plugins can hook into various lifecycle events and modify the toolbox
3123
+ * to provide additional functionality to commands.
3124
+ * @param plugin The plugin to register
3125
+ * @returns The CLI instance for method chaining
3126
+ * @example
3127
+ * ```typescript
3128
+ * cli.addPlugin({
3129
+ * name: 'logger',
3130
+ * execute: (toolbox) => {
3131
+ * toolbox.logger = createCustomLogger();
3132
+ * }
3133
+ * });
3134
+ * ```
3135
+ */
3136
+ addPlugin(plugin) {
3137
+ this.getPluginManager().register(plugin);
3138
+ return this;
3139
+ }
3140
+ /**
3141
+ * Gets the plugin manager instance for advanced plugin management.
3142
+ * @returns The plugin manager instance
3143
+ */
3144
+ getPluginManager() {
3145
+ if (this.#pluginManager) {
3146
+ return this.#pluginManager;
3147
+ }
3148
+ this.#pluginManager = new PluginManager(this.#logger);
3149
+ this.#pluginManager.register({
3150
+ description: "Attaches the logger to the toolbox",
3151
+ execute: (toolbox) => {
3152
+ toolbox.logger = this.#logger;
3153
+ },
3154
+ name: "logger"
3155
+ });
3156
+ return this.#pluginManager;
3157
+ }
3158
+ /**
3159
+ * Gets the CLI application name.
3160
+ */
3161
+ getCliName() {
3162
+ return this.#cliName;
3163
+ }
3164
+ /**
3165
+ * Gets the package version if configured.
3166
+ * @returns The package version or undefined
3167
+ */
3168
+ getPackageVersion() {
3169
+ return this.#packageVersion;
3170
+ }
3171
+ /**
3172
+ * Gets the package name if configured.
3173
+ * @returns The package name or undefined
3174
+ */
3175
+ getPackageName() {
3176
+ return this.#packageName;
3177
+ }
3178
+ /**
3179
+ * Gets all registered commands.
3180
+ * @returns A map of command names to command definitions
3181
+ */
3182
+ getCommands() {
3183
+ return this.#commands;
3184
+ }
3185
+ /**
3186
+ * Gets the current working directory.
3187
+ * @returns The current working directory path
3188
+ */
3189
+ getCwd() {
3190
+ return this.#cwd;
3191
+ }
3192
+ /**
3193
+ * Disposes the CLI instance and cleans up resources.
3194
+ *
3195
+ * This method removes event listeners and performs cleanup to prevent memory leaks.
3196
+ * Call this method when the CLI instance is no longer needed, especially in long-running
3197
+ * processes or when creating multiple CLI instances.
3198
+ * @example
3199
+ * ```typescript
3200
+ * const cli = new Cerebro('my-app');
3201
+ * // ... use the cli
3202
+ * cli.dispose(); // Clean up when done
3203
+ * ```
3204
+ */
3205
+ dispose() {
3206
+ this.#exceptionHandlerCleanup?.();
3207
+ }
3208
+ /**
3209
+ * Runs the CLI application.
3210
+ *
3211
+ * This method parses command line arguments, executes the appropriate command,
3212
+ * and handles the complete CLI lifecycle including plugin initialization,
3213
+ * error handling, process termination, and automatic cleanup.
3214
+ * @param extraOptions Additional options to pass to commands
3215
+ * @param extraOptions.shouldExitProcess Whether to exit the process after execution (default: true)
3216
+ * @param extraOptions.autoDispose Whether to automatically cleanup/dispose resources after execution (default: true)
3217
+ * @returns A promise that resolves when execution completes
3218
+ * @throws {CommandNotFoundError} If the specified command doesn't exist
3219
+ * @throws {Error} If command arguments are invalid or conflicting options are provided
3220
+ * @example
3221
+ * ```typescript
3222
+ * // Run with default behavior (exits process and auto-disposes)
3223
+ * await cli.run();
3224
+ *
3225
+ * // Run without exiting (for testing)
3226
+ * await cli.run({ shouldExitProcess: false });
3227
+ *
3228
+ * // Run without auto-disposing (for reuse)
3229
+ * await cli.run({ autoDispose: false });
3230
+ * ```
3231
+ */
3232
+ // eslint-disable-next-line sonarjs/cognitive-complexity
3233
+ async run(extraOptions = {}) {
3234
+ const { autoDispose = true, shouldExitProcess = true, ...otherExtraOptions } = extraOptions;
3235
+ if (!this.#commands.has("help")) {
3236
+ this.addCommand(new HelpCommand(this.#commands));
3237
+ }
3238
+ const commandNames = this.#getCommandNames();
3239
+ const commandPathMap = this.#commandPaths;
3240
+ this.#ensureExceptionHandlers();
3241
+ const argv = this.#getArgv();
3242
+ let parsedCommandPath;
3243
+ let remainingArgv = [...argv];
3244
+ const execPath = getExecPath();
3245
+ const execArgv = getExecArgv();
3246
+ const runtimeArgv = getArgv();
3247
+ this.#logger.debug(`process.execPath: ${execPath}`);
3248
+ this.#logger.debug(`process.execArgv: ${execArgv.join(" ")}`);
3249
+ this.#logger.debug(`process.argv: ${runtimeArgv.join(" ")}`);
3250
+ const nestedResult = parseNestedCommand(commandPathMap, [...argv]);
3251
+ if (nestedResult.commandPath) {
3252
+ parsedCommandPath = nestedResult.commandPath;
3253
+ remainingArgv = nestedResult.argv;
3254
+ } else {
3255
+ if (argv.length > 1 && argv[0] && argv[1] && !isOption(argv[0]) && !isOption(argv[1])) {
3256
+ const attemptedPath = [];
3257
+ let i = 0;
3258
+ while (i < argv.length) {
3259
+ const argument = argv[i];
3260
+ if (!argument || isOption(argument)) {
3261
+ break;
3262
+ }
3263
+ attemptedPath.push(argument);
3264
+ i += 1;
3265
+ }
3266
+ const attemptedPathKey = getCommandPathKey(attemptedPath);
3267
+ if (attemptedPath[0] && !commandNames.includes(attemptedPath[0])) {
3268
+ const allCommandPaths = this.#getAllCommandPaths();
3269
+ const alternatives = findAlternatives(attemptedPathKey, allCommandPaths);
3270
+ throw new CommandNotFoundError(attemptedPathKey, alternatives);
3271
+ }
3272
+ }
3273
+ let parsedArguments;
3274
+ try {
3275
+ parsedArguments = commandLineCommands([null, ...commandNames], [...argv]);
3276
+ } catch (error) {
3277
+ if (error instanceof Error && error.name === "INVALID_COMMAND" && "command" in error) {
3278
+ const invalidCommand = error.command;
3279
+ const allCommandPaths = this.#getAllCommandPaths();
3280
+ const alternatives = findAlternatives(invalidCommand, allCommandPaths);
3281
+ throw new CommandNotFoundError(invalidCommand, alternatives);
3282
+ }
3283
+ throw error;
3284
+ }
3285
+ if (parsedArguments.command) {
3286
+ parsedCommandPath = [parsedArguments.command];
3287
+ remainingArgv = parsedArguments.argv;
3288
+ }
3289
+ }
3290
+ if (!parsedCommandPath) {
3291
+ if (this.#defaultCommand) {
3292
+ parsedCommandPath = [this.#defaultCommand];
3293
+ } else {
3294
+ const allCommandPaths = this.#getAllCommandPaths();
3295
+ throw new CommandNotFoundError("", allCommandPaths);
3296
+ }
3297
+ }
3298
+ const pathKey = getCommandPathKey(parsedCommandPath);
3299
+ const storedPath = this.#commandPaths.get(pathKey);
3300
+ let command;
3301
+ if (storedPath) {
3302
+ command = this.#commandsByPath.get(pathKey);
3303
+ if (!command || getCommandPathKey(storedPath) !== pathKey) {
3304
+ const allCommandPaths = this.#getAllCommandPaths();
3305
+ const alternatives = findAlternatives(pathKey, allCommandPaths);
3306
+ throw new CommandNotFoundError(pathKey, alternatives);
3307
+ }
3308
+ } else {
3309
+ const commandName = parsedCommandPath.at(-1);
3310
+ command = commandName ? this.#commands.get(commandName) : void 0;
3311
+ if (!command) {
3312
+ const allCommandPaths = this.#getAllCommandPaths();
3313
+ const alternatives = findAlternatives(pathKey, allCommandPaths);
3314
+ throw new CommandNotFoundError(pathKey, alternatives);
3315
+ }
3316
+ }
3317
+ if (typeof command.execute !== "function") {
3318
+ this.#logger.error(`Command "${command.name}" has no function to execute.`);
3319
+ return shouldExitProcess ? exitProcess(1) : void 0;
3320
+ }
3321
+ const commandArguments = remainingArgv;
3322
+ const { commandArgs, toolbox } = this.#executeCommandInternal(command, commandArguments, otherExtraOptions, pathKey);
3323
+ const pluginManager = this.getPluginManager();
3324
+ try {
3325
+ if (!this.#pluginsInitialized && pluginManager.hasPlugins()) {
3326
+ await pluginManager.init({
3327
+ cli: this,
3328
+ cwd: this.#cwd,
3329
+ logger: this.#logger
3330
+ });
3331
+ this.#pluginsInitialized = true;
3332
+ }
3333
+ await pluginManager.executeLifecycle("execute", toolbox);
3334
+ await pluginManager.executeLifecycle("beforeCommand", toolbox);
3335
+ let result;
3336
+ const globalOptions = commandArgs.global;
3337
+ if (globalOptions?.help) {
3338
+ const helpCommand = this.#commands.get("help");
3339
+ if (!helpCommand) {
3340
+ throw new CerebroError("Help command not found", "COMMAND_NOT_FOUND");
3341
+ }
3342
+ result = await executeCommand(helpCommand, toolbox);
3343
+ } else if (globalOptions?.version ?? globalOptions?.V) {
3344
+ const versionCommand = this.#commands.get("version");
3345
+ if (!versionCommand) {
3346
+ throw new CerebroError("Version command not found", "COMMAND_NOT_FOUND");
3347
+ }
3348
+ result = await executeCommand(versionCommand, toolbox);
3349
+ } else {
3350
+ result = await executeCommand(command, toolbox);
3351
+ }
3352
+ await pluginManager.executeLifecycle("afterCommand", toolbox, result);
3353
+ return shouldExitProcess ? exitProcess(0) : void 0;
3354
+ } catch (error) {
3355
+ await pluginManager.executeErrorHandlers(error, toolbox);
3356
+ throw error;
3357
+ } finally {
3358
+ if (autoDispose) {
3359
+ this.dispose();
3360
+ }
3361
+ }
3362
+ }
3363
+ /**
3364
+ * Runs a command programmatically from within another command.
3365
+ *
3366
+ * This method allows commands to call other commands during execution,
3367
+ * enabling composition of commands and reusable command logic.
3368
+ * @param commandName The name of the command to execute
3369
+ * @param options Optional options including argv and other command options
3370
+ * @returns A promise that resolves with the command's result
3371
+ * @throws {CommandNotFoundError} If the specified command doesn't exist
3372
+ * @throws {CerebroError} If command validation fails
3373
+ * @example
3374
+ * ```typescript
3375
+ * cli.addCommand({
3376
+ * name: 'deploy',
3377
+ * execute: async ({ runtime, logger }) => {
3378
+ * logger.info('Building...');
3379
+ * await runtime.runCommand('build', { argv: ['--production'] });
3380
+ *
3381
+ * logger.info('Testing...');
3382
+ * await runtime.runCommand('test', { argv: ['--coverage'] });
3383
+ * }
3384
+ * });
3385
+ * ```
3386
+ */
3387
+ async runCommand(commandName, options = {}) {
3388
+ const { argv: providedArgv = [], ...extraOptions } = options;
3389
+ validateNonEmptyString(commandName, "Command name");
3390
+ const commandPath = commandName.split(" ").filter(Boolean);
3391
+ const pathKey = getCommandPathKey(commandPath);
3392
+ const storedPath = this.#commandPaths.get(pathKey);
3393
+ const command = storedPath ? this.#commandsByPath.get(pathKey) : this.#commands.get(commandName);
3394
+ if (!command) {
3395
+ const allCommandPaths = this.#getAllCommandPaths();
3396
+ const alternatives = findAlternatives(pathKey || commandName, allCommandPaths);
3397
+ throw new CommandNotFoundError(commandName, alternatives);
3398
+ }
3399
+ if (typeof command.execute !== "function") {
3400
+ throw new CerebroError(`Command "${command.name}" has no function to execute`, "INVALID_COMMAND", { commandName: command.name });
3401
+ }
3402
+ const sanitizedArgv = sanitizeArguments(providedArgv);
3403
+ const commandArguments = [...sanitizedArgv];
3404
+ this.#logger.debug(`running command '${commandName}' programmatically with args: ${commandArguments.join(", ")}`);
3405
+ const { commandArgs, toolbox } = this.#executeCommandInternal(command, commandArguments, extraOptions, pathKey || commandName);
3406
+ const pluginManager = this.getPluginManager();
3407
+ try {
3408
+ if (!this.#pluginsInitialized && pluginManager.hasPlugins()) {
3409
+ await pluginManager.init({
3410
+ cli: this,
3411
+ cwd: this.#cwd,
3412
+ logger: this.#logger
3413
+ });
3414
+ this.#pluginsInitialized = true;
3415
+ }
3416
+ await pluginManager.executeLifecycle("execute", toolbox);
3417
+ await pluginManager.executeLifecycle("beforeCommand", toolbox);
3418
+ let result;
3419
+ const runGlobalOptions = commandArgs.global;
3420
+ if (runGlobalOptions?.help) {
3421
+ const helpCommand = this.#commands.get("help");
3422
+ if (!helpCommand) {
3423
+ throw new CerebroError("Help command not found", "COMMAND_NOT_FOUND");
3424
+ }
3425
+ result = await executeCommand(helpCommand, toolbox);
3426
+ } else if (runGlobalOptions?.version ?? runGlobalOptions?.V) {
3427
+ const versionCommand = this.#commands.get("version");
3428
+ if (!versionCommand) {
3429
+ throw new CerebroError("Version command not found", "COMMAND_NOT_FOUND");
3430
+ }
3431
+ result = await executeCommand(versionCommand, toolbox);
3432
+ } else {
3433
+ result = await executeCommand(command, toolbox);
3434
+ }
3435
+ await pluginManager.executeLifecycle("afterCommand", toolbox, result);
3436
+ return result;
3437
+ } catch (error) {
3438
+ await pluginManager.executeErrorHandlers(error, toolbox);
3439
+ throw error;
3440
+ }
3441
+ }
3442
+ }
3443
+
3444
+ export { Cli };