import { debounce } from 'ts-debounce'; import { getLogger } from './index'; declare const JWT_DEV: boolean; const logger = getLogger('BlockDB:event_bus'); export class BlockEventBus { private readonly _eventBus: EventTarget; private readonly _eventCallbackCache: Map; private readonly _scopedCache: Map>; constructor(event_bus?: EventTarget) { this._eventBus = event_bus || new EventTarget(); this._eventCallbackCache = new Map(); this._scopedCache = new Map(); } protected on_listener( topic: string, name: string, listener: (e: Event) => void ) { const handler_name = `${topic}/${name}`; if (!this._eventCallbackCache.has(handler_name)) { this._eventBus.addEventListener(topic, listener); this._eventCallbackCache.set(handler_name, listener); } else { JWT_DEV && logger(`event handler ${handler_name} is existing`); } } protected off_listener(topic: string, name: string) { const handler_name = `${topic}/${name}`; const listener = this._eventCallbackCache.get(handler_name); if (listener) { this._eventBus.removeEventListener(topic, listener); this._eventCallbackCache.delete(handler_name); } else { JWT_DEV && logger(`event handler ${handler_name} is not existing`); } } protected has_listener(topic: string, name: string) { return this._eventCallbackCache.has(`${topic}/${name}`); } protected emit_event(topic: string, detail?: T) { this._eventBus.dispatchEvent(new CustomEvent(topic, { detail })); } topic(topic: string): BlockScopedEventBus { return ( this._scopedCache.get(topic) || new BlockScopedEventBus(topic, this._eventBus) ); } } type DebounceOptions = { wait: number; maxWait?: number; }; type ListenerOptions = { debounce?: DebounceOptions; }; class BlockScopedEventBus extends BlockEventBus { private readonly _topic: string; constructor(topic: string, event_bus?: EventTarget) { super(event_bus); this._topic = topic; } on( name: string, listener: ((e: T) => Promise) | ((e: T) => void), options?: ListenerOptions ) { if (options?.debounce) { const { wait, maxWait } = options.debounce; const debounced = debounce(listener, wait, { maxWait }); this.on_listener(this._topic, name, e => { debounced((e as CustomEvent)?.detail); }); } else { this.on_listener(this._topic, name, e => { listener((e as CustomEvent)?.detail); }); } } off(name: string) { this.off_listener(this._topic, name); } has(name: string) { return this.has_listener(this._topic, name); } emit(detail?: T) { this.emit_event(this._topic, detail); } }