@ruvector/router-wasm
v0.1.0
Published
WebAssembly bindings for ruvector-router - Semantic router with HNSW vector search for browsers
Downloads
104
Maintainers
Readme
Router WASM
WebAssembly bindings for intelligent neural routing and vector search in the browser.
Bring powerful vector database capabilities to the client-side. Run sub-millisecond vector search entirely in the browser with zero server dependencies. Perfect for edge computing, offline AI, and privacy-first applications.
🌟 Why Router WASM?
Traditional vector databases require backend infrastructure and constant network connectivity. Router WASM changes that.
The Browser-First Advantage
- ⚡ Zero Latency: No network roundtrips—search happens entirely in the browser
- 🔒 Privacy First: User data never leaves the device
- 🌐 Offline Capable: Full functionality without internet connection
- 💰 Cost Effective: Eliminate backend infrastructure and API costs
- 🚀 Edge Computing: Deploy intelligent routing to CDN edge nodes
- 📦 Small Bundle: Optimized WASM binary for fast page loads
🚀 Features
Core Capabilities
- Client-Side Vector Search: Sub-millisecond similarity search in the browser
- Neural Routing: Intelligent request routing and pattern matching
- Multiple Distance Metrics: Euclidean, Cosine, Dot Product, Manhattan
- HNSW Indexing: Fast approximate nearest neighbor search
- Memory Efficient: Optimized for browser memory constraints
- TypeScript Support: Full type definitions included
- Framework Agnostic: Works with React, Vue, Svelte, vanilla JS
- Web Worker Ready: Run computations off the main thread
Browser-Specific Optimizations
- SIMD Acceleration: Hardware-accelerated vector operations where available
- Progressive Loading: Load and initialize asynchronously
- Lazy Initialization: Initialize only when needed
- Small Footprint: <100KB gzipped WASM binary
- Memory Pooling: Efficient memory management for long-running sessions
- IndexedDB Integration: Persist vector data locally
📦 Installation
NPM/Yarn
# Using npm
npm install router-wasm
# Using yarn
yarn add router-wasm
# Using pnpm
pnpm add router-wasmCDN (Unpkg)
<script type="module">
import init, { VectorDB } from 'https://unpkg.com/router-wasm/router_wasm.js';
await init();
const db = new VectorDB(128);
</script>⚡ Quick Start
Basic Usage (ES Modules)
import init, { VectorDB, DistanceMetric } from 'router-wasm';
// Initialize WASM module (only once)
await init();
// Create a vector database with 128 dimensions
const db = new VectorDB(128);
// Insert vectors
db.insert('doc1', new Float32Array([0.1, 0.2, 0.3, /* ... 125 more */]));
db.insert('doc2', new Float32Array([0.4, 0.5, 0.6, /* ... 125 more */]));
db.insert('doc3', new Float32Array([0.7, 0.8, 0.9, /* ... 125 more */]));
// Search for similar vectors
const query = new Float32Array([0.15, 0.25, 0.35, /* ... 125 more */]);
const results = db.search(query, 5); // Top 5 results
// Process results
for (const result of results) {
console.log(`ID: ${result.id}, Score: ${result.score}`);
}
// Get collection size
console.log(`Total vectors: ${db.count()}`);
// Delete a vector
db.delete('doc2');TypeScript Support
import init, { VectorDB, DistanceMetric } from 'router-wasm';
interface SearchResult {
id: string;
score: number;
}
async function initializeVectorSearch(): Promise<VectorDB> {
// Initialize WASM
await init();
// Create database with 384 dimensions (e.g., for sentence embeddings)
const db = new VectorDB(384);
return db;
}
async function semanticSearch(
db: VectorDB,
queryEmbedding: Float32Array,
topK: number = 10
): Promise<SearchResult[]> {
const results = db.search(queryEmbedding, topK);
return results;
}React Integration
import React, { useState, useEffect } from 'react';
import init, { VectorDB } from 'router-wasm';
function VectorSearchApp() {
const [db, setDb] = useState(null);
const [loading, setLoading] = useState(true);
const [results, setResults] = useState([]);
useEffect(() => {
async function initialize() {
await init();
const vectorDb = new VectorDB(128);
// Populate with sample data
vectorDb.insert('item1', new Float32Array(128).fill(0.1));
vectorDb.insert('item2', new Float32Array(128).fill(0.5));
setDb(vectorDb);
setLoading(false);
}
initialize();
}, []);
const handleSearch = async (queryVector) => {
if (!db) return;
const searchResults = db.search(queryVector, 10);
setResults(searchResults);
};
if (loading) return <div>Loading vector database...</div>;
return (
<div>
<h1>Client-Side Vector Search</h1>
<button onClick={() => handleSearch(new Float32Array(128).fill(0.2))}>
Search
</button>
<ul>
{results.map(r => (
<li key={r.id}>
{r.id}: {r.score.toFixed(4)}
</li>
))}
</ul>
</div>
);
}
export default VectorSearchApp;Vue 3 Integration
<template>
<div>
<h1>Vector Search</h1>
<input v-model="searchQuery" @input="handleSearch" placeholder="Search..." />
<ul>
<li v-for="result in results" :key="result.id">
{{ result.id }}: {{ result.score.toFixed(4) }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import init, { VectorDB } from 'router-wasm';
const db = ref(null);
const searchQuery = ref('');
const results = ref([]);
onMounted(async () => {
await init();
db.value = new VectorDB(128);
// Populate database
db.value.insert('doc1', new Float32Array(128).fill(0.1));
db.value.insert('doc2', new Float32Array(128).fill(0.5));
});
const handleSearch = () => {
if (!db.value || !searchQuery.value) return;
// Convert query to embedding (simplified example)
const queryVector = new Float32Array(128).fill(parseFloat(searchQuery.value) || 0);
results.value = db.value.search(queryVector, 5);
};
</script>🎯 Use Cases
Client-Side AI Applications
Semantic Search in the Browser
// RAG (Retrieval Augmented Generation) in the browser
import init, { VectorDB } from 'router-wasm';
import { generateEmbedding } from './embeddings'; // Your embedding model
await init();
const knowledgeBase = new VectorDB(384);
// Index documents
const docs = [
{ id: 'doc1', text: 'Rust is a systems programming language' },
{ id: 'doc2', text: 'WebAssembly enables near-native performance' },
{ id: 'doc3', text: 'Vector databases power semantic search' }
];
for (const doc of docs) {
const embedding = await generateEmbedding(doc.text);
knowledgeBase.insert(doc.id, embedding);
}
// Query with natural language
const queryEmbedding = await generateEmbedding('What is WASM?');
const relevantDocs = knowledgeBase.search(queryEmbedding, 3);Offline Recommender System
// Product recommendations without backend
const productDb = new VectorDB(256);
// Index product features
products.forEach(product => {
const featureVector = extractFeatures(product);
productDb.insert(product.id, featureVector);
});
// Get recommendations based on user preferences
const userPreferences = getUserPreferenceVector();
const recommendations = productDb.search(userPreferences, 10);Privacy-First Search
// Search user data without sending to server
const privateDb = new VectorDB(512);
// User data stays in browser
userDocuments.forEach(doc => {
const embedding = embedDocument(doc);
privateDb.insert(doc.id, embedding);
});
// All searches happen locally
const results = privateDb.search(queryEmbedding, 20);Edge Computing & CDN
Cloudflare Workers
// Deploy to Cloudflare Workers
import init, { VectorDB } from 'router-wasm';
export default {
async fetch(request, env, ctx) {
await init();
const db = new VectorDB(128);
// Load pre-computed vectors from KV store
const vectors = await env.VECTORS.get('index', 'json');
for (const [id, vector] of Object.entries(vectors)) {
db.insert(id, new Float32Array(vector));
}
// Handle search at edge
const { query } = await request.json();
const results = db.search(new Float32Array(query), 10);
return new Response(JSON.stringify(results), {
headers: { 'content-type': 'application/json' }
});
}
};Deno Deploy
// Edge function with vector search
import init, { VectorDB } from 'https://esm.sh/router-wasm';
Deno.serve(async (req) => {
await init();
const db = new VectorDB(256);
// Your edge routing logic
return new Response('OK');
});Web Workers
// worker.js - Run vector search off main thread
import init, { VectorDB } from 'router-wasm';
let db = null;
self.addEventListener('message', async (e) => {
const { type, payload } = e.data;
if (type === 'init') {
await init();
db = new VectorDB(payload.dimensions);
self.postMessage({ type: 'ready' });
}
if (type === 'insert') {
db.insert(payload.id, new Float32Array(payload.vector));
self.postMessage({ type: 'inserted', id: payload.id });
}
if (type === 'search') {
const results = db.search(new Float32Array(payload.query), payload.k);
self.postMessage({ type: 'results', data: results });
}
});// main.js - Use the worker
const worker = new Worker('worker.js', { type: 'module' });
worker.postMessage({ type: 'init', payload: { dimensions: 128 } });
worker.addEventListener('message', (e) => {
if (e.data.type === 'ready') {
console.log('Vector DB ready in worker');
// Insert data
worker.postMessage({
type: 'insert',
payload: { id: 'doc1', vector: new Array(128).fill(0.1) }
});
// Search
worker.postMessage({
type: 'search',
payload: { query: new Array(128).fill(0.2), k: 5 }
});
}
if (e.data.type === 'results') {
console.log('Search results:', e.data.data);
}
});🔧 Advanced Features
Persistent Storage (IndexedDB)
import init, { VectorDB } from 'router-wasm';
// Initialize with persistent storage path
await init();
const db = new VectorDB(128, 'my-vector-store');
// Data persists across sessions
db.insert('doc1', new Float32Array(128));
// Reload in future session
const db2 = new VectorDB(128, 'my-vector-store');
console.log(db2.count()); // Previously inserted data is availableDistance Metrics
import { VectorDB, DistanceMetric } from 'router-wasm';
const db = new VectorDB(128);
// Different similarity measures available:
// - DistanceMetric.Euclidean (L2 distance)
// - DistanceMetric.Cosine (cosine similarity)
// - DistanceMetric.DotProduct (dot product)
// - DistanceMetric.Manhattan (L1 distance)
// Note: Distance metric is set at index build time in router-coreBatch Operations
// Efficient bulk insertion
const vectors = [
{ id: 'doc1', vector: new Float32Array(128).fill(0.1) },
{ id: 'doc2', vector: new Float32Array(128).fill(0.2) },
{ id: 'doc3', vector: new Float32Array(128).fill(0.3) },
];
vectors.forEach(({ id, vector }) => db.insert(id, vector));
// Batch search (multiple queries)
const queries = [
new Float32Array(128).fill(0.15),
new Float32Array(128).fill(0.25),
];
const allResults = queries.map(query => db.search(query, 5));Memory Management
// Check collection size
const count = db.count();
console.log(`Vectors in database: ${count}`);
// Clean up when done (especially important in SPAs)
// Note: Drop the reference and let garbage collector handle it
db = null;
// For explicit cleanup in long-running apps
function cleanupVectorDb(db) {
const ids = getAllIds(); // Your tracking logic
ids.forEach(id => db.delete(id));
}📊 Performance Optimization
Bundle Size Optimization
Tree Shaking
// Import only what you need
import init, { VectorDB } from 'router-wasm';
// Don't import unused distance metrics or typesCode Splitting
// Lazy load WASM module
const loadVectorDB = async () => {
const { default: init, VectorDB } = await import('router-wasm');
await init();
return VectorDB;
};
// Use when needed
button.addEventListener('click', async () => {
const VectorDB = await loadVectorDB();
const db = new VectorDB(128);
});Webpack Configuration
// webpack.config.js
module.exports = {
experiments: {
asyncWebAssembly: true,
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};Runtime Performance
Pre-compute Embeddings
// Generate embeddings server-side or during build
// Ship pre-computed vectors to reduce client computation
const precomputedVectors = await fetch('/vectors.json').then(r => r.json());
await init();
const db = new VectorDB(128);
for (const [id, vector] of Object.entries(precomputedVectors)) {
db.insert(id, new Float32Array(vector));
}Dimension Reduction
// Use lower dimensions for faster search
// 128 or 256 dimensions often sufficient for many use cases
const db = new VectorDB(128); // Instead of 384 or 768
// Consider PCA or other dimensionality reduction techniquesLimit Result Sets
// Request only what you need
const results = db.search(query, 10); // Top 10, not 100
// Implement pagination if needed
function paginatedSearch(query, page = 0, pageSize = 10) {
const allResults = db.search(query, (page + 1) * pageSize);
return allResults.slice(page * pageSize, (page + 1) * pageSize);
}🔨 Building from Source
Prerequisites
- Rust: 1.77 or higher
- wasm-pack:
cargo install wasm-pack - Node.js: 18.0 or higher (for testing)
Build Commands
# Clone repository
git clone https://github.com/ruvnet/ruvector.git
cd ruvector/crates/router-wasm
# Build for web (ES modules)
wasm-pack build --target web --release
# Build for Node.js
wasm-pack build --target nodejs --release
# Build for bundlers (webpack, etc.)
wasm-pack build --target bundler --release
# Build with optimizations
wasm-pack build --target web --release -- --features simd
# Run tests
wasm-pack test --headless --chromeBuild Output
After building, the pkg/ directory contains:
pkg/
├── router_wasm.js # JavaScript bindings
├── router_wasm.d.ts # TypeScript definitions
├── router_wasm_bg.wasm # WebAssembly binary
├── router_wasm_bg.wasm.d.ts
└── package.json # NPM package metadataCustom Build Profiles
# Cargo.toml - Already optimized for size
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
codegen-units = 1 # Better optimization
panic = "abort" # Smaller binary🌐 Browser Compatibility
| Browser | Version | WASM | SIMD | Notes | |---------|---------|------|------|-------| | Chrome | 87+ | ✅ | ✅ | Full support | | Firefox | 89+ | ✅ | ✅ | Full support | | Safari | 15+ | ✅ | ⚠️ | WASM SIMD in 16.4+ | | Edge | 87+ | ✅ | ✅ | Full support | | Opera | 73+ | ✅ | ✅ | Full support | | Mobile Safari | 15+ | ✅ | ⚠️ | Limited SIMD | | Mobile Chrome | 87+ | ✅ | ✅ | Full support |
Notes:
- ✅ Full support
- ⚠️ Partial support (SIMD acceleration may not be available)
- All modern browsers support WebAssembly
- SIMD provides 2-4x performance boost where available
🔗 Integration with Ruvector Ecosystem
With ruvector-wasm
import initRouter, { VectorDB as RouterDB } from 'router-wasm';
import initRuvector, { VectorDB } from 'ruvector-wasm';
// Initialize both modules
await Promise.all([initRouter(), initRuvector()]);
// Router WASM: Intelligent routing and pattern matching
const router = new RouterDB(128);
// Ruvector WASM: Full-featured vector database
const vectorDb = new VectorDB(128);
// Use together for advanced use casesWith Node.js Backend
// Frontend (router-wasm)
import init, { VectorDB } from 'router-wasm';
await init();
const clientDb = new VectorDB(128);
// Backend (ruvector Node.js bindings)
const { VectorDB } = require('ruvector');
const serverDb = new VectorDB();
// Hybrid architecture: Local search + server sync📚 API Reference
VectorDB
class VectorDB {
/**
* Create a new vector database
* @param dimensions - Vector dimensionality (e.g., 128, 256, 384, 768)
* @param storage_path - Optional persistent storage path
*/
constructor(dimensions: number, storage_path?: string);
/**
* Insert a vector into the database
* @param id - Unique identifier
* @param vector - Float32Array of specified dimensions
* @returns The inserted ID
*/
insert(id: string, vector: Float32Array): string;
/**
* Search for similar vectors
* @param vector - Query vector
* @param k - Number of results to return
* @returns Array of search results with id and score
*/
search(vector: Float32Array, k: number): SearchResult[];
/**
* Delete a vector by ID
* @param id - ID to delete
* @returns true if deleted, false if not found
*/
delete(id: string): boolean;
/**
* Get total number of vectors
* @returns Vector count
*/
count(): number;
}Types
interface SearchResult {
id: string;
score: number;
}
enum DistanceMetric {
Euclidean,
Cosine,
DotProduct,
Manhattan
}🎓 Examples
Complete RAG Application
See examples/browser-rag for a full-featured Retrieval Augmented Generation application running entirely in the browser.
Product Search
See examples/product-search for an offline product recommendation system.
Edge Routing
See examples/edge-routing for Cloudflare Workers integration.
🐛 Troubleshooting
WASM Module Not Loading
// Ensure init() is called before creating VectorDB
import init, { VectorDB } from 'router-wasm';
// ❌ Wrong
const db = new VectorDB(128); // Error: WASM not initialized
// ✅ Correct
await init();
const db = new VectorDB(128);Large Bundle Size
// Use dynamic imports for code splitting
const { default: init, VectorDB } = await import('router-wasm');
await init();Memory Errors in Browser
// Reduce dimensions or limit database size
const db = new VectorDB(128); // Instead of 768
// Clear vectors periodically in long-running apps
if (db.count() > 10000) {
// Implement your pruning logic
oldIds.forEach(id => db.delete(id));
}TypeScript Errors
// Ensure TypeScript can find declarations
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"types": ["router-wasm"]
}
}📖 Documentation
- Quick Start Guide - Get started in 5 minutes
- WASM API Reference - Complete API documentation
- Performance Tuning - Optimization strategies
- Main README - Ruvector ecosystem overview
🤝 Contributing
Contributions are welcome! See Contributing Guidelines.
Development Setup
# Clone and setup
git clone https://github.com/ruvnet/ruvector.git
cd ruvector/crates/router-wasm
# Build
wasm-pack build --target web
# Test
wasm-pack test --headless --chrome --firefox
# Format
cargo fmt
# Lint
cargo clippy -- -D warnings📜 License
MIT License - see LICENSE for details.
🙏 Acknowledgments
Built with:
- wasm-bindgen: Rust/JavaScript interop
- router-core: High-performance vector routing engine
- HNSW: Fast approximate nearest neighbor search
- SIMD: Hardware-accelerated vector operations
🌐 Links
- GitHub: github.com/ruvnet/ruvector
- NPM: npmjs.com/package/router-wasm
- Documentation: ruvector docs
- Discord: Join community
- Website: ruv.io
Built by rUv • Part of Ruvector • MIT Licensed
Browser-First Vector Search | Zero Backend Required | Privacy First
