import { noError } from "./errors.ts"; export interface EventEmitter { on: On; dispatch(event: E, ...args: Parameters): void; } export interface On { (...args: OnParametersFor): () => void; } export type OnParametersFor = Extract< OnParameters, [event: E, cb: any] >; export type OnParameters = { [E in keyof M]: [event: E, cb: M[E]]; }[keyof M]; interface EventMap { [event: string]: (...args: any[]) => void; } type EventListeners = { [E in keyof M]?: Array; }; export function createEventEmitter(): EventEmitter { const listeners: EventListeners = {}; return { on(...[event, cb]: OnParameters): () => void { listeners[event] ??= []; listeners[event].push(cb); return () => { listeners[event] = listeners[event]?.filter((listener) => cb !== listener); }; }, dispatch(event: E, ...args: Parameters): void { if (listeners[event]) { let firstError: unknown = noError; for (const cb of listeners[event]) { try { cb(...args); } catch (err) { if (firstError === noError) { firstError = err; } } } /* node:coverage disable */ if (firstError !== noError && typeof window !== "undefined") { window.dispatchEvent(new ErrorEvent("error", { error: firstError })); } /* node:coverage enable */ } }, }; }