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

vantadb

v0.9.0

Published

VantaDB TypeScript SDK — browser AI agent memory: WASM-powered embedded vector & graph memory for JS runtimes (Node, Bun, Deno, browser)

Readme

VantaDB TypeScript SDK

WASM-powered embedded vector & graph memory for JavaScript runtimes — browser AI agent memory.

Status: active (core-promise until 1.0 — published as vantadb on npm).

Try it in the browser: AI agent demo — Transformers.js embeddings on-device + OPFS memory (connect_persistent() from the raw vantadb-wasm binding), no server.

import { Client } from "vantadb";

const db = Client.create();

await db.put({ namespace: "docs", key: "intro", payload: "VantaDB is a vector database", vector: [0.1, 0.2, ...] });
const results = await db.search({ namespace: "docs", query_vector: [0.15, 0.25, ...], top_k: 5 });

Features

  • Vector search — cosine/euclidean HNSW search
  • Hybrid search — vector + BM25 text fusion (RRF)
  • Graph queries — BFS, DFS, topological sort
  • Persistence — export/import JSONL
  • TTL — auto-expiring records
  • Works everywhere — Node.js, Bun, Deno, browsers

Install

npm install vantadb

Quick Start

import { Client } from "vantadb";

// In-memory (default)
const db = Client.create();

// Or persistent:
// const db = Client.open("./vanta_data");

// Store
await db.put({
  namespace: "memories",
  key: "greeting",
  payload: "Hello, world!",
  metadata: { lang: "en" },
  vector: [0.1, 0.2, 0.3],
});

// Search
const hits = await db.search({
  namespace: "memories",
  query_vector: [0.1, 0.2, 0.3],
  top_k: 10,
});

console.log(hits[0].record.payload); // "Hello, world!"

db.close();

Module Formats (ESM / CommonJS)

The package is ESM-only (the vantadb-wasm dependency is an ES module and the API is fully synchronous, so a CommonJS build would not be loadable without an async rewrite). import works everywhere — Node, Bun, Deno, bundlers.

CommonJS consumers on Node.js ≥ 22.12 can require("vantadb") directly: Node's require(esm) loads the module graph because it contains no top-level await. On older Node versions, migrate the consumer to import, or use the native backend (vantadb-node) whose API is async and CommonJS-friendly.

// Node.js >= 22.12
const { Client } = require("vantadb");
const db = Client.create();
// ...same synchronous API as the ESM build

WASM bundle & lazy loading

The package is backed by the vantadb-wasm wasm-bindgen build; the compiled engine binary is vantadb-wasm/pkg/vantadb_wasm_bg.wasm (~1.8 MB raw, ~739 KB gzipped transfer — measured 2026-10-04, see the bundle strategy). How it is loaded depends on the runtime:

  • Bundlers (Vite/Webpack/esbuild) — the wasm-bindgen glue (vantadb_wasm.js) imports the .wasm as an ES module, which bundlers cannot handle natively. Add vite-plugin-wasm (or the equivalent for your bundler) so the binary is fetched + instantiated on demand. Without a plugin the build fails at bundle time.
  • Node.js — the wasm-bindgen Node loader reads the .wasm file from disk at first use; no plugin required.
  • Vanta Studio desktop/web builds — the WASM backend is code-split out: the glue module is externalized so the lazy import() never executes in Tauri/HTTP modes (see feature-freez/desktop/vite.config.ts, WASM-02). It only loads in vite build --mode wasm (WASM-03).

Zero-install CDN usage (verified 2026-08-26)

| CDN entry | Works? | Why | |-----------|--------|-----| | https://cdn.jsdelivr.net/npm/vantadb@latest/+esm | ❌ No | The SDK bundle itself is fine, but it imports /npm/[email protected]/+esm, and jsDelivr's Rollup/esbuild pipeline fails to bundle the wasm-bindgen glue: the generated vantadb_wasm.js contains import * as wasm from "./vantadb_wasm_bg.wasm" (wasm-pack bundler target — a binary imported as an ES module). Rollup cannot resolve it and jsDelivr serves a stub module that throws on import: "Failed to bundle using Rollup: failed to resolve an internal import". | | https://esm.sh/vantadb@latest | ✅ Yes | esm.sh's build inlines the .wasm binary as a base64 byte array into the served .mjs (no sidecar fetch needed), so the bundler-target glue initializes directly in the browser. |

