| 32 | } |
| 33 | |
| 34 | export class EventBus { |
| 35 | private listeners = new Map<string, Set<EventHandler>>(); |
| 36 | private onceListeners = new Map<string, Set<EventHandler>>(); |
| 37 | private options: Required<EventBusOptions>; |
| 38 | private eventHistory: EventMetadata[] = []; |
| 39 | private readonly MAX_HISTORY = 1000; |
| 40 | |
| 41 | constructor(options: EventBusOptions = {}) { |
| 42 | this.options = { |
| 43 | maxListeners: options.maxListeners ?? 100, |
| 44 | enableLogging: options.enableLogging ?? false, |
| 45 | timeout: options.timeout ?? 5000 |
| 46 | }; |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Subscribe to an event. |
| 51 | */ |
| 52 | on<T = any>(event: string, handler: EventHandler<T>): EventUnsubscriber { |
| 53 | this.validateEventName(event); |
| 54 | this.validateHandler(handler); |
| 55 | |
| 56 | if (!this.listeners.has(event)) { |
| 57 | this.listeners.set(event, new Set()); |
| 58 | } |
| 59 | |
| 60 | const handlers = this.listeners.get(event)!; |
| 61 | |
| 62 | // Enforce max listeners per event. |
| 63 | if (handlers.size >= this.options.maxListeners) { |
| 64 | throw new Error(`Too many listeners for event '${event}'. Maximum is ${this.options.maxListeners}`); |
| 65 | } |
| 66 | |
| 67 | handlers.add(handler); |
| 68 | |
| 69 | if (this.options.enableLogging) { |
| 70 | log.debug('Added listener', { event, totalListeners: handlers.size }); |
| 71 | } |
| 72 | |
| 73 | // Return the unsubscribe function. |
| 74 | return () => this.off(event, handler); |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Subscribe to an event once. |
| 79 | */ |
| 80 | once<T = any>(event: string, handler: EventHandler<T>): EventUnsubscriber { |
| 81 | this.validateEventName(event); |
| 82 | this.validateHandler(handler); |
| 83 | |
| 84 | if (!this.onceListeners.has(event)) { |
| 85 | this.onceListeners.set(event, new Set()); |
| 86 | } |
| 87 | |
| 88 | const handlers = this.onceListeners.get(event)!; |
| 89 | handlers.add(handler); |
| 90 | |
| 91 | if (this.options.enableLogging) { |
nothing calls this directly
no outgoing calls
no test coverage detected