evnty
Version:
Async-first, reactive event handling library for complex event flows in browser and Node.js
1 lines • 5.07 kB
Source Map (JSON)
{"version":3,"sources":["../src/signal.ts"],"sourcesContent":["import { Async } from './async.js';\n\n/**\n * Promise-based async coordination primitive.\n * `emit()` resolves the pending `receive()` promise (shared across callers).\n * Reusable - after each emission a new round of `receive()` calls can be made.\n * Disposable via `[Symbol.dispose]()` or an optional AbortSignal.\n *\n * @template T The type of value that this signal carries.\n *\n * @example\n * ```typescript\n * const signal = new Signal<string>();\n *\n * const promise = signal.receive();\n * signal.emit('hello');\n * await promise; // 'hello'\n * ```\n */\nexport class Signal<T> extends Async<T, boolean> {\n #rx?: PromiseWithResolvers<T>;\n\n readonly [Symbol.toStringTag] = 'Signal';\n\n /**\n * Merges multiple source signals into a target signal.\n * Values from any source signal are emitted to the target signal.\n * The merge continues until the target signal is disposed.\n *\n * Note: When the target is disposed, iteration stops after the next value\n * from each source. For immediate cleanup, dispose source signals directly.\n *\n * @param target The signal that will receive values from all sources\n * @param signals The source signals to merge from\n *\n * @example\n * ```typescript\n * // Create a target signal and source signals\n * const target = new Signal<string>();\n * const source1 = new Signal<string>();\n * const source2 = new Signal<string>();\n *\n * // Merge sources into target\n * Signal.merge(target, source1, source2);\n *\n * // Values from any source appear in target\n * const promise = target.receive();\n * source1.emit('Hello');\n * const value = await promise; // 'Hello'\n * ```\n */\n static merge<T>(target: Signal<T>, ...signals: Signal<T>[]): void {\n if (target.disposed) {\n return;\n }\n for (const source of signals) {\n void (async () => {\n try {\n const sink = target.sink;\n for await (const value of source) {\n if (!sink(value) && target.disposed) {\n return;\n }\n }\n } catch {\n // ignore disposed signal\n }\n })();\n }\n }\n\n /**\n * Creates a new Signal instance.\n *\n * @param abortSignal An optional AbortSignal that can be used to cancel the signal operation.\n *\n * @example\n * ```typescript\n * // Create a signal with abort capability\n * const controller = new AbortController();\n * const signal = new Signal<number>(controller.signal);\n *\n * // Signal can be cancelled\n * controller.abort('Operation cancelled');\n * ```\n */\n constructor(abortSignal?: AbortSignal) {\n super(abortSignal);\n }\n\n /**\n * Sends a value to the waiting receiver, if any.\n *\n * @param value - The value to send.\n * @returns `true` if the value was emitted.\n */\n emit(value: T): boolean {\n if (!this.#rx) return false;\n this.#rx.resolve(value);\n this.#rx = undefined;\n return true;\n }\n\n /**\n * Waits for the next value to be sent to this signal. If the signal has been aborted\n * or disposed, this method rejects with Error('Disposed').\n *\n * @returns A promise that resolves with the next value sent to the signal.\n *\n * @example\n * ```typescript\n * const signal = new Signal<string>();\n *\n * // Wait for a value\n * const valuePromise = signal.receive();\n *\n * // Send a value from elsewhere\n * signal.emit('Hello');\n *\n * const value = await valuePromise; // 'Hello'\n * ```\n */\n receive(): Promise<T> {\n if (this.disposed) {\n return Promise.reject(new Error('Disposed'));\n }\n this.#rx ??= Promise.withResolvers<T>();\n return this.#rx.promise;\n }\n\n dispose(): void {\n this.#rx?.reject(new Error('Disposed'));\n this.#rx = undefined;\n }\n}\n"],"names":["Signal","Async","Symbol","toStringTag","merge","target","signals","disposed","source","sink","value","abortSignal","emit","resolve","undefined","receive","Promise","reject","Error","withResolvers","promise","dispose"],"mappings":";;;;+BAmBaA;;;eAAAA;;;0BAnBS;AAmBf,MAAMA,eAAkBC,eAAK;IAClC,CAAA,EAAG,CAA2B;IAErB,CAACC,OAAOC,WAAW,CAAC,GAAG,SAAS;IA6BzC,OAAOC,MAASC,MAAiB,EAAE,GAAGC,OAAoB,EAAQ;QAChE,IAAID,OAAOE,QAAQ,EAAE;YACnB;QACF;QACA,KAAK,MAAMC,UAAUF,QAAS;YAC5B,KAAK,AAAC,CAAA;gBACJ,IAAI;oBACF,MAAMG,OAAOJ,OAAOI,IAAI;oBACxB,WAAW,MAAMC,SAASF,OAAQ;wBAChC,IAAI,CAACC,KAAKC,UAAUL,OAAOE,QAAQ,EAAE;4BACnC;wBACF;oBACF;gBACF,EAAE,OAAM,CAER;YACF,CAAA;QACF;IACF;IAiBA,YAAYI,WAAyB,CAAE;QACrC,KAAK,CAACA;IACR;IAQAC,KAAKF,KAAQ,EAAW;QACtB,IAAI,CAAC,IAAI,CAAC,CAAA,EAAG,EAAE,OAAO;QACtB,IAAI,CAAC,CAAA,EAAG,CAACG,OAAO,CAACH;QACjB,IAAI,CAAC,CAAA,EAAG,GAAGI;QACX,OAAO;IACT;IAqBAC,UAAsB;QACpB,IAAI,IAAI,CAACR,QAAQ,EAAE;YACjB,OAAOS,QAAQC,MAAM,CAAC,IAAIC,MAAM;QAClC;QACA,IAAI,CAAC,CAAA,EAAG,KAAKF,QAAQG,aAAa;QAClC,OAAO,IAAI,CAAC,CAAA,EAAG,CAACC,OAAO;IACzB;IAEAC,UAAgB;QACd,IAAI,CAAC,CAAA,EAAG,EAAEJ,OAAO,IAAIC,MAAM;QAC3B,IAAI,CAAC,CAAA,EAAG,GAAGJ;IACb;AACF"}