Source: event-bus.js

// Simple event bus for pub/sub communication
/**
 * EventBus provides a minimal publish/subscribe API for decoupled communication.
 * @class
 */
export class EventBus {
    /**
     * Create a new EventBus instance.
     */
    constructor() {
        this.listeners = new Map()
    }

    /**
     * Subscribe to an event
     * @param {string} event - Event name
     * @param {Function} callback - Event handler function
     * @returns {Function} Unsubscribe function
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, new Set())
        }
        this.listeners.get(event).add(callback)
        // Return unsubscribe function
        return () => {
            const callbacks = this.listeners.get(event)
            if (callbacks) {
                callbacks.delete(callback)
                if (callbacks.size === 0) {
                    this.listeners.delete(event)
                }
            }
        }
    }

    /**
     * Emit an event
     * @param {string} event - Event name
     * @param {*} data - Event data
     */
    emit(event, data) {
        // Optionally log or handle errors here
        const callbacks = this.listeners.get(event)
        if (callbacks) {
            callbacks.forEach(callback => {
                try {
                    callback(data)
                } catch (error) {
                    // Optionally handle listener errors
                    // console.error(`Error in event listener for "${event}":`, error)
                }
            })
        }
    }

    /**
     * Remove all listeners for an event or all events
     * @param {string} [event] - Event name (optional, removes all if not specified)
     */
    removeAllListeners(event) {
        if (event) {
            this.listeners.delete(event)
        } else {
            this.listeners.clear()
        }
    }
}

// Singleton instance for convenience
export const eventBus = new EventBus()

// Direct API for convenience (for legacy/common usage)
export const on = eventBus.on.bind(eventBus)
export const off = (event, callback) => {
    const callbacks = eventBus.listeners.get(event)
    if (callbacks) {
        callbacks.delete(callback)
        if (callbacks.size === 0) {
            eventBus.listeners.delete(event)
        }
    }
}
export const emit = eventBus.emit.bind(eventBus)

// For testability: reset all listeners
export const _reset = () => eventBus.removeAllListeners()

// Usage:
// import { eventBus, on, off, emit, _reset } from 'evb/src/event-bus.js'
// const unsubscribe = on('my:event', data => { ... })
// emit('my:event', { foo: 'bar' })
// off('my:event', unsubscribe)