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

@bfc-node/core

v0.2.0

Published

Node.js bindings for BFC — a single-file container format with POSIX metadata, compression and encryption

Readme

bfc-node

CI npm License

Node.js bindings for BFC — a single-file container format with POSIX metadata, ZSTD compression and ChaCha20-Poly1305 encryption.

Installation

npm install @bfc-node/core

That is the whole setup. You do not need Git, CMake, a C or C++ compiler, Python, node-gyp, the BFC sources, or ZSTD/libsodium development headers. The package ships prebuilt binaries and picks the right one for your platform; ZSTD and libsodium are linked statically into the addon.

Quick start

import { createArchive, openArchive } from "@bfc-node/core";

await createArchive({
  output: "./frontend.bfc",
  inputs: ["./dist"],
});

const archive = await openArchive("./frontend.bfc");
const entries = await archive.list();

await archive.close();

Supported platforms

| Platform | Architecture | libc | Package | | -------- | ------------ | ----- | ------- | | macOS | arm64 | — | @bfc-node/darwin-arm64 | | Linux | x64 | glibc | @bfc-node/linux-x64-gnu | | Linux | arm64 | glibc | @bfc-node/linux-arm64-gnu | | Windows | x64 | — | @bfc-node/win32-x64-msvc |

Requires Node.js 18.17 or newer. The addon is built against Node-API 8, so it keeps working across Node major versions without a rebuild.

macOS x64, Windows arm64, Linux musl and FreeBSD are planned. The loader already resolves those package names, so adding a target is a build-matrix change rather than an API change. Until then those platforms raise UNSUPPORTED_PLATFORM — see building from source.

On Windows, extractAll creates symbolic links, which needs either Developer Mode or an elevated process. Archives without symlinks extract normally either way. POSIX permission bits are stored faithfully in the archive but cannot be fully applied to a Windows filesystem on extraction.

API

createArchive(options)

Recursively archives inputs, preserving POSIX permissions, nanosecond mtime, directories and symbolic links. Entries are added in sorted order, so two runs over an unchanged tree produce the same archive contents.

await createArchive({
  output: "./site.bfc",
  inputs: ["./dist"],
  compression: true,
  password: "correct horse",
  filter: (relPath) => !relPath.startsWith("node_modules"),
  onProgress: ({ path, entriesDone }) => console.log(entriesDone, path),
});

| Option | Default | Notes | | ------ | ------- | ----- | | output | — | Path of the archive to create. | | inputs | — | Files and directories to archive. | | baseDir | common parent of inputs | A single directory input becomes the base itself, so inputs: ["./dist"] stores index.html, not dist/index.html. A single file input uses its parent directory. | | blockSize | BFC default | Container block size in bytes. | | compression | false | true means { type: "zstd", level: 3, threshold: 64 }. | | password / key | none | Enables encryption. key must be exactly 32 bytes. Passing both is an INVAL error. | | followSymlinks | false | When true, links are stored as regular files. | | filter | none | Return false to skip an entry; a skipped directory is not descended into. | | onProgress | none | Called once per stored entry. |

Returns { path, entries, bytesWritten }.

openArchive(path, options?)

const archive = await openArchive("./site.bfc", { password: "correct horse" });

| Member | Description | | ------ | ----------- | | archive.path | Path the archive was opened from. | | archive.encrypted | Whether any entry is encrypted. | | list(prefix?) | All entries, optionally limited to a directory prefix. Recursive. | | stat(path) | One entry. Rejects with NOT_FOUND if absent. | | read(path, { offset?, length? }) | Entry content as a Buffer. | | extract(path, destPath) | Write one entry to disk, creating parent directories. Validates CRC. | | extractAll(destDir, { prefix? }) | Restore entries, recreating directories, symlinks, modes and timestamps. | | verify({ deep? }) | Check the index, or with deep the content checksums too. | | close() | Release the file handle. |

openWriter(options)

Lower-level writing, when you want to add entries one at a time rather than walk a tree.

const writer = await openWriter({ output: "./out.bfc", compression: true });
await writer.addDir("assets", { mode: 0o755 });
await writer.addFile("assets/app.js", Buffer.from("console.log(1)"));
await writer.addFile("index.html", "./dist/index.html"); // path keeps mode and mtime
await writer.addSymlink("latest.html", "index.html");
await writer.finish();
await writer.close();

finish() writes the index and footer. Without it the archive cannot be opened.

buildInfo

import { buildInfo } from "@bfc-node/core";
console.log(buildInfo);
{
  packageVersion: '0.1.0',
  napiVersion: 8,
  bfcVersion: 'v1.3.0-2-gb3777f3',
  bfcCommit: 'b3777f3',
  platform: 'darwin',
  architecture: 'arm64',
  libc: null,
  compression: true,
  encryption: true
}

Everything except packageVersion is baked in when the binary is built, so it describes the binary you actually loaded rather than the machine you are running on.

Errors

Failures reject with an error named BfcError carrying a code, and a path where one applies. Use the exported isBfcError helper rather than instanceof: errors crossing the native boundary are tagged plain Error objects.

| Code | Meaning | | ---- | ------- | | BAD_MAGIC | Not a BFC container. | | IO | Filesystem or read/write failure. | | CRC | Checksum mismatch — content is corrupted. | | INVAL | Invalid argument, including a path that escapes the container root. | | EXISTS | Entry already present. | | NOT_FOUND | No such entry. | | PERM | Missing decryption key. | | CLOSED | Handle already closed. | | UNSUPPORTED_PLATFORM | No prebuilt binary for this platform. |

Behaviour notes

Concurrency. BFC handles are not thread-safe, so operations on one Archive or Writer are serialised by a mutex held for the whole operation. Different handles run in parallel. Every heavy operation runs on the libuv thread pool and does not block the event loop.

Integrity. verify({ deep: true }) and extract() validate CRC32C and fail with CRC on corruption, compressed and encrypted entries included. When an archive is encrypted and no key was supplied, verify({ deep: true }) reports PERM: the content could not be checked, which is a different statement from finding it corrupt. read() does not validate — it supports arbitrary offsets, and a partial slice cannot be checked against the whole-object checksum. Use verify or extract when integrity matters.

Extraction safety. Archives are untrusted input. extractAll resolves every entry path before writing anything and rejects with INVAL if one would land outside the destination directory.

Determinism. createArchive sorts entries by path, so repeated runs over an unchanged tree store the same entries in the same order.

Building from source

Prebuilt binaries cover the platforms listed above. To build the addon yourself — for an unsupported platform, or to work on the bindings — see BUILDING.md.

Point the loader at your own binary with BFC_NODE_NATIVE_PATH=/path/to/bfc_node.node.

License

Apache-2.0. Prebuilt packages statically link Zstandard (BSD-3-Clause) and libsodium (ISC); see NOTICE.