// deno-lint-ignore-file no-explicit-any import { jit } from "./jit.ts"; export class Emitter void> { #ls: { f: F; once: boolean }[] = []; #argc = 0; get argc() { return this.#argc; } get size() { return this.#ls.length; } constructor() { this.emit = this.#emit_fast; } on(f: F, once: boolean) { this.#ls.push({ f, once }); this.#argc = Math.max(this.#argc, f.length); if (once) this.emit = this.#emit_slow; else this.#emit_fast = this.#compile(); if (this.emit !== this.#emit_slow) this.emit = this.#emit_fast; return this; } off(f: F) { const ls = this.#ls; let once = false; for (let i = ls.length; i-- !== 0; ) { if (ls[i].f === f) { (once = ls[i].once), ls.splice(i, 1); break; } } this.#argc = ls.length ? Math.max(...ls.map(({ f }) => f.length)) : 0; if (!once) this.#emit_fast = this.#compile(); if (this.emit !== this.#emit_slow) this.emit = this.#emit_fast; return this; } // deno-lint-ignore no-unused-vars emit(...args: Parameters) { return false; } listeners() { return this.#ls.map(({ f }) => f); } #emit_fast = this.#compile(); #emit_slow(...args: Parameters) { const ls = this.#ls; (this.#ls = ls.filter(({ once }) => !once)), (this.emit = this.#emit_fast); for (let i = 0, n = ls.length; i < n; i++) Reflect.apply(ls[i].f, undefined, args); return ls.length !== 0; } #compile(): typeof this.emit { const ls = this.#ls.filter(({ once }) => !once); const ps = [...Array(this.#argc).keys()].map((i) => jit.raw`a${i}`); return jit.compiled`function emit(${jit.fragment(", ", ...ps)}) { ${jit.map(" ", ls, ({ f }) => { return jit`${f}(${jit.fragment(", ", ...ps.slice(0, f.length))});`; })} return ${ls.length !== 0}; }`; } } export type EventMap = Record void>; export class TypedEmitter { readonly #events: { [K in keyof T]?: Emitter } = Object.create(null); on(name: K, f: T[K]) { return (this.#events[name] ??= new Emitter()).on(f, false), this; } once(name: K, f: T[K]) { return (this.#events[name] ??= new Emitter()).on(f, true), this; } off(name: K, f: T[K]) { const events = this.#events; if (events[name]?.off(f).size === 0) delete events[name]; return this; } emit(name: K, ...args: Parameters) { const event = this.#events[name]; if (event) return event.emit.apply(event, args); else return false; } listeners(name: K) { return this.#events[name]?.listeners() ?? []; } static { this.prototype.emit = function emit( this: TypedEmitter, name: string, a: any, b: any, c: any, d: any, e: any ) { const event = this.#events[name]; if (!event) return false; const { argc } = event; if (argc < 6) { return event.emit(a, b, c, d, e); } else { const args = Array(argc); for (let i = 0; i < argc; i++) args[i] = arguments[i + 1]; return event.emit.apply(undefined, args); } } as any; } }