@visulima/cerebro 3.0.0-alpha.9 → 3.0.1

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