<script type="module">
  // Verified working (wasm is inlined by esm.sh's build):
  import { Client } from "https://esm.sh/vantadb";
  const db = Client.create();
  await db.put({ namespace: "demo", key: "k", payload: "hello" });
  console.log((await db.get({ namespace: "demo", key: "k" }))?.payload); // "hello"
  db.close();
</script>

If you prefer self-hosting over third-party CDN transforms, rebuild the binding for browsers with wasm-pack build --target web (the web target uses fetch() + WebAssembly.instantiateStreaming instead of the ES-module import) and serve the output files yourself.

SSR / React hooks guidance:

  • Do not instantiate the engine during server rendering — the wasm loader needs browser APIs (fetch/WebAssembly) or the file system, neither of which is guaranteed in a server context.
  • Create the client lazily in client-only code: Client.create() inside a useEffect/useMemo (or a framework's client boundary), never at module top-level of an SSR-shared file.
  • On Node.js, prefer the native backend (NativeVantaDB) — it is loaded lazily via dynamic import() and gives real filesystem persistence (fjall/WAL) that the WASM build cannot.

Console logging

The WASM engine installs a console.log-based tracing subscriber at level WARN — DEBUG traces (e.g. the core's env-var config dump) are not printed by default. To opt into more detail, set the global before the first client is created:

globalThis.VANTADB_LOG = "debug"; // "trace" | "debug" | "info" | "warn" | "error"
const db = Client.create();

The value is read once per process (the subscriber is global); an absent or invalid value falls back to WARN.

Bundle size vs JavaScript-only competitors

Measured 2026-10-04 (VantaDB row; competitor rows as dated below). Reproducible: see ../vantadb-wasm/README.md §1.

| Library | Version | Gzipped | Vector | Hybrid | Persistence | |---------|---------|--------:|--------|--------|-------------| | @orama/orama | 3.1.18 | 23.8 KB | ✅ | ✅ RRF | ❌ (in-mem + plugin) | | MiniSearch | latest | 5.9 KB | ❌ | ❌ | ❌ | | Lunr | 2.3.9 | 8.1 KB | ❌ | ❌ | ❌ | | vantadb WASM | 0.8.x | ~739 KB transfer (1.77 MB raw wasm; measured 2026-10-04) | ✅ HNSW | ✅ BM25 + RRF | ✅ OPFS / IDB / in-mem |

VantaDB is ~31× larger than Orama gzipped, but ships OPFS persistence, HNSW (p99 441 µs at 100K), TTL auto-expiry, capability graph — features none of the JS-only engines include. Honest tradeoff: choose Orama (23.8 KB) if you only need full-text + RAG in-memory with no persistence; choose VantaDB if any of those features matter. Full feature-gap analysis: ../vantadb-wasm/README.md §4.

vantadb vs vantadb-node (npm)

Two npm packages exist; they are not the same thing:

| Package | What it is | Published | API | |---------|------------|-----------|-----| | vantadb | TypeScript SDK over the WASM build — works in browsers, Node, Bun, Deno | ✅ 0.8.0 | Synchronous, ESM-only | | vantadb-node | Native Node.js bindings (napi-rs) — real filesystem persistence (fjall/WAL/fsync), async API, platform-specific .node binaries | ❌ not yet published (registry 404) | Async, ESM + CommonJS |

vantadb-node is the native backend you reach via NativeVantaDB.connect() (lazy-loaded); vantadb is the WASM backend (Client.create()). See ADR-0030 (docs/dev/architecture/adr/ADR-0030-brand-identity-naming-convention.md) for the full naming convention across registries.

Errors

All operations throw VantaError, an Error subclass with a machine-readable code. The WASM binding attaches the code structurally (no message parsing required); older vantadb-wasm builds fall back to message classification.

| code | Meaning | |--------|---------| | NOT_FOUND | Node / namespace / record not found | | VALIDATION_ERROR | Invalid input, dimension mismatch, duplicate node, zero-norm cosine query, empty namespace… | | CORRUPT | Incompatible binary format, WAL version mismatch, serialization/schema error | | RESOURCE_LIMIT | Memory or configured resource limit exceeded | | TIMEOUT | Operation exceeded its time budget | | BUSY | Database busy (lock held) | | IO_ERROR | WAL / IO / backend storage error | | CLOSED | Operation on a closed instance | | WASM_ERROR | Unclassified engine error (catch-all) |

try {
  db.search({ namespace: "docs", query_vector: [0, 0, 0] });
} catch (err) {
  if (err instanceof VantaError) {
    console.log(err.code); // "VALIDATION_ERROR"
  }
}

Real Embeddings

The examples above use toy vectors. Generate real ones with your own client — local Ollama or the OpenAI API — and pass them to put() / search():

const res = await fetch("http://localhost:11434/api/embed", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ model: "nomic-embed-text", input: "Hello, world!" }),
});
const [vec] = (await res.json()).embeddings;

