UNPKG

@thi.ng/rstream-graph

Version:

Declarative dataflow graph construction for @thi.ng/rstream

119 lines (118 loc) 3.74 kB
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 };