/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-empty-object-type */ /* eslint-disable @typescript-eslint/no-unsafe-function-type */ import { Injectable } from './decorators/index.mjs' import { InjectableScope } from './enums/index.mjs' /* eslint-disable @typescript-eslint/no-explicit-any */ export type EventsConfig = { [event: string]: any[] } export type EventsNames = Exclude< keyof Events, symbol | number > export type EventsArgs< Events extends EventsConfig, Name extends EventsNames, > = Events[Name] extends any[] ? Events[Name] : [] @Injectable({ scope: InjectableScope.Transient }) export class EventEmitter { private listeners: Map, Set> = new Map() on, Args extends EventsArgs>( event: E, listener: (...args: Args) => void | Promise, ) { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()) } this.listeners.get(event)!.add(listener) return () => { this.off(event, listener) } } off, Args extends EventsArgs>( event: E, listener: (...args: Args) => void | Promise, ) { if (!this.listeners.has(event)) { return } this.listeners.get(event)!.delete(listener) if (this.listeners.get(event)!.size === 0) { this.listeners.delete(event) } } once, Args extends EventsArgs>( event: E, listener: (...args: Args) => void | Promise, ) { const off = this.on(event, (...args) => { off() // @ts-expect-error - This is a valid call listener(...args) }) return off } async emit, Args extends EventsArgs>( event: E, ...args: Args ): Promise { if (!this.listeners.has(event)) { return } return Promise.all( Array.from(this.listeners.get(event)!).map((listener) => listener(...args), ), ) } }