// deno-lint-ignore-file no-explicit-any
type Payload<T = any> = Record<string, unknown> & { instance?: T }
type Listener<T = any> = (payload: Payload<T>) => void;
type InstanceListener<T = any> = (payload: Payload<T>) => void;
type Constructor<T extends EventEmitter> = (new (...args: any[]) => T) & typeof EventEmitter
export class EventEmitter<Events extends string = any> {
constructor(..._args: unknown[]) {}
//region Class handlers
private static __classListeners: Map<string, Map<string, Set<Listener>>> = new Map();
private static get ClassListeners() {
if(!this.__classListeners.has(this.name)) this.__classListeners.set(this.name, new Map());
return this.__classListeners.get(this.name)!;
}
static on<T extends EventEmitter>(
this: Constructor<T>,
event: string,
listener: InstanceListener<T>
) {
if (!this.ClassListeners.has(event)) {
this.ClassListeners.set(event, new Set());
}
this.ClassListeners.get(event)!.add(listener as Listener);
}
static once<T extends EventEmitter>(
this: Constructor<T>,
event: string,
listener: InstanceListener<T>
) {
const wrapper = (payload: Payload<T>) => {
listener(payload);
this.off(event, wrapper);
};
this.on(event, wrapper);
}
static off<T extends EventEmitter>(
this: Constructor<T>,
event: string,
listener: InstanceListener<T>
) {
this.ClassListeners.get(event)?.delete(listener as Listener);
}
static emit<T extends EventEmitter>(
this: Constructor<T>,
event: string,
payload: Payload<T>
) {
this.ClassListeners.get(event)?.forEach((listener) => listener(payload));
}
//region Instance handlers
private __instanceListeners: Map<string, Set<Listener>> = new Map();
on<E extends Events>(event: E, listener: Listener<this>) {
if (!this.__instanceListeners.has(event)) {
this.__instanceListeners.set(event, new Set());
}
this.__instanceListeners.get(event)!.add(listener);
}
once<E extends Events>(event: E, listener: Listener<this>) {
const wrapper = (payload: Payload<this>) => {
listener(payload);
this.off(event, wrapper);
};
this.on(event, wrapper);
}
off<E extends Events>(event: E, listener: Listener<this>) {
this.__instanceListeners.get(event)?.delete(listener);
}
emit<E extends Events>(event: E): void {
// Instance listeners
this.__instanceListeners.get(event)?.forEach((listener) =>
listener({ instance: this })
);
// Class-level listeners
const constructor = this.constructor as Constructor<EventEmitter>;
constructor.emit(event, { instance: this });
}
}