@thi.ng/rstream-graph
Version:
Declarative dataflow graph construction for @thi.ng/rstream
119 lines (118 loc) • 3.74 kB
JavaScript
import { isFunction } from "@thi.ng/checks/is-function";
import { isPlainObject } from "@thi.ng/checks/is-plain-object";
import { isString } from "@thi.ng/checks/is-string";
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
import { getInUnsafe } from "@thi.ng/paths/get-in";
import { absPath, resolve } from "@thi.ng/resolve-map";
import { fromIterableSync } from "@thi.ng/rstream/iterable";
import { sync } from "@thi.ng/rstream/sync";
import { fromViewUnsafe } from "@thi.ng/rstream/view";
import { map } from "@thi.ng/transducers/map";
const initGraph = (state, spec) => {
const res = {};
for (let id in spec) {
const n = spec[id];
res[id] = __isNodeSpec(n) ? nodeFromSpec(state, n, id) : n;
}
return resolve(res);
};
const __isNodeSpec = (x) => isPlainObject(x) && isFunction(x.fn);
const nodeFromSpec = (state, spec, id) => (resolve2) => {
const ins = __prepareNodeInputs(spec.ins, state, resolve2);
const node3 = spec.fn(ins, id);
const outs = __prepareNodeOutputs(spec.outs, node3, state, id);
return { ins, node: node3, outs };
};
const __prepareNodeInputs = (ins, state, resolve2) => {
const res = {};
if (!ins) return res;
for (let id in ins) {
const i = ins[id];
const src = __getNodeInput(i, id, state, resolve2);
res[id] = i.xform ? src.transform(i.xform, { id }) : src;
}
return res;
};
const __getNodeInput = (i, id, state, resolve2) => i.path ? fromViewUnsafe(state, { path: i.path }) : i.stream ? isString(i.stream) ? resolve2(i.stream) : i.stream(resolve2) : i.const !== void 0 ? fromIterableSync([isFunction(i.const) ? i.const(resolve2) : i.const], {
closeIn: "never"
}) : illegalArgs(`invalid node input: ${id}`);
const __prepareNodeOutputs = (outs, node3, state, nodeID) => {
const res = {};
if (!outs) return res;
for (let id in outs) {
const out = outs[id];
res[id] = isFunction(out) ? out(node3, id) : id == "*" ? __nodeOutAll(node3, state, nodeID, out) : __nodeOutID(node3, state, nodeID, out, id);
}
return res;
};
const __nodeOutAll = (node3, state, nodeID, path) => node3.subscribe(
{
next: (x) => state.resetIn(path, x)
},
{ id: `out-${nodeID}` }
);
const __nodeOutID = (node3, state, nodeID, path, id) => node3.subscribe(
{
next: (x) => state.resetIn(path, x)
},
{
xform: map((x) => x != null ? x[id] : x),
id: `out-${nodeID}-${id}`
}
);
const addNode = (graph, state, id, spec) => {
graph[id] && illegalArgs(`graph already contains a node with ID: ${id}`);
return graph[id] = nodeFromSpec(
state,
spec,
id
)((path) => getInUnsafe(graph, absPath([id], path)));
};
const removeNode = (graph, id) => {
const node3 = graph[id];
if (node3) {
node3.node.unsubscribe();
for (let id2 in node3.outs) {
node3.outs[id2].unsubscribe();
}
delete graph[id];
return true;
}
return false;
};
const stop = (graph) => {
for (let id in graph) {
graph[id].node.unsubscribe();
}
};
const node = (xform, inputIDs, reset = false) => (src, id) => {
ensureInputs(src, inputIDs, id);
return sync({ src, xform, id, reset });
};
const node1 = (xform, inputID = "src") => (src, id) => {
ensureInputs(src, [inputID], id);
return src[inputID].subscribe({}, { xform, id });
};
const node2 = (xform, inputIDs = ["a", "b"], reset = false) => node(xform, inputIDs, reset);
const ensureInputs = (src, inputIDs, nodeID) => {
if (inputIDs) {
const missing = [];
for (let i of inputIDs) {
!src[i] && missing.push(i);
}
missing.length && illegalArgs(
`node "${nodeID}": missing input(s): ${missing.join(", ")}`
);
}
};
export {
addNode,
ensureInputs,
initGraph,
node,
node1,
node2,
nodeFromSpec,
removeNode,
stop
};