# @ruvector/rudag [![npm version](https://img.shields.io/npm/v/@ruvector/rudag.svg)](https://www.npmjs.com/package/@ruvector/rudag) [![npm downloads](https://img.shields.io/npm/dm/@ruvector/rudag.svg)](https://www.npmjs.com/package/@ruvector/rudag) [![license](https://img.shields.io/npm/l/@ruvector/rudag.svg)](https://github.com/ruvnet/ruvector/blob/main/LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/) [![WebAssembly](https://img.shields.io/badge/WebAssembly-Rust-orange.svg)](https://webassembly.org/) [![Node.js](https://img.shields.io/badge/Node.js-16+-green.svg)](https://nodejs.org/) [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@ruvector/rudag)](https://bundlephobia.com/package/@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. ## Installation ```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" ``` --- ## The Problem 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]` | | How long will it all take? | `criticalPath()` | `A→C→D = 8s` (B runs parallel) | | What should I optimize? | `attention()` | Task A scores highest — fix that first! | ## Where You'll Use This | 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 | ## Why rudag? | Without rudag | With rudag | |---------------|------------| | Write graph algorithms from scratch | One-liner: `dag.criticalPath()` | | 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 | `npm install` and go | ## Comparison with Alternatives | 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** | ✅ | ❌ | ❌ | ❌ | ### When to Use What | 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 | ## Key Capabilities ### 🧠 Self-Learning Optimization rudag uses **ML-inspired attention mechanisms** to learn which nodes matter most. The more you use it, the smarter it gets at identifying bottlenecks and suggesting optimizations. ```typescript // Get importance scores for each node const scores = dag.attention(AttentionMechanism.CRITICAL_PATH); // Nodes on the critical path score higher → optimize these first! ``` ### ⚡ WASM-Accelerated Performance Core algorithms run in **Rust compiled to WebAssembly** - the same technology powering Figma, Google Earth, and AutoCAD in the browser. Get native-like speed without leaving JavaScript. ### 🔄 Automatic Cycle Detection DAGs can't have cycles by definition. rudag **automatically prevents** invalid edges that would create loops: ```typescript dag.addEdge(a, b); // ✅ OK dag.addEdge(b, c); // ✅ OK dag.addEdge(c, a); // ❌ Returns false - would create cycle! ``` ### 📊 Critical Path Analysis Instantly find the **longest path** through your graph - the sequence of tasks that determines total execution time. This is what you need to optimize first. ### 💾 Zero-Config Persistence Your DAGs automatically save to **IndexedDB** in browsers or **files** in Node.js. No database setup, no configuration - just works. ### 🔌 Serialization & Interop Export to **JSON** (human-readable) or **binary** (compact, fast). Share DAGs between services, store in databases, or send over the network. ## Quick Start ```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(); ``` ## Features ### Core Operations | 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 | ### Operators ```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 ``` ### Attention Mechanisms 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); ``` ### Persistence **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 }); ``` ### Serialization ```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); ``` ## CLI Tool After installing globally or in your project: ```bash # If installed globally: npm install -g @ruvector/rudag rudag create my-query > my-query.dag # Or run directly with npx (no install needed) npx @ruvector/rudag create my-query > my-query.dag ``` ### Commands ```bash # Create a sample DAG rudag create my-query > my-query.dag # Show DAG information rudag info my-query.dag # Print topological sort rudag topo my-query.dag # Find critical path rudag critical my-query.dag # Compute attention scores rudag attention my-query.dag critical # Convert between formats rudag convert my-query.dag my-query.json rudag convert my-query.json my-query.dag # JSON output rudag info my-query.dag --json # Help rudag help ``` ## Use Cases ### 1. SQL Query Optimizer 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; } ``` ### 2. Task Scheduler 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. Build System ```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`); ``` ### 4. Data Pipeline (ETL) ```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 ### With Express.js (REST API) ```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); ``` ### With React (State Management) ```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(' → ')}

); } ``` ### With D3.js (Visualization) ```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); } ``` ### With Bull (Job Queue) ```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; } ``` ### With GraphQL ```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 Dag { id: String! name: String nodeCount: Int! edgeCount: Int! criticalPath: CriticalPath! } 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; }, }, }; ``` ### With RxJS (Reactive Streams) ```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(); } ``` ## Performance | Operation | rudag (WASM) | Pure JS | |-----------|--------------|---------| | Add 10k nodes | ~15ms | ~150ms | | Topological sort (10k) | ~2ms | ~50ms | | Critical path (10k) | ~3ms | ~80ms | | Serialization (10k) | ~5ms | ~100ms | ## Browser Support - Chrome 57+ - Firefox 52+ - Safari 11+ - Edge 79+ Requires WebAssembly support. ## API Reference ### RuDag ```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; } ``` ### Options ```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 } ``` ## License MIT OR Apache-2.0