@akala/core
Version:
180 lines • 6.73 kB
JavaScript
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