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

verso-db

v0.8.1

Published

Embedded TypeScript vector database for Bun, Node.js, and browsers, with portable HNSW search and OPFS persistence.

Readme

Verso

Verso is an embedded TypeScript vector database for Bun, Node.js, and browser applications. Its stable API is intentionally small:

VectorDB -> Collection<TMetadata> -> records and search

HNSW, storage backends, workers, WAL, and quantization are advanced extension surfaces. They are not part of the root package contract.

Install

bun add verso-db
# or
npm install verso-db

Open, write, search

import { VectorDB, type CollectionConfig } from 'verso-db';

type Document = {
  title: string;
  category: 'tech' | 'science';
  score: number;
  tags: string[];
};

const db = await VectorDB.open({ path: './vectors' });
const config: CollectionConfig<Document> = {
  vector: { dimensions: 768, metric: 'cosine' },
  index: { type: 'hnsw', profile: 'balanced' },
};
const documents = await db.createCollection<Document>('documents', config);

await documents.insert([
  {
    id: 'doc-1',
    vector: embedding,
    metadata: {
      title: 'HNSW explained',
      category: 'tech',
      score: 0.94,
      tags: ['vector', 'search'],
    },
  },
]);

const result = await documents.search({
  vector: queryEmbedding,
  limit: 10,
  filter: {
    $and: [
      { category: { $in: ['tech', 'science'] } },
      { score: { $gte: 0.8 } },
    ],
  },
});

for (const match of result.matches) {
  console.log(match.id, match.score, match.metadata?.title);
}

await db.close();

distance is metric-native and lower is always better. score is the stable application-facing value and higher is always better: cosine uses similarity, while Euclidean and dot-product scores are the negated metric distance.

Collection operations

await documents.upsert(records);
await documents.insertPacked({ ids, vectors: packedFloat32, metadata });
await documents.import(recordsAsyncIterable, { mode: 'upsert', batchSize: 2_000 });

const record = await documents.get('doc-1', {
  include: { vector: true },
});
const records = await documents.getMany(['doc-1', 'doc-2']);
const page = await documents.list({ limit: 100, cursor: pageCursor });

for await (const record of documents.scan({ filter })) {
  // export or inspect records without a large array allocation
}

await documents.update('doc-1', {
  metadata: { score: 0.97 }, // shallow merge
});
await documents.delete({ ids: ['doc-2'] });
await documents.delete({ filter: { category: 'science' } });

insert rejects duplicate IDs. upsert replaces the supplied vector and metadata. update merges metadata and optionally replaces the vector. Deletes are tombstones until compact() physically rebuilds the index.

Filters

Filters are strict and support equality, comparisons, membership, existence, array membership, prefixes, ranges, nested dotted paths, and Boolean composition:

import { where } from 'verso-db';

const filter = where.and(
  where.in('category', ['tech', 'science']),
  where.gte('score', 0.8),
  where.contains('tags', 'vector'),
);

Supported operators are $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $contains, $containsAny, $containsAll, $startsWith, $between, $and, $or, and $not. Comparisons never coerce strings and numbers. Missing fields differ from explicit null; $ne and $nin match a missing field, while positive comparisons do not. Metadata must be plain, finite, JSON-compatible values.

Search strategies and diagnostics

The ordinary API is independent of HNSW tuning:

const exact = await documents.search({
  vector: queryEmbedding,
  limit: 10,
  strategy: 'exact',
});

const highRecall = await documents.search({
  vector: queryEmbedding,
  limit: 10,
  accuracy: 'high',
  tuning: { efSearch: 256, quantization: 'auto', oversampling: 4 },
  explain: true,
});

const many = await documents.searchMany([
  { vector: queryA, limit: 10 },
  { vector: queryB, limit: 10 },
], { concurrency: 'auto' });

Verso intentionally stops at vector retrieval. Full-text indexing, hybrid fusion, query expansion, and model reranking belong in the application search layer. Exact-vs-approximate recall comparison is available from the explicit verso-db/advanced subpath for tuning and regression tests.

Lifecycle, persistence, and operations

VectorDB.open() is asynchronous and discovers corruption or unavailable persistence before returning. Node and Bun use filesystem storage; browsers use OPFS when available. A requested persistent backend fails closed by default. Use { storage: { type: 'opfs', fallback: 'memory' } } only when an explicit in-memory fallback is acceptable.

const info = await documents.describe();
const stats = await documents.stats();
const report = await documents.verify();
const compacted = await documents.compact({ onProgress: console.log });

await db.setAlias('documents-current', 'documents');
const current = await db.collection('documents-current');
const snapshot = await db.snapshot();
await db.restore(snapshot, { overwrite: true });
const collectionSnapshot = await documents.exportSnapshot();
await db.importCollectionSnapshot(collectionSnapshot, { name: 'documents-copy' });
await db.backupTo('./backups/documents');

Use durability: 'manual' for bulk ingestion and call flush() explicitly; the default is immediate persistence. write()/batch() provide a named mutation session for application workflows, while insertMany() and import()/export() support async iterables. namespace(value) creates a scoped handle over an indexed metadata field.

Package boundaries

The root export contains VectorDB, the generic Collection type, typed record/filter/search types, where, and structured errors. Advanced APIs are explicit:

import { HNSWIndex, ScalarQuantizer } from 'verso-db/hnsw';
import { MemoryStorage, defineStorageAdapter } from 'verso-db/storage';
import { WorkerPool, WriteAheadLog } from 'verso-db/advanced';

The root does not export concrete storage implementations, raw HNSW classes, WAL/worker protocols, or quantizer internals.

Development

bun install
bun test
bun run build
bun run verify:dist
bun run test:browser

Performance measurements are workload-specific. Use the bundled benchmark commands when making recall or latency claims.