| Crates.io | ruvector-router-wasm |
| lib.rs | ruvector-router-wasm |
| version | 0.1.29 |
| created_at | 2025-11-21 15:11:25.153696+00 |
| updated_at | 2025-12-29 19:17:45.713761+00 |
| description | WASM bindings for ruvector-router-core |
| homepage | |
| repository | https://github.com/ruvnet/ruvector |
| max_upload_size | |
| id | 1943707 |
| size | 56,748 |
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.
Traditional vector databases require backend infrastructure and constant network connectivity. Router WASM changes that.
# Using npm
npm install router-wasm
# Using yarn
yarn add router-wasm
# Using pnpm
pnpm add router-wasm
<script type="module">
import init, { VectorDB } from 'https://unpkg.com/router-wasm/router_wasm.js';
await init();
const db = new VectorDB(128);
</script>
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');
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;
}
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;
<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>
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);
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');
});
// 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);
}
});
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 available
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-core
// 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));
// 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));
}
Tree Shaking
// Import only what you need
import init, { VectorDB } from 'router-wasm';
// Don't import unused distance metrics or types
Code 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',
},
},
};
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 techniques
Limit 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);
}
cargo install wasm-pack# 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 --chrome
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 metadata
# 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 | 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:
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 cases
// 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
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;
}
interface SearchResult {
id: string;
score: number;
}
enum DistanceMetric {
Euclidean,
Cosine,
DotProduct,
Manhattan
}
See examples/browser-rag for a full-featured Retrieval Augmented Generation application running entirely in the browser.
See examples/product-search for an offline product recommendation system.
See examples/edge-routing for Cloudflare Workers integration.
// 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);
// Use dynamic imports for code splitting
const { default: init, VectorDB } = await import('router-wasm');
await init();
// 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));
}
// Ensure TypeScript can find declarations
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"types": ["router-wasm"]
}
}
Contributions are welcome! See Contributing Guidelines.
# 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
MIT License - see LICENSE for details.
Built with:
Built by rUv β’ Part of Ruvector β’ MIT Licensed
Browser-First Vector Search | Zero Backend Required | Privacy First
Get Started β’ Documentation β’ Examples