Skip to content

catpea/muriel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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)

  1. Arrays always imply parallelism
  2. Parallel stages always auto-join
  3. Join happens before the next stage
  4. Output pipes only receive joined packets
  5. 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

About

Lightweight Filtergraph Flow Engine

Resources

Stars

Watchers

Forks

Packages

No packages published