await db.put({ namespace: "memories", key: "greeting", payload: "Hello, world!", vector: vec });
const hits = await db.search({ namespace: "memories", query_vector: vec, top_k: 10 });
console.log(hits[0].record.payload); // "Hello, world!"

Use one embedding model per namespace — stored and query vectors must share the same dimensionality. Full walkthrough: QUICKSTART → Real Embeddings.

API

Lifecycle

| Method | Description | |--------|-------------| | VantaDB.create(config?) | Create in-memory or configured instance | | Client.open(path) | Open persistent store from disk | | .close() | Free WASM resources |

CRUD

| Method | Description | |--------|-------------| | .put(input) | Store a memory record | | .putBatch(inputs) | Batch store | | .get({namespace, key}) | Retrieve by key | | .delete({namespace, key}) | Remove by key | | .deleteByFilter({namespace, filter}) | Batch delete matching an AND-combined filter; returns count (rejects empty filter) | | .list({namespace, ...options}) | List with pagination | | .listNamespaces() | List all namespaces |

Search

| Method | Description | |--------|-------------| | .search(request) | Hybrid vector + text search | | .searchVector({vector, topK}) | Pure vector search | | .explainSearch(request) | Search with score breakdown |

Graph

| Method | Description | |--------|-------------| | .insertNode(id, content?, vector?, fields?) | Create a graph node | | .getNode(id) | Get node with edges | | .deleteNode(id, reason?) | Remove node | | .addEdge(source, target, label?, weight?) | Create edge | | .graphBfs(roots, maxDepth?) | BFS traversal | | .graphDfs(roots, maxDepth?) | DFS traversal | | .graphTopologicalSort(roots) | Topological sort | | .graphIsDag(roots) | Check if DAG |

Maintenance

| Method | Description | WASM caveat | |--------|-------------|-------------| | .flush() | Flush WAL to storage | Engine buffers only — not a durability guarantee; browser persistence is via OPFS/IDB (WASM bundle & lazy loading), real WAL/fsync on the native backend | | .compactWal() | Compact WAL | WASM: engine-internal WAL — same durability caveat as .flush() | | .purgeExpired() | Remove TTL-expired records | — | | .rebuildIndex() | Rebuild ANN index | — | | .compactLayout() | Compact storage layout | — | | .operationalMetrics() | Get runtime metrics | — | | .capabilities() | Get build capabilities | — |

Export / Import

| Method | Description | WASM caveat | |--------|-------------|-------------| | .exportNamespace(path, namespace) | Export a namespace to JSONL | Not supported on the WASM runtime — throws IO error: operation not supported on this platform (no std::fs) | | .exportAll(path) | Export all namespaces to JSONL | Not supported on the WASM runtime — same platform IO error | | .importRecords(records) | Import records from an array | — (no filesystem involved) | | .importFile(path) | Import records from a JSONL file | Not supported on the WASM runtime — same platform IO error; use .importRecords() for array input |

FS-backed export/import needs a filesystem-backed runtime, which the WASM build cannot reach (tracked as FIND-79; a native/WASI wiring is deferred). For file JSONL round-trips today use vanta-cli export / vanta-cli import or the Python SDK (PYTHON_SDK.md); .importRecords() covers in-memory import. Backend split: vantadb vs vantadb-node.

Text Index

