npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

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

About

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

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

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

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

Open Software & Tools

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

© 2026 – Pkg Stats / Ryan Hefner

@wiscale/velesdb-wasm

v3.12.0

Published

VelesDB for WebAssembly - Vector search in the browser

Readme

VelesDB WASM

npm License

WebAssembly build of VelesDB - vector search in the browser.

Features

  • In-browser vector search - No server required
  • Multiple metrics - Cosine, Euclidean, Dot Product, Hamming, Jaccard
  • Memory optimization - SQ8 (4x) and Binary (32x) quantization
  • Knowledge Graph - In-memory graph store with BFS/DFS traversal
  • Agent Memory - Semantic memory for AI agents (store/query knowledge facts)
  • Memory Wedge - The full remember/recall/recallFused/relate/forget/why agent memory wedge, in-memory only
  • VelesQL parser - Parse and validate VelesQL queries client-side
  • Sparse search - Inverted index with RRF hybrid fusion
  • Lightweight - Minimal bundle size

Installation

npm install @wiscale/velesdb-wasm

Usage

import init, { VectorStore } from '@wiscale/velesdb-wasm';

async function main() {
  // Initialize WASM module
  await init();

  // Create a vector store (768 dimensions, cosine similarity)
  const store = new VectorStore(768, 'cosine');

  // Insert vectors (use BigInt for IDs)
  store.insert(1n, new Float32Array([0.1, 0.2, ...]));
  store.insert(2n, new Float32Array([0.3, 0.4, ...]));

  // Search for similar vectors
  const query = new Float32Array([0.15, 0.25, ...]);
  const results = store.search(query, 5); // Top 5 results

  // Results: [[id, score], [id, score], ...]
  console.log(results);
}

main();

High-Performance Bulk Insert

For optimal performance when inserting many vectors:

// Pre-allocate capacity (avoids repeated memory allocations)
const store = VectorStore.with_capacity(768, 'cosine', 100000);

// Batch insert (much faster than individual inserts)
const batch = [
  [1n, [0.1, 0.2, ...]],
  [2n, [0.3, 0.4, ...]],
  // ... more vectors
];
store.insert_batch(batch);

// Or reserve capacity on existing store
store.reserve(50000);

API

VectorStore

class VectorStore {
  // Create a new store
  constructor(dimension: number, metric: 'cosine' | 'euclidean' | 'dot' | 'hamming' | 'jaccard');
  
  // Create with storage mode (sq8/binary for memory optimization)
  static new_with_mode(dimension: number, metric: string, mode: 'full' | 'sq8' | 'binary'): VectorStore;
  
  // Create with pre-allocated capacity (performance optimization)
  static with_capacity(dimension: number, metric: string, capacity: number): VectorStore;

  // Properties
  readonly len: number;
  readonly is_empty: boolean;
  readonly dimension: number;
  readonly storage_mode: string;  // "full", "sq8", or "binary"

  // Methods
  insert(id: bigint, vector: Float32Array): void;
  insert_with_payload(id: bigint, vector: Float32Array, payload: object): void;
  insert_batch(batch: Array<[bigint, number[]]>): void;  // Bulk insert
  insertBatchRaw(ids: BigUint64Array, vectors: Float32Array, dimension: number): void;  // Flat raw-bulk insert (since 2026-06-14)
  search(query: Float32Array, k: number): Array<[bigint, number]>;
  search_with_filter(query: Float32Array, k: number, filter: object): Array<{id, score, payload}>;
  text_search(query: string, k: number, field?: string): Array<{id, score, payload}>;
  get(id: bigint): {id, vector, payload} | null;
  remove(id: bigint): boolean;
  clear(): void;
  reserve(additional: number): void;  // Pre-allocate memory
  memory_usage(): number;  // Accurate for each storage mode
  
  //
  multi_query_search(vectors: Float32Array, num_vectors: number, k: number, strategy?: string, rrf_k?: number): Array<[bigint, number]>;
  hybrid_search(vector: Float32Array, text_query: string, k: number, vector_weight?: number): Array<{id, score, payload}>;
  batch_search(vectors: Float32Array, num_vectors: number, k: number): Array<Array<[bigint, number]>>;
  similarity_search(query: Float32Array, threshold: number, operator: string, k: number): Array<[bigint, number]>;
  query(query_vector: Float32Array, k: number): Array<{nodeId, vectorScore, graphScore, fusedScore, bindings, columnData}>;

  // Sparse search (inverted index, on VectorStore)
  sparse_insert(doc_id: bigint, indices: Uint32Array, values: Float32Array): void;
  sparse_search(indices: Uint32Array, values: Float32Array, k: number): Array<{doc_id, score}>;

