UNPKG

@akala/core

Version:
180 lines 6.73 kB
import { AsyncTeardownManager, combineSubscriptions } from "../teardown-manager.js"; import { Event } from "./shared.js"; /** * EventEmitter class to manage events and listeners. * @template T * @implements {Disposable} */ export class EventEmitter extends AsyncTeardownManager { /** * Checks if there are listeners for a given event. * @template TKey * @param {TKey} name - The name of the event. * @returns {boolean} - True if there are listeners, false otherwise. */ hasListener(name) { return this.events[name] && this.events[name].hasListeners; } // protected readonly specialEvents: Partial<SpecialEvents> = {} events = {}; maxListeners = Event.maxListeners; /** * Gets the defined events. * @returns {AllEventKeys<T>[]} - An array of defined event names. */ get definedEvents() { return Object.keys(this.events); } /** * Creates an instance of EventEmitter. * @param {number | T} [init] - Initial value or number of max listeners. */ constructor(init, abort) { super(); switch (typeof init) { case 'number': this.maxListeners = init; break; case 'object': this.events = init; break; case 'undefined': break; default: throw new Error('Unsupported usage'); } abort?.addEventListener('abort', () => this[Symbol.dispose]()); } // eslint-disable-next-line @typescript-eslint/no-unused-vars eventFactory(_name) { return new Event(this.maxListeners); } /** * Sets an event. * @template TEvent * @param {TEvent} eventName - The name of the event. * @param {AllEvents<T>[TEvent]} event - The event to set. */ set(eventName, event) { if (!(eventName in this.events) || !this.events[eventName].hasListeners) this.events[eventName] = event; //as EventMap<AllEvents<T>>[TEvent]; else throw new Error('This event (' + event.toString() + ') already has registered listeners, the type cannot be changed'); } /** * Gets an event. * @template TEvent * @param {TEvent} eventName - The name of the event. * @returns {AllEvents<T>[TEvent]} - The event. */ get(eventName) { return this.events[eventName]; // = event //as EventMap<AllEvents<T>>[TEvent]; } /** * Gets or creates an event. * @template TEvent * @param {TEvent} eventName - The name of the event. * @returns {AllEvents<T>[TEvent]} - The event. */ getOrCreate(eventName) { return this.events[eventName] || (this.events[eventName] = this.eventFactory(eventName)); ; // = event //as EventMap<AllEvents<T>>[TEvent]; } /** * Sets the maximum number of listeners for an event. * @template TEvent * @param {number} maxListeners - The maximum number of listeners. * @param {TEvent} [event] - The event to set the max listeners for. */ setMaxListeners(maxListeners, event) { if (!(event in this.events)) this.events[event] = new Event(maxListeners); else this.events[event].maxListeners = maxListeners; } /** * Emits an event. * @template TEvent * @param {TEvent} event - The event to emit. * @param {...EventArgs<T[TEvent]>} args - The arguments to pass to the event listeners. * @returns {false | EventReturnType<T[TEvent]>} - The return value of the event listeners or false if the event does not exist. */ emit(event, ...args) { if (!(event in this.events)) return false; return this.events[event].emit(...args); } /** * Adds a listener for an event. * @template TEvent * @param {TEvent} event - The event to listen to. * @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler. * @param {EventOptions<AllEvents<T>[TEvent]>} [options] - The event options. * @returns {Subscription} - The subscription. */ on(event, handler, options) { if (!(event in this.events)) this.events[event] = this.eventFactory(event); return this.events[event].addListener(handler, options); } /** * Adds a one-time listener for an event. * @template TEvent * @param {TEvent} event - The event to listen to. * @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler. * @param {Omit<EventOptions<AllEvents<T>[TEvent]>, 'once'>} [options] - The event options. * @returns {Subscription} - The subscription. */ once(event, handler, options) { return this.on(event, handler, (options ? { once: true, ...options } : { once: true })); } /** * Removes a listener for an event. * @template TEvent * @param {TEvent} event - The event to remove the listener from. * @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler. * @returns {boolean} - True if the listener was removed, false otherwise. */ off(event, handler) { if (!(event in this.events)) return false; return this.events[event].removeListener(handler); } /** * Disposes the event emitter. */ [Symbol.dispose]() { if (this.events[Symbol.dispose]) this.events[Symbol.dispose].emit(); super[Symbol.dispose](); for (const prop in this.events) { if (this.events[prop][Symbol.dispose]) this.events[prop][Symbol.dispose](); } } } export class TopDownNamespaceEventEmitter extends EventEmitter { on(event, handler, options) { if (typeof event === "string") { const namespaces = event.split('.').map((n, i, array) => array.slice(array.length - i).join('.')); const subs = []; for (const namespace in namespaces) subs.push(super.on(namespace, handler, options)); return combineSubscriptions(...subs); } else return super.on(event, handler, options); } } export class BottomUpNamespaceEventEmitter extends EventEmitter { emit(event, ...args) { if (typeof event === "string") { const namespaces = event.split('.').map((n, i, array) => array.slice(array.length - i).join('.')); let result; for (const namespace in namespaces) result = result || super.emit(namespace, ...args); return result; } else return super.emit(event, ...args); } } //# sourceMappingURL=event-emitter.js.map