Nodes and ports
The runtime's nodes, ports and flows, and the rules they enforce.
@hlang-org/runtime is the engine. It has three node base classes, a Port factory and Flow.
Nodes
| Class | Kind | Ports | Implement |
|---|---|---|---|
ReadableNode | R | outputs, plus @node/error | _read($o) |
TransformNode | T | inputs and outputs, plus @node/error | _transform($i, $o) |
WriteableNode | W | inputs only, no error port | _write($i) |
Create ports in the constructor with Port.I(name).attach(this) and Port.O(name).attach(this). The constructor argument is stored as this.opts. The interpreter and the compiler pass { nodeId, flowId, env, middleware, config, signal }; a node's parameters are in this.opts.config.params.
The runtime calls _read, _transform or _write once per flow. $i(name) and $o(name) return the node's input and output ports. Inside a node, this.I(name), this.O(name) and this.E() (the error port) return the same ports. Readable and Transform nodes have throw(error), which sends error on @node/error.
// Three nodes, one of each kind. Each declares its ports in the constructor.
import { ReadableNode, TransformNode, WriteableNode, Port } from '@hlang-org/runtime';
/** R: sends one greeting on `out`. */
export class Greeting extends ReadableNode {
constructor(opts) {
super(opts);
Port.O('out').attach(this);
}
_read($o) {
$o('out').send('hello, hlang');
}
}
/** T: upper-cases every text it receives on `in` and sends it on `out`. */
export class Uppercase extends TransformNode {
constructor(opts) {
super(opts);
Port.I('in').attach(this);
Port.O('out').attach(this);
}
_transform($i, $o) {
$i('in').receive(text => $o('out').send(text.toUpperCase()));
}
}
/** W: prints every text it receives on `in`. */
export class Print extends WriteableNode {
constructor(opts) {
super(opts);
Port.I('in').attach(this);
}
_write($i) {
$i('in').receive(text => console.log(text));
}
}Ports
A port wraps an RxJS Subject.
| Method | Effect |
|---|---|
send(message) | Emits message. |
receive(listener) | Subscribes listener and returns the RxJS Subscription. |
pipe(...operators) | Applies RxJS operators to what the port emits and returns { receive }. |
connect(input) | Connects this output port to an input port. Throws unless the direction is output to input. |
Ports are multicast: one output can feed several inputs, and an input can receive from several outputs.
Flows
new Flow().run(readable) collects the nodes reachable from the Readable root. It starts the Writable nodes first, then the Transform nodes, then the root, so every subscriber exists before data is sent. run throws if its argument is not a Readable node; run each Readable root of a graph.
import { Flow } from '@hlang-org/runtime';
import { Greeting, Uppercase, Print } from './lib/nodes.mjs';
const greeting = new Greeting();
const uppercase = new Uppercase();
const print = new Print();
// Wiring is always output to input.
greeting.O('out').connect(uppercase.I('in'));
uppercase.O('out').connect(print.I('in'));
// Run from the Readable root.
new Flow().run(greeting);HELLO, HLANGTransform nodes can form a cycle. Bound a feedback loop, for example with
takeor a counter, so it ends.
The full API is in the runtime reference.
The examples on this page run in CI. Edit this page on GitHub