@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
JavaScript
"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);
}