  // Persistence
  save(db_name: string): Promise<void>;
  static load(db_name: string): Promise<VectorStore>;
  static delete_database(db_name: string): Promise<void>;
  export_to_bytes(): Uint8Array;
  static import_from_bytes(bytes: Uint8Array): VectorStore;

  // Metadata-only store
  static new_metadata_only(): VectorStore;
  readonly is_metadata_only: boolean;
}

Filter Format

// Equality filter
const filter = {
  condition: { type: "eq", field: "category", value: "tech" }
};

// Comparison filters
const filter = {
  condition: { type: "gt", field: "price", value: 100 }
};  // Also: gte, lt, lte, neq

// Logical operators
const filter = {
  condition: {
    type: "and",
    conditions: [
      { type: "eq", field: "category", value: "tech" },
      { type: "gt", field: "views", value: 1000 }
    ]
  }
};  // Also: or, not

Distance Metrics

| Metric | Description | Best For | |--------|-------------|----------| | cosine | Cosine similarity | Text embeddings (BERT, GPT) | | euclidean | L2 distance | Image features, spatial data | | dot | Dot product | Pre-normalized vectors | | hamming | Hamming distance | Binary vectors, fingerprints | | jaccard | Jaccard similarity | Set similarity, sparse vectors |

Storage Modes (Memory Optimization)

Reduce memory usage with quantization:

// Full precision (default) - best recall
const full = new VectorStore(768, 'cosine');

// SQ8: 4x memory reduction (~1% recall loss)
const sq8 = VectorStore.new_with_mode(768, 'cosine', 'sq8');

// Binary: 32x memory reduction (~5-10% recall loss)
const binary = VectorStore.new_with_mode(768, 'hamming', 'binary');

console.log(sq8.storage_mode);  // "sq8"

| Mode | Memory (768D) | Compression | Use Case | |------|---------------|-------------|----------| | full | 3080 bytes | 1x | Default, max precision | | sq8 | 784 bytes | 4x | Scale, RAM-constrained | | binary | 104 bytes | 32x | Edge, IoT, mobile PWA |

IndexedDB Persistence

Save and restore your vector store for offline-first applications with built-in async methods:

import init, { VectorStore } from '@wiscale/velesdb-wasm';

async function main() {
  await init();

  // Create and populate a store
  const store = new VectorStore(768, 'cosine');
  store.insert(1n, new Float32Array(768).fill(0.1));
  store.insert(2n, new Float32Array(768).fill(0.2));

  // Save to IndexedDB (single async call)
  await store.save('my-vectors-db');
  console.log('Saved!', store.len, 'vectors');

  // Later: Load from IndexedDB
  const restored = await VectorStore.load('my-vectors-db');
  console.log('Restored!', restored.len, 'vectors');

  // Clean up: Delete database
  await VectorStore.delete_database('my-vectors-db');
}

main();

Persistence API

class VectorStore {
  // Save to IndexedDB (async)
  save(db_name: string): Promise<void>;
  
  // Load from IndexedDB (async, static)
  static load(db_name: string): Promise<VectorStore>;
  
  // Delete IndexedDB database (async, static)
  static delete_database(db_name: string): Promise<void>;
  
  // Manual binary export/import (for localStorage, file download, etc.)
  export_to_bytes(): Uint8Array;
  static import_from_bytes(bytes: Uint8Array): VectorStore;
}

Binary Format

| Field | Size | Description | |-------|------|-------------| | Magic | 4 bytes | "VELS" | | Version | 1 byte | Format version (1) | | Dimension | 4 bytes | Vector dimension (u32 LE) | | Metric | 1 byte | 0=cosine, 1=euclidean, 2=dot | | Count | 8 bytes | Number of vectors (u64 LE) | | Vectors | variable | id (8B) + data (dim × 4B) each |

Performance

Ultra-fast serialization thanks to contiguous memory layout:

| Operation | 10k vectors (768D) | Throughput | |-----------|-------------------|------------| | Export | ~7 ms | 4479 MB/s | | Import | ~10 ms | 2943 MB/s |

Use Cases

  • Browser-based RAG - 100% client-side semantic search
  • Offline-first apps - Works without internet, persists to IndexedDB
  • Privacy-preserving AI - Data never leaves the browser
  • Electron/Tauri apps - Desktop AI without a server
  • PWA applications - Full offline support with service workers

⚠️ Limitations vs REST Backend

The WASM build is optimized for client-side use cases but has some limitations compared to the full REST server.

Feature Comparison

