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

@fpisani/memvid

v1.3.1

Published

Node.js bindings for memvid-core - unlimited local AI memory

Readme

@fpisani/memvid

Node.js bindings for memvid-core - a single-file memory layer for AI agents.

Packages documents, embeddings, search indices, and metadata into a portable .mv2 file.

Installation

npm install @fpisani/memvid

Supported platforms:

  • Windows x64
  • macOS x64 (Intel)
  • macOS arm64 (Apple Silicon)
  • Linux x64

Quick Start

import { create, open, OpenAIEmbeddings } from '@fpisani/memvid';

// Create a new memvid file
const mem = create('/path/to/memory.mv2');

// Enable search indices
mem.enableLex();  // Full-text search
mem.enableVec();  // Vector similarity search

// Store documents
mem.put(Buffer.from('Hello world'), { title: 'Greeting' });
mem.put(Buffer.from('AI is transforming the world'), { title: 'AI Article' });
mem.commit();

// Text search
const results = mem.find('hello');
console.log(results.hits);

// Vector search with OpenAI embeddings
const embedder = new OpenAIEmbeddings({ apiKey: process.env.OPENAI_API_KEY! });
const queryVec = await embedder.embedQuery('artificial intelligence');
const vecResults = mem.vecSearch(queryVec, 5);
console.log(vecResults.hits);

// Clean up
mem.close();

API Reference

Core Functions

create(path: string): Memvid

Create a new memvid file.

const mem = create('/tmp/memory.mv2');

open(path: string): Memvid

Open an existing memvid file with an exclusive lock.

const mem = open('/tmp/existing.mv2');

openReadOnly(path: string): Memvid

Open an existing memvid file in read-only mode (shared lock) for concurrent readers.

const mem = openReadOnly('/tmp/existing.mv2');

openSnapshot(path: string): Memvid

Open an existing memvid file as a snapshot without acquiring a lock. This reads the last committed footer for a consistent view even if a writer is active. While snapshot readers are active, shrink operations (vacuum/rebuild/footer truncation) are deferred and the latest footer is appended.

const mem = openSnapshot('/tmp/existing.mv2');

version(): string

Get the memvid-node version.

console.log(version()); // "1.0.0"

Memvid Class

The main interface for working with .mv2 files.

Properties

| Property | Type | Description | |----------|------|-------------| | path | string | File path | | isClosed | boolean | Whether handle is closed |

Methods

close(): void

Close the handle and release resources.

mem.close();
stats(): Stats

Get file statistics.

const stats = mem.stats();
console.log(stats.frameCount);      // Total frames
console.log(stats.sizeBytes);       // File size
console.log(stats.hasLexIndex);     // Text search enabled
console.log(stats.hasVecIndex);     // Vector search enabled
put(content: Buffer, options?: PutOptions): number

Store a document. Returns the frame ID.

const frameId = mem.put(Buffer.from('Document content'), {
  title: 'My Document',
  uri: 'doc://unique-id',
  kind: 'article',
  labels: ['important', 'reviewed']
});
putWithEmbedding(content: Buffer, embedding: number[], options?: PutOptions): number

Store a document with a pre-computed embedding vector.

const embedding = await embedder.embedQuery('Document content');
const frameId = mem.putWithEmbedding(
  Buffer.from('Document content'),
  embedding,
  { title: 'My Document' }
);
putMany(documents, embedder?): Promise<PutManyResult>

Batch store multiple documents with optional auto-embedding.

const result = await mem.putMany([
  { content: 'First doc', options: { title: 'Doc 1' } },
  { content: 'Second doc', options: { title: 'Doc 2' } },
], embedder);

console.log(`Stored ${result.successCount}/${result.results.length}`);
console.log(result.frameIds); // [0, 1]
commit(): void

Persist all pending changes to disk.

mem.put(Buffer.from('content'));
mem.commit(); // Write to disk
enableLex(): void

Enable full-text search index. Must be called before using find().

mem.enableLex();
enableVec(): void

Enable vector similarity search index. Must be called before using vecSearch() or putWithEmbedding().

mem.enableVec();
find(query: string, options?: SearchOptions | number): SearchResult

Full-text search with optional filtering.

// Simple usage (just topK)
const results = mem.find('search query', 10);

// With filter options
const filtered = mem.find('AI', {
  topK: 10,
  uri: 'doc://specific',           // Exact URI match
  scope: 'doc://articles/',        // URI prefix match
  excludeFrameIds: [0, 1, 2],      // Exclude specific frames
  excludeUris: ['doc://skip-me'],  // Exclude specific URIs
});

