@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
365 lines (364 loc) • 12.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandRegistry = void 0;
const events_1 = require("events");
const error_handler_1 = require("../utils/error-handler");
class CommandRegistry extends events_1.EventEmitter {
constructor(logger, analytics, config, plugins) {
super();
this.logger = logger;
this.analytics = analytics;
this.config = config;
this.plugins = plugins;
this.commands = new Map();
this.aliases = new Map();
this.categories = new Map();
this.middleware = [];
this.commandHistory = [];
}
/**
* Register a new command
*/
async register(definition) {
this.emit('command:registering', definition);
// Validate command definition
this.validateCommand(definition);
// Check for conflicts
if (this.commands.has(definition.name)) {
const existing = this.commands.get(definition.name);
if (existing.priority !== undefined && definition.priority !== undefined) {
if (definition.priority > existing.priority) {
this.logger.debug(`Overriding command ${definition.name} due to higher priority`);
}
else {
throw new error_handler_1.ValidationError(`Command ${definition.name} already registered with higher priority`);
}
}
else if (!definition.plugin) {
throw new error_handler_1.ValidationError(`Command ${definition.name} already registered`);
}
}
// Register command
this.commands.set(definition.name, definition);
// Register aliases
if (definition.alias) {
for (const alias of definition.alias) {
if (this.aliases.has(alias)) {
throw new error_handler_1.ValidationError(`Alias ${alias} already registered for command ${this.aliases.get(alias)}`);
}
this.aliases.set(alias, definition.name);
}
}
// Add to category
if (definition.category) {
if (!this.categories.has(definition.category)) {
this.categories.set(definition.category, []);
}
this.categories.get(definition.category).push(definition);
}
this.emit('command:registered', definition);
this.logger.debug(`Registered command: ${definition.name}`);
}
/**
* Unregister a command
*/
async unregister(name) {
const command = this.commands.get(name);
if (!command) {
throw new error_handler_1.ValidationError(`Command ${name} not found`);
}
this.emit('command:unregistering', command);
// Remove from commands
this.commands.delete(name);
// Remove aliases
if (command.alias) {
for (const alias of command.alias) {
this.aliases.delete(alias);
}
}
// Remove from category
if (command.category) {
const categoryCommands = this.categories.get(command.category);
if (categoryCommands) {
const index = categoryCommands.indexOf(command);
if (index !== -1) {
categoryCommands.splice(index, 1);
}
}
}
this.emit('command:unregistered', command);
this.logger.debug(`Unregistered command: ${name}`);
}
/**
* Get a command by name or alias
*/
get(nameOrAlias) {
const name = this.aliases.get(nameOrAlias) || nameOrAlias;
return this.commands.get(name);
}
/**
* Get all commands
*/
getAll() {
return Array.from(this.commands.values());
}
/**
* Get commands by category
*/
getByCategory(category) {
return this.categories.get(category) || [];
}
/**
* Get all categories
*/
getCategories() {
return Array.from(this.categories.keys());
}
/**
* Add global middleware
*/
addMiddleware(middleware) {
this.middleware.push(middleware);
}
/**
* Create commander command from definition
*/
createCommand(program, definition) {
const cmd = program
.command(definition.name)
.description(definition.description);
// Add aliases
if (definition.alias && definition.alias.length > 0) {
for (const alias of definition.alias) {
cmd.alias(alias);
}
}
// Add arguments
if (definition.arguments) {
for (const arg of definition.arguments) {
const argString = arg.variadic
? `<${arg.name}...>`
: arg.required
? `<${arg.name}>`
: `[${arg.name}]`;
cmd.argument(argString, arg.description, arg.defaultValue);
}
}
// Add options
if (definition.options) {
for (const opt of definition.options) {
cmd.option(opt.flag, opt.description, opt.defaultValue);
// Add choices validation
if (opt.choices) {
cmd.addHelpText('after', `\n Choices for ${opt.flag}: ${opt.choices.join(', ')}`);
}
}
}
// Add examples to help
if (definition.examples) {
const examplesText = definition.examples
.map(ex => ` ${ex.description}\n $ ${ex.command}`)
.join('\n\n');
cmd.addHelpText('after', `\nExamples:\n${examplesText}`);
}
// Set action handler with middleware
cmd.action(async (...args) => {
const context = this.createContext(definition, cmd, args);
await this.executeCommand(context);
});
return cmd;
}
/**
* Execute command with middleware
*/
async executeCommand(context) {
const execution = {
command: context.command.name,
args: context.args,
options: context.options,
startTime: Date.now(),
status: 'running'
};
this.commandHistory.push(execution);
this.emit('command:executing', context);
try {
// Run pre middleware
await this.runMiddleware(context, 'pre');
// Execute command handler
await context.command.handler(context);
// Run post middleware
await this.runMiddleware(context, 'post');
execution.status = 'success';
execution.endTime = Date.now();
this.emit('command:executed', context);
}
catch (error) {
execution.status = 'error';
execution.endTime = Date.now();
execution.error = error;
this.emit('command:error', { context, error });
throw error;
}
finally {
// Track analytics
this.analytics.track('command_executed', {
command: context.command.name,
duration: execution.endTime - execution.startTime,
status: execution.status,
plugin: context.command.plugin?.name
});
}
}
/**
* Run middleware for a phase
*/
async runMiddleware(context, phase) {
// Global middleware
const globalMiddleware = this.middleware.filter(m => m.phase === phase);
// Command-specific middleware
const commandMiddleware = (context.command.middleware || [])
.filter(m => m.phase === phase);
// Combine and execute
const allMiddleware = [...globalMiddleware, ...commandMiddleware];
for (const middleware of allMiddleware) {
this.logger.debug(`Running ${phase} middleware: ${middleware.name}`);
await middleware.handler(context);
}
}
/**
* Create command context
*/
createContext(definition, cmd, args) {
// Parse arguments
const parsedArgs = {};
if (definition.arguments) {
definition.arguments.forEach((arg, index) => {
if (arg.variadic) {
parsedArgs[arg.name] = args.slice(index, args.length - 1);
}
else {
parsedArgs[arg.name] = args[index];
}
});
}
// Get options
const options = cmd.opts();
// Create spinner
const spinner = this.createSpinner();
return {
command: definition,
args: parsedArgs,
options,
program: cmd,
logger: this.logger,
spinner,
analytics: this.analytics,
config: this.config,
plugins: this.plugins
};
}
/**
* Create spinner instance
*/
createSpinner() {
let ora;
let instance;
return {
start(message) {
if (!ora) {
ora = require('ora');
}
instance = ora(message).start();
},
succeed(message) {
if (instance) {
instance.succeed(message);
instance = null;
}
},
fail(message) {
if (instance) {
instance.fail(message);
instance = null;
}
},
info(message) {
if (instance) {
instance.info(message);
instance = null;
}
},
warn(message) {
if (instance) {
instance.warn(message);
instance = null;
}
},
stop() {
if (instance) {
instance.stop();
instance = null;
}
},
get isSpinning() {
return instance?.isSpinning || false;
}
};
}
/**
* Validate command definition
*/
validateCommand(definition) {
if (!definition.name) {
throw new error_handler_1.ValidationError('Command name is required');
}
if (!definition.description) {
throw new error_handler_1.ValidationError('Command description is required');
}
if (!definition.handler) {
throw new error_handler_1.ValidationError('Command handler is required');
}
// Validate arguments
if (definition.arguments) {
let variadicFound = false;
for (const arg of definition.arguments) {
if (variadicFound) {
throw new error_handler_1.ValidationError('Variadic argument must be last');
}
if (arg.variadic) {
variadicFound = true;
}
}
}
// Validate options
if (definition.options) {
for (const opt of definition.options) {
if (!opt.flag || !opt.description) {
throw new error_handler_1.ValidationError('Option flag and description are required');
}
}
}
}
/**
* Get command history
*/
getHistory() {
return [...this.commandHistory];
}
/**
* Clear command history
*/
clearHistory() {
this.commandHistory = [];
}
/**
* Search commands
*/
search(query) {
const lowerQuery = query.toLowerCase();
return this.getAll().filter(cmd => cmd.name.toLowerCase().includes(lowerQuery) ||
cmd.description.toLowerCase().includes(lowerQuery) ||
cmd.keywords?.some((k) => k.toLowerCase().includes(lowerQuery)));
}
}
exports.CommandRegistry = CommandRegistry;