muriel
v1.0.6
Published
Lightweight Filtergraph Flow Engine
Readme
Muriel: The Lightweight Filtergraph Flow Engine
A tiny, human-readable flow engine inspired by FFmpeg filtergraphs and Processing modes.
No nodes. No operators. No DSL gymnastics.
Core Ideas
- Named pipes are event channels
- Edges describe pipelines
- Arrays mean parallel
- Parallel stages auto-join
- Everything else is series
- Worker threads are optional
NOTE: The worker pool is more suited for CPU-bound pure-JS transforms where you want to avoid blocking the main thread — e.g., heavy string processing, JSON transformations, or math-intensive filters across thousands of packets.
Transform API
function myTransform(options = {}) {
return (send, packet) => {
send({ ...packet, value: packet.value * 2 });
};
}- Outer function = configuration
- Inner function = execution
- Async supported
Filtergraph Syntax
An edge is:
[
inputPipe,
stage1,
stage2,
...stageN,
outputPipe
]Series
['post', normalize, verify, 'updated']Parallel (auto-joined)
['post', [cover, audio, post], 'updated']Parallel → Series
['post', [cover, audio, post], verify, backup, 'updated']Semantics:
(post)
├─► cover
├─► audio
└─► post
│
[auto-join]
│
verify → backup → (updated)No explicit barrier required.
Producers
function socket(channel) {
return send => {
setTimeout(() => {
send({ payload: { type: 'new-post' }, topic: channel });
}, 100);
};
}Usage:
[socket('post'), 'post']Example: Blog Builder
import { flow } from './index.js';
const blog = flow(
[
[socket('post'), 'post'],
['post', [cover, audio, post], verify, backup, 'updated'],
['updated', pagerizer, 'done']
],
{
context: { user: 'alice' },
workers: 8 // optional
}
);
blog.start();Worker Threads
- Disabled when
workers === undefined - Enabled with fixed pool size
- Best for CPU-heavy transforms
flow(graph, { workers: 4 });Disposal
Always explicit.
blog.dispose();- Removes all listeners
- Terminates worker threads
- Tears down entire graph
Design Rules (Important)
- Arrays always imply parallelism
- Parallel stages always auto-join
- Join happens before the next stage
- Output pipes only receive joined packets
- Graphs are static and readable
If you can draw the graph on a whiteboard, the API is correct.
Philosophy
Think FFmpeg filtergraphs, not Node-RED nodes. Think dataflow, not operators.
License
MIT
