UNPKG

@thi.ng/rstream

Version:

Reactive streams & subscription primitives for constructing dataflow graphs / pipelines

151 lines (150 loc) 3.97 kB
import { comp } from "@thi.ng/transducers/comp"; import { labeled } from "@thi.ng/transducers/labeled"; import { mapVals } from "@thi.ng/transducers/map-vals"; import { partitionSync } from "@thi.ng/transducers/partition-sync"; import { isFirstOrLastInput } from "./checks.js"; import { __optsWithID } from "./idgen.js"; import { __removeAllIDs } from "./internal/remove.js"; import { LOGGER } from "./logger.js"; import { Subscription } from "./subscription.js"; const sync = (opts) => new StreamSync(opts); class StreamSync extends Subscription { /** * maps actual inputs to their virtual input subs */ sources; /** * maps real source IDs to their actual input */ idSources; /** * maps (potentially aliased) input IDs to their actual src.id */ realSourceIDs; /** * maps real src.id to (potentially aliased) input IDs */ invRealSourceIDs; psync; clean; constructor(opts) { const psync = partitionSync(/* @__PURE__ */ new Set(), { key: (x) => x[0], mergeOnly: opts.mergeOnly === true, reset: opts.reset === true, all: opts.all !== false, backPressure: opts.backPressure || 0 }); const mapv = mapVals((x) => x[1]); super( void 0, __optsWithID("streamsync", { ...opts, xform: opts.xform ? comp(psync, mapv, opts.xform) : comp(psync, mapv) }) ); this.sources = /* @__PURE__ */ new Map(); this.realSourceIDs = /* @__PURE__ */ new Map(); this.invRealSourceIDs = /* @__PURE__ */ new Map(); this.idSources = /* @__PURE__ */ new Map(); this.psync = psync; this.clean = !!opts.clean; opts.src && this.addAll(opts.src); } add(src, id) { id || (id = src.id); this.ensureState(); this.psync.add(id); this.realSourceIDs.set(id, src.id); this.invRealSourceIDs.set(src.id, id); this.idSources.set(src.id, src); this.sources.set( src, src.subscribe( { next: (x) => ( // if received value is sub, add it as source x[1] instanceof Subscription ? this.add(x[1]) : this.next(x) ), done: () => this.markDone(src), __owner: this }, { xform: labeled(id), id: `in-${id}` } ) ); } addAll(src) { for (let id in src) { this.psync.add(id); } for (let id in src) { this.add(src[id], id); } } remove(src) { const sub = this.sources.get(src); if (sub) { const id = this.invRealSourceIDs.get(src.id); LOGGER.info(`removing src: ${src.id} (${id})`); this.psync.delete(id, this.clean); this.realSourceIDs.delete(id); this.invRealSourceIDs.delete(src.id); this.idSources.delete(src.id); this.sources.delete(src); sub.unsubscribe(); return true; } return false; } removeID(id) { const src = this.getSourceForID(id); return src ? this.remove(src) : false; } removeAll(src) { for (let s of src) { this.psync.delete(this.invRealSourceIDs.get(s.id)); } let ok = true; for (let s of src) { ok = this.remove(s) && ok; } return ok; } removeAllIDs(ids) { return __removeAllIDs(this, ids); } getSourceForID(id) { return this.idSources.get(this.realSourceIDs.get(id)); } getSources() { const res = {}; for (let [id, src] of this.idSources) { res[this.invRealSourceIDs.get(id)] = src; } return res; } unsubscribe(sub) { if (!sub) { LOGGER.debug(this.id, "unsub sources"); for (let s of this.sources.values()) { s.unsubscribe(); } this.sources.clear(); this.psync.clear(); this.realSourceIDs.clear(); this.invRealSourceIDs.clear(); this.idSources.clear(); } return super.unsubscribe(sub); } markDone(src) { this.remove(src); isFirstOrLastInput(this.closeIn, this.sources.size) && this.done(); } } export { StreamSync, sync };