UNPKG

@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

474 lines (473 loc) 17.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BuiltinHooks = exports.PluginHookAPI = exports.PluginHookSystem = exports.HookPriority = exports.HookType = void 0; exports.createHookSystem = createHookSystem; exports.isValidHookType = isValidHookType; const events_1 = require("events"); const chalk_1 = __importDefault(require("chalk")); // Hook types and priorities var HookType; (function (HookType) { // CLI lifecycle hooks HookType["CLI_INIT"] = "cli:init"; HookType["CLI_EXIT"] = "cli:exit"; HookType["CLI_ERROR"] = "cli:error"; // Command hooks HookType["COMMAND_BEFORE"] = "command:before"; HookType["COMMAND_AFTER"] = "command:after"; HookType["COMMAND_ERROR"] = "command:error"; HookType["COMMAND_REGISTER"] = "command:register"; // Workspace hooks HookType["WORKSPACE_CREATE"] = "workspace:create"; HookType["WORKSPACE_UPDATE"] = "workspace:update"; HookType["WORKSPACE_DELETE"] = "workspace:delete"; HookType["WORKSPACE_BUILD"] = "workspace:build"; // File hooks HookType["FILE_CHANGE"] = "file:change"; HookType["FILE_CREATE"] = "file:create"; HookType["FILE_DELETE"] = "file:delete"; HookType["FILE_WATCH"] = "file:watch"; // Build hooks HookType["BUILD_START"] = "build:start"; HookType["BUILD_END"] = "build:end"; HookType["BUILD_ERROR"] = "build:error"; HookType["BUILD_SUCCESS"] = "build:success"; // Plugin hooks HookType["PLUGIN_LOAD"] = "plugin:load"; HookType["PLUGIN_ACTIVATE"] = "plugin:activate"; HookType["PLUGIN_DEACTIVATE"] = "plugin:deactivate"; // Configuration hooks HookType["CONFIG_LOAD"] = "config:load"; HookType["CONFIG_SAVE"] = "config:save"; HookType["CONFIG_VALIDATE"] = "config:validate"; // Custom hooks (plugins can define their own) HookType["CUSTOM"] = "custom"; })(HookType || (exports.BuiltinHooks = exports.HookType = HookType = {})); var HookPriority; (function (HookPriority) { HookPriority[HookPriority["HIGHEST"] = 1] = "HIGHEST"; HookPriority[HookPriority["HIGH"] = 25] = "HIGH"; HookPriority[HookPriority["NORMAL"] = 50] = "NORMAL"; HookPriority[HookPriority["LOW"] = 75] = "LOW"; HookPriority[HookPriority["LOWEST"] = 100] = "LOWEST"; })(HookPriority || (exports.HookPriority = HookPriority = {})); // Plugin hooks system class PluginHookSystem extends events_1.EventEmitter { constructor(options = {}) { super(); this.hooks = new Map(); this.middleware = []; this.executionStats = new Map(); this.isEnabled = true; this.debugMode = false; this.debugMode = options.debugMode || false; this.initializeBuiltinHooks(); } // Initialize built-in hooks initializeBuiltinHooks() { // Register all hook types Object.values(HookType).forEach(hookType => { this.hooks.set(hookType, []); }); // Add default middleware this.addMiddleware({ name: 'logger', before: (context) => { if (this.debugMode) { console.log(chalk_1.default.gray(`[Hook] ${context.hookType} - ${context.pluginName}`)); } }, error: (context, error) => { console.error(chalk_1.default.red(`[Hook Error] ${context.hookType} - ${context.pluginName}: ${error.message}`)); } }); } // Register a hook handler register(hookType, handler, pluginName, options = {}) { const hookKey = hookType; const handlerId = this.generateHandlerId(pluginName, hookType); const hookHandler = { id: handlerId, pluginName, handler, priority: options.priority || HookPriority.NORMAL, once: options.once || false, condition: options.condition, description: options.description, metadata: options.metadata }; // Initialize hook type if it doesn't exist (for custom hooks) if (!this.hooks.has(hookKey)) { this.hooks.set(hookKey, []); } // Add handler and sort by priority const handlers = this.hooks.get(hookKey); handlers.push(hookHandler); handlers.sort((a, b) => a.priority - b.priority); this.emit('hook-registered', { hookType: hookKey, handlerId, pluginName, priority: hookHandler.priority }); if (this.debugMode) { console.log(chalk_1.default.blue(`[Hook] Registered ${hookType} handler for ${pluginName}`)); } return handlerId; } // Unregister a hook handler unregister(hookType, handlerId) { const hookKey = hookType; const handlers = this.hooks.get(hookKey); if (!handlers) return false; const index = handlers.findIndex(h => h.id === handlerId); if (index === -1) return false; const removed = handlers.splice(index, 1)[0]; this.emit('hook-unregistered', { hookType: hookKey, handlerId, pluginName: removed.pluginName }); if (this.debugMode) { console.log(chalk_1.default.yellow(`[Hook] Unregistered ${hookType} handler ${handlerId}`)); } return true; } // Unregister all hooks for a plugin unregisterAll(pluginName) { let removed = 0; for (const [hookType, handlers] of this.hooks.entries()) { const initialLength = handlers.length; this.hooks.set(hookType, handlers.filter(h => h.pluginName !== pluginName)); removed += initialLength - handlers.length; } this.emit('plugin-hooks-unregistered', { pluginName, removed }); if (this.debugMode && removed > 0) { console.log(chalk_1.default.yellow(`[Hook] Unregistered ${removed} hooks for plugin ${pluginName}`)); } return removed; } // Execute hooks async execute(hookType, data = {}) { if (!this.isEnabled) { return { success: true, results: [], errors: [], aborted: false, executionTime: 0, context: { hookType: hookType, pluginName: 'system', timestamp: Date.now(), data } }; } const startTime = Date.now(); const hookKey = hookType; const handlers = this.hooks.get(hookKey) || []; const context = { hookType: hookKey, pluginName: 'system', timestamp: startTime, data, metadata: {} }; const result = { success: true, results: [], errors: [], aborted: false, executionTime: 0, context }; if (handlers.length === 0) { result.executionTime = Date.now() - startTime; return result; } try { // Execute middleware before hooks await this.executeMiddleware('before', context); // Execute handlers for (const handler of handlers) { // Check condition if specified if (handler.condition && !handler.condition(data)) { continue; } context.pluginName = handler.pluginName; try { const handlerStartTime = Date.now(); // Execute handler const handlerResult = await Promise.resolve(handler.handler(data, context)); // Track execution time const executionTime = Date.now() - handlerStartTime; this.updateExecutionStats(handler.pluginName, executionTime); result.results.push({ pluginName: handler.pluginName, handlerId: handler.id, result: handlerResult, executionTime }); // Remove one-time handlers if (handler.once) { this.unregister(hookType, handler.id); } // Check for abort signal if (handlerResult && handlerResult.abort) { result.aborted = true; break; } } catch (error) { const hookError = error instanceof Error ? error : new Error(String(error)); result.errors.push({ pluginName: handler.pluginName, error: hookError }); context.error = hookError; // Execute middleware error handler await this.executeMiddleware('error', context, hookError); // Continue with other handlers unless it's a critical error if (hookError.message.includes('CRITICAL')) { result.aborted = true; break; } } } // Execute middleware after hooks await this.executeMiddleware('after', context, result.results); } catch (error) { result.success = false; result.errors.push({ pluginName: 'system', error: error instanceof Error ? error : new Error(String(error)) }); } result.success = result.errors.length === 0 && !result.aborted; result.executionTime = Date.now() - startTime; this.emit('hooks-executed', { hookType: hookKey, handlersCount: handlers.length, resultsCount: result.results.length, errorsCount: result.errors.length, executionTime: result.executionTime, success: result.success }); return result; } // Execute hooks synchronously (for simple cases) executeSync(hookType, data = {}) { if (!this.isEnabled) return []; const hookKey = hookType; const handlers = this.hooks.get(hookKey) || []; const results = []; for (const handler of handlers) { // Check condition if specified if (handler.condition && !handler.condition(data)) { continue; } try { const context = { hookType: hookKey, pluginName: handler.pluginName, timestamp: Date.now(), data }; const result = handler.handler(data, context); results.push({ pluginName: handler.pluginName, result }); // Remove one-time handlers if (handler.once) { this.unregister(hookType, handler.id); } } catch (error) { if (this.debugMode) { console.error(chalk_1.default.red(`[Hook Error] ${hookType} - ${handler.pluginName}: ${error}`)); } } } return results; } // Add middleware addMiddleware(middleware) { this.middleware.push(middleware); this.emit('middleware-added', { name: middleware.name }); if (this.debugMode) { console.log(chalk_1.default.green(`[Hook] Added middleware: ${middleware.name}`)); } } // Remove middleware removeMiddleware(name) { const index = this.middleware.findIndex(m => m.name === name); if (index === -1) return false; this.middleware.splice(index, 1); this.emit('middleware-removed', { name }); if (this.debugMode) { console.log(chalk_1.default.yellow(`[Hook] Removed middleware: ${name}`)); } return true; } // Execute middleware async executeMiddleware(phase, context, extra) { for (const middleware of this.middleware) { try { if (phase === 'before' && middleware.before) { await middleware.before(context); } else if (phase === 'after' && middleware.after) { await middleware.after(context, extra); } else if (phase === 'error' && middleware.error) { await middleware.error(context, extra); } } catch (error) { if (this.debugMode) { console.error(chalk_1.default.red(`[Middleware Error] ${middleware.name}: ${error}`)); } } } } // Get registered hooks getHooks(hookType) { if (hookType) { return this.hooks.get(hookType) || []; } return new Map(this.hooks); } // Get hooks for a specific plugin getPluginHooks(pluginName) { const pluginHooks = []; for (const handlers of this.hooks.values()) { pluginHooks.push(...handlers.filter(h => h.pluginName === pluginName)); } return pluginHooks; } // Get hook statistics getStats() { const stats = { totalHooks: 0, hooksByType: {}, hooksByPlugin: {}, executionStats: Object.fromEntries(this.executionStats), middleware: this.middleware.map(m => m.name) }; for (const [hookType, handlers] of this.hooks.entries()) { stats.totalHooks += handlers.length; stats.hooksByType[hookType] = handlers.length; for (const handler of handlers) { stats.hooksByPlugin[handler.pluginName] = (stats.hooksByPlugin[handler.pluginName] || 0) + 1; } } return stats; } // Update execution statistics updateExecutionStats(pluginName, executionTime) { const currentTime = this.executionStats.get(pluginName) || 0; this.executionStats.set(pluginName, currentTime + executionTime); } // Generate handler ID generateHandlerId(pluginName, hookType) { const timestamp = Date.now(); const random = Math.random().toString(36).substr(2, 5); return `${pluginName}_${hookType}_${timestamp}_${random}`; } // Enable/disable hook system setEnabled(enabled) { this.isEnabled = enabled; this.emit('system-toggled', { enabled }); } // Set debug mode setDebugMode(debug) { this.debugMode = debug; this.emit('debug-toggled', { debug }); } // Clear all hooks clear() { this.hooks.clear(); this.executionStats.clear(); this.initializeBuiltinHooks(); this.emit('system-cleared'); } // Create a scoped hook system for a plugin createPluginScope(pluginName) { return new PluginHookAPI(this, pluginName); } } exports.PluginHookSystem = PluginHookSystem; // Plugin-scoped hook API class PluginHookAPI { constructor(hookSystem, pluginName) { this.hookSystem = hookSystem; this.pluginName = pluginName; } // Register a hook (automatically includes plugin name) register(hookType, handler, options) { return this.hookSystem.register(hookType, handler, this.pluginName, options); } // Unregister a hook unregister(hookType, handlerId) { return this.hookSystem.unregister(hookType, handlerId); } // Execute hooks async execute(hookType, data) { return this.hookSystem.execute(hookType, data); } // Execute hooks synchronously executeSync(hookType, data) { return this.hookSystem.executeSync(hookType, data); } // Get plugin's hooks getHooks() { return this.hookSystem.getPluginHooks(this.pluginName); } // Register a custom hook type registerCustomHook(name) { const customHookType = `${this.pluginName}:${name}`; return customHookType; } // Convenience methods for common hooks onCommand(command, handler, options) { return this.register(HookType.COMMAND_BEFORE, (data, context) => { if (data.command === command) { return handler(data, context); } }, options); } onFileChange(pattern, handler, options) { return this.register(HookType.FILE_CHANGE, (data, context) => { const filePath = data.filePath || data.path; if (pattern instanceof RegExp ? pattern.test(filePath) : filePath.includes(pattern)) { return handler(data, context); } }, options); } onWorkspaceBuild(workspace, handler, options) { return this.register(HookType.BUILD_START, (data, context) => { if (data.workspace === workspace || workspace === '*') { return handler(data, context); } }, options); } } exports.PluginHookAPI = PluginHookAPI; // Utility functions function createHookSystem(options) { return new PluginHookSystem(options); } function isValidHookType(hookType) { return Object.values(HookType).includes(hookType); }