@thi.ng/rstream
Version:
Reactive streams & subscription primitives for constructing dataflow graphs / pipelines
151 lines (150 loc) • 3.97 kB
JavaScript
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
};