for (const hit of results.hits) {
  console.log(hit.text, hit.score, hit.frameId);
}
vecSearch(queryEmbedding: number[], options?: SearchOptions | number): SearchResult

Vector similarity search with optional filtering.

const queryVec = await embedder.embedQuery('semantic query');

// Simple usage
const results = mem.vecSearch(queryVec, 5);

// With filter options
const filtered = mem.vecSearch(queryVec, {
  topK: 10,
  scope: 'doc://articles/',        // Only search within scope
  excludeFrameIds: [0, 1, 2],      // Exclude specific frames
  excludeUris: ['doc://skip-me'],  // Exclude specific URIs
});

for (const hit of results.hits) {
  console.log(hit.text, hit.score); // score = distance (lower is better)
}
timeline(options?: TimelineOptions): TimelineEntry[]

Get chronological view of frames.

const entries = mem.timeline({
  limit: 10,
  reverse: true,  // Newest first
  since: Date.now() - 86400000, // Last 24 hours
});

for (const entry of entries) {
  console.log(entry.timestamp, entry.preview);
}
view(frameId: number): string

Get frame content by ID.

const content = mem.view(0);
console.log(content);
frame(frameId: number): FrameInfo

Get frame metadata by ID.

const info = mem.frame(0);
console.log(info.id, info.title, info.timestamp);
delete(frameId: number): number

Soft delete a frame. Returns the deleted frame ID.

mem.delete(0);
mem.commit();
verify(deep?: boolean): boolean

Verify file integrity.

if (!mem.verify(true)) {
  console.error('File is corrupted!');
}

Embedding Providers

All providers implement the EmbeddingProvider interface:

interface EmbeddingProvider {
  embedQuery(text: string): Promise<number[]>;
  embedDocuments(texts: string[]): Promise<number[][]>;
  dimension: number;
}

OpenAIEmbeddings

import { OpenAIEmbeddings } from '@fpisani/memvid';

const embedder = new OpenAIEmbeddings({
  apiKey: process.env.OPENAI_API_KEY!,
  model: 'text-embedding-3-small', // default
  baseUrl: 'https://api.openai.com/v1', // optional
  timeoutMs: 30000, // optional
});

const embedding = await embedder.embedQuery('Hello world');
const embeddings = await embedder.embedDocuments(['doc1', 'doc2']);

Supported models:

  • text-embedding-3-small (1536 dimensions) - default
  • text-embedding-3-large (3072 dimensions)
  • text-embedding-ada-002 (1536 dimensions)

CohereEmbeddings

import { CohereEmbeddings } from '@fpisani/memvid';

const embedder = new CohereEmbeddings({
  apiKey: process.env.COHERE_API_KEY!,
  model: 'embed-english-v3.0', // default
});

Supported models:

  • embed-english-v3.0 (1024 dimensions) - default
  • embed-multilingual-v3.0 (1024 dimensions)
  • embed-english-light-v3.0 (384 dimensions)
  • embed-multilingual-light-v3.0 (384 dimensions)

VoyageEmbeddings

import { VoyageEmbeddings } from '@fpisani/memvid';

const embedder = new VoyageEmbeddings({
  apiKey: process.env.VOYAGE_API_KEY!,
  model: 'voyage-2', // default
});

Supported models:

  • voyage-2 (1024 dimensions) - default
  • voyage-large-2 (1536 dimensions)
  • voyage-code-2 (1536 dimensions)

MockEmbeddings

For testing without API calls:

import { MockEmbeddings } from '@fpisani/memvid';

const embedder = new MockEmbeddings({ dimension: 1536 });
const embedding = await embedder.embedQuery('test'); // Deterministic fake embedding

Types

PutOptions

interface PutOptions {
  title?: string;      // Document title
  uri?: string;        // Unique identifier
  kind?: string;       // Document type
  labels?: string[];   // Categorization labels
}

SearchOptions

interface SearchOptions {
  topK?: number;           // Max results to return (default: 10)
  uri?: string;            // Filter to exact URI match
  scope?: string;          // Filter to URI prefix
  excludeFrameIds?: number[];  // Exclude specific frame IDs
  excludeUris?: string[];  // Exclude specific URIs
}

SearchResult

interface SearchResult {
  totalHits: number;
  hits: SearchHit[];
  engine: string;      // 'Tantivy' or 'Vec'
  cursor?: string;     // For pagination
}

