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

libzaungast

v0.5.0

Published

Read-only, offline reader for the local Microsoft Teams cache (Chromium IndexedDB-on-LevelDB): decodes it into clean, structured, query-able data — conversations, messages, people, events, calls. No Graph API, no cloud, no credentials. Not affiliated with

Readme

libzaungast

Read-only, offline reader for the local Microsoft Teams cache. It decodes the new Teams client's on-disk store, a Chromium IndexedDB database layered on LevelDB, into clean structured data you can query directly: conversations, messages, people, calendar events, and calls. It needs no Graph API, no cloud, and no credentials, and it has zero runtime dependencies.

libzaungast is the data layer behind the zaungast MCP server, and it is equally usable on its own as a library.

Not affiliated with or endorsed by Microsoft.

Install

npm install libzaungast

Requires Node.js 22.5 or newer, since it uses the built-in node:sqlite.

Quick start

import { openStore } from 'libzaungast';

using store = openStore('/path/to/https_teams.microsoft.com_0.indexeddb.leveldb');

// Full-text search across every message in the cache.
const found = store.messages.search({ query: 'release plan', limit: 10 });
if (found.ok) {
  for (const hit of found.rows) {
    console.log(new Date(hit.ts).toISOString(), hit.senderName, hit.snippet);
  }
}

// Read the most recent messages in a chosen conversation.
const chat = store.conversations.list({ n: 1 })[0];
if (chat) {
  const recent = store.messages.inConversation(chat.id, { limit: 20 });
  if (recent.ok) {
    for (const m of recent.rows) console.log(m.senderName, m.content);
  }
}

For a long-running process, openLiveStore returns a handle that refreshes as the cache changes on disk. Its incrementalMode chooses how each refresh re-reads the store. The default copy-reuse reuses the immutable table parses and re-reads only the write-ahead log, while reparse re-reads everything each time. Both produce identical results.

import { openLiveStore } from 'libzaungast';

using live = openLiveStore({
  dir: '/path/to/https_teams.microsoft.com_0.indexeddb.leveldb',
  incrementalMode: 'reparse', // default is 'copy-reuse'
});

// current() returns the latest reading; the handle refreshes in the background.
console.log(live.current().meta.counts);

Public surface

libzaungast has four entry points, one for each audience. Everything else is internal and cannot be reached through the package's exports.

| Import | Audience | Description | | ------------------------ | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | libzaungast | Application and script authors | The high-level data API and the primary path for reading a Teams cache. You open a store from a leveldb directory and query six orthogonal namespaces that return clean typed rows, so you can read conversations and messages or run full-text search without ever handling the underlying byte format. | | libzaungast/web | Browser authors | The browser-safe subset of the data API: the same static query namespaces, but reading from an in-memory SnapshotSource on an injected SQLite driver instead of the filesystem and node:sqlite. Runs the whole pipeline client-side with no Node builtins. See Browser usage. | | libzaungast/format | Power users and tooling | The decode and schema layer that sits beneath the data API. It exposes the Chromium byte readers, the structured-clone value decoder, the content fingerprinting, and the schema description utilities, for callers who need to inspect or reinterpret the raw store rather than the resolved rows. | | libzaungast/engine-spi | Engine authors | The service provider interface for implementing an ingest engine. It hands an implementer the contract types, the schema inputs, and a factory for wrapping a finished store, so an alternative engine such as a native accelerator can be built in a separate package and injected without libzaungast ever depending on it. |

The data API: libzaungast

This is the default import and the primary way to read a cache. openStore(dir) performs a one-shot read and returns a handle, while openLiveStore() returns an auto-refreshing handle for a long-running process. Either handle exposes the same six query namespaces. The conversations namespace lists and resolves chats, channels, and meetings together with their participants and activity. The messages namespace reads the messages inside a conversation, walks threads, runs full-text search, and does point lookups. The people namespace is the directory of senders and participants resolved from the cache. The events namespace surfaces calendar events and meetings, the calls namespace surfaces call history with its direction, duration, and recordings, and the topics namespace computes the distinctive trending phrases over a time window. Namespaces return plain typed rows such as Conversation, Message, Person, CalendarEvent, Call, and Topic, and fallible lookups return an explicit result object carrying an ok flag rather than failing silently. The companions tryOpen and inspect let you check a directory before committing to a full open, and htmlToText renders Teams rich-text bodies to plain text.

The browser entry: libzaungast/web

The same static query facade, made to run in the browser. Instead of a directory path you build a MemorySource from the leveldb files' bytes (from a folder <input> or the File System Access API), and instead of node:sqlite you inject a SqlDriver backed by a WebAssembly SQLite build such as @sqlite.org/sqlite-wasm (which ships FTS5, so full-text search works). openStoreFromSource(source, { driver }) then returns the same TeamsStore handle — conversations, messages, people, events, calls, topics — minus live-refresh. libzaungast carries no browser dependency; the wasm SQLite build is yours to choose and bundle. A reference driver lives at examples/sqlite-wasm-driver.ts. Full walkthrough: Browser usage.

The format layer: libzaungast/format

This is the decode and schema layer beneath the data API, intended for power users and tooling. It exposes the raw building blocks that turn bytes into rows: the Chromium IndexedDB and LevelDB readers, the Blink structured-clone value decoder, the content fingerprinting, and the schema description utilities. Its libzaungast/format/engine sub-path adds the storage engine seam types for callers who add or reinterpret a storage format. Most consumers never touch this layer, because the data API is built on top of it and hands back finished rows.

The engine SPI: libzaungast/engine-spi

This is the service provider interface for engine authors. An ingest engine reads a leveldb directory and produces the queryable store, and the built-in TypeScript engine is the default. This subpath provides the contract types, the schema inputs such as the DDL and the bundled schema mappings, a conformance version to verify against, and a factory for wrapping a finished store file, so an alternative engine such as a native Rust accelerator can be implemented in a separate package and injected through the engine option on openStore or openLiveStore. libzaungast never depends on any such engine; the consumer chooses one and passes it in.

Guarantees

The reader only ever reads or copies the Teams directory. It never writes to it, never locks it, and never memory maps it, so it cannot corrupt the store. It is fully offline with no network, no Graph API, and no credentials, and it carries zero runtime dependencies. Media surfaces as metadata markers and never as bytes.

Before version 1.0 the entry-point split and the error contract are stable, while row and query details may still change.

License

Apache-2.0. Part of the zaungast monorepo.