| Feature | WASM | REST Server | |---------|------|-------------| | Vector search (NEAR) | ✅ | ✅ | | Metadata filtering | ✅ | ✅ | | Hybrid search (vector + text) | ✅ | ✅ | | Full-text search | ✅ | ✅ | | Multi-query fusion (MQG) | ✅ | ✅ | | Batch search | ✅ | ✅ | | Sparse search | ✅ | ✅ | | Knowledge Graph (nodes, edges, traversal) | ✅ | ✅ | | Agent Memory (SemanticMemory) | ✅ | ✅ | | VelesQL parsing and validation | ✅ | ✅ | | VelesQL query execution | ✅ (see carve-outs below) | ✅ | | Column projection / aliases / window functions | ✅ | ✅ | | Aggregate ORDER BY (over a GROUP BY) | ✅ | ✅ | | Machine-readable error codes (VELES-*) | ✅ | ✅ | | EXPLAIN (core plan vocabulary) | ✅ | ✅ | | JOIN operations | ✅ (INNER, LEFT) | ✅ | | Aggregations (GROUP BY / HAVING) | ✅ | ✅ | | Set operations (UNION / INTERSECT / EXCEPT) | ✅ | ✅ | | MATCH (graph traversal) | ✅ (1–2 hop) | ✅ | | Cross-collection MATCH (@collection) | ❌ | ✅ | | Persistence | IndexedDB | Disk (mmap) | | Max vectors | ~100K (browser RAM) | Millions |

VelesQL (Parser + Execution)

VelesQL parsing, validation, and execution are all available in WASM. You can parse queries and inspect their AST client-side, and you can run queries against a WasmDatabase via executeQuery(). The single-collection executor supports SELECT (with WHERE, NEAR, similarity()), column projection / aliases / window functions (ROW_NUMBER/RANK/DENSE_RANK), GROUP BY/HAVING, aggregations, ORDER BY (payload columns, similarity(), arithmetic expressions, and aggregate ORDER BY over a GROUP BY), a default LIMIT 10, UNION/INTERSECT/EXCEPT, INNER/LEFT JOIN, INSERT/UPSERT/UPDATE/DELETE, DDL, and 1–2 hop MATCH graph traversal. EXPLAIN uses the same plan vocabulary as the REST server (core QueryPlan::to_plan_steps()), and execution errors carry machine-readable VELES-* codes.

Features that require the REST server (rejected with a descriptive error)

