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

distillate

v0.2.0

Published

Probabilistic data structures for JavaScript. Space-efficient, approximate-membership filters (Bloom, Blocked Bloom, Binary Fuse) with tunable error and a portable binary format; zero dependencies, universal.

Readme

distillate

CI npm license

Probabilistic data structures for JavaScript: space-efficient, approximate answers with tunable error and zero false negatives. TypeScript-first, zero dependencies, and the right structure per workload. It opens with a family of membership filters (the next-generation successor to Bloom filter packages) and is built to grow into other sketches.

Pre-release (0.x). The published structures are correct, tested, and benchmarked, but the public API may still change before 1.0, and more structures (Cuckoo, Scalable Bloom) are on the way. Pin a version if you depend on it.

Why

An approximate-membership query (AMQ) filter answers "is this in the set?" with a tunable false-positive rate and zero false negatives, in a fraction of the space of storing the set itself.

  • Runs anywhere: Node, Bun, Deno, browsers, and Cloudflare/Vercel edge. No eval, no required WASM compile.
  • Correct: no false negatives (property-tested); false-positive rates validated against theory.
  • Small: per-structure subpath imports, sideEffects: false, zero runtime dependencies.
  • Portable: a versioned little-endian binary format (toBytes / fromBytes) for persistence and cross-language reads.

Install

npm install distillate
# or: pnpm add distillate / bun add distillate / deno add npm:distillate

Requires Node 20+ (or any modern Bun/Deno/browser/edge runtime).

Runtime support

distillate targets ES2022 with zero runtime dependencies and no eval, so it runs on every modern JavaScript runtime:

  • Node.js 20, 22, 24 (LTS and current)
  • Bun and Deno
  • Browsers and Cloudflare/Vercel edge

Every push runs a CI smoke matrix that imports the built package on Node 20/22/24, Bun, and Deno, so cross-runtime support is verified, not assumed.

Structures

Each structure ships as its own subpath, so you only bundle what you import.

| Import | Structure | Mutable? | Use for | | -------------------- | ------------- | -------- | ---------------------------------------------------- | | distillate/bloom | Classic Bloom | yes | Familiar default, migration from bloom-filters | | distillate/blocked | Blocked Bloom | yes | Streaming inserts, speed-first, cache-friendly | | distillate/fuse | Binary Fuse | no | Static set built once and queried a lot; least space |

Classic Bloom (distillate/bloom)

import { BloomFilter } from "distillate/bloom";

const filter = BloomFilter.create(100_000, 0.01); // capacity, target FPR
filter.add("alice");
filter.has("alice"); // true
filter.has("bob"); // false (or a ~1% false positive)

const bytes = filter.toBytes();
const restored = BloomFilter.fromBytes(bytes);

Also: union(other) (merge equal-parameter filters), bitsPerKey, and a low-level new BloomFilter({ m, k, seed }).

Blocked Bloom (distillate/blocked)

import { BlockedBloomFilter } from "distillate/blocked";

const filter = BlockedBloomFilter.create(100_000, 0.01);
filter.add("alice");
filter.has("alice"); // true

Same surface as Classic Bloom (add / has / union / toBytes / fromBytes / bitsPerKey). Confines every lookup to one cache line, trading ~20-30% more space for cache-friendly throughput.

Binary Fuse (distillate/fuse)

A static filter: built once from the full key set, then immutable. The most space-efficient option (~9 bits/key at ~0.39% FPR for 8-bit; ~19 bits/key at ~1/65536 for 16-bit).

import { BinaryFuse8, BinaryFuse16 } from "distillate/fuse";

const filter = BinaryFuse8.from(["alice", "bob", "carol"]);
filter.has("alice"); // true
filter.size; // 3
filter.bitsPerKey; // ~9

// Lower false-positive rate, twice the space:
const precise = BinaryFuse16.from(["alice", "bob", "carol"]);

Also: toBytes / fromBytes. No add / delete; rebuild from the new set to change membership.

Performance

Classic Bloom head-to-head at a matched 1% false-positive rate over the same 100k keys, measured by identical code (Node, Apple M5):

| Classic Bloom | bits/key | measured FPR | has throughput | | -------------- | -------- | ------------ | ---------------- | | distillate | 9.59 | 1.03% | ~7.0 M ops/s | | bloom-filters | 9.59 | 0.99% | ~0.29 M ops/s |

Same space, same accuracy, ~24x the lookup throughput of bloom-filters (the package distillate replaces), while hashing UTF-8 bytes with MurmurHash3 so filters stay portable and cross-language readable.

These are a point-in-time snapshot on one machine. The full report (blocked/fuse, 1M capacity, the bloomfilter micro-package) and exactly how it is measured live in the distillate-bench repo: RESULTS.md, METHODOLOGY.md.

Docs

Design notes, the structure decision matrix, hashing, and the binary format live in docs/:

License

MIT © Akshay