npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ruvector/rudag

v0.1.1

Published

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.

Downloads

85

Readme

@ruvector/rudag

npm version npm downloads license TypeScript WebAssembly Node.js Bundle Size

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

npm install @ruvector/rudag
// 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.

// 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:

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

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

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:

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:

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):

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:

const dag = new RuDag({ storage: null, autoSave: false });

Serialization

// 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:

# 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

# 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:

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:

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<string, number>();

  // 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

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)

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)

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)

import { useState, useEffect } from 'react';
import { RuDag, DagOperator } from '@ruvector/rudag';

function useDag(name: string) {
  const [dag, setDag] = useState<RuDag | null>(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<number[]>([]);

  useEffect(() => {
    if (dag && dag.nodeCount > 0) {
      setCriticalPath(dag.criticalPath().path);
    }
  }, [dag]);

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <p>Nodes: {dag?.nodeCount}</p>
      <p>Critical Path: {criticalPath.join(' → ')}</p>
    </div>
  );
}

With D3.js (Visualization)

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)

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<number, string> = {};

  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

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)

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

class RuDag {
  constructor(options?: RuDagOptions);
  init(): Promise<this>;

  // 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<StoredDag | null>;
  static load(id: string, storage?): Promise<RuDag | null>;
  static fromBytes(data: Uint8Array, options?): Promise<RuDag>;
  static fromJSON(json: string, options?): Promise<RuDag>;
  static listStored(storage?): Promise<StoredDag[]>;
  static deleteStored(id: string, storage?): Promise<boolean>;

  // Lifecycle
  getId(): string;
  getName(): string | undefined;
  setName(name: string): void;
  dispose(): void;
}

Options

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