interface SearchHit {
  frameId: number;
  score?: number;      // Relevance (lex) or distance (vec)
  text: string;        // Matched snippet
  rangeStart: number;  // Byte range in content
  rangeEnd: number;
  title?: string;
  uri?: string;
}

Stats

interface Stats {
  frameCount: number;
  activeFrameCount: number;
  sizeBytes: number;
  payloadBytes: number;
  logicalBytes: number;
  savedBytes: number;
  compressionRatioPercent: number;
  savingsPercent: number;
  averageFramePayloadBytes: number;
  averageFrameLogicalBytes: number;
  vectorCount: number;
  hasLexIndex: boolean;
  hasVecIndex: boolean;
  hasClipIndex: boolean;
  hasTimeIndex: boolean;
}

TimelineOptions

interface TimelineOptions {
  limit?: number;      // Max entries to return
  since?: number;      // After this timestamp (Unix ms)
  until?: number;      // Before this timestamp (Unix ms)
  reverse?: boolean;   // Newest first
}

TimelineEntry

interface TimelineEntry {
  frameId: number;
  timestamp: number;   // Unix ms
  preview: string;     // Text preview
  uri?: string;
}

FrameInfo

interface FrameInfo {
  id: number;
  timestamp: number;   // Unix ms
  uri?: string;
  title?: string;
  kind?: string;
  payloadLength: number;
}

Error Handling

All errors extend MemvidError:

import {
  MemvidError,
  LexNotEnabledError,
  VecNotEnabledError,
  VecDimensionMismatchError,
  FrameNotFoundError,
  HandleClosedError,
  FileNotFoundError,
  InvalidFileError,
  CorruptedFileError,
  EmbeddingError,
} from '@fpisani/memvid';

try {
  mem.find('query');
} catch (error) {
  if (error instanceof LexNotEnabledError) {
    mem.enableLex();
    // retry
  } else if (error instanceof MemvidError) {
    console.error(`Error [${error.code}]: ${error.message}`);
  }
}

| Error Class | Code | Description | |------------|------|-------------| | LexNotEnabledError | LEX_NOT_ENABLED | Call enableLex() first | | VecNotEnabledError | VEC_NOT_ENABLED | Call enableVec() first | | VecDimensionMismatchError | VEC_DIM_MISMATCH | Embedding dimension mismatch | | FrameNotFoundError | FRAME_NOT_FOUND | Frame ID doesn't exist | | HandleClosedError | HANDLE_CLOSED | Handle was closed | | FileNotFoundError | FILE_NOT_FOUND | File doesn't exist | | InvalidFileError | INVALID_FILE | Not a valid .mv2 file | | CorruptedFileError | CORRUPTED_FILE | File is corrupted | | EmbeddingError | EMBEDDING_ERROR | Embedding API failed |


Examples

RAG Pipeline

import { create, OpenAIEmbeddings } from '@fpisani/memvid';

const embedder = new OpenAIEmbeddings({ apiKey: process.env.OPENAI_API_KEY! });
const mem = create('knowledge.mv2');
mem.enableLex();
mem.enableVec();

// Ingest documents
const docs = [
  { content: 'TypeScript is a typed superset of JavaScript.', title: 'TypeScript' },
  { content: 'Rust is a systems programming language.', title: 'Rust' },
  { content: 'Python is great for machine learning.', title: 'Python' },
];

for (const doc of docs) {
  const embedding = await embedder.embedQuery(doc.content);
  mem.putWithEmbedding(Buffer.from(doc.content), embedding, { title: doc.title });
}
mem.commit();

// Query with semantic search
const queryVec = await embedder.embedQuery('What language is good for ML?');
const results = mem.vecSearch(queryVec, 3);

console.log('Top matches:');
for (const hit of results.hits) {
  console.log(`- ${hit.title}: ${hit.text} (distance: ${hit.score})`);
}

mem.close();

Hybrid Search

// Combine text and vector search
function hybridSearch(mem: Memvid, query: string, embedder: EmbeddingProvider, topK = 10) {
  // Text search
  const lexResults = mem.find(query, topK);

  // Vector search
  const queryVec = await embedder.embedQuery(query);
  const vecResults = mem.vecSearch(queryVec, topK);

  // Combine and dedupe by frameId
  const seen = new Set<number>();
  const combined = [];

  for (const hit of [...lexResults.hits, ...vecResults.hits]) {
    if (!seen.has(hit.frameId)) {
      seen.add(hit.frameId);
      combined.push(hit);
    }
  }

  return combined.slice(0, topK);
}

License

Apache-2.0