UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1 lines 6.7 kB
{"version":3,"file":"event-emitter.cjs","sources":["../../src/event-emitter.ts"],"sourcesContent":["/**\n * Generic type-safe event emitter\n * @template TEvents - Record of event names to event payload types\n */\nexport class EventEmitter<TEvents extends Record<string, any>> {\n private listeners = new Map<\n keyof TEvents,\n Map<(event: TEvents[keyof TEvents]) => void, object>\n >()\n private onceCallbacks = new WeakMap<\n (event: TEvents[keyof TEvents]) => void,\n (event: TEvents[keyof TEvents]) => void\n >()\n\n /**\n * Subscribe to an event\n * @param event - Event name to listen for\n * @param callback - Function to call when event is emitted\n * @returns Unsubscribe function\n */\n on<T extends keyof TEvents>(\n event: T,\n callback: (event: TEvents[T]) => void,\n ): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Map())\n }\n const listeners = this.listeners.get(event)!\n const registered = callback as (event: any) => void\n let registration = listeners.get(registered)\n if (!registration) {\n registration = {}\n listeners.set(registered, registration)\n }\n\n return () => {\n const current = this.listeners.get(event)\n if (current?.get(registered) === registration) {\n current.delete(registered)\n }\n }\n }\n\n /**\n * Subscribe to an event once (automatically unsubscribes after first emission)\n * @param event - Event name to listen for\n * @param callback - Function to call when event is emitted\n * @returns Unsubscribe function\n */\n once<T extends keyof TEvents>(\n event: T,\n callback: (event: TEvents[T]) => void,\n ): () => void {\n let unsubscribe = () => {}\n const listener = (eventPayload: TEvents[T]) => {\n unsubscribe()\n callback(eventPayload)\n }\n this.onceCallbacks.set(\n listener as (event: TEvents[keyof TEvents]) => void,\n callback as (event: TEvents[keyof TEvents]) => void,\n )\n unsubscribe = this.on(event, listener)\n return unsubscribe\n }\n\n /**\n * Unsubscribe from an event\n * @param event - Event name to stop listening for\n * @param callback - Function to remove\n */\n off<T extends keyof TEvents>(\n event: T,\n callback: (event: TEvents[T]) => void,\n ): void {\n const listeners = this.listeners.get(event)\n if (!listeners) return\n for (const listener of listeners.keys()) {\n if (\n listener === callback ||\n this.onceCallbacks.get(listener) === callback\n ) {\n listeners.delete(listener)\n }\n }\n }\n\n /**\n * Wait for an event to be emitted\n * @param event - Event name to wait for\n * @param timeout - Optional timeout in milliseconds\n * @returns Promise that resolves with the event payload\n */\n waitFor<T extends keyof TEvents>(\n event: T,\n timeout?: number,\n ): Promise<TEvents[T]> {\n return new Promise((resolve, reject) => {\n let timeoutId: NodeJS.Timeout | undefined\n const unsubscribe = this.on(event, (eventPayload) => {\n if (timeoutId) {\n clearTimeout(timeoutId)\n timeoutId = undefined\n }\n resolve(eventPayload)\n unsubscribe()\n })\n if (timeout) {\n timeoutId = setTimeout(() => {\n timeoutId = undefined\n unsubscribe()\n reject(new Error(`Timeout waiting for event ${String(event)}`))\n }, timeout)\n }\n })\n }\n\n /**\n * Emit an event to all listeners\n * @param event - Event name to emit\n * @param eventPayload - Event payload\n * @internal For use by subclasses - subclasses should wrap this with a public emit if needed\n */\n protected emitInner<T extends keyof TEvents>(\n event: T,\n eventPayload: TEvents[T],\n ): void {\n this.emitInnerWhile(event, eventPayload, () => true)\n }\n\n /** Emit until a reentrant callback invalidates the event being delivered. */\n protected emitInnerWhile<T extends keyof TEvents>(\n event: T,\n eventPayload: TEvents[T],\n isCurrent: () => boolean,\n ): void {\n const listeners = this.listeners.get(event)\n if (!listeners) return\n for (const [listener, registration] of [...listeners]) {\n if (!isCurrent()) break\n if (this.listeners.get(event)?.get(listener) !== registration) continue\n try {\n listener(eventPayload)\n } catch (error) {\n // Re-throw in a microtask to surface the error\n queueMicrotask(() => {\n throw error\n })\n }\n }\n }\n\n /**\n * Clear all listeners\n */\n protected clearListeners(): void {\n this.listeners.clear()\n }\n}\n"],"names":[],"mappings":";;AAIO,MAAM,aAAkD;AAAA,EAAxD,cAAA;AACL,SAAQ,gCAAgB,IAAA;AAIxB,SAAQ,oCAAoB,QAAA;AAAA,EAG1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQF,GACE,OACA,UACY;AACZ,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,KAAK;AAAA,IACrC;AACA,UAAM,YAAY,KAAK,UAAU,IAAI,KAAK;AAC1C,UAAM,aAAa;AACnB,QAAI,eAAe,UAAU,IAAI,UAAU;AAC3C,QAAI,CAAC,cAAc;AACjB,qBAAe,CAAA;AACf,gBAAU,IAAI,YAAY,YAAY;AAAA,IACxC;AAEA,WAAO,MAAM;AACX,YAAM,UAAU,KAAK,UAAU,IAAI,KAAK;AACxC,UAAI,SAAS,IAAI,UAAU,MAAM,cAAc;AAC7C,gBAAQ,OAAO,UAAU;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KACE,OACA,UACY;AACZ,QAAI,cAAc,MAAM;AAAA,IAAC;AACzB,UAAM,WAAW,CAAC,iBAA6B;AAC7C,kBAAA;AACA,eAAS,YAAY;AAAA,IACvB;AACA,SAAK,cAAc;AAAA,MACjB;AAAA,MACA;AAAA,IAAA;AAEF,kBAAc,KAAK,GAAG,OAAO,QAAQ;AACrC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IACE,OACA,UACM;AACN,UAAM,YAAY,KAAK,UAAU,IAAI,KAAK;AAC1C,QAAI,CAAC,UAAW;AAChB,eAAW,YAAY,UAAU,QAAQ;AACvC,UACE,aAAa,YACb,KAAK,cAAc,IAAI,QAAQ,MAAM,UACrC;AACA,kBAAU,OAAO,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QACE,OACA,SACqB;AACrB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACJ,YAAM,cAAc,KAAK,GAAG,OAAO,CAAC,iBAAiB;AACnD,YAAI,WAAW;AACb,uBAAa,SAAS;AACtB,sBAAY;AAAA,QACd;AACA,gBAAQ,YAAY;AACpB,oBAAA;AAAA,MACF,CAAC;AACD,UAAI,SAAS;AACX,oBAAY,WAAW,MAAM;AAC3B,sBAAY;AACZ,sBAAA;AACA,iBAAO,IAAI,MAAM,6BAA6B,OAAO,KAAK,CAAC,EAAE,CAAC;AAAA,QAChE,GAAG,OAAO;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,UACR,OACA,cACM;AACN,SAAK,eAAe,OAAO,cAAc,MAAM,IAAI;AAAA,EACrD;AAAA;AAAA,EAGU,eACR,OACA,cACA,WACM;AACN,UAAM,YAAY,KAAK,UAAU,IAAI,KAAK;AAC1C,QAAI,CAAC,UAAW;AAChB,eAAW,CAAC,UAAU,YAAY,KAAK,CAAC,GAAG,SAAS,GAAG;AACrD,UAAI,CAAC,YAAa;AAClB,UAAI,KAAK,UAAU,IAAI,KAAK,GAAG,IAAI,QAAQ,MAAM,aAAc;AAC/D,UAAI;AACF,iBAAS,YAAY;AAAA,MACvB,SAAS,OAAO;AAEd,uBAAe,MAAM;AACnB,gBAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKU,iBAAuB;AAC/B,SAAK,UAAU,MAAA;AAAA,EACjB;AACF;;"}