UNPKG

evnty

Version:

Async-first, reactive event handling library for complex event flows in browser and Node.js

206 lines (204 loc) 6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: Object.getOwnPropertyDescriptor(all, name).get }); } _export(exports, { get Broadcast () { return Broadcast; }, get BroadcastIterator () { return BroadcastIterator; }, get ConsumerHandle () { return ConsumerHandle; } }); const _ringbuffercjs = require("./ring-buffer.cjs"); const _signalcjs = require("./signal.cjs"); const _asynccjs = require("./async.cjs"); const _utilscjs = require("./utils.cjs"); class ConsumerHandle { #broadcast; constructor(broadcast){ this.#broadcast = broadcast; } get cursor() { return this.#broadcast.getCursor(this); } [Symbol.dispose]() { this.#broadcast.leave(this); } } class BroadcastIterator { #broadcast; #signal; #handle; constructor(broadcast, signal, handle){ this.#broadcast = broadcast; this.#signal = signal; this.#handle = handle; } async next() { try { while(true){ const result = this.#broadcast.tryConsume(this.#handle); if (!result.done) { return { value: result.value, done: false }; } await this.#signal.receive(); } } catch { return { value: undefined, done: true }; } } async return() { this.#broadcast.leave(this.#handle); return { value: undefined, done: true }; } } class Broadcast { #buffer = new _ringbuffercjs.RingBuffer(); #signal = new _signalcjs.Signal(); #disposer; #sink; #nextId = 0; #cursors = new Map(); #handles = new WeakMap(); #minCursor = 0; #registry = new FinalizationRegistry((id)=>{ const cursor = this.#cursors.get(id); this.#cursors.delete(id); if (cursor === this.#minCursor) { this.#minCursor = (0, _utilscjs.min)(this.#cursors.values(), this.#buffer.right); const shift = this.#minCursor - this.#buffer.left; if (shift > 0) this.#buffer.shiftN(shift); } }); [Symbol.toStringTag] = 'Broadcast'; constructor(){ this.#disposer = new _asynccjs.Disposer(this); } get sink() { return this.#sink ??= this.emit.bind(this); } handleEvent(event) { this.emit(event); } get size() { return this.#cursors.size; } emit(value) { if (this.#disposer.disposed) { return false; } this.#buffer.push(value); this.#signal.emit(value); return true; } receive() { return this.#signal.receive(); } then(onfulfilled, onrejected) { return this.receive().then(onfulfilled, onrejected); } catch(onrejected) { return this.receive().catch(onrejected); } finally(onfinally) { return this.receive().finally(onfinally); } join() { const id = this.#nextId++; const cursor = this.#buffer.right; const handle = new ConsumerHandle(this); this.#handles.set(handle, id); this.#cursors.set(id, cursor); if (this.#cursors.size === 1 || cursor < this.#minCursor) { this.#minCursor = cursor; } this.#registry.register(handle, id, handle); return handle; } getCursor(handle) { const id = this.#handles.get(handle); if (id === undefined) throw new Error('Invalid handle'); const cursor = this.#cursors.get(id); if (cursor === undefined) throw new Error('Invalid handle'); return cursor; } leave(handle) { const id = this.#handles.get(handle); if (id === undefined) return; const cursor = this.#cursors.get(id); this.#handles.delete(handle); this.#cursors.delete(id); this.#registry.unregister(handle); if (cursor === this.#minCursor) { this.#minCursor = (0, _utilscjs.min)(this.#cursors.values(), this.#buffer.right); const shift = this.#minCursor - this.#buffer.left; if (shift > 0) this.#buffer.shiftN(shift); } } consume(handle) { const result = this.tryConsume(handle); if (result.done) { throw new Error('No value available'); } return result.value; } tryConsume(handle) { const id = this.#handles.get(handle); if (id === undefined) throw new Error('Invalid handle'); const cursor = this.#cursors.get(id); if (cursor === undefined) throw new Error('Invalid handle'); if (cursor >= this.#buffer.right) { return { value: undefined, done: true }; } const value = this.#buffer.peekAt(cursor); this.#cursors.set(id, cursor + 1); if (cursor === this.#minCursor) { this.#minCursor = (0, _utilscjs.min)(this.#cursors.values(), this.#buffer.right); const shift = this.#minCursor - this.#buffer.left; if (shift > 0) this.#buffer.shiftN(shift); } return { value, done: false }; } readable(handle) { return this.getCursor(handle) < this.#buffer.right; } [Symbol.asyncIterator]() { return new BroadcastIterator(this, this.#signal, this.join()); } dispose() { this[Symbol.dispose](); } [Symbol.dispose]() { if (this.#disposer[Symbol.dispose]()) { this.#signal[Symbol.dispose](); this.#buffer.clear(); this.#cursors.clear(); } } } //# sourceMappingURL=broadcast.cjs.map