UNPKG

claude-flow-multilang

Version:

Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture

515 lines (432 loc) 12.7 kB
/** * EventBus - Central event communication system for the Web UI * Enables loose coupling between components and real-time updates */ export class EventBus { constructor() { this.events = new Map(); this.onceEvents = new Map(); this.wildcardHandlers = new Map(); this.eventHistory = []; this.maxHistorySize = 1000; this.isLogging = true; } /** * Subscribe to an event */ on(event, handler, context = null) { if (typeof handler !== 'function') { throw new Error('Event handler must be a function'); } if (!this.events.has(event)) { this.events.set(event, []); } const handlerInfo = { handler, context, id: this.generateHandlerId(), }; this.events.get(event).push(handlerInfo); if (this.isLogging) { console.debug(`📡 EventBus: Subscribed to '${event}'`); } // Return unsubscribe function return () => this.off(event, handler); } /** * Subscribe to an event (once only) */ once(event, handler, context = null) { if (typeof handler !== 'function') { throw new Error('Event handler must be a function'); } if (!this.onceEvents.has(event)) { this.onceEvents.set(event, []); } const handlerInfo = { handler, context, id: this.generateHandlerId(), }; this.onceEvents.get(event).push(handlerInfo); if (this.isLogging) { console.debug(`📡 EventBus: Subscribed once to '${event}'`); } // Return unsubscribe function return () => this.offOnce(event, handler); } /** * Subscribe to events with wildcard patterns */ onWildcard(pattern, handler, context = null) { if (typeof handler !== 'function') { throw new Error('Event handler must be a function'); } if (!this.wildcardHandlers.has(pattern)) { this.wildcardHandlers.set(pattern, []); } const handlerInfo = { handler, context, id: this.generateHandlerId(), regex: this.createWildcardRegex(pattern), }; this.wildcardHandlers.get(pattern).push(handlerInfo); if (this.isLogging) { console.debug(`📡 EventBus: Subscribed to wildcard pattern '${pattern}'`); } // Return unsubscribe function return () => this.offWildcard(pattern, handler); } /** * Unsubscribe from an event */ off(event, handler) { const handlers = this.events.get(event); if (!handlers) return false; const index = handlers.findIndex((h) => h.handler === handler); if (index === -1) return false; handlers.splice(index, 1); if (handlers.length === 0) { this.events.delete(event); } if (this.isLogging) { console.debug(`📡 EventBus: Unsubscribed from '${event}'`); } return true; } /** * Unsubscribe from a once event */ offOnce(event, handler) { const handlers = this.onceEvents.get(event); if (!handlers) return false; const index = handlers.findIndex((h) => h.handler === handler); if (index === -1) return false; handlers.splice(index, 1); if (handlers.length === 0) { this.onceEvents.delete(event); } return true; } /** * Unsubscribe from wildcard pattern */ offWildcard(pattern, handler) { const handlers = this.wildcardHandlers.get(pattern); if (!handlers) return false; const index = handlers.findIndex((h) => h.handler === handler); if (index === -1) return false; handlers.splice(index, 1); if (handlers.length === 0) { this.wildcardHandlers.delete(pattern); } return true; } /** * Emit an event */ emit(event, data = null) { const eventInfo = { event, data, timestamp: Date.now(), id: this.generateEventId(), }; // Add to history this.addToHistory(eventInfo); if (this.isLogging) { console.debug(`📡 EventBus: Emitting '${event}'`, data); } let handlersExecuted = 0; // Execute regular event handlers const handlers = this.events.get(event); if (handlers) { for (const handlerInfo of [...handlers]) { try { if (handlerInfo.context) { handlerInfo.handler.call(handlerInfo.context, data, eventInfo); } else { handlerInfo.handler(data, eventInfo); } handlersExecuted++; } catch (error) { console.error(`📡 EventBus: Error in handler for '${event}':`, error); this.emit('error', { event, error, handlerInfo }); } } } // Execute once event handlers const onceHandlers = this.onceEvents.get(event); if (onceHandlers) { for (const handlerInfo of [...onceHandlers]) { try { if (handlerInfo.context) { handlerInfo.handler.call(handlerInfo.context, data, eventInfo); } else { handlerInfo.handler(data, eventInfo); } handlersExecuted++; } catch (error) { console.error(`📡 EventBus: Error in once handler for '${event}':`, error); this.emit('error', { event, error, handlerInfo }); } } // Clear once handlers after execution this.onceEvents.delete(event); } // Execute wildcard handlers for (const [pattern, handlers] of this.wildcardHandlers) { for (const handlerInfo of handlers) { if (handlerInfo.regex.test(event)) { try { if (handlerInfo.context) { handlerInfo.handler.call(handlerInfo.context, data, eventInfo); } else { handlerInfo.handler(data, eventInfo); } handlersExecuted++; } catch (error) { console.error(`📡 EventBus: Error in wildcard handler for '${pattern}':`, error); this.emit('error', { event, error, handlerInfo, pattern }); } } } } return handlersExecuted; } /** * Emit event asynchronously */ async emitAsync(event, data = null) { const eventInfo = { event, data, timestamp: Date.now(), id: this.generateEventId(), }; // Add to history this.addToHistory(eventInfo); if (this.isLogging) { console.debug(`📡 EventBus: Emitting async '${event}'`, data); } const promises = []; // Execute regular event handlers const handlers = this.events.get(event); if (handlers) { for (const handlerInfo of [...handlers]) { const promise = this.executeHandlerAsync(handlerInfo, data, eventInfo); promises.push(promise); } } // Execute once event handlers const onceHandlers = this.onceEvents.get(event); if (onceHandlers) { for (const handlerInfo of [...onceHandlers]) { const promise = this.executeHandlerAsync(handlerInfo, data, eventInfo); promises.push(promise); } // Clear once handlers after execution this.onceEvents.delete(event); } // Execute wildcard handlers for (const [pattern, handlers] of this.wildcardHandlers) { for (const handlerInfo of handlers) { if (handlerInfo.regex.test(event)) { const promise = this.executeHandlerAsync(handlerInfo, data, eventInfo); promises.push(promise); } } } const results = await Promise.allSettled(promises); // Handle any rejections const failures = results.filter((r) => r.status === 'rejected'); if (failures.length > 0) { console.error(`📡 EventBus: ${failures.length} handlers failed for '${event}'`); failures.forEach((failure) => { this.emit('error', { event, error: failure.reason }); }); } return results.length; } /** * Execute handler asynchronously */ async executeHandlerAsync(handlerInfo, data, eventInfo) { try { let result; if (handlerInfo.context) { result = handlerInfo.handler.call(handlerInfo.context, data, eventInfo); } else { result = handlerInfo.handler(data, eventInfo); } // If handler returns a promise, await it if (result && typeof result.then === 'function') { return await result; } return result; } catch (error) { console.error(`📡 EventBus: Error in async handler:`, error); throw error; } } /** * Wait for an event to be emitted */ waitFor(event, timeout = 5000) { return new Promise((resolve, reject) => { let timeoutId; const unsubscribe = this.once(event, (data) => { if (timeoutId) { clearTimeout(timeoutId); } resolve(data); }); if (timeout > 0) { timeoutId = setTimeout(() => { unsubscribe(); reject(new Error(`Timeout waiting for event '${event}' after ${timeout}ms`)); }, timeout); } }); } /** * Remove all event listeners */ removeAllListeners(event = null) { if (event) { this.events.delete(event); this.onceEvents.delete(event); // Remove matching wildcard handlers for (const [pattern, handlers] of this.wildcardHandlers) { if (pattern === event) { this.wildcardHandlers.delete(pattern); } } } else { this.events.clear(); this.onceEvents.clear(); this.wildcardHandlers.clear(); } if (this.isLogging) { console.debug(`📡 EventBus: Removed all listeners${event ? ` for '${event}'` : ''}`); } } /** * Get event listeners count */ listenerCount(event) { const regular = this.events.get(event)?.length || 0; const once = this.onceEvents.get(event)?.length || 0; let wildcard = 0; for (const [pattern, handlers] of this.wildcardHandlers) { const regex = this.createWildcardRegex(pattern); if (regex.test(event)) { wildcard += handlers.length; } } return regular + once + wildcard; } /** * Get all event names */ eventNames() { const names = new Set(); for (const event of this.events.keys()) { names.add(event); } for (const event of this.onceEvents.keys()) { names.add(event); } return Array.from(names); } /** * Get event history */ getEventHistory(limit = 100) { return this.eventHistory.slice(-limit); } /** * Get events by pattern */ getEventsByPattern(pattern, limit = 100) { const regex = this.createWildcardRegex(pattern); return this.eventHistory.filter((event) => regex.test(event.event)).slice(-limit); } /** * Clear event history */ clearHistory() { this.eventHistory = []; } /** * Enable/disable logging */ setLogging(enabled) { this.isLogging = enabled; } /** * Create wildcard regex */ createWildcardRegex(pattern) { const escaped = pattern .replace(/[.+^${}()|[\]\\]/g, '\\$&') .replace(/\*/g, '.*') .replace(/\?/g, '.'); return new RegExp(`^${escaped}$`); } /** * Generate unique handler ID */ generateHandlerId() { return `handler_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Generate unique event ID */ generateEventId() { return `event_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Add event to history */ addToHistory(eventInfo) { this.eventHistory.push(eventInfo); // Keep history size under control if (this.eventHistory.length > this.maxHistorySize) { this.eventHistory = this.eventHistory.slice(-this.maxHistorySize); } } /** * Get statistics */ getStats() { return { regularEvents: this.events.size, onceEvents: this.onceEvents.size, wildcardPatterns: this.wildcardHandlers.size, historySize: this.eventHistory.length, totalHandlers: Array.from(this.events.values()).reduce((sum, handlers) => sum + handlers.length, 0) + Array.from(this.onceEvents.values()).reduce((sum, handlers) => sum + handlers.length, 0) + Array.from(this.wildcardHandlers.values()).reduce( (sum, handlers) => sum + handlers.length, 0, ), }; } /** * Debug information */ debug() { const stats = this.getStats(); console.group('📡 EventBus Debug Info'); console.log('Statistics:', stats); console.log('Regular Events:', Array.from(this.events.keys())); console.log('Once Events:', Array.from(this.onceEvents.keys())); console.log('Wildcard Patterns:', Array.from(this.wildcardHandlers.keys())); console.log('Recent History:', this.getEventHistory(10)); console.groupEnd(); } } export default EventBus;