These are loud rejections, not silent no-ops — WASM never returns a wrong-but-quiet result for an unsupported shape:

  • Cross-collection MATCH (@collection) — needs Database-level routing.
  • MATCH traversals beyond 2 hops.
  • RIGHT / FULL JOIN and TRAIN QUANTIZER.
  • LET score bindingsLET x = ... SELECT ... is rejected (LET bindings are not supported in WASM).
  • Scalar subqueriesWHERE x = (SELECT ...) is rejected (Subqueries are not supported in WASM).
  • USING FUSION(strategy='weighted'|'rsf') on a single-vector NEAR — WASM has no BM25/graph branch to fuse against, so these weight-sensitive strategies are rejected (use rrf/maximum/average, or a metadata filter without FUSION). On a multi-vector NEAR_FUSED query the strategy is not rejected, but only rrf / average / maximum are honored — weighted / rsf silently fall back to RRF (matching core's fused_config_to_strategy).
  • ORDER BY similarity(field, $v) (a named/secondary vector) — WASM stores only the primary vector, so the named-vector form is rejected on both the SELECT and MATCH paths. Use ORDER BY similarity() (the search score) or a payload column. The MATCH path performs no vector scoring, so it additionally rejects bare similarity() and arithmetic ORDER BY (order by depth or alias.property instead).
import { VelesQL } from '@wiscale/velesdb-wasm';

// Parse and inspect a query
const parsed = VelesQL.parse("SELECT * FROM docs WHERE vector NEAR $v LIMIT 10");
console.log(parsed.tableName);       // "docs"
console.log(parsed.hasVectorSearch); // true
console.log(parsed.limit);          // 10

// Validate syntax
VelesQL.isValid("SELECT * FROM docs");        // true
VelesQL.isValid("SELEC * FROM docs");         // false

// Parse MATCH (graph) queries
const match = VelesQL.parse("MATCH (p:Person)-[:KNOWS]->(f:Person) RETURN f.name");
console.log(match.isMatch);              // true
console.log(match.matchNodeCount);       // 2
console.log(match.matchRelationshipCount); // 1

Knowledge Graph (GraphStore)

Build and traverse in-memory knowledge graphs entirely in the browser:

import { GraphStore, GraphNode, GraphEdge } from '@wiscale/velesdb-wasm';

const graph = new GraphStore();

// Create nodes
const alice = new GraphNode(1n, "Person");
alice.set_string_property("name", "Alice");
const bob = new GraphNode(2n, "Person");
bob.set_string_property("name", "Bob");

graph.add_node(alice);
graph.add_node(bob);

// Create edges
const edge = new GraphEdge(1n, 1n, 2n, "KNOWS");
graph.add_edge(edge);

// Traverse
const neighbors = graph.get_neighbors(1n);   // [2n]
const outgoing = graph.get_outgoing(1n);      // [GraphEdge]
const bfsResults = graph.bfs_traverse(1n, 3, 100); // BFS up to depth 3

Agent Memory (SemanticMemory)

Store and retrieve knowledge facts by semantic similarity for AI agent workloads:

import { SemanticMemory } from '@wiscale/velesdb-wasm';

const memory = new SemanticMemory(384);

// Store knowledge with embedding vectors
memory.store(1n, "Paris is the capital of France", embedding1);
memory.store(2n, "Berlin is the capital of Germany", embedding2);

// Query by similarity
const results = memory.query(queryEmbedding, 5);
// [{id, score, content}, ...]

console.log(memory.len());       // 2
console.log(memory.dimension()); // 384

Memory Wedge (MemoryService)

The full local-first agent memory wedge — remember/recall/recallWhere/ recallFused/relate/forget/why — built on top of SemanticMemory (above) plus an in-memory graph store, so a fact reached only through a typed link (not vector similarity) still surfaces. The same wedge as @wiscale/velesdb-memory-node and the Python binding; in-memory only here (no filesystem access under WASM). Most consumers should use the higher- level MemoryService re-exported from @wiscale/velesdb-sdk instead, which wraps this class with Promise-returning methods and the SDK's typed error hierarchy.

import init, { MemoryService } from '@wiscale/velesdb-wasm';

await init();
const memory = new MemoryService(384);

const pr = memory.remember('PR #42 swaps the mutex for parking_lot', [], null);
const decision = memory.remember(
  'we chose parking_lot to avoid lock poisoning',
  [{ target: pr, relation: 'decided_in' }],
  null
);

const hits = memory.recall('lock poisoning', 5, null);          // vector recall
const fused = memory.recallFused('lock poisoning', 5, null, null); // + graph promotion
const { nodes, edges } = memory.why('why parking_lot', 2, null);   // seed + connected subgraph

Ids are decimal strings; every method is synchronous (no Promise) and throws a JsValue Error carrying a .code (INVALID_INPUT / NOT_FOUND / INTERNAL) on failure.

Sparse Search (SparseIndex)

Inverted-index search with sparse vectors and RRF hybrid fusion:

import { SparseIndex, hybrid_search_fuse } from '@wiscale/velesdb-wasm';

const index = new SparseIndex();

// Insert sparse vectors (term indices + weights)
index.insert(1n, new Uint32Array([10, 20, 30]), new Float32Array([1.0, 0.5, 0.3]));
index.insert(2n, new Uint32Array([10, 40]),     new Float32Array([0.8, 1.2]));

// Search
const results = index.search(new Uint32Array([10, 20]), new Float32Array([1.0, 1.0]), 5);

// Fuse dense + sparse results with RRF
const fused = hybrid_search_fuse(denseResults, sparseResults, 60, 10);

When to Use REST Backend

Consider using the REST server if you need:

  • Cross-collection MATCH - The @collection annotation requires Database-level query routing, which is only available on the server (WASM operates on a single collection)
  • Multi-hop MATCH - Graph traversals beyond 2 hops (WASM supports 1–2 hop MATCH)
  • Large datasets - More than 100K vectors
  • Server-side processing - Centralized vector database

Migration from WASM to REST

// WASM (client-side)
import { VectorStore } from '@wiscale/velesdb-wasm';
const store = new VectorStore(768, 'cosine');
const results = store.search(query, 10);

// REST (server-side) - using fetch
const response = await fetch('http://localhost:8080/collections/docs/search', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ vector: query, top_k: 10 })
});
const results = await response.json();

// REST with VelesQL
const response = await fetch('http://localhost:8080/query', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: "SELECT * FROM docs WHERE vector NEAR $v AND category = 'tech' LIMIT 10",
    params: { v: query }
  })
});

Building from Source

# Install wasm-pack
cargo install wasm-pack

# Build for browser
wasm-pack build --target web

# Build for Node.js
wasm-pack build --target nodejs

Performance

Typical latencies on modern browsers:

| Operation | 768D vectors | 10K vectors | |-----------|--------------|-------------| | Insert | ~1 µs | ~10 ms | | Search | ~50 µs | ~5 ms |

License

Licensed under the VelesDB Core License 1.0 (source-available). velesdb-wasm compiles the VelesDB engine to WebAssembly, so the published artifact embeds the engine and is governed by the Core License.