type EventsBase = Record; type PromiseLike = Promise | T; export type EventMap = { [K in keyof E]?: EventCallback }; export type EventCallback = (...args: E[T]) => PromiseLike; export type EventItem = { id: number; event: T; callback: EventCallback; }; let count = 1; const nextId = () => count++; const noop = () => {}; export class BaseEventManager { #callbacks: EventItem[] = $state.raw([]); on(subscriptions: EventMap): () => void { const cleanups = Object.entries(subscriptions).map(([event, callback]) => this.#onEvent(event as keyof Events, callback as EventCallback), ); return () => { for (const cleanup of cleanups) { cleanup(); } }; } #onEvent(event: T, callback?: EventCallback) { if (!callback) { return noop; } // eslint-disable-next-line @typescript-eslint/no-explicit-any const item = { id: nextId(), event, callback } as EventItem; this.#callbacks = [...this.#callbacks, item]; return () => { this.#callbacks = this.#callbacks.filter((current) => current.id !== item.id); }; } emit(event: T, ...params: Events[T]) { const listeners = this.getListeners(event); for (const listener of listeners) { void listener(...params); } } hasListeners(event: T) { return this.#callbacks.some((item) => item.event === event); } private getListeners(event: T) { return this.#callbacks .filter((item) => item.event === event) .map((item) => item.callback as EventCallback); } }