export class PubSub { #target = new EventTarget(); publish(trigger: string, payload: unknown): Promise { this.#target.dispatchEvent(new CustomEvent(trigger, { detail: payload })); return Promise.resolve(); } asyncIterator(triggers: string | string[]): AsyncIterableIterator { const names = Array.isArray(triggers) ? triggers : [triggers]; const pulls: Array<(result: IteratorResult) => void> = []; const pushes: T[] = []; let done = false; const listener = (event: Event) => { const value = (event as CustomEvent).detail; const pull = pulls.shift(); if (pull) pull({ done: false, value }); else pushes.push(value); }; for (const name of names) this.#target.addEventListener(name, listener); const close = (): Promise> => { if (!done) { done = true; for (const name of names) this.#target.removeEventListener(name, listener); for (const pull of pulls.splice(0)) pull({ done: true, value: undefined }); pushes.length = 0; } return Promise.resolve({ done: true, value: undefined }); }; return { next: () => { if (done) return Promise.resolve({ done: true, value: undefined }); if (pushes.length > 0) return Promise.resolve({ done: false, value: pushes.shift() as T }); return new Promise((resolve) => pulls.push(resolve)); }, return: close, throw: (error: unknown) => { close(); return Promise.reject(error); }, [Symbol.asyncIterator]() { return this; } }; } }