import SplitIO from '../../types/splitio'; const NEW_LISTENER_EVENT = 'newListener'; const REMOVE_LISTENER_EVENT = 'removeListener'; function checkListener(listener: unknown) { if (typeof listener !== 'function') { throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); } } // @TODO implement missing methods, check spec and add UTs export class EventEmitter implements SplitIO.IEventEmitter { private listeners: Record void, // the event listener boolean // whether it is a one-time listener or not ]>> = {}; private registerListener(type: string, listener: (...args: any[]) => void, oneTime: boolean) { checkListener(listener); // To avoid recursion in the case that type === "newListener" before // adding it to the listeners, first emit "newListener". this.emit(NEW_LISTENER_EVENT, type, listener); if (!this.listeners[type]) { this.listeners[type] = [[listener, oneTime]]; } else { this.listeners[type].push([listener, oneTime]); } return this; } addListener(type: string, listener: (...args: any[]) => void) { return this.registerListener(type, listener, false); } // alias of addListener on(type: string, listener: (...args: any[]) => void) { return this.addListener(type, listener); } once(type: string, listener: (...args: any[]) => void) { return this.registerListener(type, listener, true); } // @ts-ignore removeListener(/* type: string, listener: (...args: any[]) => void */) { throw new Error('Method not implemented.'); } // @ts-ignore alias of removeListener off(/* type: string, listener: (...args: any[]) => void */) { return this.removeListener(/* type, listener */); } emit(type: string, ...args: any[]): boolean { // Returns false if the event doesn't have listeners if (!this.listeners[type] || this.listeners[type].length === 0) return false; // Call listeners while removing one-time listeners this.listeners[type] = this.listeners[type].filter(listenerEntry => { listenerEntry[0](...args); return !listenerEntry[1]; }); return true; } removeAllListeners(type?: string) { if (!this.listeners[REMOVE_LISTENER_EVENT]) { // if not listening for `removeListener`, no need to emit if (type) { if (this.listeners[type]) delete this.listeners[type]; } else { this.listeners = {}; } } else { // emit `removeListener` for all listeners if (type) { const listeners = this.listeners[type]; if (listeners) { // LIFO order for (let i = listeners.length - 1; i >= 0; i--) { this.emit(REMOVE_LISTENER_EVENT, type, listeners[i]); } delete this.listeners[type]; } } else { const keys = Object.keys(this.listeners); for (let i = 0; i < keys.length; ++i) { const key = keys[i]; if (key === REMOVE_LISTENER_EVENT) continue; this.removeAllListeners(key); } this.listeners = {}; } } return this; } }