Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
26
vendor/ruvector/npm/packages/rudag/.gitignore
vendored
Normal file
26
vendor/ruvector/npm/packages/rudag/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build artifacts (generated by npm run build)
|
||||
# Note: dist/, pkg/, pkg-node/ are kept for publishing
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# npm
|
||||
npm-debug.log*
|
||||
.npm
|
||||
|
||||
# Test coverage
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
21
vendor/ruvector/npm/packages/rudag/LICENSE
vendored
Normal file
21
vendor/ruvector/npm/packages/rudag/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 rUv
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
757
vendor/ruvector/npm/packages/rudag/README.md
vendored
Normal file
757
vendor/ruvector/npm/packages/rudag/README.md
vendored
Normal file
@@ -0,0 +1,757 @@
|
||||
# @ruvector/rudag
|
||||
|
||||
[](https://www.npmjs.com/package/@ruvector/rudag)
|
||||
[](https://www.npmjs.com/package/@ruvector/rudag)
|
||||
[](https://github.com/ruvnet/ruvector/blob/main/LICENSE)
|
||||
[](https://www.typescriptlang.org/)
|
||||
[](https://webassembly.org/)
|
||||
[](https://nodejs.org/)
|
||||
[](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<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
|
||||
|
||||
```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<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)
|
||||
|
||||
```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<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
|
||||
|
||||
```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<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
|
||||
|
||||
```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
|
||||
378
vendor/ruvector/npm/packages/rudag/bin/cli.js
vendored
Normal file
378
vendor/ruvector/npm/packages/rudag/bin/cli.js
vendored
Normal file
@@ -0,0 +1,378 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* rudag CLI - Command-line interface for DAG operations
|
||||
*
|
||||
* @security File paths are validated to prevent reading arbitrary files
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// Lazy load to improve startup time
|
||||
let RuDag, DagOperator, AttentionMechanism;
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0];
|
||||
|
||||
const help = `
|
||||
rudag - Self-learning DAG query optimization CLI
|
||||
|
||||
Usage: rudag <command> [options]
|
||||
|
||||
Commands:
|
||||
create <name> Create a new DAG and output to stdout
|
||||
load <file> Load DAG from file (must be .dag or .json)
|
||||
info <file> Show DAG information
|
||||
topo <file> Print topological sort
|
||||
critical <file> Find critical path
|
||||
attention <file> [type] Compute attention scores (type: topo|critical|uniform)
|
||||
convert <in> <out> Convert between JSON and binary formats
|
||||
help Show this help message
|
||||
|
||||
Examples:
|
||||
rudag create my-query > my-query.dag
|
||||
rudag info ./data/my-query.dag
|
||||
rudag critical ./queries/query.dag
|
||||
rudag attention query.dag critical
|
||||
|
||||
Options:
|
||||
--json Output in JSON format
|
||||
--verbose Verbose output
|
||||
|
||||
Security:
|
||||
- Only .dag and .json files are allowed
|
||||
- Paths are restricted to current directory and subdirectories
|
||||
`;
|
||||
|
||||
/**
|
||||
* Validate file path for security
|
||||
* @security Prevents path traversal and restricts to allowed extensions
|
||||
*/
|
||||
function validateFilePath(filePath) {
|
||||
if (!filePath || typeof filePath !== 'string') {
|
||||
throw new Error('File path is required');
|
||||
}
|
||||
|
||||
// Check extension
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
if (ext !== '.dag' && ext !== '.json') {
|
||||
throw new Error(`Invalid file extension: ${ext}. Only .dag and .json files are allowed.`);
|
||||
}
|
||||
|
||||
// Resolve to absolute path
|
||||
const resolved = path.resolve(filePath);
|
||||
const cwd = process.cwd();
|
||||
|
||||
// Ensure path is within current directory (prevents traversal)
|
||||
if (!resolved.startsWith(cwd + path.sep) && resolved !== cwd) {
|
||||
// Allow absolute paths within cwd or relative paths
|
||||
const normalized = path.normalize(filePath);
|
||||
if (normalized.startsWith('..') || path.isAbsolute(normalized)) {
|
||||
// Check if absolute path is within cwd
|
||||
if (!resolved.startsWith(cwd)) {
|
||||
throw new Error('Access denied: file path must be within current directory');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional check: no null bytes
|
||||
if (filePath.includes('\0')) {
|
||||
throw new Error('Invalid file path: contains null bytes');
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate output file path
|
||||
*/
|
||||
function validateOutputPath(filePath) {
|
||||
if (!filePath || typeof filePath !== 'string') {
|
||||
throw new Error('Output file path is required');
|
||||
}
|
||||
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
if (ext !== '.dag' && ext !== '.json') {
|
||||
throw new Error(`Invalid output extension: ${ext}. Only .dag and .json files are allowed.`);
|
||||
}
|
||||
|
||||
const resolved = path.resolve(filePath);
|
||||
const cwd = process.cwd();
|
||||
|
||||
// Must be within current directory
|
||||
if (!resolved.startsWith(cwd + path.sep) && resolved !== cwd) {
|
||||
throw new Error('Access denied: output path must be within current directory');
|
||||
}
|
||||
|
||||
if (filePath.includes('\0')) {
|
||||
throw new Error('Invalid file path: contains null bytes');
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy load dependencies
|
||||
*/
|
||||
async function loadDependencies() {
|
||||
if (!RuDag) {
|
||||
const mod = require('../dist/index.js');
|
||||
RuDag = mod.RuDag;
|
||||
DagOperator = mod.DagOperator;
|
||||
AttentionMechanism = mod.AttentionMechanism;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (!command || command === 'help' || command === '--help') {
|
||||
console.log(help);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const isJson = args.includes('--json');
|
||||
const verbose = args.includes('--verbose');
|
||||
|
||||
try {
|
||||
await loadDependencies();
|
||||
|
||||
switch (command) {
|
||||
case 'create': {
|
||||
const name = args[1] || 'untitled';
|
||||
|
||||
// Validate name (alphanumeric only)
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
||||
throw new Error('Invalid name: must be alphanumeric with dashes/underscores only');
|
||||
}
|
||||
|
||||
const dag = new RuDag({ name, storage: null, autoSave: false });
|
||||
await dag.init();
|
||||
|
||||
// Create a simple example DAG
|
||||
const scan = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
const filter = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
const project = dag.addNode(DagOperator.PROJECT, 1.0);
|
||||
|
||||
dag.addEdge(scan, filter);
|
||||
dag.addEdge(filter, project);
|
||||
|
||||
if (isJson) {
|
||||
console.log(dag.toJSON());
|
||||
} else {
|
||||
const bytes = dag.toBytes();
|
||||
process.stdout.write(Buffer.from(bytes));
|
||||
}
|
||||
|
||||
dag.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'load': {
|
||||
const file = validateFilePath(args[1]);
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`File not found: ${args[1]}`);
|
||||
}
|
||||
|
||||
const data = fs.readFileSync(file);
|
||||
let dag;
|
||||
|
||||
if (file.endsWith('.json')) {
|
||||
dag = await RuDag.fromJSON(data.toString(), { storage: null });
|
||||
} else {
|
||||
dag = await RuDag.fromBytes(new Uint8Array(data), { storage: null });
|
||||
}
|
||||
|
||||
console.log(`Loaded DAG with ${dag.nodeCount} nodes and ${dag.edgeCount} edges`);
|
||||
dag.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'info': {
|
||||
const file = validateFilePath(args[1]);
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`File not found: ${args[1]}`);
|
||||
}
|
||||
|
||||
const data = fs.readFileSync(file);
|
||||
let dag;
|
||||
|
||||
if (file.endsWith('.json')) {
|
||||
dag = await RuDag.fromJSON(data.toString(), { storage: null });
|
||||
} else {
|
||||
dag = await RuDag.fromBytes(new Uint8Array(data), { storage: null });
|
||||
}
|
||||
|
||||
const critPath = dag.criticalPath();
|
||||
const info = {
|
||||
file: args[1],
|
||||
nodes: dag.nodeCount,
|
||||
edges: dag.edgeCount,
|
||||
criticalPath: critPath,
|
||||
};
|
||||
|
||||
if (isJson) {
|
||||
console.log(JSON.stringify(info, null, 2));
|
||||
} else {
|
||||
console.log(`File: ${info.file}`);
|
||||
console.log(`Nodes: ${info.nodes}`);
|
||||
console.log(`Edges: ${info.edges}`);
|
||||
console.log(`Critical Path: ${info.criticalPath.path.join(' -> ')}`);
|
||||
console.log(`Total Cost: ${info.criticalPath.cost}`);
|
||||
}
|
||||
|
||||
dag.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'topo': {
|
||||
const file = validateFilePath(args[1]);
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`File not found: ${args[1]}`);
|
||||
}
|
||||
|
||||
const data = fs.readFileSync(file);
|
||||
let dag;
|
||||
|
||||
if (file.endsWith('.json')) {
|
||||
dag = await RuDag.fromJSON(data.toString(), { storage: null });
|
||||
} else {
|
||||
dag = await RuDag.fromBytes(new Uint8Array(data), { storage: null });
|
||||
}
|
||||
|
||||
const topo = dag.topoSort();
|
||||
|
||||
if (isJson) {
|
||||
console.log(JSON.stringify(topo));
|
||||
} else {
|
||||
console.log('Topological order:', topo.join(' -> '));
|
||||
}
|
||||
|
||||
dag.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'critical': {
|
||||
const file = validateFilePath(args[1]);
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`File not found: ${args[1]}`);
|
||||
}
|
||||
|
||||
const data = fs.readFileSync(file);
|
||||
let dag;
|
||||
|
||||
if (file.endsWith('.json')) {
|
||||
dag = await RuDag.fromJSON(data.toString(), { storage: null });
|
||||
} else {
|
||||
dag = await RuDag.fromBytes(new Uint8Array(data), { storage: null });
|
||||
}
|
||||
|
||||
const result = dag.criticalPath();
|
||||
|
||||
if (isJson) {
|
||||
console.log(JSON.stringify(result));
|
||||
} else {
|
||||
console.log('Critical Path:', result.path.join(' -> '));
|
||||
console.log('Total Cost:', result.cost);
|
||||
}
|
||||
|
||||
dag.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'attention': {
|
||||
const file = validateFilePath(args[1]);
|
||||
const type = args[2] || 'critical';
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`File not found: ${args[1]}`);
|
||||
}
|
||||
|
||||
const data = fs.readFileSync(file);
|
||||
let dag;
|
||||
|
||||
if (file.endsWith('.json')) {
|
||||
dag = await RuDag.fromJSON(data.toString(), { storage: null });
|
||||
} else {
|
||||
dag = await RuDag.fromBytes(new Uint8Array(data), { storage: null });
|
||||
}
|
||||
|
||||
let mechanism;
|
||||
switch (type) {
|
||||
case 'topo':
|
||||
case 'topological':
|
||||
mechanism = AttentionMechanism.TOPOLOGICAL;
|
||||
break;
|
||||
case 'critical':
|
||||
case 'critical_path':
|
||||
mechanism = AttentionMechanism.CRITICAL_PATH;
|
||||
break;
|
||||
case 'uniform':
|
||||
mechanism = AttentionMechanism.UNIFORM;
|
||||
break;
|
||||
default:
|
||||
dag.dispose();
|
||||
throw new Error(`Unknown attention type: ${type}. Use: topo, critical, or uniform`);
|
||||
}
|
||||
|
||||
const scores = dag.attention(mechanism);
|
||||
|
||||
if (isJson) {
|
||||
console.log(JSON.stringify({ type, scores }));
|
||||
} else {
|
||||
console.log(`Attention type: ${type}`);
|
||||
scores.forEach((score, i) => {
|
||||
console.log(` Node ${i}: ${score.toFixed(4)}`);
|
||||
});
|
||||
}
|
||||
|
||||
dag.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'convert': {
|
||||
const inFile = validateFilePath(args[1]);
|
||||
const outFile = validateOutputPath(args[2]);
|
||||
|
||||
if (!fs.existsSync(inFile)) {
|
||||
throw new Error(`Input file not found: ${args[1]}`);
|
||||
}
|
||||
|
||||
const data = fs.readFileSync(inFile);
|
||||
let dag;
|
||||
|
||||
if (inFile.endsWith('.json')) {
|
||||
dag = await RuDag.fromJSON(data.toString(), { storage: null });
|
||||
} else {
|
||||
dag = await RuDag.fromBytes(new Uint8Array(data), { storage: null });
|
||||
}
|
||||
|
||||
if (outFile.endsWith('.json')) {
|
||||
fs.writeFileSync(outFile, dag.toJSON());
|
||||
} else {
|
||||
fs.writeFileSync(outFile, Buffer.from(dag.toBytes()));
|
||||
}
|
||||
|
||||
console.log(`Converted ${args[1]} -> ${args[2]}`);
|
||||
dag.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.error(`Unknown command: ${command}`);
|
||||
console.log('Run "rudag help" for usage information');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
if (verbose) {
|
||||
console.error(error.stack);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
110
vendor/ruvector/npm/packages/rudag/package.json
vendored
Normal file
110
vendor/ruvector/npm/packages/rudag/package.json
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"name": "@ruvector/rudag",
|
||||
"version": "0.1.0",
|
||||
"description": "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.",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"bin": {
|
||||
"rudag": "./bin/cli.js"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./dist/browser.d.ts",
|
||||
"import": "./dist/browser.js",
|
||||
"require": "./dist/browser.js"
|
||||
},
|
||||
"./node": {
|
||||
"types": "./dist/node.d.ts",
|
||||
"import": "./dist/node.js",
|
||||
"require": "./dist/node.js"
|
||||
},
|
||||
"./wasm": {
|
||||
"types": "./pkg/ruvector_dag_wasm.d.ts",
|
||||
"import": "./pkg/ruvector_dag_wasm.js",
|
||||
"require": "./pkg-node/ruvector_dag_wasm.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"pkg",
|
||||
"pkg-node",
|
||||
"bin",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build:wasm": "npm run build:wasm:bundler && npm run build:wasm:node",
|
||||
"build:wasm:bundler": "cd ../../../crates/ruvector-dag-wasm && wasm-pack build --target bundler --out-dir ../../npm/packages/rudag/pkg",
|
||||
"build:wasm:node": "cd ../../../crates/ruvector-dag-wasm && wasm-pack build --target nodejs --out-dir ../../npm/packages/rudag/pkg-node",
|
||||
"build:ts": "tsc && tsc -p tsconfig.esm.json",
|
||||
"build": "npm run build:wasm && npm run build:ts",
|
||||
"test": "node --test dist/**/*.test.js",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"dag",
|
||||
"directed-acyclic-graph",
|
||||
"graph",
|
||||
"topological-sort",
|
||||
"topo-sort",
|
||||
"critical-path",
|
||||
"task-scheduler",
|
||||
"task-scheduling",
|
||||
"job-scheduler",
|
||||
"dependency-graph",
|
||||
"dependency-resolution",
|
||||
"workflow",
|
||||
"workflow-engine",
|
||||
"pipeline",
|
||||
"data-pipeline",
|
||||
"etl",
|
||||
"build-system",
|
||||
"wasm",
|
||||
"webassembly",
|
||||
"rust",
|
||||
"indexeddb",
|
||||
"persistence",
|
||||
"query-optimizer",
|
||||
"sql-optimizer",
|
||||
"self-learning",
|
||||
"machine-learning",
|
||||
"attention-mechanism",
|
||||
"bottleneck",
|
||||
"performance",
|
||||
"typescript",
|
||||
"browser",
|
||||
"nodejs"
|
||||
],
|
||||
"author": "rUv Team <team@ruv.io>",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ruvnet/ruvector.git",
|
||||
"directory": "npm/packages/rudag"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ruvnet/ruvector/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ruvnet/ruvector/tree/main/crates/ruvector-dag",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.25",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"idb": "^8.0.0"
|
||||
}
|
||||
}
|
||||
1
vendor/ruvector/npm/packages/rudag/pkg-node/.npmkeep
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/pkg-node/.npmkeep
vendored
Normal file
@@ -0,0 +1 @@
|
||||
# Keep WASM files for npm
|
||||
16
vendor/ruvector/npm/packages/rudag/pkg-node/package.json
vendored
Normal file
16
vendor/ruvector/npm/packages/rudag/pkg-node/package.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "ruvector-dag-wasm",
|
||||
"collaborators": [
|
||||
"RuVector Contributors"
|
||||
],
|
||||
"description": "Minimal WASM DAG library for browser and embedded systems",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"files": [
|
||||
"ruvector_dag_wasm_bg.wasm",
|
||||
"ruvector_dag_wasm.js",
|
||||
"ruvector_dag_wasm.d.ts"
|
||||
],
|
||||
"main": "ruvector_dag_wasm.js",
|
||||
"types": "ruvector_dag_wasm.d.ts"
|
||||
}
|
||||
60
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm.d.ts
vendored
Normal file
60
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm.d.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export class WasmDag {
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
/**
|
||||
* Get number of edges
|
||||
*/
|
||||
edge_count(): number;
|
||||
/**
|
||||
* Deserialize from bytes
|
||||
*/
|
||||
static from_bytes(data: Uint8Array): WasmDag;
|
||||
/**
|
||||
* Get number of nodes
|
||||
*/
|
||||
node_count(): number;
|
||||
/**
|
||||
* Find critical path (longest path by cost)
|
||||
* Returns JSON: {"path": [node_ids], "cost": total}
|
||||
*/
|
||||
critical_path(): any;
|
||||
/**
|
||||
* Create new empty DAG
|
||||
*/
|
||||
constructor();
|
||||
/**
|
||||
* Serialize to JSON
|
||||
*/
|
||||
to_json(): string;
|
||||
/**
|
||||
* Add edge from -> to
|
||||
* Returns false if creates cycle (simple check)
|
||||
*/
|
||||
add_edge(from: number, to: number): boolean;
|
||||
/**
|
||||
* Add a node with operator type and cost
|
||||
* Returns node ID
|
||||
*/
|
||||
add_node(op: number, cost: number): number;
|
||||
/**
|
||||
* Serialize to bytes (bincode format)
|
||||
*/
|
||||
to_bytes(): Uint8Array;
|
||||
/**
|
||||
* Compute attention scores for nodes
|
||||
* mechanism: 0=topological, 1=critical_path, 2=uniform
|
||||
*/
|
||||
attention(mechanism: number): Float32Array;
|
||||
/**
|
||||
* Deserialize from JSON
|
||||
*/
|
||||
static from_json(json: string): WasmDag;
|
||||
/**
|
||||
* Topological sort using Kahn's algorithm
|
||||
* Returns node IDs in topological order
|
||||
*/
|
||||
topo_sort(): Uint32Array;
|
||||
}
|
||||
367
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm.js
vendored
Normal file
367
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm.js
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
|
||||
let imports = {};
|
||||
imports['__wbindgen_placeholder__'] = module.exports;
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 132) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function getArrayF32FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
|
||||
}
|
||||
|
||||
function getArrayU32FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getUint32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
}
|
||||
|
||||
let cachedDataViewMemory0 = null;
|
||||
function getDataViewMemory0() {
|
||||
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
||||
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
||||
}
|
||||
return cachedDataViewMemory0;
|
||||
}
|
||||
|
||||
let cachedFloat32ArrayMemory0 = null;
|
||||
function getFloat32ArrayMemory0() {
|
||||
if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {
|
||||
cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedFloat32ArrayMemory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return decodeText(ptr, len);
|
||||
}
|
||||
|
||||
let cachedUint32ArrayMemory0 = null;
|
||||
function getUint32ArrayMemory0() {
|
||||
if (cachedUint32ArrayMemory0 === null || cachedUint32ArrayMemory0.byteLength === 0) {
|
||||
cachedUint32ArrayMemory0 = new Uint32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint32ArrayMemory0;
|
||||
}
|
||||
|
||||
let cachedUint8ArrayMemory0 = null;
|
||||
function getUint8ArrayMemory0() {
|
||||
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
||||
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8ArrayMemory0;
|
||||
}
|
||||
|
||||
function getObject(idx) { return heap[idx]; }
|
||||
|
||||
let heap = new Array(128).fill(undefined);
|
||||
heap.push(undefined, null, true, false);
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function passArray8ToWasm0(arg, malloc) {
|
||||
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
||||
getUint8ArrayMemory0().set(arg, ptr / 1);
|
||||
WASM_VECTOR_LEN = arg.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length, 1) >>> 0;
|
||||
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len, 1) >>> 0;
|
||||
|
||||
const mem = getUint8ArrayMemory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
||||
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = cachedTextEncoder.encodeInto(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
cachedTextDecoder.decode();
|
||||
function decodeText(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
const cachedTextEncoder = new TextEncoder();
|
||||
|
||||
if (!('encodeInto' in cachedTextEncoder)) {
|
||||
cachedTextEncoder.encodeInto = function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
const WasmDagFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||
? { register: () => {}, unregister: () => {} }
|
||||
: new FinalizationRegistry(ptr => wasm.__wbg_wasmdag_free(ptr >>> 0, 1));
|
||||
|
||||
/**
|
||||
* Minimal DAG structure for WASM
|
||||
* Self-contained with no external dependencies beyond wasm-bindgen
|
||||
*/
|
||||
class WasmDag {
|
||||
static __wrap(ptr) {
|
||||
ptr = ptr >>> 0;
|
||||
const obj = Object.create(WasmDag.prototype);
|
||||
obj.__wbg_ptr = ptr;
|
||||
WasmDagFinalization.register(obj, obj.__wbg_ptr, obj);
|
||||
return obj;
|
||||
}
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.__wbg_ptr;
|
||||
this.__wbg_ptr = 0;
|
||||
WasmDagFinalization.unregister(this);
|
||||
return ptr;
|
||||
}
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_wasmdag_free(ptr, 0);
|
||||
}
|
||||
/**
|
||||
* Get number of edges
|
||||
* @returns {number}
|
||||
*/
|
||||
edge_count() {
|
||||
const ret = wasm.wasmdag_edge_count(this.__wbg_ptr);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* Deserialize from bytes
|
||||
* @param {Uint8Array} data
|
||||
* @returns {WasmDag}
|
||||
*/
|
||||
static from_bytes(data) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_export);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.wasmdag_from_bytes(retptr, ptr0, len0);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
||||
if (r2) {
|
||||
throw takeObject(r1);
|
||||
}
|
||||
return WasmDag.__wrap(r0);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get number of nodes
|
||||
* @returns {number}
|
||||
*/
|
||||
node_count() {
|
||||
const ret = wasm.wasmdag_node_count(this.__wbg_ptr);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* Find critical path (longest path by cost)
|
||||
* Returns JSON: {"path": [node_ids], "cost": total}
|
||||
* @returns {any}
|
||||
*/
|
||||
critical_path() {
|
||||
const ret = wasm.wasmdag_critical_path(this.__wbg_ptr);
|
||||
return takeObject(ret);
|
||||
}
|
||||
/**
|
||||
* Create new empty DAG
|
||||
*/
|
||||
constructor() {
|
||||
const ret = wasm.wasmdag_new();
|
||||
this.__wbg_ptr = ret >>> 0;
|
||||
WasmDagFinalization.register(this, this.__wbg_ptr, this);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Serialize to JSON
|
||||
* @returns {string}
|
||||
*/
|
||||
to_json() {
|
||||
let deferred1_0;
|
||||
let deferred1_1;
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_to_json(retptr, this.__wbg_ptr);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
deferred1_0 = r0;
|
||||
deferred1_1 = r1;
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_export2(deferred1_0, deferred1_1, 1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add edge from -> to
|
||||
* Returns false if creates cycle (simple check)
|
||||
* @param {number} from
|
||||
* @param {number} to
|
||||
* @returns {boolean}
|
||||
*/
|
||||
add_edge(from, to) {
|
||||
const ret = wasm.wasmdag_add_edge(this.__wbg_ptr, from, to);
|
||||
return ret !== 0;
|
||||
}
|
||||
/**
|
||||
* Add a node with operator type and cost
|
||||
* Returns node ID
|
||||
* @param {number} op
|
||||
* @param {number} cost
|
||||
* @returns {number}
|
||||
*/
|
||||
add_node(op, cost) {
|
||||
const ret = wasm.wasmdag_add_node(this.__wbg_ptr, op, cost);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* Serialize to bytes (bincode format)
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
to_bytes() {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_to_bytes(retptr, this.__wbg_ptr);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var v1 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_export2(r0, r1 * 1, 1);
|
||||
return v1;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Compute attention scores for nodes
|
||||
* mechanism: 0=topological, 1=critical_path, 2=uniform
|
||||
* @param {number} mechanism
|
||||
* @returns {Float32Array}
|
||||
*/
|
||||
attention(mechanism) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_attention(retptr, this.__wbg_ptr, mechanism);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var v1 = getArrayF32FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_export2(r0, r1 * 4, 4);
|
||||
return v1;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Deserialize from JSON
|
||||
* @param {string} json
|
||||
* @returns {WasmDag}
|
||||
*/
|
||||
static from_json(json) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passStringToWasm0(json, wasm.__wbindgen_export, wasm.__wbindgen_export3);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.wasmdag_from_json(retptr, ptr0, len0);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
||||
if (r2) {
|
||||
throw takeObject(r1);
|
||||
}
|
||||
return WasmDag.__wrap(r0);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Topological sort using Kahn's algorithm
|
||||
* Returns node IDs in topological order
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
topo_sort() {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_topo_sort(retptr, this.__wbg_ptr);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var v1 = getArrayU32FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_export2(r0, r1 * 4, 4);
|
||||
return v1;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Symbol.dispose) WasmDag.prototype[Symbol.dispose] = WasmDag.prototype.free;
|
||||
exports.WasmDag = WasmDag;
|
||||
|
||||
exports.__wbg___wbindgen_throw_dd24417ed36fc46e = function(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
|
||||
exports.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) {
|
||||
// Cast intrinsic for `Ref(String) -> Externref`.
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
const wasmPath = `${__dirname}/ruvector_dag_wasm_bg.wasm`;
|
||||
const wasmBytes = require('fs').readFileSync(wasmPath);
|
||||
const wasmModule = new WebAssembly.Module(wasmBytes);
|
||||
const wasm = exports.__wasm = new WebAssembly.Instance(wasmModule, imports).exports;
|
||||
BIN
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm_bg.wasm
vendored
Normal file
BIN
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm_bg.wasm
vendored
Normal file
Binary file not shown.
20
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm_bg.wasm.d.ts
vendored
Normal file
20
vendor/ruvector/npm/packages/rudag/pkg-node/ruvector_dag_wasm_bg.wasm.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export const __wbg_wasmdag_free: (a: number, b: number) => void;
|
||||
export const wasmdag_add_edge: (a: number, b: number, c: number) => number;
|
||||
export const wasmdag_add_node: (a: number, b: number, c: number) => number;
|
||||
export const wasmdag_attention: (a: number, b: number, c: number) => void;
|
||||
export const wasmdag_critical_path: (a: number) => number;
|
||||
export const wasmdag_edge_count: (a: number) => number;
|
||||
export const wasmdag_from_bytes: (a: number, b: number, c: number) => void;
|
||||
export const wasmdag_from_json: (a: number, b: number, c: number) => void;
|
||||
export const wasmdag_new: () => number;
|
||||
export const wasmdag_node_count: (a: number) => number;
|
||||
export const wasmdag_to_bytes: (a: number, b: number) => void;
|
||||
export const wasmdag_to_json: (a: number, b: number) => void;
|
||||
export const wasmdag_topo_sort: (a: number, b: number) => void;
|
||||
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||
export const __wbindgen_export: (a: number, b: number) => number;
|
||||
export const __wbindgen_export2: (a: number, b: number, c: number) => void;
|
||||
export const __wbindgen_export3: (a: number, b: number, c: number, d: number) => number;
|
||||
1
vendor/ruvector/npm/packages/rudag/pkg/.npmkeep
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/pkg/.npmkeep
vendored
Normal file
@@ -0,0 +1 @@
|
||||
# Keep WASM files for npm
|
||||
21
vendor/ruvector/npm/packages/rudag/pkg/package.json
vendored
Normal file
21
vendor/ruvector/npm/packages/rudag/pkg/package.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "ruvector-dag-wasm",
|
||||
"collaborators": [
|
||||
"RuVector Contributors"
|
||||
],
|
||||
"description": "Minimal WASM DAG library for browser and embedded systems",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"files": [
|
||||
"ruvector_dag_wasm_bg.wasm",
|
||||
"ruvector_dag_wasm.js",
|
||||
"ruvector_dag_wasm_bg.js",
|
||||
"ruvector_dag_wasm.d.ts"
|
||||
],
|
||||
"module": "ruvector_dag_wasm.js",
|
||||
"types": "ruvector_dag_wasm.d.ts",
|
||||
"sideEffects": [
|
||||
"./ruvector_dag_wasm.js",
|
||||
"./snippets/*"
|
||||
]
|
||||
}
|
||||
60
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm.d.ts
vendored
Normal file
60
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm.d.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export class WasmDag {
|
||||
free(): void;
|
||||
[Symbol.dispose](): void;
|
||||
/**
|
||||
* Get number of edges
|
||||
*/
|
||||
edge_count(): number;
|
||||
/**
|
||||
* Deserialize from bytes
|
||||
*/
|
||||
static from_bytes(data: Uint8Array): WasmDag;
|
||||
/**
|
||||
* Get number of nodes
|
||||
*/
|
||||
node_count(): number;
|
||||
/**
|
||||
* Find critical path (longest path by cost)
|
||||
* Returns JSON: {"path": [node_ids], "cost": total}
|
||||
*/
|
||||
critical_path(): any;
|
||||
/**
|
||||
* Create new empty DAG
|
||||
*/
|
||||
constructor();
|
||||
/**
|
||||
* Serialize to JSON
|
||||
*/
|
||||
to_json(): string;
|
||||
/**
|
||||
* Add edge from -> to
|
||||
* Returns false if creates cycle (simple check)
|
||||
*/
|
||||
add_edge(from: number, to: number): boolean;
|
||||
/**
|
||||
* Add a node with operator type and cost
|
||||
* Returns node ID
|
||||
*/
|
||||
add_node(op: number, cost: number): number;
|
||||
/**
|
||||
* Serialize to bytes (bincode format)
|
||||
*/
|
||||
to_bytes(): Uint8Array;
|
||||
/**
|
||||
* Compute attention scores for nodes
|
||||
* mechanism: 0=topological, 1=critical_path, 2=uniform
|
||||
*/
|
||||
attention(mechanism: number): Float32Array;
|
||||
/**
|
||||
* Deserialize from JSON
|
||||
*/
|
||||
static from_json(json: string): WasmDag;
|
||||
/**
|
||||
* Topological sort using Kahn's algorithm
|
||||
* Returns node IDs in topological order
|
||||
*/
|
||||
topo_sort(): Uint32Array;
|
||||
}
|
||||
4
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm.js
vendored
Normal file
4
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import * as wasm from "./ruvector_dag_wasm_bg.wasm";
|
||||
export * from "./ruvector_dag_wasm_bg.js";
|
||||
import { __wbg_set_wasm } from "./ruvector_dag_wasm_bg.js";
|
||||
__wbg_set_wasm(wasm);
|
||||
370
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm_bg.js
vendored
Normal file
370
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm_bg.js
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
let wasm;
|
||||
export function __wbg_set_wasm(val) {
|
||||
wasm = val;
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 132) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function getArrayF32FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
|
||||
}
|
||||
|
||||
function getArrayU32FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getUint32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
}
|
||||
|
||||
let cachedDataViewMemory0 = null;
|
||||
function getDataViewMemory0() {
|
||||
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
||||
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
||||
}
|
||||
return cachedDataViewMemory0;
|
||||
}
|
||||
|
||||
let cachedFloat32ArrayMemory0 = null;
|
||||
function getFloat32ArrayMemory0() {
|
||||
if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {
|
||||
cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedFloat32ArrayMemory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return decodeText(ptr, len);
|
||||
}
|
||||
|
||||
let cachedUint32ArrayMemory0 = null;
|
||||
function getUint32ArrayMemory0() {
|
||||
if (cachedUint32ArrayMemory0 === null || cachedUint32ArrayMemory0.byteLength === 0) {
|
||||
cachedUint32ArrayMemory0 = new Uint32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint32ArrayMemory0;
|
||||
}
|
||||
|
||||
let cachedUint8ArrayMemory0 = null;
|
||||
function getUint8ArrayMemory0() {
|
||||
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
||||
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8ArrayMemory0;
|
||||
}
|
||||
|
||||
function getObject(idx) { return heap[idx]; }
|
||||
|
||||
let heap = new Array(128).fill(undefined);
|
||||
heap.push(undefined, null, true, false);
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function passArray8ToWasm0(arg, malloc) {
|
||||
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
||||
getUint8ArrayMemory0().set(arg, ptr / 1);
|
||||
WASM_VECTOR_LEN = arg.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length, 1) >>> 0;
|
||||
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len, 1) >>> 0;
|
||||
|
||||
const mem = getUint8ArrayMemory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
||||
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = cachedTextEncoder.encodeInto(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
cachedTextDecoder.decode();
|
||||
const MAX_SAFARI_DECODE_BYTES = 2146435072;
|
||||
let numBytesDecoded = 0;
|
||||
function decodeText(ptr, len) {
|
||||
numBytesDecoded += len;
|
||||
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
|
||||
cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
cachedTextDecoder.decode();
|
||||
numBytesDecoded = len;
|
||||
}
|
||||
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
const cachedTextEncoder = new TextEncoder();
|
||||
|
||||
if (!('encodeInto' in cachedTextEncoder)) {
|
||||
cachedTextEncoder.encodeInto = function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
const WasmDagFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||
? { register: () => {}, unregister: () => {} }
|
||||
: new FinalizationRegistry(ptr => wasm.__wbg_wasmdag_free(ptr >>> 0, 1));
|
||||
|
||||
/**
|
||||
* Minimal DAG structure for WASM
|
||||
* Self-contained with no external dependencies beyond wasm-bindgen
|
||||
*/
|
||||
export class WasmDag {
|
||||
static __wrap(ptr) {
|
||||
ptr = ptr >>> 0;
|
||||
const obj = Object.create(WasmDag.prototype);
|
||||
obj.__wbg_ptr = ptr;
|
||||
WasmDagFinalization.register(obj, obj.__wbg_ptr, obj);
|
||||
return obj;
|
||||
}
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.__wbg_ptr;
|
||||
this.__wbg_ptr = 0;
|
||||
WasmDagFinalization.unregister(this);
|
||||
return ptr;
|
||||
}
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_wasmdag_free(ptr, 0);
|
||||
}
|
||||
/**
|
||||
* Get number of edges
|
||||
* @returns {number}
|
||||
*/
|
||||
edge_count() {
|
||||
const ret = wasm.wasmdag_edge_count(this.__wbg_ptr);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* Deserialize from bytes
|
||||
* @param {Uint8Array} data
|
||||
* @returns {WasmDag}
|
||||
*/
|
||||
static from_bytes(data) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_export);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.wasmdag_from_bytes(retptr, ptr0, len0);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
||||
if (r2) {
|
||||
throw takeObject(r1);
|
||||
}
|
||||
return WasmDag.__wrap(r0);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get number of nodes
|
||||
* @returns {number}
|
||||
*/
|
||||
node_count() {
|
||||
const ret = wasm.wasmdag_node_count(this.__wbg_ptr);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* Find critical path (longest path by cost)
|
||||
* Returns JSON: {"path": [node_ids], "cost": total}
|
||||
* @returns {any}
|
||||
*/
|
||||
critical_path() {
|
||||
const ret = wasm.wasmdag_critical_path(this.__wbg_ptr);
|
||||
return takeObject(ret);
|
||||
}
|
||||
/**
|
||||
* Create new empty DAG
|
||||
*/
|
||||
constructor() {
|
||||
const ret = wasm.wasmdag_new();
|
||||
this.__wbg_ptr = ret >>> 0;
|
||||
WasmDagFinalization.register(this, this.__wbg_ptr, this);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Serialize to JSON
|
||||
* @returns {string}
|
||||
*/
|
||||
to_json() {
|
||||
let deferred1_0;
|
||||
let deferred1_1;
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_to_json(retptr, this.__wbg_ptr);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
deferred1_0 = r0;
|
||||
deferred1_1 = r1;
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_export2(deferred1_0, deferred1_1, 1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add edge from -> to
|
||||
* Returns false if creates cycle (simple check)
|
||||
* @param {number} from
|
||||
* @param {number} to
|
||||
* @returns {boolean}
|
||||
*/
|
||||
add_edge(from, to) {
|
||||
const ret = wasm.wasmdag_add_edge(this.__wbg_ptr, from, to);
|
||||
return ret !== 0;
|
||||
}
|
||||
/**
|
||||
* Add a node with operator type and cost
|
||||
* Returns node ID
|
||||
* @param {number} op
|
||||
* @param {number} cost
|
||||
* @returns {number}
|
||||
*/
|
||||
add_node(op, cost) {
|
||||
const ret = wasm.wasmdag_add_node(this.__wbg_ptr, op, cost);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* Serialize to bytes (bincode format)
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
to_bytes() {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_to_bytes(retptr, this.__wbg_ptr);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var v1 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_export2(r0, r1 * 1, 1);
|
||||
return v1;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Compute attention scores for nodes
|
||||
* mechanism: 0=topological, 1=critical_path, 2=uniform
|
||||
* @param {number} mechanism
|
||||
* @returns {Float32Array}
|
||||
*/
|
||||
attention(mechanism) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_attention(retptr, this.__wbg_ptr, mechanism);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var v1 = getArrayF32FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_export2(r0, r1 * 4, 4);
|
||||
return v1;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Deserialize from JSON
|
||||
* @param {string} json
|
||||
* @returns {WasmDag}
|
||||
*/
|
||||
static from_json(json) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passStringToWasm0(json, wasm.__wbindgen_export, wasm.__wbindgen_export3);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.wasmdag_from_json(retptr, ptr0, len0);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
||||
if (r2) {
|
||||
throw takeObject(r1);
|
||||
}
|
||||
return WasmDag.__wrap(r0);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Topological sort using Kahn's algorithm
|
||||
* Returns node IDs in topological order
|
||||
* @returns {Uint32Array}
|
||||
*/
|
||||
topo_sort() {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.wasmdag_topo_sort(retptr, this.__wbg_ptr);
|
||||
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
||||
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
||||
var v1 = getArrayU32FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_export2(r0, r1 * 4, 4);
|
||||
return v1;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Symbol.dispose) WasmDag.prototype[Symbol.dispose] = WasmDag.prototype.free;
|
||||
|
||||
export function __wbg___wbindgen_throw_dd24417ed36fc46e(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
|
||||
export function __wbindgen_cast_2241b6af4c4b2941(arg0, arg1) {
|
||||
// Cast intrinsic for `Ref(String) -> Externref`.
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
BIN
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm_bg.wasm
vendored
Normal file
BIN
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm_bg.wasm
vendored
Normal file
Binary file not shown.
20
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm_bg.wasm.d.ts
vendored
Normal file
20
vendor/ruvector/npm/packages/rudag/pkg/ruvector_dag_wasm_bg.wasm.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export const __wbg_wasmdag_free: (a: number, b: number) => void;
|
||||
export const wasmdag_add_edge: (a: number, b: number, c: number) => number;
|
||||
export const wasmdag_add_node: (a: number, b: number, c: number) => number;
|
||||
export const wasmdag_attention: (a: number, b: number, c: number) => void;
|
||||
export const wasmdag_critical_path: (a: number) => number;
|
||||
export const wasmdag_edge_count: (a: number) => number;
|
||||
export const wasmdag_from_bytes: (a: number, b: number, c: number) => void;
|
||||
export const wasmdag_from_json: (a: number, b: number, c: number) => void;
|
||||
export const wasmdag_new: () => number;
|
||||
export const wasmdag_node_count: (a: number) => number;
|
||||
export const wasmdag_to_bytes: (a: number, b: number) => void;
|
||||
export const wasmdag_to_json: (a: number, b: number) => void;
|
||||
export const wasmdag_topo_sort: (a: number, b: number) => void;
|
||||
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||
export const __wbindgen_export: (a: number, b: number) => number;
|
||||
export const __wbindgen_export2: (a: number, b: number, c: number) => void;
|
||||
export const __wbindgen_export3: (a: number, b: number, c: number, d: number) => number;
|
||||
29
vendor/ruvector/npm/packages/rudag/src/browser.d.ts
vendored
Normal file
29
vendor/ruvector/npm/packages/rudag/src/browser.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Browser-specific entry point with IndexedDB support
|
||||
*/
|
||||
export * from './index';
|
||||
import { RuDag } from './index';
|
||||
/**
|
||||
* Create a browser-optimized DAG with IndexedDB persistence
|
||||
*/
|
||||
export declare function createBrowserDag(name?: string): Promise<RuDag>;
|
||||
/**
|
||||
* Browser storage manager for DAGs
|
||||
*/
|
||||
export declare class BrowserDagManager {
|
||||
private storage;
|
||||
private initialized;
|
||||
constructor();
|
||||
init(): Promise<void>;
|
||||
createDag(name?: string): Promise<RuDag>;
|
||||
loadDag(id: string): Promise<RuDag | null>;
|
||||
listDags(): Promise<import("./storage").StoredDag[]>;
|
||||
deleteDag(id: string): Promise<boolean>;
|
||||
clearAll(): Promise<void>;
|
||||
getStats(): Promise<{
|
||||
count: number;
|
||||
totalSize: number;
|
||||
}>;
|
||||
close(): void;
|
||||
}
|
||||
//# sourceMappingURL=browser.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/browser.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/browser.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAc,MAAM,SAAS,CAAC;AAE5C;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAKpE;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,WAAW,CAAS;;IAMtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAOxC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAK1C,QAAQ;IAKR,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKvC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKzB,QAAQ;;;;IAKd,KAAK,IAAI,IAAI;CAId"}
|
||||
80
vendor/ruvector/npm/packages/rudag/src/browser.js
vendored
Normal file
80
vendor/ruvector/npm/packages/rudag/src/browser.js
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Browser-specific entry point with IndexedDB support
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BrowserDagManager = void 0;
|
||||
exports.createBrowserDag = createBrowserDag;
|
||||
__exportStar(require("./index"), exports);
|
||||
// Re-export with browser-specific defaults
|
||||
const index_1 = require("./index");
|
||||
/**
|
||||
* Create a browser-optimized DAG with IndexedDB persistence
|
||||
*/
|
||||
async function createBrowserDag(name) {
|
||||
const storage = new index_1.DagStorage();
|
||||
const dag = new index_1.RuDag({ name, storage });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
/**
|
||||
* Browser storage manager for DAGs
|
||||
*/
|
||||
class BrowserDagManager {
|
||||
constructor() {
|
||||
this.initialized = false;
|
||||
this.storage = new index_1.DagStorage();
|
||||
}
|
||||
async init() {
|
||||
if (this.initialized)
|
||||
return;
|
||||
await this.storage.init();
|
||||
this.initialized = true;
|
||||
}
|
||||
async createDag(name) {
|
||||
await this.init();
|
||||
const dag = new index_1.RuDag({ name, storage: this.storage });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
async loadDag(id) {
|
||||
await this.init();
|
||||
return index_1.RuDag.load(id, this.storage);
|
||||
}
|
||||
async listDags() {
|
||||
await this.init();
|
||||
return this.storage.list();
|
||||
}
|
||||
async deleteDag(id) {
|
||||
await this.init();
|
||||
return this.storage.delete(id);
|
||||
}
|
||||
async clearAll() {
|
||||
await this.init();
|
||||
return this.storage.clear();
|
||||
}
|
||||
async getStats() {
|
||||
await this.init();
|
||||
return this.storage.stats();
|
||||
}
|
||||
close() {
|
||||
this.storage.close();
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
exports.BrowserDagManager = BrowserDagManager;
|
||||
//# sourceMappingURL=browser.js.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/browser.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/browser.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"browser.js","sourceRoot":"","sources":["browser.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;AAUH,4CAKC;AAbD,0CAAwB;AAExB,2CAA2C;AAC3C,mCAA4C;AAE5C;;GAEG;AACI,KAAK,UAAU,gBAAgB,CAAC,IAAa;IAClD,MAAM,OAAO,GAAG,IAAI,kBAAU,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,aAAK,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACjB,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAa,iBAAiB;IAI5B;QAFQ,gBAAW,GAAG,KAAK,CAAC;QAG1B,IAAI,CAAC,OAAO,GAAG,IAAI,kBAAU,EAAE,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAC7B,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAa;QAC3B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,MAAM,GAAG,GAAG,IAAI,aAAK,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACvD,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,aAAK,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU;QACxB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;IAC3B,CAAC;CACF;AAlDD,8CAkDC"}
|
||||
73
vendor/ruvector/npm/packages/rudag/src/browser.ts
vendored
Normal file
73
vendor/ruvector/npm/packages/rudag/src/browser.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Browser-specific entry point with IndexedDB support
|
||||
*/
|
||||
|
||||
export * from './index';
|
||||
|
||||
// Re-export with browser-specific defaults
|
||||
import { RuDag, DagStorage } from './index';
|
||||
|
||||
/**
|
||||
* Create a browser-optimized DAG with IndexedDB persistence
|
||||
*/
|
||||
export async function createBrowserDag(name?: string): Promise<RuDag> {
|
||||
const storage = new DagStorage();
|
||||
const dag = new RuDag({ name, storage });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser storage manager for DAGs
|
||||
*/
|
||||
export class BrowserDagManager {
|
||||
private storage: DagStorage;
|
||||
private initialized = false;
|
||||
|
||||
constructor() {
|
||||
this.storage = new DagStorage();
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
if (this.initialized) return;
|
||||
await this.storage.init();
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
async createDag(name?: string): Promise<RuDag> {
|
||||
await this.init();
|
||||
const dag = new RuDag({ name, storage: this.storage });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
|
||||
async loadDag(id: string): Promise<RuDag | null> {
|
||||
await this.init();
|
||||
return RuDag.load(id, this.storage);
|
||||
}
|
||||
|
||||
async listDags() {
|
||||
await this.init();
|
||||
return this.storage.list();
|
||||
}
|
||||
|
||||
async deleteDag(id: string): Promise<boolean> {
|
||||
await this.init();
|
||||
return this.storage.delete(id);
|
||||
}
|
||||
|
||||
async clearAll(): Promise<void> {
|
||||
await this.init();
|
||||
return this.storage.clear();
|
||||
}
|
||||
|
||||
async getStats() {
|
||||
await this.init();
|
||||
return this.storage.stats();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.storage.close();
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
258
vendor/ruvector/npm/packages/rudag/src/dag.d.ts
vendored
Normal file
258
vendor/ruvector/npm/packages/rudag/src/dag.d.ts
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* High-level DAG API with WASM acceleration
|
||||
* Provides a TypeScript-friendly interface to the WASM DAG implementation
|
||||
*
|
||||
* @security All inputs are validated to prevent injection attacks
|
||||
* @performance Results are cached to minimize WASM calls
|
||||
*/
|
||||
import { DagStorage, MemoryStorage, StoredDag } from './storage';
|
||||
/**
|
||||
* Operator types for DAG nodes
|
||||
*/
|
||||
export declare enum DagOperator {
|
||||
/** Table scan operation */
|
||||
SCAN = 0,
|
||||
/** Filter/WHERE clause */
|
||||
FILTER = 1,
|
||||
/** Column projection/SELECT */
|
||||
PROJECT = 2,
|
||||
/** Join operation */
|
||||
JOIN = 3,
|
||||
/** Aggregation (GROUP BY) */
|
||||
AGGREGATE = 4,
|
||||
/** Sort/ORDER BY */
|
||||
SORT = 5,
|
||||
/** Limit/TOP N */
|
||||
LIMIT = 6,
|
||||
/** Union of results */
|
||||
UNION = 7,
|
||||
/** Custom user-defined operator */
|
||||
CUSTOM = 255
|
||||
}
|
||||
/**
|
||||
* Attention mechanism types for node scoring
|
||||
*/
|
||||
export declare enum AttentionMechanism {
|
||||
/** Score by position in topological order */
|
||||
TOPOLOGICAL = 0,
|
||||
/** Score by distance from critical path */
|
||||
CRITICAL_PATH = 1,
|
||||
/** Equal scores for all nodes */
|
||||
UNIFORM = 2
|
||||
}
|
||||
/**
|
||||
* Node representation in the DAG
|
||||
*/
|
||||
export interface DagNode {
|
||||
/** Unique identifier for this node */
|
||||
id: number;
|
||||
/** The operator type (e.g., SCAN, FILTER, JOIN) */
|
||||
operator: DagOperator | number;
|
||||
/** Execution cost estimate for this node */
|
||||
cost: number;
|
||||
/** Optional arbitrary metadata attached to the node */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
/**
|
||||
* Edge representation (directed connection between nodes)
|
||||
*/
|
||||
export interface DagEdge {
|
||||
/** Source node ID */
|
||||
from: number;
|
||||
/** Target node ID */
|
||||
to: number;
|
||||
}
|
||||
/**
|
||||
* Critical path result from DAG analysis
|
||||
*/
|
||||
export interface CriticalPath {
|
||||
/** Node IDs in the critical path */
|
||||
path: number[];
|
||||
/** Total cost of the critical path */
|
||||
cost: number;
|
||||
}
|
||||
/**
|
||||
* DAG configuration options
|
||||
*/
|
||||
export interface RuDagOptions {
|
||||
/** Custom ID for the DAG (auto-generated if not provided) */
|
||||
id?: string;
|
||||
/** Human-readable name */
|
||||
name?: string;
|
||||
/** Storage backend (IndexedDB/Memory/null for no persistence) */
|
||||
storage?: DagStorage | MemoryStorage | null;
|
||||
/** Auto-save changes to storage (default: true) */
|
||||
autoSave?: boolean;
|
||||
/** Error handler for background save failures */
|
||||
onSaveError?: (error: unknown) => void;
|
||||
}
|
||||
/**
|
||||
* RuDag - High-performance DAG with WASM acceleration and persistence
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const dag = await new RuDag({ name: 'my-query' }).init();
|
||||
* const scan = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
* const filter = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
* dag.addEdge(scan, filter);
|
||||
* const { path, cost } = dag.criticalPath();
|
||||
* ```
|
||||
*/
|
||||
export declare class RuDag {
|
||||
private wasm;
|
||||
private nodes;
|
||||
private storage;
|
||||
private readonly id;
|
||||
private name?;
|
||||
private autoSave;
|
||||
private initialized;
|
||||
private onSaveError?;
|
||||
private _topoCache;
|
||||
private _criticalPathCache;
|
||||
private _dirty;
|
||||
constructor(options?: RuDagOptions);
|
||||
/**
|
||||
* Initialize the DAG with WASM module and storage
|
||||
* @returns This instance for chaining
|
||||
* @throws {Error} If WASM module fails to load
|
||||
* @throws {Error} If storage initialization fails
|
||||
*/
|
||||
init(): Promise<this>;
|
||||
/**
|
||||
* Ensure DAG is initialized
|
||||
* @throws {Error} If DAG not initialized
|
||||
*/
|
||||
private ensureInit;
|
||||
/**
|
||||
* Handle background save errors
|
||||
*/
|
||||
private handleSaveError;
|
||||
/**
|
||||
* Invalidate caches (called when DAG structure changes)
|
||||
*/
|
||||
private invalidateCache;
|
||||
/**
|
||||
* Add a node to the DAG
|
||||
* @param operator - The operator type
|
||||
* @param cost - Execution cost estimate (must be non-negative)
|
||||
* @param metadata - Optional metadata
|
||||
* @returns The new node ID
|
||||
* @throws {Error} If cost is invalid
|
||||
*/
|
||||
addNode(operator: DagOperator | number, cost: number, metadata?: Record<string, unknown>): number;
|
||||
/**
|
||||
* Add an edge between nodes
|
||||
* @param from - Source node ID
|
||||
* @param to - Target node ID
|
||||
* @returns true if edge was added, false if it would create a cycle
|
||||
* @throws {Error} If node IDs are invalid
|
||||
*/
|
||||
addEdge(from: number, to: number): boolean;
|
||||
/**
|
||||
* Get node count
|
||||
*/
|
||||
get nodeCount(): number;
|
||||
/**
|
||||
* Get edge count
|
||||
*/
|
||||
get edgeCount(): number;
|
||||
/**
|
||||
* Get topological sort (cached)
|
||||
* @returns Array of node IDs in topological order
|
||||
*/
|
||||
topoSort(): number[];
|
||||
/**
|
||||
* Find critical path (cached)
|
||||
* @returns Object with path (node IDs) and total cost
|
||||
* @throws {Error} If WASM returns invalid data
|
||||
*/
|
||||
criticalPath(): CriticalPath;
|
||||
/**
|
||||
* Compute attention scores for nodes
|
||||
* @param mechanism - Attention mechanism to use
|
||||
* @returns Array of scores (one per node)
|
||||
*/
|
||||
attention(mechanism?: AttentionMechanism): number[];
|
||||
/**
|
||||
* Get node by ID
|
||||
*/
|
||||
getNode(id: number): DagNode | undefined;
|
||||
/**
|
||||
* Get all nodes
|
||||
*/
|
||||
getNodes(): DagNode[];
|
||||
/**
|
||||
* Serialize to bytes (bincode format)
|
||||
*/
|
||||
toBytes(): Uint8Array;
|
||||
/**
|
||||
* Serialize to JSON string
|
||||
*/
|
||||
toJSON(): string;
|
||||
/**
|
||||
* Save DAG to storage
|
||||
* @returns StoredDag record or null if no storage configured
|
||||
*/
|
||||
save(): Promise<StoredDag | null>;
|
||||
/**
|
||||
* Load DAG from storage by ID
|
||||
* @param id - DAG ID to load
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
* @returns Loaded DAG or null if not found
|
||||
* @throws {Error} If ID contains invalid characters
|
||||
*/
|
||||
static load(id: string, storage?: DagStorage | MemoryStorage): Promise<RuDag | null>;
|
||||
/**
|
||||
* Create DAG from bytes
|
||||
* @param data - Serialized DAG data
|
||||
* @param options - Configuration options
|
||||
* @throws {Error} If data is empty or invalid
|
||||
*/
|
||||
static fromBytes(data: Uint8Array, options?: RuDagOptions): Promise<RuDag>;
|
||||
/**
|
||||
* Create DAG from JSON
|
||||
* @param json - JSON string
|
||||
* @param options - Configuration options
|
||||
* @throws {Error} If JSON is empty or invalid
|
||||
*/
|
||||
static fromJSON(json: string, options?: RuDagOptions): Promise<RuDag>;
|
||||
/**
|
||||
* List all stored DAGs
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
*/
|
||||
static listStored(storage?: DagStorage | MemoryStorage): Promise<StoredDag[]>;
|
||||
/**
|
||||
* Delete a stored DAG
|
||||
* @param id - DAG ID to delete
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
* @throws {Error} If ID contains invalid characters
|
||||
*/
|
||||
static deleteStored(id: string, storage?: DagStorage | MemoryStorage): Promise<boolean>;
|
||||
/**
|
||||
* Get storage statistics
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
*/
|
||||
static storageStats(storage?: DagStorage | MemoryStorage): Promise<{
|
||||
count: number;
|
||||
totalSize: number;
|
||||
}>;
|
||||
/**
|
||||
* Get DAG ID
|
||||
*/
|
||||
getId(): string;
|
||||
/**
|
||||
* Get DAG name
|
||||
*/
|
||||
getName(): string | undefined;
|
||||
/**
|
||||
* Set DAG name
|
||||
* @param name - New name for the DAG
|
||||
*/
|
||||
setName(name: string): void;
|
||||
/**
|
||||
* Cleanup resources (WASM memory and storage connection)
|
||||
* Always call this when done with a DAG to prevent memory leaks
|
||||
*/
|
||||
dispose(): void;
|
||||
}
|
||||
//# sourceMappingURL=dag.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/dag.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/dag.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["dag.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAiB,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAwBhF;;GAEG;AACH,oBAAY,WAAW;IACrB,2BAA2B;IAC3B,IAAI,IAAI;IACR,0BAA0B;IAC1B,MAAM,IAAI;IACV,+BAA+B;IAC/B,OAAO,IAAI;IACX,qBAAqB;IACrB,IAAI,IAAI;IACR,6BAA6B;IAC7B,SAAS,IAAI;IACb,oBAAoB;IACpB,IAAI,IAAI;IACR,kBAAkB;IAClB,KAAK,IAAI;IACT,uBAAuB;IACvB,KAAK,IAAI;IACT,mCAAmC;IACnC,MAAM,MAAM;CACb;AAED;;GAEG;AACH,oBAAY,kBAAkB;IAC5B,6CAA6C;IAC7C,WAAW,IAAI;IACf,2CAA2C;IAC3C,aAAa,IAAI;IACjB,iCAAiC;IACjC,OAAO,IAAI;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,mDAAmD;IACnD,QAAQ,EAAE,WAAW,GAAG,MAAM,CAAC;IAC/B,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI,CAAC;IAC5C,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAkFD;;;;;;;;;;;GAWG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAC,CAA2B;IAG/C,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,MAAM,CAAQ;gBAEV,OAAO,GAAE,YAAiB;IAQtC;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B3B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;IA4BjG;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IA0B1C;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;;OAGG;IACH,QAAQ,IAAI,MAAM,EAAE;IAUpB;;;;OAIG;IACH,YAAY,IAAI,YAAY;IA4B5B;;;;OAIG;IACH,SAAS,CAAC,SAAS,GAAE,kBAAqD,GAAG,MAAM,EAAE;IAQrF;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIxC;;OAEG;IACH,QAAQ,IAAI,OAAO,EAAE;IAIrB;;OAEG;IACH,OAAO,IAAI,UAAU;IAIrB;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAcvC;;;;;;OAMG;WACU,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IA4B1F;;;;;OAKG;WACU,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,CAAC;IA6BpF;;;;;OAKG;WACU,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,CAAC;IA6B/E;;;OAGG;WACU,UAAU,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAenF;;;;;OAKG;WACU,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAmB7F;;;OAGG;WACU,YAAY,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAe9G;;OAEG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,OAAO,IAAI,MAAM,GAAG,SAAS;IAI7B;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO3B;;;OAGG;IACH,OAAO,IAAI,IAAI;CAchB"}
|
||||
616
vendor/ruvector/npm/packages/rudag/src/dag.js
vendored
Normal file
616
vendor/ruvector/npm/packages/rudag/src/dag.js
vendored
Normal file
@@ -0,0 +1,616 @@
|
||||
"use strict";
|
||||
/**
|
||||
* High-level DAG API with WASM acceleration
|
||||
* Provides a TypeScript-friendly interface to the WASM DAG implementation
|
||||
*
|
||||
* @security All inputs are validated to prevent injection attacks
|
||||
* @performance Results are cached to minimize WASM calls
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RuDag = exports.AttentionMechanism = exports.DagOperator = void 0;
|
||||
const storage_1 = require("./storage");
|
||||
/**
|
||||
* Operator types for DAG nodes
|
||||
*/
|
||||
var DagOperator;
|
||||
(function (DagOperator) {
|
||||
/** Table scan operation */
|
||||
DagOperator[DagOperator["SCAN"] = 0] = "SCAN";
|
||||
/** Filter/WHERE clause */
|
||||
DagOperator[DagOperator["FILTER"] = 1] = "FILTER";
|
||||
/** Column projection/SELECT */
|
||||
DagOperator[DagOperator["PROJECT"] = 2] = "PROJECT";
|
||||
/** Join operation */
|
||||
DagOperator[DagOperator["JOIN"] = 3] = "JOIN";
|
||||
/** Aggregation (GROUP BY) */
|
||||
DagOperator[DagOperator["AGGREGATE"] = 4] = "AGGREGATE";
|
||||
/** Sort/ORDER BY */
|
||||
DagOperator[DagOperator["SORT"] = 5] = "SORT";
|
||||
/** Limit/TOP N */
|
||||
DagOperator[DagOperator["LIMIT"] = 6] = "LIMIT";
|
||||
/** Union of results */
|
||||
DagOperator[DagOperator["UNION"] = 7] = "UNION";
|
||||
/** Custom user-defined operator */
|
||||
DagOperator[DagOperator["CUSTOM"] = 255] = "CUSTOM";
|
||||
})(DagOperator || (exports.DagOperator = DagOperator = {}));
|
||||
/**
|
||||
* Attention mechanism types for node scoring
|
||||
*/
|
||||
var AttentionMechanism;
|
||||
(function (AttentionMechanism) {
|
||||
/** Score by position in topological order */
|
||||
AttentionMechanism[AttentionMechanism["TOPOLOGICAL"] = 0] = "TOPOLOGICAL";
|
||||
/** Score by distance from critical path */
|
||||
AttentionMechanism[AttentionMechanism["CRITICAL_PATH"] = 1] = "CRITICAL_PATH";
|
||||
/** Equal scores for all nodes */
|
||||
AttentionMechanism[AttentionMechanism["UNIFORM"] = 2] = "UNIFORM";
|
||||
})(AttentionMechanism || (exports.AttentionMechanism = AttentionMechanism = {}));
|
||||
// WASM module singleton with loading promise for concurrent access
|
||||
let wasmModule = null;
|
||||
let wasmLoadPromise = null;
|
||||
/**
|
||||
* Initialize WASM module (singleton pattern with concurrent safety)
|
||||
* @throws {Error} If WASM module fails to load
|
||||
*/
|
||||
async function initWasm() {
|
||||
if (wasmModule)
|
||||
return wasmModule;
|
||||
// Prevent concurrent loading
|
||||
if (wasmLoadPromise)
|
||||
return wasmLoadPromise;
|
||||
wasmLoadPromise = (async () => {
|
||||
try {
|
||||
// Try browser bundler version first
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mod = await Promise.resolve().then(() => __importStar(require('../pkg/ruvector_dag_wasm.js')));
|
||||
if (typeof mod.default === 'function') {
|
||||
await mod.default();
|
||||
}
|
||||
wasmModule = mod;
|
||||
return wasmModule;
|
||||
}
|
||||
catch {
|
||||
try {
|
||||
// Fallback to Node.js version
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mod = await Promise.resolve().then(() => __importStar(require('../pkg-node/ruvector_dag_wasm.js')));
|
||||
wasmModule = mod;
|
||||
return wasmModule;
|
||||
}
|
||||
catch (e) {
|
||||
wasmLoadPromise = null; // Allow retry on failure
|
||||
throw new Error(`Failed to load WASM module: ${e}`);
|
||||
}
|
||||
}
|
||||
})();
|
||||
return wasmLoadPromise;
|
||||
}
|
||||
/**
|
||||
* Type guard for CriticalPath validation
|
||||
* @security Prevents prototype pollution from untrusted WASM output
|
||||
*/
|
||||
function isCriticalPath(obj) {
|
||||
if (typeof obj !== 'object' || obj === null)
|
||||
return false;
|
||||
if (Object.getPrototypeOf(obj) !== Object.prototype && Object.getPrototypeOf(obj) !== null)
|
||||
return false;
|
||||
const candidate = obj;
|
||||
if (!('path' in candidate) || !Array.isArray(candidate.path))
|
||||
return false;
|
||||
if (!candidate.path.every((item) => typeof item === 'number' && Number.isFinite(item)))
|
||||
return false;
|
||||
if (!('cost' in candidate) || typeof candidate.cost !== 'number')
|
||||
return false;
|
||||
if (!Number.isFinite(candidate.cost))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Validate DAG ID to prevent injection attacks
|
||||
* @security Prevents path traversal and special character injection
|
||||
*/
|
||||
function isValidDagId(id) {
|
||||
if (typeof id !== 'string' || id.length === 0 || id.length > 256)
|
||||
return false;
|
||||
// Only allow alphanumeric, dash, underscore
|
||||
return /^[a-zA-Z0-9_-]+$/.test(id);
|
||||
}
|
||||
/**
|
||||
* Sanitize ID or generate a safe one
|
||||
*/
|
||||
function sanitizeOrGenerateId(id) {
|
||||
if (id && isValidDagId(id))
|
||||
return id;
|
||||
// Generate safe ID
|
||||
const timestamp = Date.now();
|
||||
const random = Math.random().toString(36).slice(2, 8);
|
||||
return `dag-${timestamp}-${random}`;
|
||||
}
|
||||
/**
|
||||
* RuDag - High-performance DAG with WASM acceleration and persistence
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const dag = await new RuDag({ name: 'my-query' }).init();
|
||||
* const scan = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
* const filter = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
* dag.addEdge(scan, filter);
|
||||
* const { path, cost } = dag.criticalPath();
|
||||
* ```
|
||||
*/
|
||||
class RuDag {
|
||||
constructor(options = {}) {
|
||||
this.wasm = null;
|
||||
this.nodes = new Map();
|
||||
this.initialized = false;
|
||||
// Cache for expensive operations
|
||||
this._topoCache = null;
|
||||
this._criticalPathCache = null;
|
||||
this._dirty = true;
|
||||
this.id = sanitizeOrGenerateId(options.id);
|
||||
this.name = options.name;
|
||||
this.storage = options.storage === undefined ? (0, storage_1.createStorage)() : options.storage;
|
||||
this.autoSave = options.autoSave ?? true;
|
||||
this.onSaveError = options.onSaveError;
|
||||
}
|
||||
/**
|
||||
* Initialize the DAG with WASM module and storage
|
||||
* @returns This instance for chaining
|
||||
* @throws {Error} If WASM module fails to load
|
||||
* @throws {Error} If storage initialization fails
|
||||
*/
|
||||
async init() {
|
||||
if (this.initialized)
|
||||
return this;
|
||||
const mod = await initWasm();
|
||||
try {
|
||||
this.wasm = new mod.WasmDag();
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to create WASM DAG instance: ${error}`);
|
||||
}
|
||||
try {
|
||||
if (this.storage) {
|
||||
await this.storage.init();
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// Cleanup WASM on storage failure
|
||||
if (this.wasm) {
|
||||
this.wasm.free();
|
||||
this.wasm = null;
|
||||
}
|
||||
throw new Error(`Failed to initialize storage: ${error}`);
|
||||
}
|
||||
this.initialized = true;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Ensure DAG is initialized
|
||||
* @throws {Error} If DAG not initialized
|
||||
*/
|
||||
ensureInit() {
|
||||
if (!this.wasm) {
|
||||
throw new Error('DAG not initialized. Call init() first.');
|
||||
}
|
||||
return this.wasm;
|
||||
}
|
||||
/**
|
||||
* Handle background save errors
|
||||
*/
|
||||
handleSaveError(error) {
|
||||
if (this.onSaveError) {
|
||||
this.onSaveError(error);
|
||||
}
|
||||
else {
|
||||
console.warn('[RuDag] Background save failed:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Invalidate caches (called when DAG structure changes)
|
||||
*/
|
||||
invalidateCache() {
|
||||
this._dirty = true;
|
||||
this._topoCache = null;
|
||||
this._criticalPathCache = null;
|
||||
}
|
||||
/**
|
||||
* Add a node to the DAG
|
||||
* @param operator - The operator type
|
||||
* @param cost - Execution cost estimate (must be non-negative)
|
||||
* @param metadata - Optional metadata
|
||||
* @returns The new node ID
|
||||
* @throws {Error} If cost is invalid
|
||||
*/
|
||||
addNode(operator, cost, metadata) {
|
||||
// Input validation
|
||||
if (!Number.isFinite(cost) || cost < 0) {
|
||||
throw new Error(`Invalid cost: ${cost}. Must be a non-negative finite number.`);
|
||||
}
|
||||
if (!Number.isInteger(operator) || operator < 0 || operator > 255) {
|
||||
throw new Error(`Invalid operator: ${operator}. Must be an integer 0-255.`);
|
||||
}
|
||||
const wasm = this.ensureInit();
|
||||
const id = wasm.add_node(operator, cost);
|
||||
this.nodes.set(id, {
|
||||
id,
|
||||
operator,
|
||||
cost,
|
||||
metadata,
|
||||
});
|
||||
this.invalidateCache();
|
||||
if (this.autoSave) {
|
||||
this.save().catch((e) => this.handleSaveError(e));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
/**
|
||||
* Add an edge between nodes
|
||||
* @param from - Source node ID
|
||||
* @param to - Target node ID
|
||||
* @returns true if edge was added, false if it would create a cycle
|
||||
* @throws {Error} If node IDs are invalid
|
||||
*/
|
||||
addEdge(from, to) {
|
||||
// Input validation
|
||||
if (!Number.isInteger(from) || from < 0) {
|
||||
throw new Error(`Invalid 'from' node ID: ${from}`);
|
||||
}
|
||||
if (!Number.isInteger(to) || to < 0) {
|
||||
throw new Error(`Invalid 'to' node ID: ${to}`);
|
||||
}
|
||||
if (from === to) {
|
||||
throw new Error('Self-loops are not allowed in a DAG');
|
||||
}
|
||||
const wasm = this.ensureInit();
|
||||
const success = wasm.add_edge(from, to);
|
||||
if (success) {
|
||||
this.invalidateCache();
|
||||
if (this.autoSave) {
|
||||
this.save().catch((e) => this.handleSaveError(e));
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
/**
|
||||
* Get node count
|
||||
*/
|
||||
get nodeCount() {
|
||||
return this.ensureInit().node_count();
|
||||
}
|
||||
/**
|
||||
* Get edge count
|
||||
*/
|
||||
get edgeCount() {
|
||||
return this.ensureInit().edge_count();
|
||||
}
|
||||
/**
|
||||
* Get topological sort (cached)
|
||||
* @returns Array of node IDs in topological order
|
||||
*/
|
||||
topoSort() {
|
||||
if (!this._dirty && this._topoCache) {
|
||||
return [...this._topoCache]; // Return copy to prevent mutation
|
||||
}
|
||||
const result = this.ensureInit().topo_sort();
|
||||
this._topoCache = Array.from(result);
|
||||
return [...this._topoCache];
|
||||
}
|
||||
/**
|
||||
* Find critical path (cached)
|
||||
* @returns Object with path (node IDs) and total cost
|
||||
* @throws {Error} If WASM returns invalid data
|
||||
*/
|
||||
criticalPath() {
|
||||
if (!this._dirty && this._criticalPathCache) {
|
||||
return { ...this._criticalPathCache, path: [...this._criticalPathCache.path] };
|
||||
}
|
||||
const result = this.ensureInit().critical_path();
|
||||
let parsed;
|
||||
if (typeof result === 'string') {
|
||||
try {
|
||||
parsed = JSON.parse(result);
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`Invalid critical path JSON from WASM: ${e}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
parsed = result;
|
||||
}
|
||||
if (!isCriticalPath(parsed)) {
|
||||
throw new Error('Invalid critical path structure from WASM');
|
||||
}
|
||||
this._criticalPathCache = parsed;
|
||||
this._dirty = false;
|
||||
return { ...parsed, path: [...parsed.path] };
|
||||
}
|
||||
/**
|
||||
* Compute attention scores for nodes
|
||||
* @param mechanism - Attention mechanism to use
|
||||
* @returns Array of scores (one per node)
|
||||
*/
|
||||
attention(mechanism = AttentionMechanism.CRITICAL_PATH) {
|
||||
if (!Number.isInteger(mechanism) || mechanism < 0 || mechanism > 2) {
|
||||
throw new Error(`Invalid attention mechanism: ${mechanism}`);
|
||||
}
|
||||
const result = this.ensureInit().attention(mechanism);
|
||||
return Array.from(result);
|
||||
}
|
||||
/**
|
||||
* Get node by ID
|
||||
*/
|
||||
getNode(id) {
|
||||
return this.nodes.get(id);
|
||||
}
|
||||
/**
|
||||
* Get all nodes
|
||||
*/
|
||||
getNodes() {
|
||||
return Array.from(this.nodes.values());
|
||||
}
|
||||
/**
|
||||
* Serialize to bytes (bincode format)
|
||||
*/
|
||||
toBytes() {
|
||||
return this.ensureInit().to_bytes();
|
||||
}
|
||||
/**
|
||||
* Serialize to JSON string
|
||||
*/
|
||||
toJSON() {
|
||||
return this.ensureInit().to_json();
|
||||
}
|
||||
/**
|
||||
* Save DAG to storage
|
||||
* @returns StoredDag record or null if no storage configured
|
||||
*/
|
||||
async save() {
|
||||
if (!this.storage)
|
||||
return null;
|
||||
const data = this.toBytes();
|
||||
return this.storage.save(this.id, data, {
|
||||
name: this.name,
|
||||
metadata: {
|
||||
nodeCount: this.nodeCount,
|
||||
edgeCount: this.edgeCount,
|
||||
nodes: Object.fromEntries(this.nodes),
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Load DAG from storage by ID
|
||||
* @param id - DAG ID to load
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
* @returns Loaded DAG or null if not found
|
||||
* @throws {Error} If ID contains invalid characters
|
||||
*/
|
||||
static async load(id, storage) {
|
||||
if (!isValidDagId(id)) {
|
||||
throw new Error(`Invalid DAG ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || (0, storage_1.createStorage)();
|
||||
try {
|
||||
await store.init();
|
||||
const record = await store.get(id);
|
||||
if (!record) {
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
return null;
|
||||
}
|
||||
return RuDag.fromBytes(record.data, {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
storage: store,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create DAG from bytes
|
||||
* @param data - Serialized DAG data
|
||||
* @param options - Configuration options
|
||||
* @throws {Error} If data is empty or invalid
|
||||
*/
|
||||
static async fromBytes(data, options = {}) {
|
||||
if (!data || data.length === 0) {
|
||||
throw new Error('Cannot create DAG from empty or null data');
|
||||
}
|
||||
const mod = await initWasm();
|
||||
const dag = new RuDag(options);
|
||||
try {
|
||||
dag.wasm = mod.WasmDag.from_bytes(data);
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to deserialize DAG from bytes: ${error}`);
|
||||
}
|
||||
dag.initialized = true;
|
||||
if (dag.storage) {
|
||||
try {
|
||||
await dag.storage.init();
|
||||
}
|
||||
catch (error) {
|
||||
dag.wasm?.free();
|
||||
dag.wasm = null;
|
||||
throw new Error(`Failed to initialize storage: ${error}`);
|
||||
}
|
||||
}
|
||||
return dag;
|
||||
}
|
||||
/**
|
||||
* Create DAG from JSON
|
||||
* @param json - JSON string
|
||||
* @param options - Configuration options
|
||||
* @throws {Error} If JSON is empty or invalid
|
||||
*/
|
||||
static async fromJSON(json, options = {}) {
|
||||
if (!json || json.trim().length === 0) {
|
||||
throw new Error('Cannot create DAG from empty or null JSON');
|
||||
}
|
||||
const mod = await initWasm();
|
||||
const dag = new RuDag(options);
|
||||
try {
|
||||
dag.wasm = mod.WasmDag.from_json(json);
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to deserialize DAG from JSON: ${error}`);
|
||||
}
|
||||
dag.initialized = true;
|
||||
if (dag.storage) {
|
||||
try {
|
||||
await dag.storage.init();
|
||||
}
|
||||
catch (error) {
|
||||
dag.wasm?.free();
|
||||
dag.wasm = null;
|
||||
throw new Error(`Failed to initialize storage: ${error}`);
|
||||
}
|
||||
}
|
||||
return dag;
|
||||
}
|
||||
/**
|
||||
* List all stored DAGs
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
*/
|
||||
static async listStored(storage) {
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || (0, storage_1.createStorage)();
|
||||
try {
|
||||
await store.init();
|
||||
const result = await store.list();
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Delete a stored DAG
|
||||
* @param id - DAG ID to delete
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
* @throws {Error} If ID contains invalid characters
|
||||
*/
|
||||
static async deleteStored(id, storage) {
|
||||
if (!isValidDagId(id)) {
|
||||
throw new Error(`Invalid DAG ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || (0, storage_1.createStorage)();
|
||||
try {
|
||||
await store.init();
|
||||
const result = await store.delete(id);
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get storage statistics
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
*/
|
||||
static async storageStats(storage) {
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || (0, storage_1.createStorage)();
|
||||
try {
|
||||
await store.init();
|
||||
const result = await store.stats();
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
if (isOwnedStorage)
|
||||
store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get DAG ID
|
||||
*/
|
||||
getId() {
|
||||
return this.id;
|
||||
}
|
||||
/**
|
||||
* Get DAG name
|
||||
*/
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
/**
|
||||
* Set DAG name
|
||||
* @param name - New name for the DAG
|
||||
*/
|
||||
setName(name) {
|
||||
this.name = name;
|
||||
if (this.autoSave) {
|
||||
this.save().catch((e) => this.handleSaveError(e));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cleanup resources (WASM memory and storage connection)
|
||||
* Always call this when done with a DAG to prevent memory leaks
|
||||
*/
|
||||
dispose() {
|
||||
if (this.wasm) {
|
||||
this.wasm.free();
|
||||
this.wasm = null;
|
||||
}
|
||||
if (this.storage) {
|
||||
this.storage.close();
|
||||
this.storage = null;
|
||||
}
|
||||
this.nodes.clear();
|
||||
this._topoCache = null;
|
||||
this._criticalPathCache = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
exports.RuDag = RuDag;
|
||||
//# sourceMappingURL=dag.js.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/dag.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/dag.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
699
vendor/ruvector/npm/packages/rudag/src/dag.ts
vendored
Normal file
699
vendor/ruvector/npm/packages/rudag/src/dag.ts
vendored
Normal file
@@ -0,0 +1,699 @@
|
||||
/**
|
||||
* High-level DAG API with WASM acceleration
|
||||
* Provides a TypeScript-friendly interface to the WASM DAG implementation
|
||||
*
|
||||
* @security All inputs are validated to prevent injection attacks
|
||||
* @performance Results are cached to minimize WASM calls
|
||||
*/
|
||||
|
||||
import { createStorage, DagStorage, MemoryStorage, StoredDag } from './storage';
|
||||
|
||||
// WASM module type definitions
|
||||
interface WasmDagModule {
|
||||
WasmDag: {
|
||||
new(): WasmDagInstance;
|
||||
from_bytes(data: Uint8Array): WasmDagInstance;
|
||||
from_json(json: string): WasmDagInstance;
|
||||
};
|
||||
}
|
||||
|
||||
interface WasmDagInstance {
|
||||
add_node(op: number, cost: number): number;
|
||||
add_edge(from: number, to: number): boolean;
|
||||
node_count(): number;
|
||||
edge_count(): number;
|
||||
topo_sort(): Uint32Array;
|
||||
critical_path(): string | CriticalPath;
|
||||
attention(mechanism: number): Float32Array;
|
||||
to_bytes(): Uint8Array;
|
||||
to_json(): string;
|
||||
free(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator types for DAG nodes
|
||||
*/
|
||||
export enum DagOperator {
|
||||
/** Table scan operation */
|
||||
SCAN = 0,
|
||||
/** Filter/WHERE clause */
|
||||
FILTER = 1,
|
||||
/** Column projection/SELECT */
|
||||
PROJECT = 2,
|
||||
/** Join operation */
|
||||
JOIN = 3,
|
||||
/** Aggregation (GROUP BY) */
|
||||
AGGREGATE = 4,
|
||||
/** Sort/ORDER BY */
|
||||
SORT = 5,
|
||||
/** Limit/TOP N */
|
||||
LIMIT = 6,
|
||||
/** Union of results */
|
||||
UNION = 7,
|
||||
/** Custom user-defined operator */
|
||||
CUSTOM = 255,
|
||||
}
|
||||
|
||||
/**
|
||||
* Attention mechanism types for node scoring
|
||||
*/
|
||||
export enum AttentionMechanism {
|
||||
/** Score by position in topological order */
|
||||
TOPOLOGICAL = 0,
|
||||
/** Score by distance from critical path */
|
||||
CRITICAL_PATH = 1,
|
||||
/** Equal scores for all nodes */
|
||||
UNIFORM = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Node representation in the DAG
|
||||
*/
|
||||
export interface DagNode {
|
||||
/** Unique identifier for this node */
|
||||
id: number;
|
||||
/** The operator type (e.g., SCAN, FILTER, JOIN) */
|
||||
operator: DagOperator | number;
|
||||
/** Execution cost estimate for this node */
|
||||
cost: number;
|
||||
/** Optional arbitrary metadata attached to the node */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edge representation (directed connection between nodes)
|
||||
*/
|
||||
export interface DagEdge {
|
||||
/** Source node ID */
|
||||
from: number;
|
||||
/** Target node ID */
|
||||
to: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical path result from DAG analysis
|
||||
*/
|
||||
export interface CriticalPath {
|
||||
/** Node IDs in the critical path */
|
||||
path: number[];
|
||||
/** Total cost of the critical path */
|
||||
cost: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* DAG configuration options
|
||||
*/
|
||||
export interface RuDagOptions {
|
||||
/** Custom ID for the DAG (auto-generated if not provided) */
|
||||
id?: string;
|
||||
/** Human-readable name */
|
||||
name?: string;
|
||||
/** Storage backend (IndexedDB/Memory/null for no persistence) */
|
||||
storage?: DagStorage | MemoryStorage | null;
|
||||
/** Auto-save changes to storage (default: true) */
|
||||
autoSave?: boolean;
|
||||
/** Error handler for background save failures */
|
||||
onSaveError?: (error: unknown) => void;
|
||||
}
|
||||
|
||||
// WASM module singleton with loading promise for concurrent access
|
||||
let wasmModule: WasmDagModule | null = null;
|
||||
let wasmLoadPromise: Promise<WasmDagModule> | null = null;
|
||||
|
||||
/**
|
||||
* Initialize WASM module (singleton pattern with concurrent safety)
|
||||
* @throws {Error} If WASM module fails to load
|
||||
*/
|
||||
async function initWasm(): Promise<WasmDagModule> {
|
||||
if (wasmModule) return wasmModule;
|
||||
|
||||
// Prevent concurrent loading
|
||||
if (wasmLoadPromise) return wasmLoadPromise;
|
||||
|
||||
wasmLoadPromise = (async () => {
|
||||
try {
|
||||
// Try browser bundler version first
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mod = await import('../pkg/ruvector_dag_wasm.js') as any;
|
||||
if (typeof mod.default === 'function') {
|
||||
await mod.default();
|
||||
}
|
||||
wasmModule = mod as WasmDagModule;
|
||||
return wasmModule;
|
||||
} catch {
|
||||
try {
|
||||
// Fallback to Node.js version
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mod = await import('../pkg-node/ruvector_dag_wasm.js') as any;
|
||||
wasmModule = mod as WasmDagModule;
|
||||
return wasmModule;
|
||||
} catch (e) {
|
||||
wasmLoadPromise = null; // Allow retry on failure
|
||||
throw new Error(`Failed to load WASM module: ${e}`);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return wasmLoadPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for CriticalPath validation
|
||||
* @security Prevents prototype pollution from untrusted WASM output
|
||||
*/
|
||||
function isCriticalPath(obj: unknown): obj is CriticalPath {
|
||||
if (typeof obj !== 'object' || obj === null) return false;
|
||||
if (Object.getPrototypeOf(obj) !== Object.prototype && Object.getPrototypeOf(obj) !== null) return false;
|
||||
|
||||
const candidate = obj as Record<string, unknown>;
|
||||
|
||||
if (!('path' in candidate) || !Array.isArray(candidate.path)) return false;
|
||||
if (!candidate.path.every((item: unknown) => typeof item === 'number' && Number.isFinite(item))) return false;
|
||||
if (!('cost' in candidate) || typeof candidate.cost !== 'number') return false;
|
||||
if (!Number.isFinite(candidate.cost)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate DAG ID to prevent injection attacks
|
||||
* @security Prevents path traversal and special character injection
|
||||
*/
|
||||
function isValidDagId(id: string): boolean {
|
||||
if (typeof id !== 'string' || id.length === 0 || id.length > 256) return false;
|
||||
// Only allow alphanumeric, dash, underscore
|
||||
return /^[a-zA-Z0-9_-]+$/.test(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize ID or generate a safe one
|
||||
*/
|
||||
function sanitizeOrGenerateId(id?: string): string {
|
||||
if (id && isValidDagId(id)) return id;
|
||||
// Generate safe ID
|
||||
const timestamp = Date.now();
|
||||
const random = Math.random().toString(36).slice(2, 8);
|
||||
return `dag-${timestamp}-${random}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* RuDag - High-performance DAG with WASM acceleration and persistence
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const dag = await new RuDag({ name: 'my-query' }).init();
|
||||
* const scan = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
* const filter = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
* dag.addEdge(scan, filter);
|
||||
* const { path, cost } = dag.criticalPath();
|
||||
* ```
|
||||
*/
|
||||
export class RuDag {
|
||||
private wasm: WasmDagInstance | null = null;
|
||||
private nodes: Map<number, DagNode> = new Map();
|
||||
private storage: DagStorage | MemoryStorage | null;
|
||||
private readonly id: string;
|
||||
private name?: string;
|
||||
private autoSave: boolean;
|
||||
private initialized = false;
|
||||
private onSaveError?: (error: unknown) => void;
|
||||
|
||||
// Cache for expensive operations
|
||||
private _topoCache: number[] | null = null;
|
||||
private _criticalPathCache: CriticalPath | null = null;
|
||||
private _dirty = true;
|
||||
|
||||
constructor(options: RuDagOptions = {}) {
|
||||
this.id = sanitizeOrGenerateId(options.id);
|
||||
this.name = options.name;
|
||||
this.storage = options.storage === undefined ? createStorage() : options.storage;
|
||||
this.autoSave = options.autoSave ?? true;
|
||||
this.onSaveError = options.onSaveError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the DAG with WASM module and storage
|
||||
* @returns This instance for chaining
|
||||
* @throws {Error} If WASM module fails to load
|
||||
* @throws {Error} If storage initialization fails
|
||||
*/
|
||||
async init(): Promise<this> {
|
||||
if (this.initialized) return this;
|
||||
|
||||
const mod = await initWasm();
|
||||
|
||||
try {
|
||||
this.wasm = new mod.WasmDag();
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create WASM DAG instance: ${error}`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.storage) {
|
||||
await this.storage.init();
|
||||
}
|
||||
} catch (error) {
|
||||
// Cleanup WASM on storage failure
|
||||
if (this.wasm) {
|
||||
this.wasm.free();
|
||||
this.wasm = null;
|
||||
}
|
||||
throw new Error(`Failed to initialize storage: ${error}`);
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure DAG is initialized
|
||||
* @throws {Error} If DAG not initialized
|
||||
*/
|
||||
private ensureInit(): WasmDagInstance {
|
||||
if (!this.wasm) {
|
||||
throw new Error('DAG not initialized. Call init() first.');
|
||||
}
|
||||
return this.wasm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle background save errors
|
||||
*/
|
||||
private handleSaveError(error: unknown): void {
|
||||
if (this.onSaveError) {
|
||||
this.onSaveError(error);
|
||||
} else {
|
||||
console.warn('[RuDag] Background save failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate caches (called when DAG structure changes)
|
||||
*/
|
||||
private invalidateCache(): void {
|
||||
this._dirty = true;
|
||||
this._topoCache = null;
|
||||
this._criticalPathCache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a node to the DAG
|
||||
* @param operator - The operator type
|
||||
* @param cost - Execution cost estimate (must be non-negative)
|
||||
* @param metadata - Optional metadata
|
||||
* @returns The new node ID
|
||||
* @throws {Error} If cost is invalid
|
||||
*/
|
||||
addNode(operator: DagOperator | number, cost: number, metadata?: Record<string, unknown>): number {
|
||||
// Input validation
|
||||
if (!Number.isFinite(cost) || cost < 0) {
|
||||
throw new Error(`Invalid cost: ${cost}. Must be a non-negative finite number.`);
|
||||
}
|
||||
if (!Number.isInteger(operator) || operator < 0 || operator > 255) {
|
||||
throw new Error(`Invalid operator: ${operator}. Must be an integer 0-255.`);
|
||||
}
|
||||
|
||||
const wasm = this.ensureInit();
|
||||
const id = wasm.add_node(operator, cost);
|
||||
|
||||
this.nodes.set(id, {
|
||||
id,
|
||||
operator,
|
||||
cost,
|
||||
metadata,
|
||||
});
|
||||
|
||||
this.invalidateCache();
|
||||
|
||||
if (this.autoSave) {
|
||||
this.save().catch((e) => this.handleSaveError(e));
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an edge between nodes
|
||||
* @param from - Source node ID
|
||||
* @param to - Target node ID
|
||||
* @returns true if edge was added, false if it would create a cycle
|
||||
* @throws {Error} If node IDs are invalid
|
||||
*/
|
||||
addEdge(from: number, to: number): boolean {
|
||||
// Input validation
|
||||
if (!Number.isInteger(from) || from < 0) {
|
||||
throw new Error(`Invalid 'from' node ID: ${from}`);
|
||||
}
|
||||
if (!Number.isInteger(to) || to < 0) {
|
||||
throw new Error(`Invalid 'to' node ID: ${to}`);
|
||||
}
|
||||
if (from === to) {
|
||||
throw new Error('Self-loops are not allowed in a DAG');
|
||||
}
|
||||
|
||||
const wasm = this.ensureInit();
|
||||
const success = wasm.add_edge(from, to);
|
||||
|
||||
if (success) {
|
||||
this.invalidateCache();
|
||||
|
||||
if (this.autoSave) {
|
||||
this.save().catch((e) => this.handleSaveError(e));
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get node count
|
||||
*/
|
||||
get nodeCount(): number {
|
||||
return this.ensureInit().node_count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get edge count
|
||||
*/
|
||||
get edgeCount(): number {
|
||||
return this.ensureInit().edge_count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get topological sort (cached)
|
||||
* @returns Array of node IDs in topological order
|
||||
*/
|
||||
topoSort(): number[] {
|
||||
if (!this._dirty && this._topoCache) {
|
||||
return [...this._topoCache]; // Return copy to prevent mutation
|
||||
}
|
||||
|
||||
const result = this.ensureInit().topo_sort();
|
||||
this._topoCache = Array.from(result);
|
||||
return [...this._topoCache];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find critical path (cached)
|
||||
* @returns Object with path (node IDs) and total cost
|
||||
* @throws {Error} If WASM returns invalid data
|
||||
*/
|
||||
criticalPath(): CriticalPath {
|
||||
if (!this._dirty && this._criticalPathCache) {
|
||||
return { ...this._criticalPathCache, path: [...this._criticalPathCache.path] };
|
||||
}
|
||||
|
||||
const result = this.ensureInit().critical_path();
|
||||
|
||||
let parsed: unknown;
|
||||
if (typeof result === 'string') {
|
||||
try {
|
||||
parsed = JSON.parse(result);
|
||||
} catch (e) {
|
||||
throw new Error(`Invalid critical path JSON from WASM: ${e}`);
|
||||
}
|
||||
} else {
|
||||
parsed = result;
|
||||
}
|
||||
|
||||
if (!isCriticalPath(parsed)) {
|
||||
throw new Error('Invalid critical path structure from WASM');
|
||||
}
|
||||
|
||||
this._criticalPathCache = parsed;
|
||||
this._dirty = false;
|
||||
|
||||
return { ...parsed, path: [...parsed.path] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute attention scores for nodes
|
||||
* @param mechanism - Attention mechanism to use
|
||||
* @returns Array of scores (one per node)
|
||||
*/
|
||||
attention(mechanism: AttentionMechanism = AttentionMechanism.CRITICAL_PATH): number[] {
|
||||
if (!Number.isInteger(mechanism) || mechanism < 0 || mechanism > 2) {
|
||||
throw new Error(`Invalid attention mechanism: ${mechanism}`);
|
||||
}
|
||||
const result = this.ensureInit().attention(mechanism);
|
||||
return Array.from(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get node by ID
|
||||
*/
|
||||
getNode(id: number): DagNode | undefined {
|
||||
return this.nodes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all nodes
|
||||
*/
|
||||
getNodes(): DagNode[] {
|
||||
return Array.from(this.nodes.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize to bytes (bincode format)
|
||||
*/
|
||||
toBytes(): Uint8Array {
|
||||
return this.ensureInit().to_bytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize to JSON string
|
||||
*/
|
||||
toJSON(): string {
|
||||
return this.ensureInit().to_json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save DAG to storage
|
||||
* @returns StoredDag record or null if no storage configured
|
||||
*/
|
||||
async save(): Promise<StoredDag | null> {
|
||||
if (!this.storage) return null;
|
||||
|
||||
const data = this.toBytes();
|
||||
return this.storage.save(this.id, data, {
|
||||
name: this.name,
|
||||
metadata: {
|
||||
nodeCount: this.nodeCount,
|
||||
edgeCount: this.edgeCount,
|
||||
nodes: Object.fromEntries(this.nodes),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load DAG from storage by ID
|
||||
* @param id - DAG ID to load
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
* @returns Loaded DAG or null if not found
|
||||
* @throws {Error} If ID contains invalid characters
|
||||
*/
|
||||
static async load(id: string, storage?: DagStorage | MemoryStorage): Promise<RuDag | null> {
|
||||
if (!isValidDagId(id)) {
|
||||
throw new Error(`Invalid DAG ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || createStorage();
|
||||
|
||||
try {
|
||||
await store.init();
|
||||
const record = await store.get(id);
|
||||
|
||||
if (!record) {
|
||||
if (isOwnedStorage) store.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
return RuDag.fromBytes(record.data, {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
storage: store,
|
||||
});
|
||||
} catch (error) {
|
||||
if (isOwnedStorage) store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create DAG from bytes
|
||||
* @param data - Serialized DAG data
|
||||
* @param options - Configuration options
|
||||
* @throws {Error} If data is empty or invalid
|
||||
*/
|
||||
static async fromBytes(data: Uint8Array, options: RuDagOptions = {}): Promise<RuDag> {
|
||||
if (!data || data.length === 0) {
|
||||
throw new Error('Cannot create DAG from empty or null data');
|
||||
}
|
||||
|
||||
const mod = await initWasm();
|
||||
const dag = new RuDag(options);
|
||||
|
||||
try {
|
||||
dag.wasm = mod.WasmDag.from_bytes(data);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to deserialize DAG from bytes: ${error}`);
|
||||
}
|
||||
|
||||
dag.initialized = true;
|
||||
|
||||
if (dag.storage) {
|
||||
try {
|
||||
await dag.storage.init();
|
||||
} catch (error) {
|
||||
dag.wasm?.free();
|
||||
dag.wasm = null;
|
||||
throw new Error(`Failed to initialize storage: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return dag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create DAG from JSON
|
||||
* @param json - JSON string
|
||||
* @param options - Configuration options
|
||||
* @throws {Error} If JSON is empty or invalid
|
||||
*/
|
||||
static async fromJSON(json: string, options: RuDagOptions = {}): Promise<RuDag> {
|
||||
if (!json || json.trim().length === 0) {
|
||||
throw new Error('Cannot create DAG from empty or null JSON');
|
||||
}
|
||||
|
||||
const mod = await initWasm();
|
||||
const dag = new RuDag(options);
|
||||
|
||||
try {
|
||||
dag.wasm = mod.WasmDag.from_json(json);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to deserialize DAG from JSON: ${error}`);
|
||||
}
|
||||
|
||||
dag.initialized = true;
|
||||
|
||||
if (dag.storage) {
|
||||
try {
|
||||
await dag.storage.init();
|
||||
} catch (error) {
|
||||
dag.wasm?.free();
|
||||
dag.wasm = null;
|
||||
throw new Error(`Failed to initialize storage: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return dag;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all stored DAGs
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
*/
|
||||
static async listStored(storage?: DagStorage | MemoryStorage): Promise<StoredDag[]> {
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || createStorage();
|
||||
|
||||
try {
|
||||
await store.init();
|
||||
const result = await store.list();
|
||||
if (isOwnedStorage) store.close();
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (isOwnedStorage) store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a stored DAG
|
||||
* @param id - DAG ID to delete
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
* @throws {Error} If ID contains invalid characters
|
||||
*/
|
||||
static async deleteStored(id: string, storage?: DagStorage | MemoryStorage): Promise<boolean> {
|
||||
if (!isValidDagId(id)) {
|
||||
throw new Error(`Invalid DAG ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || createStorage();
|
||||
|
||||
try {
|
||||
await store.init();
|
||||
const result = await store.delete(id);
|
||||
if (isOwnedStorage) store.close();
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (isOwnedStorage) store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage statistics
|
||||
* @param storage - Storage backend (creates default if not provided)
|
||||
*/
|
||||
static async storageStats(storage?: DagStorage | MemoryStorage): Promise<{ count: number; totalSize: number }> {
|
||||
const isOwnedStorage = !storage;
|
||||
const store = storage || createStorage();
|
||||
|
||||
try {
|
||||
await store.init();
|
||||
const result = await store.stats();
|
||||
if (isOwnedStorage) store.close();
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (isOwnedStorage) store.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DAG ID
|
||||
*/
|
||||
getId(): string {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DAG name
|
||||
*/
|
||||
getName(): string | undefined {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DAG name
|
||||
* @param name - New name for the DAG
|
||||
*/
|
||||
setName(name: string): void {
|
||||
this.name = name;
|
||||
if (this.autoSave) {
|
||||
this.save().catch((e) => this.handleSaveError(e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup resources (WASM memory and storage connection)
|
||||
* Always call this when done with a DAG to prevent memory leaks
|
||||
*/
|
||||
dispose(): void {
|
||||
if (this.wasm) {
|
||||
this.wasm.free();
|
||||
this.wasm = null;
|
||||
}
|
||||
if (this.storage) {
|
||||
this.storage.close();
|
||||
this.storage = null;
|
||||
}
|
||||
this.nodes.clear();
|
||||
this._topoCache = null;
|
||||
this._criticalPathCache = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
1
vendor/ruvector/npm/packages/rudag/src/index.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,UAAU,EACV,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC;AAGnB,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG"}
|
||||
1
vendor/ruvector/npm/packages/rudag/src/index.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,6BAQe;AAPb,4FAAA,KAAK,OAAA;AACL,kGAAA,WAAW,OAAA;AACX,yGAAA,kBAAkB,OAAA;AAOpB,qCAOmB;AANjB,qGAAA,UAAU,OAAA;AACV,wGAAA,aAAa,OAAA;AACb,wGAAA,aAAa,OAAA;AACb,+GAAA,oBAAoB,OAAA;AAKtB,eAAe;AACF,QAAA,OAAO,GAAG,OAAO,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG"}
|
||||
216
vendor/ruvector/npm/packages/rudag/src/index.test.ts
vendored
Normal file
216
vendor/ruvector/npm/packages/rudag/src/index.test.ts
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* Tests for @ruvector/rudag
|
||||
*/
|
||||
|
||||
import { test, describe, beforeEach, afterEach } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import { RuDag, DagOperator, AttentionMechanism, MemoryStorage, createStorage } from './index';
|
||||
|
||||
describe('RuDag', () => {
|
||||
let dag: RuDag;
|
||||
|
||||
beforeEach(async () => {
|
||||
dag = new RuDag({ storage: new MemoryStorage(), autoSave: false });
|
||||
await dag.init();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dag.dispose();
|
||||
});
|
||||
|
||||
test('should create empty DAG', () => {
|
||||
assert.strictEqual(dag.nodeCount, 0);
|
||||
assert.strictEqual(dag.edgeCount, 0);
|
||||
});
|
||||
|
||||
test('should add nodes', () => {
|
||||
const id1 = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
const id2 = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
|
||||
assert.strictEqual(id1, 0);
|
||||
assert.strictEqual(id2, 1);
|
||||
assert.strictEqual(dag.nodeCount, 2);
|
||||
});
|
||||
|
||||
test('should add edges', () => {
|
||||
const n1 = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
const n2 = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
|
||||
const success = dag.addEdge(n1, n2);
|
||||
assert.strictEqual(success, true);
|
||||
assert.strictEqual(dag.edgeCount, 1);
|
||||
});
|
||||
|
||||
test('should reject cycles', () => {
|
||||
const n1 = dag.addNode(DagOperator.SCAN, 1.0);
|
||||
const n2 = dag.addNode(DagOperator.FILTER, 1.0);
|
||||
const n3 = dag.addNode(DagOperator.PROJECT, 1.0);
|
||||
|
||||
dag.addEdge(n1, n2);
|
||||
dag.addEdge(n2, n3);
|
||||
|
||||
// This should fail - would create cycle
|
||||
const success = dag.addEdge(n3, n1);
|
||||
assert.strictEqual(success, false);
|
||||
});
|
||||
|
||||
test('should compute topological sort', () => {
|
||||
const n1 = dag.addNode(DagOperator.SCAN, 1.0);
|
||||
const n2 = dag.addNode(DagOperator.FILTER, 1.0);
|
||||
const n3 = dag.addNode(DagOperator.PROJECT, 1.0);
|
||||
|
||||
dag.addEdge(n1, n2);
|
||||
dag.addEdge(n2, n3);
|
||||
|
||||
const topo = dag.topoSort();
|
||||
assert.deepStrictEqual(topo, [0, 1, 2]);
|
||||
});
|
||||
|
||||
test('should find critical path', () => {
|
||||
const n1 = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
const n2 = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
const n3 = dag.addNode(DagOperator.PROJECT, 1.0);
|
||||
|
||||
dag.addEdge(n1, n2);
|
||||
dag.addEdge(n2, n3);
|
||||
|
||||
const result = dag.criticalPath();
|
||||
assert.deepStrictEqual(result.path, [0, 1, 2]);
|
||||
assert.strictEqual(result.cost, 13); // 10 + 2 + 1
|
||||
});
|
||||
|
||||
test('should compute attention scores', () => {
|
||||
dag.addNode(DagOperator.SCAN, 1.0);
|
||||
dag.addNode(DagOperator.FILTER, 2.0);
|
||||
dag.addNode(DagOperator.PROJECT, 3.0);
|
||||
|
||||
const uniform = dag.attention(AttentionMechanism.UNIFORM);
|
||||
assert.strictEqual(uniform.length, 3);
|
||||
// All should be approximately 0.333
|
||||
assert.ok(Math.abs(uniform[0] - 0.333) < 0.01);
|
||||
|
||||
const topo = dag.attention(AttentionMechanism.TOPOLOGICAL);
|
||||
assert.strictEqual(topo.length, 3);
|
||||
|
||||
const critical = dag.attention(AttentionMechanism.CRITICAL_PATH);
|
||||
assert.strictEqual(critical.length, 3);
|
||||
});
|
||||
|
||||
test('should serialize to JSON', () => {
|
||||
dag.addNode(DagOperator.SCAN, 1.0);
|
||||
dag.addNode(DagOperator.FILTER, 2.0);
|
||||
dag.addEdge(0, 1);
|
||||
|
||||
const json = dag.toJSON();
|
||||
assert.ok(json.includes('nodes'));
|
||||
assert.ok(json.includes('edges'));
|
||||
});
|
||||
|
||||
test('should serialize to bytes', () => {
|
||||
dag.addNode(DagOperator.SCAN, 1.0);
|
||||
dag.addNode(DagOperator.FILTER, 2.0);
|
||||
dag.addEdge(0, 1);
|
||||
|
||||
const bytes = dag.toBytes();
|
||||
assert.ok(bytes instanceof Uint8Array);
|
||||
assert.ok(bytes.length > 0);
|
||||
});
|
||||
|
||||
test('should round-trip through JSON', async () => {
|
||||
const n1 = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
const n2 = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
dag.addEdge(n1, n2);
|
||||
|
||||
const json = dag.toJSON();
|
||||
const restored = await RuDag.fromJSON(json, { storage: null });
|
||||
|
||||
assert.strictEqual(restored.nodeCount, 2);
|
||||
assert.strictEqual(restored.edgeCount, 1);
|
||||
|
||||
restored.dispose();
|
||||
});
|
||||
|
||||
test('should round-trip through bytes', async () => {
|
||||
const n1 = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
const n2 = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
dag.addEdge(n1, n2);
|
||||
|
||||
const bytes = dag.toBytes();
|
||||
const restored = await RuDag.fromBytes(bytes, { storage: null });
|
||||
|
||||
assert.strictEqual(restored.nodeCount, 2);
|
||||
assert.strictEqual(restored.edgeCount, 1);
|
||||
|
||||
restored.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
describe('MemoryStorage', () => {
|
||||
let storage: MemoryStorage;
|
||||
|
||||
beforeEach(async () => {
|
||||
storage = new MemoryStorage();
|
||||
await storage.init();
|
||||
});
|
||||
|
||||
test('should save and retrieve DAG', async () => {
|
||||
const data = new Uint8Array([1, 2, 3, 4]);
|
||||
await storage.save('test-dag', data, { name: 'Test DAG' });
|
||||
|
||||
const retrieved = await storage.get('test-dag');
|
||||
assert.ok(retrieved);
|
||||
assert.strictEqual(retrieved.id, 'test-dag');
|
||||
assert.strictEqual(retrieved.name, 'Test DAG');
|
||||
assert.deepStrictEqual(Array.from(retrieved.data), [1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
test('should list all DAGs', async () => {
|
||||
await storage.save('dag-1', new Uint8Array([1]));
|
||||
await storage.save('dag-2', new Uint8Array([2]));
|
||||
|
||||
const list = await storage.list();
|
||||
assert.strictEqual(list.length, 2);
|
||||
});
|
||||
|
||||
test('should delete DAG', async () => {
|
||||
await storage.save('to-delete', new Uint8Array([1]));
|
||||
assert.ok(await storage.get('to-delete'));
|
||||
|
||||
await storage.delete('to-delete');
|
||||
assert.strictEqual(await storage.get('to-delete'), null);
|
||||
});
|
||||
|
||||
test('should find by name', async () => {
|
||||
await storage.save('dag-1', new Uint8Array([1]), { name: 'query' });
|
||||
await storage.save('dag-2', new Uint8Array([2]), { name: 'query' });
|
||||
await storage.save('dag-3', new Uint8Array([3]), { name: 'other' });
|
||||
|
||||
const results = await storage.findByName('query');
|
||||
assert.strictEqual(results.length, 2);
|
||||
});
|
||||
|
||||
test('should calculate stats', async () => {
|
||||
await storage.save('dag-1', new Uint8Array(100));
|
||||
await storage.save('dag-2', new Uint8Array(200));
|
||||
|
||||
const stats = await storage.stats();
|
||||
assert.strictEqual(stats.count, 2);
|
||||
assert.strictEqual(stats.totalSize, 300);
|
||||
});
|
||||
|
||||
test('should clear all', async () => {
|
||||
await storage.save('dag-1', new Uint8Array([1]));
|
||||
await storage.save('dag-2', new Uint8Array([2]));
|
||||
|
||||
await storage.clear();
|
||||
const list = await storage.list();
|
||||
assert.strictEqual(list.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createStorage', () => {
|
||||
test('should create MemoryStorage in Node.js', () => {
|
||||
const storage = createStorage();
|
||||
assert.ok(storage instanceof MemoryStorage);
|
||||
});
|
||||
});
|
||||
60
vendor/ruvector/npm/packages/rudag/src/index.ts
vendored
Normal file
60
vendor/ruvector/npm/packages/rudag/src/index.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @ruvector/rudag - Self-learning DAG query optimization
|
||||
*
|
||||
* Provides WASM-accelerated DAG operations with IndexedDB persistence
|
||||
* for browser environments.
|
||||
*/
|
||||
|
||||
export {
|
||||
RuDag,
|
||||
DagOperator,
|
||||
AttentionMechanism,
|
||||
type DagNode,
|
||||
type DagEdge,
|
||||
type CriticalPath,
|
||||
type RuDagOptions,
|
||||
} from './dag';
|
||||
|
||||
export {
|
||||
DagStorage,
|
||||
MemoryStorage,
|
||||
createStorage,
|
||||
isIndexedDBAvailable,
|
||||
type StoredDag,
|
||||
type DagStorageOptions,
|
||||
} from './storage';
|
||||
|
||||
// Version info
|
||||
export const VERSION = '0.1.0';
|
||||
|
||||
/**
|
||||
* Quick start example:
|
||||
*
|
||||
* ```typescript
|
||||
* import { RuDag, DagOperator, AttentionMechanism } from '@ruvector/rudag';
|
||||
*
|
||||
* // Create and initialize a DAG
|
||||
* const dag = await new RuDag({ name: 'my-query' }).init();
|
||||
*
|
||||
* // Add nodes (query operators)
|
||||
* const scan = dag.addNode(DagOperator.SCAN, 10.0);
|
||||
* const filter = dag.addNode(DagOperator.FILTER, 2.0);
|
||||
* const project = dag.addNode(DagOperator.PROJECT, 1.0);
|
||||
*
|
||||
* // Connect nodes
|
||||
* dag.addEdge(scan, filter);
|
||||
* dag.addEdge(filter, project);
|
||||
*
|
||||
* // Get critical path
|
||||
* const { path, cost } = dag.criticalPath();
|
||||
* console.log(`Critical path: ${path.join(' -> ')}, total cost: ${cost}`);
|
||||
*
|
||||
* // Compute attention scores
|
||||
* const scores = dag.attention(AttentionMechanism.CRITICAL_PATH);
|
||||
* console.log('Attention scores:', scores);
|
||||
*
|
||||
* // DAG is auto-saved to IndexedDB
|
||||
* // Load it later
|
||||
* const loadedDag = await RuDag.load(dag.getId());
|
||||
* ```
|
||||
*/
|
||||
65
vendor/ruvector/npm/packages/rudag/src/node.d.ts
vendored
Normal file
65
vendor/ruvector/npm/packages/rudag/src/node.d.ts
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Node.js-specific entry point with filesystem support
|
||||
*
|
||||
* @security Path traversal prevention via ID validation
|
||||
*/
|
||||
export * from './index';
|
||||
import { RuDag } from './index';
|
||||
/**
|
||||
* Create a Node.js DAG with memory storage
|
||||
*/
|
||||
export declare function createNodeDag(name?: string): Promise<RuDag>;
|
||||
/**
|
||||
* Stored DAG metadata
|
||||
*/
|
||||
interface StoredMeta {
|
||||
id: string;
|
||||
name?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
}
|
||||
/**
|
||||
* File-based storage for Node.js environments
|
||||
* @security All file operations validate paths to prevent traversal attacks
|
||||
*/
|
||||
export declare class FileDagStorage {
|
||||
private basePath;
|
||||
private initialized;
|
||||
constructor(basePath?: string);
|
||||
init(): Promise<void>;
|
||||
private getFilePath;
|
||||
private getMetaPath;
|
||||
save(id: string, data: Uint8Array, options?: {
|
||||
name?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
load(id: string): Promise<Uint8Array | null>;
|
||||
loadMeta(id: string): Promise<StoredMeta | null>;
|
||||
delete(id: string): Promise<boolean>;
|
||||
list(): Promise<string[]>;
|
||||
clear(): Promise<void>;
|
||||
stats(): Promise<{
|
||||
count: number;
|
||||
totalSize: number;
|
||||
}>;
|
||||
}
|
||||
/**
|
||||
* Node.js DAG manager with file persistence
|
||||
*/
|
||||
export declare class NodeDagManager {
|
||||
private storage;
|
||||
constructor(basePath?: string);
|
||||
init(): Promise<void>;
|
||||
createDag(name?: string): Promise<RuDag>;
|
||||
saveDag(dag: RuDag): Promise<void>;
|
||||
loadDag(id: string): Promise<RuDag | null>;
|
||||
deleteDag(id: string): Promise<boolean>;
|
||||
listDags(): Promise<string[]>;
|
||||
clearAll(): Promise<void>;
|
||||
getStats(): Promise<{
|
||||
count: number;
|
||||
totalSize: number;
|
||||
}>;
|
||||
}
|
||||
//# sourceMappingURL=node.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/node.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/node.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["node.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,KAAK,EAAiB,MAAM,SAAS,CAAC;AA6B/C;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAKjE;AAED;;GAEG;AACH,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;gBAEhB,QAAQ,GAAE,MAAiB;IAKjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAWb,WAAW;YAQX,WAAW;IAQnB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BtH,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAgB5C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAgBhD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAepC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAiBzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,KAAK,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAkB7D;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAiB;gBAEpB,QAAQ,CAAC,EAAE,MAAM;IAIvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAMxC,OAAO,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAQ1C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvC,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAI7B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,QAAQ,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAGhE"}
|
||||
239
vendor/ruvector/npm/packages/rudag/src/node.js
vendored
Normal file
239
vendor/ruvector/npm/packages/rudag/src/node.js
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Node.js-specific entry point with filesystem support
|
||||
*
|
||||
* @security Path traversal prevention via ID validation
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.NodeDagManager = exports.FileDagStorage = void 0;
|
||||
exports.createNodeDag = createNodeDag;
|
||||
__exportStar(require("./index"), exports);
|
||||
const index_1 = require("./index");
|
||||
const fs_1 = require("fs");
|
||||
const path_1 = require("path");
|
||||
/**
|
||||
* Validate storage ID to prevent path traversal attacks
|
||||
* @security Only allows alphanumeric, dash, underscore characters
|
||||
*/
|
||||
function isValidStorageId(id) {
|
||||
if (typeof id !== 'string' || id.length === 0 || id.length > 256)
|
||||
return false;
|
||||
// Strictly alphanumeric with dash/underscore - no dots, slashes, etc.
|
||||
return /^[a-zA-Z0-9_-]+$/.test(id);
|
||||
}
|
||||
/**
|
||||
* Ensure path is within base directory
|
||||
* @security Prevents path traversal via realpath comparison
|
||||
*/
|
||||
async function ensureWithinBase(basePath, targetPath) {
|
||||
const resolvedBase = (0, path_1.resolve)(basePath);
|
||||
const resolvedTarget = (0, path_1.resolve)(targetPath);
|
||||
if (!resolvedTarget.startsWith(resolvedBase + '/') && resolvedTarget !== resolvedBase) {
|
||||
throw new Error('Path traversal detected: target path outside base directory');
|
||||
}
|
||||
return resolvedTarget;
|
||||
}
|
||||
/**
|
||||
* Create a Node.js DAG with memory storage
|
||||
*/
|
||||
async function createNodeDag(name) {
|
||||
const storage = new index_1.MemoryStorage();
|
||||
const dag = new index_1.RuDag({ name, storage });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
/**
|
||||
* File-based storage for Node.js environments
|
||||
* @security All file operations validate paths to prevent traversal attacks
|
||||
*/
|
||||
class FileDagStorage {
|
||||
constructor(basePath = '.rudag') {
|
||||
this.initialized = false;
|
||||
// Normalize and resolve base path
|
||||
this.basePath = (0, path_1.resolve)((0, path_1.normalize)(basePath));
|
||||
}
|
||||
async init() {
|
||||
if (this.initialized)
|
||||
return;
|
||||
try {
|
||||
await fs_1.promises.mkdir(this.basePath, { recursive: true });
|
||||
this.initialized = true;
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to create storage directory: ${error}`);
|
||||
}
|
||||
}
|
||||
async getFilePath(id) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const targetPath = (0, path_1.join)(this.basePath, `${id}.dag`);
|
||||
return ensureWithinBase(this.basePath, targetPath);
|
||||
}
|
||||
async getMetaPath(id) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const targetPath = (0, path_1.join)(this.basePath, `${id}.meta.json`);
|
||||
return ensureWithinBase(this.basePath, targetPath);
|
||||
}
|
||||
async save(id, data, options = {}) {
|
||||
await this.init();
|
||||
const filePath = await this.getFilePath(id);
|
||||
const metaPath = await this.getMetaPath(id);
|
||||
// Load existing metadata for createdAt preservation
|
||||
let existingMeta = null;
|
||||
try {
|
||||
const metaContent = await fs_1.promises.readFile(metaPath, 'utf-8');
|
||||
existingMeta = JSON.parse(metaContent);
|
||||
}
|
||||
catch {
|
||||
// File doesn't exist or invalid - will create new
|
||||
}
|
||||
const now = Date.now();
|
||||
const meta = {
|
||||
id,
|
||||
name: options.name,
|
||||
metadata: options.metadata,
|
||||
createdAt: existingMeta?.createdAt || now,
|
||||
updatedAt: now,
|
||||
};
|
||||
// Write both files atomically (as much as possible)
|
||||
await Promise.all([
|
||||
fs_1.promises.writeFile(filePath, Buffer.from(data)),
|
||||
fs_1.promises.writeFile(metaPath, JSON.stringify(meta, null, 2)),
|
||||
]);
|
||||
}
|
||||
async load(id) {
|
||||
await this.init();
|
||||
const filePath = await this.getFilePath(id);
|
||||
try {
|
||||
const data = await fs_1.promises.readFile(filePath);
|
||||
return new Uint8Array(data);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async loadMeta(id) {
|
||||
await this.init();
|
||||
const metaPath = await this.getMetaPath(id);
|
||||
try {
|
||||
const content = await fs_1.promises.readFile(metaPath, 'utf-8');
|
||||
return JSON.parse(content);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async delete(id) {
|
||||
await this.init();
|
||||
const filePath = await this.getFilePath(id);
|
||||
const metaPath = await this.getMetaPath(id);
|
||||
const results = await Promise.allSettled([
|
||||
fs_1.promises.unlink(filePath),
|
||||
fs_1.promises.unlink(metaPath),
|
||||
]);
|
||||
// Return true if at least one file was deleted
|
||||
return results.some(r => r.status === 'fulfilled');
|
||||
}
|
||||
async list() {
|
||||
await this.init();
|
||||
try {
|
||||
const files = await fs_1.promises.readdir(this.basePath);
|
||||
return files
|
||||
.filter(f => f.endsWith('.dag'))
|
||||
.map(f => f.slice(0, -4)) // Remove .dag extension
|
||||
.filter(id => isValidStorageId(id)); // Extra safety filter
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
return [];
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async clear() {
|
||||
await this.init();
|
||||
const ids = await this.list();
|
||||
await Promise.all(ids.map(id => this.delete(id)));
|
||||
}
|
||||
async stats() {
|
||||
await this.init();
|
||||
const ids = await this.list();
|
||||
let totalSize = 0;
|
||||
for (const id of ids) {
|
||||
try {
|
||||
const filePath = await this.getFilePath(id);
|
||||
const stat = await fs_1.promises.stat(filePath);
|
||||
totalSize += stat.size;
|
||||
}
|
||||
catch {
|
||||
// Skip files that can't be accessed
|
||||
}
|
||||
}
|
||||
return { count: ids.length, totalSize };
|
||||
}
|
||||
}
|
||||
exports.FileDagStorage = FileDagStorage;
|
||||
/**
|
||||
* Node.js DAG manager with file persistence
|
||||
*/
|
||||
class NodeDagManager {
|
||||
constructor(basePath) {
|
||||
this.storage = new FileDagStorage(basePath);
|
||||
}
|
||||
async init() {
|
||||
await this.storage.init();
|
||||
}
|
||||
async createDag(name) {
|
||||
const dag = new index_1.RuDag({ name, storage: null, autoSave: false });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
async saveDag(dag) {
|
||||
const data = dag.toBytes();
|
||||
await this.storage.save(dag.getId(), data, { name: dag.getName() });
|
||||
}
|
||||
async loadDag(id) {
|
||||
const data = await this.storage.load(id);
|
||||
if (!data)
|
||||
return null;
|
||||
const meta = await this.storage.loadMeta(id);
|
||||
return index_1.RuDag.fromBytes(data, { id, name: meta?.name });
|
||||
}
|
||||
async deleteDag(id) {
|
||||
return this.storage.delete(id);
|
||||
}
|
||||
async listDags() {
|
||||
return this.storage.list();
|
||||
}
|
||||
async clearAll() {
|
||||
return this.storage.clear();
|
||||
}
|
||||
async getStats() {
|
||||
return this.storage.stats();
|
||||
}
|
||||
}
|
||||
exports.NodeDagManager = NodeDagManager;
|
||||
//# sourceMappingURL=node.js.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/node.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/node.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
269
vendor/ruvector/npm/packages/rudag/src/node.ts
vendored
Normal file
269
vendor/ruvector/npm/packages/rudag/src/node.ts
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* Node.js-specific entry point with filesystem support
|
||||
*
|
||||
* @security Path traversal prevention via ID validation
|
||||
*/
|
||||
|
||||
export * from './index';
|
||||
|
||||
import { RuDag, MemoryStorage } from './index';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join, normalize, resolve } from 'path';
|
||||
|
||||
/**
|
||||
* Validate storage ID to prevent path traversal attacks
|
||||
* @security Only allows alphanumeric, dash, underscore characters
|
||||
*/
|
||||
function isValidStorageId(id: string): boolean {
|
||||
if (typeof id !== 'string' || id.length === 0 || id.length > 256) return false;
|
||||
// Strictly alphanumeric with dash/underscore - no dots, slashes, etc.
|
||||
return /^[a-zA-Z0-9_-]+$/.test(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure path is within base directory
|
||||
* @security Prevents path traversal via realpath comparison
|
||||
*/
|
||||
async function ensureWithinBase(basePath: string, targetPath: string): Promise<string> {
|
||||
const resolvedBase = resolve(basePath);
|
||||
const resolvedTarget = resolve(targetPath);
|
||||
|
||||
if (!resolvedTarget.startsWith(resolvedBase + '/') && resolvedTarget !== resolvedBase) {
|
||||
throw new Error('Path traversal detected: target path outside base directory');
|
||||
}
|
||||
|
||||
return resolvedTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Node.js DAG with memory storage
|
||||
*/
|
||||
export async function createNodeDag(name?: string): Promise<RuDag> {
|
||||
const storage = new MemoryStorage();
|
||||
const dag = new RuDag({ name, storage });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stored DAG metadata
|
||||
*/
|
||||
interface StoredMeta {
|
||||
id: string;
|
||||
name?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* File-based storage for Node.js environments
|
||||
* @security All file operations validate paths to prevent traversal attacks
|
||||
*/
|
||||
export class FileDagStorage {
|
||||
private basePath: string;
|
||||
private initialized = false;
|
||||
|
||||
constructor(basePath: string = '.rudag') {
|
||||
// Normalize and resolve base path
|
||||
this.basePath = resolve(normalize(basePath));
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
if (this.initialized) return;
|
||||
|
||||
try {
|
||||
await fs.mkdir(this.basePath, { recursive: true });
|
||||
this.initialized = true;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create storage directory: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async getFilePath(id: string): Promise<string> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const targetPath = join(this.basePath, `${id}.dag`);
|
||||
return ensureWithinBase(this.basePath, targetPath);
|
||||
}
|
||||
|
||||
private async getMetaPath(id: string): Promise<string> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const targetPath = join(this.basePath, `${id}.meta.json`);
|
||||
return ensureWithinBase(this.basePath, targetPath);
|
||||
}
|
||||
|
||||
async save(id: string, data: Uint8Array, options: { name?: string; metadata?: Record<string, unknown> } = {}): Promise<void> {
|
||||
await this.init();
|
||||
|
||||
const filePath = await this.getFilePath(id);
|
||||
const metaPath = await this.getMetaPath(id);
|
||||
|
||||
// Load existing metadata for createdAt preservation
|
||||
let existingMeta: StoredMeta | null = null;
|
||||
try {
|
||||
const metaContent = await fs.readFile(metaPath, 'utf-8');
|
||||
existingMeta = JSON.parse(metaContent) as StoredMeta;
|
||||
} catch {
|
||||
// File doesn't exist or invalid - will create new
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const meta: StoredMeta = {
|
||||
id,
|
||||
name: options.name,
|
||||
metadata: options.metadata,
|
||||
createdAt: existingMeta?.createdAt || now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
// Write both files atomically (as much as possible)
|
||||
await Promise.all([
|
||||
fs.writeFile(filePath, Buffer.from(data)),
|
||||
fs.writeFile(metaPath, JSON.stringify(meta, null, 2)),
|
||||
]);
|
||||
}
|
||||
|
||||
async load(id: string): Promise<Uint8Array | null> {
|
||||
await this.init();
|
||||
|
||||
const filePath = await this.getFilePath(id);
|
||||
|
||||
try {
|
||||
const data = await fs.readFile(filePath);
|
||||
return new Uint8Array(data);
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async loadMeta(id: string): Promise<StoredMeta | null> {
|
||||
await this.init();
|
||||
|
||||
const metaPath = await this.getMetaPath(id);
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(metaPath, 'utf-8');
|
||||
return JSON.parse(content) as StoredMeta;
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<boolean> {
|
||||
await this.init();
|
||||
|
||||
const filePath = await this.getFilePath(id);
|
||||
const metaPath = await this.getMetaPath(id);
|
||||
|
||||
const results = await Promise.allSettled([
|
||||
fs.unlink(filePath),
|
||||
fs.unlink(metaPath),
|
||||
]);
|
||||
|
||||
// Return true if at least one file was deleted
|
||||
return results.some(r => r.status === 'fulfilled');
|
||||
}
|
||||
|
||||
async list(): Promise<string[]> {
|
||||
await this.init();
|
||||
|
||||
try {
|
||||
const files = await fs.readdir(this.basePath);
|
||||
return files
|
||||
.filter(f => f.endsWith('.dag'))
|
||||
.map(f => f.slice(0, -4)) // Remove .dag extension
|
||||
.filter(id => isValidStorageId(id)); // Extra safety filter
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||
return [];
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
await this.init();
|
||||
|
||||
const ids = await this.list();
|
||||
await Promise.all(ids.map(id => this.delete(id)));
|
||||
}
|
||||
|
||||
async stats(): Promise<{ count: number; totalSize: number }> {
|
||||
await this.init();
|
||||
|
||||
const ids = await this.list();
|
||||
let totalSize = 0;
|
||||
|
||||
for (const id of ids) {
|
||||
try {
|
||||
const filePath = await this.getFilePath(id);
|
||||
const stat = await fs.stat(filePath);
|
||||
totalSize += stat.size;
|
||||
} catch {
|
||||
// Skip files that can't be accessed
|
||||
}
|
||||
}
|
||||
|
||||
return { count: ids.length, totalSize };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Node.js DAG manager with file persistence
|
||||
*/
|
||||
export class NodeDagManager {
|
||||
private storage: FileDagStorage;
|
||||
|
||||
constructor(basePath?: string) {
|
||||
this.storage = new FileDagStorage(basePath);
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
await this.storage.init();
|
||||
}
|
||||
|
||||
async createDag(name?: string): Promise<RuDag> {
|
||||
const dag = new RuDag({ name, storage: null, autoSave: false });
|
||||
await dag.init();
|
||||
return dag;
|
||||
}
|
||||
|
||||
async saveDag(dag: RuDag): Promise<void> {
|
||||
const data = dag.toBytes();
|
||||
await this.storage.save(dag.getId(), data, { name: dag.getName() });
|
||||
}
|
||||
|
||||
async loadDag(id: string): Promise<RuDag | null> {
|
||||
const data = await this.storage.load(id);
|
||||
if (!data) return null;
|
||||
|
||||
const meta = await this.storage.loadMeta(id);
|
||||
return RuDag.fromBytes(data, { id, name: meta?.name });
|
||||
}
|
||||
|
||||
async deleteDag(id: string): Promise<boolean> {
|
||||
return this.storage.delete(id);
|
||||
}
|
||||
|
||||
async listDags(): Promise<string[]> {
|
||||
return this.storage.list();
|
||||
}
|
||||
|
||||
async clearAll(): Promise<void> {
|
||||
return this.storage.clear();
|
||||
}
|
||||
|
||||
async getStats(): Promise<{ count: number; totalSize: number }> {
|
||||
return this.storage.stats();
|
||||
}
|
||||
}
|
||||
136
vendor/ruvector/npm/packages/rudag/src/storage.d.ts
vendored
Normal file
136
vendor/ruvector/npm/packages/rudag/src/storage.d.ts
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* IndexedDB-based persistence layer for DAG storage
|
||||
* Provides browser-compatible persistent storage for DAGs
|
||||
*
|
||||
* @performance Single-transaction pattern for atomic operations
|
||||
* @security ID validation to prevent injection
|
||||
*/
|
||||
export interface StoredDag {
|
||||
/** Unique identifier */
|
||||
id: string;
|
||||
/** Human-readable name */
|
||||
name?: string;
|
||||
/** Serialized DAG data */
|
||||
data: Uint8Array;
|
||||
/** Creation timestamp */
|
||||
createdAt: number;
|
||||
/** Last update timestamp */
|
||||
updatedAt: number;
|
||||
/** Optional metadata */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
export interface DagStorageOptions {
|
||||
/** Custom database name */
|
||||
dbName?: string;
|
||||
/** Database version for migrations */
|
||||
version?: number;
|
||||
}
|
||||
/**
|
||||
* Check if IndexedDB is available (browser environment)
|
||||
*/
|
||||
export declare function isIndexedDBAvailable(): boolean;
|
||||
/**
|
||||
* IndexedDB storage class for DAG persistence
|
||||
*
|
||||
* @performance Uses single-transaction pattern for save operations
|
||||
*/
|
||||
export declare class DagStorage {
|
||||
private dbName;
|
||||
private version;
|
||||
private db;
|
||||
private initialized;
|
||||
constructor(options?: DagStorageOptions);
|
||||
/**
|
||||
* Initialize the database connection
|
||||
* @throws {Error} If IndexedDB is not available
|
||||
* @throws {Error} If database is blocked by another tab
|
||||
*/
|
||||
init(): Promise<void>;
|
||||
/**
|
||||
* Ensure database is initialized
|
||||
* @throws {Error} If database not initialized
|
||||
*/
|
||||
private ensureInit;
|
||||
/**
|
||||
* Save a DAG to storage (single-transaction pattern)
|
||||
* @performance Uses single transaction for atomic read-modify-write
|
||||
*/
|
||||
save(id: string, data: Uint8Array, options?: {
|
||||
name?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}): Promise<StoredDag>;
|
||||
/**
|
||||
* Save multiple DAGs in a single transaction (batch operation)
|
||||
* @performance Much faster than individual saves for bulk operations
|
||||
*/
|
||||
saveBatch(dags: Array<{
|
||||
id: string;
|
||||
data: Uint8Array;
|
||||
name?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}>): Promise<StoredDag[]>;
|
||||
/**
|
||||
* Get a DAG from storage
|
||||
*/
|
||||
get(id: string): Promise<StoredDag | null>;
|
||||
/**
|
||||
* Delete a DAG from storage
|
||||
*/
|
||||
delete(id: string): Promise<boolean>;
|
||||
/**
|
||||
* List all DAGs in storage
|
||||
*/
|
||||
list(): Promise<StoredDag[]>;
|
||||
/**
|
||||
* Search DAGs by name
|
||||
*/
|
||||
findByName(name: string): Promise<StoredDag[]>;
|
||||
/**
|
||||
* Clear all DAGs from storage
|
||||
*/
|
||||
clear(): Promise<void>;
|
||||
/**
|
||||
* Get storage statistics
|
||||
*/
|
||||
stats(): Promise<{
|
||||
count: number;
|
||||
totalSize: number;
|
||||
}>;
|
||||
/**
|
||||
* Close the database connection
|
||||
*/
|
||||
close(): void;
|
||||
}
|
||||
/**
|
||||
* In-memory storage fallback for Node.js or environments without IndexedDB
|
||||
*/
|
||||
export declare class MemoryStorage {
|
||||
private store;
|
||||
private initialized;
|
||||
init(): Promise<void>;
|
||||
save(id: string, data: Uint8Array, options?: {
|
||||
name?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}): Promise<StoredDag>;
|
||||
saveBatch(dags: Array<{
|
||||
id: string;
|
||||
data: Uint8Array;
|
||||
name?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}>): Promise<StoredDag[]>;
|
||||
get(id: string): Promise<StoredDag | null>;
|
||||
delete(id: string): Promise<boolean>;
|
||||
list(): Promise<StoredDag[]>;
|
||||
findByName(name: string): Promise<StoredDag[]>;
|
||||
clear(): Promise<void>;
|
||||
stats(): Promise<{
|
||||
count: number;
|
||||
totalSize: number;
|
||||
}>;
|
||||
close(): void;
|
||||
}
|
||||
/**
|
||||
* Create appropriate storage based on environment
|
||||
*/
|
||||
export declare function createStorage(options?: DagStorageOptions): DagStorage | MemoryStorage;
|
||||
//# sourceMappingURL=storage.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/storage.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/storage.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,SAAS;IACxB,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAWD;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED;;;;GAIG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,GAAE,iBAAsB;IAK3C;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmD3B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;;OAGG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IA2CjI;;;OAGG;IACG,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IA0CvI;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAiBhD;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB1C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAalC;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAcpD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAM5D;;OAEG;IACH,KAAK,IAAI,IAAI;CAOd;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,WAAW,CAAS;IAEtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IAqB3H,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAIjI,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAO1C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOpC,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAI5B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAI9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAM5D,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU,GAAG,aAAa,CAKzF"}
|
||||
338
vendor/ruvector/npm/packages/rudag/src/storage.js
vendored
Normal file
338
vendor/ruvector/npm/packages/rudag/src/storage.js
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
"use strict";
|
||||
/**
|
||||
* IndexedDB-based persistence layer for DAG storage
|
||||
* Provides browser-compatible persistent storage for DAGs
|
||||
*
|
||||
* @performance Single-transaction pattern for atomic operations
|
||||
* @security ID validation to prevent injection
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MemoryStorage = exports.DagStorage = void 0;
|
||||
exports.isIndexedDBAvailable = isIndexedDBAvailable;
|
||||
exports.createStorage = createStorage;
|
||||
const DB_NAME = 'rudag-storage';
|
||||
const DB_VERSION = 1;
|
||||
const STORE_NAME = 'dags';
|
||||
/**
|
||||
* Validate storage ID
|
||||
* @security Prevents injection attacks via ID
|
||||
*/
|
||||
function isValidStorageId(id) {
|
||||
if (typeof id !== 'string' || id.length === 0 || id.length > 256)
|
||||
return false;
|
||||
return /^[a-zA-Z0-9_-]+$/.test(id);
|
||||
}
|
||||
/**
|
||||
* Check if IndexedDB is available (browser environment)
|
||||
*/
|
||||
function isIndexedDBAvailable() {
|
||||
return typeof indexedDB !== 'undefined';
|
||||
}
|
||||
/**
|
||||
* IndexedDB storage class for DAG persistence
|
||||
*
|
||||
* @performance Uses single-transaction pattern for save operations
|
||||
*/
|
||||
class DagStorage {
|
||||
constructor(options = {}) {
|
||||
this.db = null;
|
||||
this.initialized = false;
|
||||
this.dbName = options.dbName || DB_NAME;
|
||||
this.version = options.version || DB_VERSION;
|
||||
}
|
||||
/**
|
||||
* Initialize the database connection
|
||||
* @throws {Error} If IndexedDB is not available
|
||||
* @throws {Error} If database is blocked by another tab
|
||||
*/
|
||||
async init() {
|
||||
if (this.initialized && this.db)
|
||||
return;
|
||||
if (!isIndexedDBAvailable()) {
|
||||
throw new Error('IndexedDB is not available in this environment');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(this.dbName, this.version);
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to open database: ${request.error?.message || 'Unknown error'}`));
|
||||
};
|
||||
request.onblocked = () => {
|
||||
reject(new Error('Database blocked - please close other tabs using this application'));
|
||||
};
|
||||
request.onsuccess = () => {
|
||||
this.db = request.result;
|
||||
this.initialized = true;
|
||||
// Handle connection errors after open
|
||||
this.db.onerror = (event) => {
|
||||
console.error('[DagStorage] Database error:', event);
|
||||
};
|
||||
// Handle version change (another tab upgraded)
|
||||
this.db.onversionchange = () => {
|
||||
this.db?.close();
|
||||
this.db = null;
|
||||
this.initialized = false;
|
||||
console.warn('[DagStorage] Database version changed - connection closed');
|
||||
};
|
||||
resolve();
|
||||
};
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
const store = db.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
||||
store.createIndex('name', 'name', { unique: false });
|
||||
store.createIndex('createdAt', 'createdAt', { unique: false });
|
||||
store.createIndex('updatedAt', 'updatedAt', { unique: false });
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Ensure database is initialized
|
||||
* @throws {Error} If database not initialized
|
||||
*/
|
||||
ensureInit() {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
return this.db;
|
||||
}
|
||||
/**
|
||||
* Save a DAG to storage (single-transaction pattern)
|
||||
* @performance Uses single transaction for atomic read-modify-write
|
||||
*/
|
||||
async save(id, data, options = {}) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const db = this.ensureInit();
|
||||
const now = Date.now();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
// First, get existing record in same transaction
|
||||
const getRequest = store.get(id);
|
||||
getRequest.onsuccess = () => {
|
||||
const existing = getRequest.result;
|
||||
const record = {
|
||||
id,
|
||||
name: options.name,
|
||||
data,
|
||||
createdAt: existing?.createdAt || now,
|
||||
updatedAt: now,
|
||||
metadata: options.metadata,
|
||||
};
|
||||
// Put in same transaction
|
||||
const putRequest = store.put(record);
|
||||
putRequest.onsuccess = () => resolve(record);
|
||||
putRequest.onerror = () => reject(new Error(`Failed to save DAG: ${putRequest.error?.message}`));
|
||||
};
|
||||
getRequest.onerror = () => {
|
||||
reject(new Error(`Failed to check existing DAG: ${getRequest.error?.message}`));
|
||||
};
|
||||
transaction.onerror = () => {
|
||||
reject(new Error(`Transaction failed: ${transaction.error?.message}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Save multiple DAGs in a single transaction (batch operation)
|
||||
* @performance Much faster than individual saves for bulk operations
|
||||
*/
|
||||
async saveBatch(dags) {
|
||||
for (const dag of dags) {
|
||||
if (!isValidStorageId(dag.id)) {
|
||||
throw new Error(`Invalid storage ID: "${dag.id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
}
|
||||
const db = this.ensureInit();
|
||||
const now = Date.now();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const results = [];
|
||||
let completed = 0;
|
||||
for (const dag of dags) {
|
||||
const getRequest = store.get(dag.id);
|
||||
getRequest.onsuccess = () => {
|
||||
const existing = getRequest.result;
|
||||
const record = {
|
||||
id: dag.id,
|
||||
name: dag.name,
|
||||
data: dag.data,
|
||||
createdAt: existing?.createdAt || now,
|
||||
updatedAt: now,
|
||||
metadata: dag.metadata,
|
||||
};
|
||||
store.put(record);
|
||||
results.push(record);
|
||||
completed++;
|
||||
};
|
||||
}
|
||||
transaction.oncomplete = () => resolve(results);
|
||||
transaction.onerror = () => reject(new Error(`Batch save failed: ${transaction.error?.message}`));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get a DAG from storage
|
||||
*/
|
||||
async get(id) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const db = this.ensureInit();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.get(id);
|
||||
request.onsuccess = () => resolve(request.result || null);
|
||||
request.onerror = () => reject(new Error(`Failed to get DAG: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Delete a DAG from storage
|
||||
*/
|
||||
async delete(id) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const db = this.ensureInit();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.delete(id);
|
||||
request.onsuccess = () => resolve(true);
|
||||
request.onerror = () => reject(new Error(`Failed to delete DAG: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* List all DAGs in storage
|
||||
*/
|
||||
async list() {
|
||||
const db = this.ensureInit();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.getAll();
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(new Error(`Failed to list DAGs: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Search DAGs by name
|
||||
*/
|
||||
async findByName(name) {
|
||||
const db = this.ensureInit();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const index = store.index('name');
|
||||
const request = index.getAll(name);
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(new Error(`Failed to find DAGs by name: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Clear all DAGs from storage
|
||||
*/
|
||||
async clear() {
|
||||
const db = this.ensureInit();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.clear();
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(new Error(`Failed to clear storage: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get storage statistics
|
||||
*/
|
||||
async stats() {
|
||||
const dags = await this.list();
|
||||
const totalSize = dags.reduce((sum, dag) => sum + dag.data.byteLength, 0);
|
||||
return { count: dags.length, totalSize };
|
||||
}
|
||||
/**
|
||||
* Close the database connection
|
||||
*/
|
||||
close() {
|
||||
if (this.db) {
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
}
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
exports.DagStorage = DagStorage;
|
||||
/**
|
||||
* In-memory storage fallback for Node.js or environments without IndexedDB
|
||||
*/
|
||||
class MemoryStorage {
|
||||
constructor() {
|
||||
this.store = new Map();
|
||||
this.initialized = false;
|
||||
}
|
||||
async init() {
|
||||
this.initialized = true;
|
||||
}
|
||||
async save(id, data, options = {}) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
const now = Date.now();
|
||||
const existing = this.store.get(id);
|
||||
const record = {
|
||||
id,
|
||||
name: options.name,
|
||||
data,
|
||||
createdAt: existing?.createdAt || now,
|
||||
updatedAt: now,
|
||||
metadata: options.metadata,
|
||||
};
|
||||
this.store.set(id, record);
|
||||
return record;
|
||||
}
|
||||
async saveBatch(dags) {
|
||||
return Promise.all(dags.map(dag => this.save(dag.id, dag.data, { name: dag.name, metadata: dag.metadata })));
|
||||
}
|
||||
async get(id) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
return this.store.get(id) || null;
|
||||
}
|
||||
async delete(id) {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
return this.store.delete(id);
|
||||
}
|
||||
async list() {
|
||||
return Array.from(this.store.values());
|
||||
}
|
||||
async findByName(name) {
|
||||
return Array.from(this.store.values()).filter(dag => dag.name === name);
|
||||
}
|
||||
async clear() {
|
||||
this.store.clear();
|
||||
}
|
||||
async stats() {
|
||||
const dags = Array.from(this.store.values());
|
||||
const totalSize = dags.reduce((sum, dag) => sum + dag.data.byteLength, 0);
|
||||
return { count: dags.length, totalSize };
|
||||
}
|
||||
close() {
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
exports.MemoryStorage = MemoryStorage;
|
||||
/**
|
||||
* Create appropriate storage based on environment
|
||||
*/
|
||||
function createStorage(options = {}) {
|
||||
if (isIndexedDBAvailable()) {
|
||||
return new DagStorage(options);
|
||||
}
|
||||
return new MemoryStorage();
|
||||
}
|
||||
//# sourceMappingURL=storage.js.map
|
||||
1
vendor/ruvector/npm/packages/rudag/src/storage.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/rudag/src/storage.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
418
vendor/ruvector/npm/packages/rudag/src/storage.ts
vendored
Normal file
418
vendor/ruvector/npm/packages/rudag/src/storage.ts
vendored
Normal file
@@ -0,0 +1,418 @@
|
||||
/**
|
||||
* IndexedDB-based persistence layer for DAG storage
|
||||
* Provides browser-compatible persistent storage for DAGs
|
||||
*
|
||||
* @performance Single-transaction pattern for atomic operations
|
||||
* @security ID validation to prevent injection
|
||||
*/
|
||||
|
||||
const DB_NAME = 'rudag-storage';
|
||||
const DB_VERSION = 1;
|
||||
const STORE_NAME = 'dags';
|
||||
|
||||
export interface StoredDag {
|
||||
/** Unique identifier */
|
||||
id: string;
|
||||
/** Human-readable name */
|
||||
name?: string;
|
||||
/** Serialized DAG data */
|
||||
data: Uint8Array;
|
||||
/** Creation timestamp */
|
||||
createdAt: number;
|
||||
/** Last update timestamp */
|
||||
updatedAt: number;
|
||||
/** Optional metadata */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface DagStorageOptions {
|
||||
/** Custom database name */
|
||||
dbName?: string;
|
||||
/** Database version for migrations */
|
||||
version?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate storage ID
|
||||
* @security Prevents injection attacks via ID
|
||||
*/
|
||||
function isValidStorageId(id: string): boolean {
|
||||
if (typeof id !== 'string' || id.length === 0 || id.length > 256) return false;
|
||||
return /^[a-zA-Z0-9_-]+$/.test(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IndexedDB is available (browser environment)
|
||||
*/
|
||||
export function isIndexedDBAvailable(): boolean {
|
||||
return typeof indexedDB !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* IndexedDB storage class for DAG persistence
|
||||
*
|
||||
* @performance Uses single-transaction pattern for save operations
|
||||
*/
|
||||
export class DagStorage {
|
||||
private dbName: string;
|
||||
private version: number;
|
||||
private db: IDBDatabase | null = null;
|
||||
private initialized = false;
|
||||
|
||||
constructor(options: DagStorageOptions = {}) {
|
||||
this.dbName = options.dbName || DB_NAME;
|
||||
this.version = options.version || DB_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database connection
|
||||
* @throws {Error} If IndexedDB is not available
|
||||
* @throws {Error} If database is blocked by another tab
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
if (this.initialized && this.db) return;
|
||||
|
||||
if (!isIndexedDBAvailable()) {
|
||||
throw new Error('IndexedDB is not available in this environment');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(this.dbName, this.version);
|
||||
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to open database: ${request.error?.message || 'Unknown error'}`));
|
||||
};
|
||||
|
||||
request.onblocked = () => {
|
||||
reject(new Error('Database blocked - please close other tabs using this application'));
|
||||
};
|
||||
|
||||
request.onsuccess = () => {
|
||||
this.db = request.result;
|
||||
this.initialized = true;
|
||||
|
||||
// Handle connection errors after open
|
||||
this.db.onerror = (event) => {
|
||||
console.error('[DagStorage] Database error:', event);
|
||||
};
|
||||
|
||||
// Handle version change (another tab upgraded)
|
||||
this.db.onversionchange = () => {
|
||||
this.db?.close();
|
||||
this.db = null;
|
||||
this.initialized = false;
|
||||
console.warn('[DagStorage] Database version changed - connection closed');
|
||||
};
|
||||
|
||||
resolve();
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = (event.target as IDBOpenDBRequest).result;
|
||||
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
const store = db.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
||||
store.createIndex('name', 'name', { unique: false });
|
||||
store.createIndex('createdAt', 'createdAt', { unique: false });
|
||||
store.createIndex('updatedAt', 'updatedAt', { unique: false });
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure database is initialized
|
||||
* @throws {Error} If database not initialized
|
||||
*/
|
||||
private ensureInit(): IDBDatabase {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
return this.db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a DAG to storage (single-transaction pattern)
|
||||
* @performance Uses single transaction for atomic read-modify-write
|
||||
*/
|
||||
async save(id: string, data: Uint8Array, options: { name?: string; metadata?: Record<string, unknown> } = {}): Promise<StoredDag> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
|
||||
const db = this.ensureInit();
|
||||
const now = Date.now();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
|
||||
// First, get existing record in same transaction
|
||||
const getRequest = store.get(id);
|
||||
|
||||
getRequest.onsuccess = () => {
|
||||
const existing = getRequest.result as StoredDag | undefined;
|
||||
|
||||
const record: StoredDag = {
|
||||
id,
|
||||
name: options.name,
|
||||
data,
|
||||
createdAt: existing?.createdAt || now,
|
||||
updatedAt: now,
|
||||
metadata: options.metadata,
|
||||
};
|
||||
|
||||
// Put in same transaction
|
||||
const putRequest = store.put(record);
|
||||
putRequest.onsuccess = () => resolve(record);
|
||||
putRequest.onerror = () => reject(new Error(`Failed to save DAG: ${putRequest.error?.message}`));
|
||||
};
|
||||
|
||||
getRequest.onerror = () => {
|
||||
reject(new Error(`Failed to check existing DAG: ${getRequest.error?.message}`));
|
||||
};
|
||||
|
||||
transaction.onerror = () => {
|
||||
reject(new Error(`Transaction failed: ${transaction.error?.message}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save multiple DAGs in a single transaction (batch operation)
|
||||
* @performance Much faster than individual saves for bulk operations
|
||||
*/
|
||||
async saveBatch(dags: Array<{ id: string; data: Uint8Array; name?: string; metadata?: Record<string, unknown> }>): Promise<StoredDag[]> {
|
||||
for (const dag of dags) {
|
||||
if (!isValidStorageId(dag.id)) {
|
||||
throw new Error(`Invalid storage ID: "${dag.id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
}
|
||||
|
||||
const db = this.ensureInit();
|
||||
const now = Date.now();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const results: StoredDag[] = [];
|
||||
let completed = 0;
|
||||
|
||||
for (const dag of dags) {
|
||||
const getRequest = store.get(dag.id);
|
||||
|
||||
getRequest.onsuccess = () => {
|
||||
const existing = getRequest.result as StoredDag | undefined;
|
||||
|
||||
const record: StoredDag = {
|
||||
id: dag.id,
|
||||
name: dag.name,
|
||||
data: dag.data,
|
||||
createdAt: existing?.createdAt || now,
|
||||
updatedAt: now,
|
||||
metadata: dag.metadata,
|
||||
};
|
||||
|
||||
store.put(record);
|
||||
results.push(record);
|
||||
completed++;
|
||||
};
|
||||
}
|
||||
|
||||
transaction.oncomplete = () => resolve(results);
|
||||
transaction.onerror = () => reject(new Error(`Batch save failed: ${transaction.error?.message}`));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DAG from storage
|
||||
*/
|
||||
async get(id: string): Promise<StoredDag | null> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
|
||||
const db = this.ensureInit();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.get(id);
|
||||
|
||||
request.onsuccess = () => resolve(request.result || null);
|
||||
request.onerror = () => reject(new Error(`Failed to get DAG: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a DAG from storage
|
||||
*/
|
||||
async delete(id: string): Promise<boolean> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
|
||||
const db = this.ensureInit();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.delete(id);
|
||||
|
||||
request.onsuccess = () => resolve(true);
|
||||
request.onerror = () => reject(new Error(`Failed to delete DAG: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* List all DAGs in storage
|
||||
*/
|
||||
async list(): Promise<StoredDag[]> {
|
||||
const db = this.ensureInit();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.getAll();
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(new Error(`Failed to list DAGs: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Search DAGs by name
|
||||
*/
|
||||
async findByName(name: string): Promise<StoredDag[]> {
|
||||
const db = this.ensureInit();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const index = store.index('name');
|
||||
const request = index.getAll(name);
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(new Error(`Failed to find DAGs by name: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all DAGs from storage
|
||||
*/
|
||||
async clear(): Promise<void> {
|
||||
const db = this.ensureInit();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.clear();
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(new Error(`Failed to clear storage: ${request.error?.message}`));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage statistics
|
||||
*/
|
||||
async stats(): Promise<{ count: number; totalSize: number }> {
|
||||
const dags = await this.list();
|
||||
const totalSize = dags.reduce((sum, dag) => sum + dag.data.byteLength, 0);
|
||||
return { count: dags.length, totalSize };
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the database connection
|
||||
*/
|
||||
close(): void {
|
||||
if (this.db) {
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
}
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In-memory storage fallback for Node.js or environments without IndexedDB
|
||||
*/
|
||||
export class MemoryStorage {
|
||||
private store: Map<string, StoredDag> = new Map();
|
||||
private initialized = false;
|
||||
|
||||
async init(): Promise<void> {
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
async save(id: string, data: Uint8Array, options: { name?: string; metadata?: Record<string, unknown> } = {}): Promise<StoredDag> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const existing = this.store.get(id);
|
||||
|
||||
const record: StoredDag = {
|
||||
id,
|
||||
name: options.name,
|
||||
data,
|
||||
createdAt: existing?.createdAt || now,
|
||||
updatedAt: now,
|
||||
metadata: options.metadata,
|
||||
};
|
||||
|
||||
this.store.set(id, record);
|
||||
return record;
|
||||
}
|
||||
|
||||
async saveBatch(dags: Array<{ id: string; data: Uint8Array; name?: string; metadata?: Record<string, unknown> }>): Promise<StoredDag[]> {
|
||||
return Promise.all(dags.map(dag => this.save(dag.id, dag.data, { name: dag.name, metadata: dag.metadata })));
|
||||
}
|
||||
|
||||
async get(id: string): Promise<StoredDag | null> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
return this.store.get(id) || null;
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<boolean> {
|
||||
if (!isValidStorageId(id)) {
|
||||
throw new Error(`Invalid storage ID: "${id}". Must be alphanumeric with dashes/underscores only.`);
|
||||
}
|
||||
return this.store.delete(id);
|
||||
}
|
||||
|
||||
async list(): Promise<StoredDag[]> {
|
||||
return Array.from(this.store.values());
|
||||
}
|
||||
|
||||
async findByName(name: string): Promise<StoredDag[]> {
|
||||
return Array.from(this.store.values()).filter(dag => dag.name === name);
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
this.store.clear();
|
||||
}
|
||||
|
||||
async stats(): Promise<{ count: number; totalSize: number }> {
|
||||
const dags = Array.from(this.store.values());
|
||||
const totalSize = dags.reduce((sum, dag) => sum + dag.data.byteLength, 0);
|
||||
return { count: dags.length, totalSize };
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create appropriate storage based on environment
|
||||
*/
|
||||
export function createStorage(options: DagStorageOptions = {}): DagStorage | MemoryStorage {
|
||||
if (isIndexedDBAvailable()) {
|
||||
return new DagStorage(options);
|
||||
}
|
||||
return new MemoryStorage();
|
||||
}
|
||||
11
vendor/ruvector/npm/packages/rudag/tsconfig.esm.json
vendored
Normal file
11
vendor/ruvector/npm/packages/rudag/tsconfig.esm.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"outDir": "./dist",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "pkg", "pkg-node"]
|
||||
}
|
||||
20
vendor/ruvector/npm/packages/rudag/tsconfig.json
vendored
Normal file
20
vendor/ruvector/npm/packages/rudag/tsconfig.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "CommonJS",
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "pkg", "pkg-node"]
|
||||
}
|
||||
Reference in New Issue
Block a user