| Method | Description | |--------|-------------| | .auditTextIndex(namespace?) | Audit text index integrity | | .auditTextIndexDeep(namespace?) | Deep structural text index audit | | .repairTextIndex() | Repair text index from canonical storage |

Utilities

| Method | Description | |--------|-------------| | .query(iqlQuery) | Execute IQL query | | .generateSnippet(payload, query, withHighlighting?) | Generate highlighted text snippet |

Cross-SDK Search Parity

VantaDB exposes the same search capabilities across bindings, with aligned names in both SDKs. The canonical method→domain map lives in docs/api/BINDINGS_NAMESPACES.md.

| Capability | TypeScript SDK | Python SDK | |---|---|---| | search() meaning | Hybrid search (vector + text) → returns SearchHit[] | Hybrid memory search (vector + text) → returns SearchHit[] | | Hybrid (vector + text) | search({ namespace, query_vector, text_query }) | search(namespace, query_vector, text_query=...) | | Pure vector ANN | searchVector(vector, topK?) | search_vector(vector, top_k=10) | | Namespace scoping | search({ namespace }) | search(namespace, ...) | | Filters | search({ filters }) | search(namespace, query_vector, filters=...) | | top_k | search({ top_k }) / searchVector(v, topK) | search(..., top_k=) / search_vector(..., top_k=) | | distance_metric | search({ distance_metric: "Cosine"/"Euclidean" }) | search(..., distance_metric="cosine"/"euclidean") | | text_query | search({ text_query }) | search(..., text_query=...) | | Explain | search({ explain }) + explainSearch() | search(..., explain=True) + explain_memory_search() | | Batch search | — | search_batch(vectors) / search_batch_requests(requests) — Python-only | | Hybrid method / profile override | — | search(..., method=...) — Python-only |

Porting note: both SDKs use search() for hybrid retrieval and a distinct searchVector() / search_vector() for pure vector ANN (AST-008 aligned). method= (dense-index override) is Python-only.

Domain Sub-clients

Every flat method is also reachable through a domain sub-client: db.memory.*, db.graph.*, db.wiki.*, db.system.*. Sub-clients are pure organizational sugar over the flat API — each call forwards verbatim to the flat method of the same behavior.

Backward-compat guarantee: the flat API is unchanged. db.memory.put(x) and db.put(x) are the same call; existing code keeps working as-is. Canonical method→domain map: docs/api/BINDINGS_NAMESPACES.md.

// memory — namespace+key records, search, TTL
await db.memory.put({ namespace: "docs", key: "intro", payload: "...", vector: [0.1, 0.2] });
const hits = await db.memory.search({ namespace: "docs", query_vector: [0.15, 0.25], top_k: 5 });
await db.memory.purgeExpired();

// graph — node/edge CRUD + traversals (traversals use short names)
const node = await db.graph.getNode(42);
const reachable = await db.graph.bfs([42], 3);
const order = await db.graph.topologicalSort([1, 2, 3]);
if (await db.graph.isDag([1, 2])) { /* safe to topologically sort */ }

// wiki — node archive recovery (summary/archive lifecycle)
const recovered = await db.wiki.recoverArchivedNodes(42);

// system — lifecycle, metrics, IQL, maintenance, import/export
console.log(await db.system.capabilities());
const result = await db.system.query("(match (node :content \"rust\") (return node))");
await db.system.flush();

Notes:

  • Sub-clients are lazy, frozen (Readonly) singletons — accessing db.graph twice returns the same object.
  • conversation / skills sub-clients do not exist yet; their capabilities live in the core crate only. Wiki slice 1 (recoverArchivedNodes) landed in TS-11; the remaining wiki / conversation / skills surfaces are tracked with exact dependencies and promotion criteria in the Sub-Client Roadmap.
  • Python exposes the equivalent grouping via db.memory, db.graph, db.system, db.wiki — see PYTHON_SDK.md → Domain Sub-clients.

Runtimes

| Runtime | Status | |---------|--------| | Node.js 22.12+ | ✅ (ESM + require() via require(esm)) | | Node.js 18–22.11 | ⚠️ ESM only — use import | | Bun | ✅ | | Deno | ✅ | | Browser | ✅ (ESM) |

Examples

See the examples/ directory:

License

Apache 2.0