Fast DAG (Directed Acyclic Graph) library with Rust/WASM. Topological sort, critical path, task scheduling, dependency resolution, workflow optimization. Self-learning ML attention. Browser & Node.js with auto-persistence.
npm install @ruvector/rudag






Smart task scheduling with self-learning optimization — powered by Rust/WASM
> "What order should I run these tasks? Which one is slowing everything down?"
rudag answers these questions instantly. It's a Directed Acyclic Graph (DAG) library that helps you manage dependencies, find bottlenecks, and optimize execution — all with self-learning intelligence that gets smarter over time.
``bash`
npm install @ruvector/rudag
`typescript`
// 3 lines to find your bottleneck
const dag = new RuDag({ name: 'my-pipeline' });
await dag.init();
const { path, cost } = dag.criticalPath(); // → "Task A → Task C takes 8 seconds"
---
You have tasks with dependencies. Task C needs A and B to finish first:
``
┌─────────────┐ ┌─────────────┐
│ Task A: 5s │ │ Task B: 3s │
└──────┬──────┘ └──────┬──────┘
│ │
└────────┬──────────┘
▼
┌─────────────┐
│ Task C: 2s │
└──────┬──────┘
▼
┌─────────────┐
│ Task D: 1s │
└─────────────┘
You need answers:
| Question | rudag Method | Answer |
|----------|--------------|--------|
| What order to run tasks? | topoSort() | [A, B, C, D] |criticalPath()
| How long will it all take? | | A→C→D = 8s (B runs parallel) |attention()
| What should I optimize? | | Task A scores highest — fix that first! |
| Use Case | Example |
|----------|---------|
| 🗄️ Query Optimization | Find which table scan is the bottleneck |
| 🔨 Build Systems | Compile dependencies in the right order |
| 📦 Package Managers | Resolve and install dependencies |
| 🔄 CI/CD Pipelines | Orchestrate test → build → deploy |
| 📊 ETL Pipelines | Schedule extract → transform → load |
| 🎮 Game AI | Plan action sequences with prerequisites |
| 📋 Workflow Engines | Manage approval chains and state machines |
| Without rudag | With rudag |
|---------------|------------|
| Write graph algorithms from scratch | One-liner: dag.criticalPath() |npm install
| Slow JavaScript loops | Rust/WASM - 10-100x faster |
| Data lost on page refresh | Auto-saves to IndexedDB |
| Hard to find bottlenecks | Attention scores highlight important nodes |
| Complex setup | and go |
| Feature | rudag | graphlib | dagre | d3-dag |
|---------|-------|----------|-------|--------|
| Performance | ⚡ WASM (10-100x faster) | JS | JS | JS |
| Critical Path | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ Manual |
| Attention/Scoring | ✅ ML-inspired | ❌ | ❌ | ❌ |
| Cycle Detection | ✅ Automatic | ✅ | ✅ | ✅ |
| Topological Sort | ✅ | ✅ | ✅ | ✅ |
| Persistence | ✅ IndexedDB + Files | ❌ | ❌ | ❌ |
| Browser + Node.js | ✅ Both | ✅ Both | ✅ Both | ⚠️ Browser |
| TypeScript | ✅ Native | ⚠️ @types | ⚠️ @types | ✅ Native |
| Bundle Size | ~50KB (WASM) | ~15KB | ~30KB | ~20KB |
| Self-Learning | ✅ | ❌ | ❌ | ❌ |
| Serialization | ✅ JSON + Binary | ✅ JSON | ✅ JSON | ❌ |
| CLI Tool | ✅ | ❌ | ❌ | ❌ |
| Use Case | Recommendation |
|----------|----------------|
| Query optimization / Task scheduling | rudag - Critical path + attention scoring |
| Graph visualization / Layout | dagre - Designed for layout algorithms |
| Simple dependency tracking | graphlib - Lightweight, no WASM overhead |
| D3 integration | d3-dag - Native D3 compatibility |
| Large graphs (10k+ nodes) | rudag - WASM performance advantage |
| Offline-first apps | rudag - Built-in persistence |
`typescript`
// Get importance scores for each node
const scores = dag.attention(AttentionMechanism.CRITICAL_PATH);
// Nodes on the critical path score higher → optimize these first!
`typescript`
dag.addEdge(a, b); // ✅ OK
dag.addEdge(b, c); // ✅ OK
dag.addEdge(c, a); // ❌ Returns false - would create cycle!
`typescript
import { RuDag, DagOperator } from '@ruvector/rudag';
// Create a DAG (auto-persists to IndexedDB in browser)
const dag = new RuDag({ name: 'my-query' });
await dag.init();
// Add nodes with operators and costs
const scan = dag.addNode(DagOperator.SCAN, 100); // Read table: 100ms
const filter = dag.addNode(DagOperator.FILTER, 10); // Filter rows: 10ms
const project = dag.addNode(DagOperator.PROJECT, 5); // Select columns: 5ms
// Connect nodes (creates edges)
dag.addEdge(scan, filter);
dag.addEdge(filter, project);
// Analyze the DAG
const topo = dag.topoSort(); // [0, 1, 2] - execution order
const { path, cost } = dag.criticalPath(); // Slowest path: 115ms
console.log(Critical path: ${path.join(' → ')} (${cost}ms));
// Output: Critical path: 0 → 1 → 2 (115ms)
// Cleanup when done
dag.dispose();
`
| Feature | Description |
|---------|-------------|
| addNode(operator, cost) | Add a node with operator type and execution cost |addEdge(from, to)
| | Connect nodes (rejects cycles automatically) |topoSort()
| | Get nodes in topological order |criticalPath()
| | Find the longest/most expensive path |attention(mechanism)
| | Score nodes by importance |
`typescript
import { DagOperator } from '@ruvector/rudag';
DagOperator.SCAN // 0 - Table scan
DagOperator.FILTER // 1 - WHERE clause
DagOperator.PROJECT // 2 - SELECT columns
DagOperator.JOIN // 3 - Table join
DagOperator.AGGREGATE // 4 - GROUP BY
DagOperator.SORT // 5 - ORDER BY
DagOperator.LIMIT // 6 - LIMIT/TOP
DagOperator.UNION // 7 - UNION
DagOperator.CUSTOM // 255 - Custom operator
`
Score nodes by their importance using ML-inspired attention:
`typescript
import { AttentionMechanism } from '@ruvector/rudag';
// Score by position in execution order
const topoScores = dag.attention(AttentionMechanism.TOPOLOGICAL);
// Score by distance from critical path (most useful)
const criticalScores = dag.attention(AttentionMechanism.CRITICAL_PATH);
// Equal scores for all nodes
const uniformScores = dag.attention(AttentionMechanism.UNIFORM);
`
Browser (IndexedDB) - Automatic:
`typescript
const dag = new RuDag({ name: 'my-dag' }); // Auto-saves to IndexedDB
await dag.init();
// Later: reload from storage
const loaded = await RuDag.load('dag-123456-abc');
// List all stored DAGs
const allDags = await RuDag.listStored();
// Delete a DAG
await RuDag.deleteStored('dag-123456-abc');
`
Node.js (File System):
`typescript
import { NodeDagManager } from '@ruvector/rudag/node';
const manager = new NodeDagManager('./.rudag');
await manager.init();
const dag = await manager.createDag('pipeline');
// ... build the DAG ...
await manager.saveDag(dag);
// Later: reload
const loaded = await manager.loadDag('pipeline-id');
`
Disable Persistence:
`typescript`
const dag = new RuDag({ storage: null, autoSave: false });
`typescript
// Binary (compact, fast)
const bytes = dag.toBytes();
const restored = await RuDag.fromBytes(bytes);
// JSON (human-readable)
const json = dag.toJSON();
const restored = await RuDag.fromJSON(json);
`
After installing globally or in your project:
`bashIf installed globally: npm install -g @ruvector/rudag
rudag create my-query > my-query.dag
$3
`bash
Create a sample DAG
rudag create my-query > my-query.dagShow DAG information
rudag info my-query.dagPrint topological sort
rudag topo my-query.dagFind critical path
rudag critical my-query.dagCompute attention scores
rudag attention my-query.dag criticalConvert between formats
rudag convert my-query.dag my-query.json
rudag convert my-query.json my-query.dagJSON output
rudag info my-query.dag --jsonHelp
rudag help
`Use Cases
$3
Build a query plan DAG and find the critical path:
`typescript
import { RuDag, DagOperator } from '@ruvector/rudag';async function analyzeQuery(sql: string) {
const dag = new RuDag({ name: sql.slice(0, 50) });
await dag.init();
// Parse SQL and build DAG (simplified example)
const scan1 = dag.addNode(DagOperator.SCAN, estimateScanCost('users'));
const scan2 = dag.addNode(DagOperator.SCAN, estimateScanCost('orders'));
const join = dag.addNode(DagOperator.JOIN, estimateJoinCost(1000, 5000));
const filter = dag.addNode(DagOperator.FILTER, 10);
const project = dag.addNode(DagOperator.PROJECT, 5);
dag.addEdge(scan1, join);
dag.addEdge(scan2, join);
dag.addEdge(join, filter);
dag.addEdge(filter, project);
const { path, cost } = dag.criticalPath();
console.log(
Estimated query time: ${cost}ms);
console.log(Bottleneck: node ${path[0]}); // Usually the scan or join return dag;
}
`$3
Schedule tasks respecting dependencies:
`typescript
import { RuDag, DagOperator } from '@ruvector/rudag';interface Task {
id: string;
duration: number;
dependencies: string[];
}
async function scheduleTasks(tasks: Task[]) {
const dag = new RuDag({ name: 'task-schedule', storage: null });
await dag.init();
const taskToNode = new Map();
// Add all tasks as nodes
for (const task of tasks) {
const nodeId = dag.addNode(DagOperator.CUSTOM, task.duration);
taskToNode.set(task.id, nodeId);
}
// Add dependencies as edges
for (const task of tasks) {
const toNode = taskToNode.get(task.id)!;
for (const dep of task.dependencies) {
const fromNode = taskToNode.get(dep)!;
dag.addEdge(fromNode, toNode);
}
}
// Get execution order
const order = dag.topoSort();
const schedule = order.map(nodeId => {
const task = tasks.find(t => taskToNode.get(t.id) === nodeId)!;
return task.id;
});
// Total time (critical path)
const { cost } = dag.criticalPath();
console.log(
Total time with parallelization: ${cost}ms); dag.dispose();
return schedule;
}
`$3
`typescript
import { RuDag, DagOperator } from '@ruvector/rudag';const dag = new RuDag({ name: 'build' });
await dag.init();
// Define build steps
const compile = dag.addNode(DagOperator.CUSTOM, 5000); // 5s
const test = dag.addNode(DagOperator.CUSTOM, 10000); // 10s
const lint = dag.addNode(DagOperator.CUSTOM, 2000); // 2s
const bundle = dag.addNode(DagOperator.CUSTOM, 3000); // 3s
const deploy = dag.addNode(DagOperator.CUSTOM, 1000); // 1s
dag.addEdge(compile, test);
dag.addEdge(compile, lint);
dag.addEdge(test, bundle);
dag.addEdge(lint, bundle);
dag.addEdge(bundle, deploy);
// Parallel execution order
const order = dag.topoSort(); // [compile, test|lint (parallel), bundle, deploy]
// Critical path: compile → test → bundle → deploy = 19s
const { cost } = dag.criticalPath();
console.log(
Minimum build time: ${cost}ms);
`$3
`typescript
import { RuDag, DagOperator, AttentionMechanism } from '@ruvector/rudag';const pipeline = new RuDag({ name: 'etl-pipeline' });
await pipeline.init();
// Extract
const extractUsers = pipeline.addNode(DagOperator.SCAN, 1000);
const extractOrders = pipeline.addNode(DagOperator.SCAN, 2000);
const extractProducts = pipeline.addNode(DagOperator.SCAN, 500);
// Transform
const cleanUsers = pipeline.addNode(DagOperator.FILTER, 100);
const joinData = pipeline.addNode(DagOperator.JOIN, 3000);
const aggregate = pipeline.addNode(DagOperator.AGGREGATE, 500);
// Load
const loadWarehouse = pipeline.addNode(DagOperator.CUSTOM, 1000);
// Wire it up
pipeline.addEdge(extractUsers, cleanUsers);
pipeline.addEdge(cleanUsers, joinData);
pipeline.addEdge(extractOrders, joinData);
pipeline.addEdge(extractProducts, joinData);
pipeline.addEdge(joinData, aggregate);
pipeline.addEdge(aggregate, loadWarehouse);
// Find bottlenecks using attention scores
const scores = pipeline.attention(AttentionMechanism.CRITICAL_PATH);
console.log('Node importance:', scores);
// Nodes on critical path have higher scores
`Integration with Other Packages
$3
`typescript
import express from 'express';
import { RuDag, DagOperator } from '@ruvector/rudag';
import { NodeDagManager } from '@ruvector/rudag/node';const app = express();
const manager = new NodeDagManager('./data/dags');
app.use(express.json());
app.post('/dags', async (req, res) => {
const dag = await manager.createDag(req.body.name);
// ... add nodes from request ...
await manager.saveDag(dag);
res.json({ id: dag.getId() });
});
app.get('/dags/:id/critical-path', async (req, res) => {
const dag = await manager.loadDag(req.params.id);
if (!dag) return res.status(404).json({ error: 'Not found' });
const result = dag.criticalPath();
dag.dispose();
res.json(result);
});
app.listen(3000);
`$3
`typescript
import { useState, useEffect } from 'react';
import { RuDag, DagOperator } from '@ruvector/rudag';function useDag(name: string) {
const [dag, setDag] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const init = async () => {
const d = new RuDag({ name });
await d.init();
setDag(d);
setLoading(false);
};
init();
return () => dag?.dispose();
}, [name]);
return { dag, loading };
}
function DagViewer({ name }: { name: string }) {
const { dag, loading } = useDag(name);
const [criticalPath, setCriticalPath] = useState([]);
useEffect(() => {
if (dag && dag.nodeCount > 0) {
setCriticalPath(dag.criticalPath().path);
}
}, [dag]);
if (loading) return
Loading...; return (
Nodes: {dag?.nodeCount}
Critical Path: {criticalPath.join(' → ')}
);
}
`$3
`typescript
import * as d3 from 'd3';
import { RuDag, DagOperator } from '@ruvector/rudag';async function visualizeDag(dag: RuDag, container: HTMLElement) {
const nodes = dag.getNodes().map(n => ({
id: n.id,
label: DagOperator[n.operator],
cost: n.cost,
}));
const topo = dag.topoSort();
const { path: criticalPath } = dag.criticalPath();
const criticalSet = new Set(criticalPath);
// Create D3 visualization
const svg = d3.select(container).append('svg');
svg.selectAll('circle')
.data(nodes)
.enter()
.append('circle')
.attr('r', d => Math.sqrt(d.cost) * 2)
.attr('fill', d => criticalSet.has(d.id) ? '#ff6b6b' : '#4dabf7')
.attr('cx', (d, i) => 100 + topo.indexOf(d.id) * 150)
.attr('cy', 100);
}
`$3
`typescript
import Queue from 'bull';
import { RuDag, DagOperator } from '@ruvector/rudag';const jobQueue = new Queue('dag-jobs');
async function queueDagExecution(dag: RuDag) {
const order = dag.topoSort();
const nodes = dag.getNodes();
// Queue jobs in topological order with dependencies
const jobIds: Record = {};
for (const nodeId of order) {
const node = nodes.find(n => n.id === nodeId)!;
const job = await jobQueue.add({
nodeId,
operator: node.operator,
cost: node.cost,
}, {
// Jobs wait for their dependencies
delay: 0,
});
jobIds[nodeId] = job.id as string;
}
return jobIds;
}
`$3
`typescript
import { ApolloServer, gql } from 'apollo-server';
import { RuDag, DagOperator } from '@ruvector/rudag';
import { NodeDagManager } from '@ruvector/rudag/node';const manager = new NodeDagManager('./dags');
const typeDefs = gql
type CriticalPath {
path: [Int!]!
cost: Float!
}
type Query {
dag(id: String!): Dag
dags: [Dag!]!
};
const resolvers = {
Query: {
dag: async (_: any, { id }: { id: string }) => {
const dag = await manager.loadDag(id);
if (!dag) return null;
const result = {
id: dag.getId(),
name: dag.getName(),
nodeCount: dag.nodeCount,
edgeCount: dag.edgeCount,
criticalPath: dag.criticalPath(),
};
dag.dispose();
return result;
},
},
};
`
`typescript
import { Subject, from } from 'rxjs';
import { mergeMap, toArray } from 'rxjs/operators';
import { RuDag, DagOperator } from '@ruvector/rudag';
async function executeWithRxJS(dag: RuDag) {
const order = dag.topoSort();
const nodes = dag.getNodes();
const results$ = from(order).pipe(
mergeMap(async (nodeId) => {
const node = nodes.find(n => n.id === nodeId)!;
// Simulate execution
await new Promise(r => setTimeout(r, node.cost));
return { nodeId, completed: true };
}, 3), // Max 3 concurrent executions
toArray()
);
return results$.toPromise();
}
`
| Operation | rudag (WASM) | Pure JS |
|-----------|--------------|---------|
| Add 10k nodes | ~15ms | ~150ms |
| Topological sort (10k) | ~2ms | ~50ms |
| Critical path (10k) | ~3ms | ~80ms |
| Serialization (10k) | ~5ms | ~100ms |
- Chrome 57+
- Firefox 52+
- Safari 11+
- Edge 79+
Requires WebAssembly support.
`typescript
class RuDag {
constructor(options?: RuDagOptions);
init(): Promise
// Graph operations
addNode(operator: DagOperator, cost: number, metadata?: object): number;
addEdge(from: number, to: number): boolean;
// Properties
nodeCount: number;
edgeCount: number;
// Analysis
topoSort(): number[];
criticalPath(): { path: number[]; cost: number };
attention(mechanism?: AttentionMechanism): number[];
// Node access
getNode(id: number): DagNode | undefined;
getNodes(): DagNode[];
// Serialization
toBytes(): Uint8Array;
toJSON(): string;
// Persistence
save(): Promise
static load(id: string, storage?): Promise
static fromBytes(data: Uint8Array, options?): Promise
static fromJSON(json: string, options?): Promise
static listStored(storage?): Promise
static deleteStored(id: string, storage?): Promise
// Lifecycle
getId(): string;
getName(): string | undefined;
setName(name: string): void;
dispose(): void;
}
`
`typescript``
interface RuDagOptions {
id?: string; // Custom ID (auto-generated if not provided)
name?: string; // Human-readable name
storage?: Storage | null; // Persistence backend (null = disabled)
autoSave?: boolean; // Auto-save on changes (default: true)
onSaveError?: (error) => void; // Handle background save errors
}
MIT OR Apache-2.0