snapdir for Node.js
The @snapdir/snapdir package wraps the same Rust core (snapdir-api → snapdir-core) that powers the CLI, so the manifests and snapshot IDs it
produces are bit-identical to snapdir id, snapdir push, and every other
binding. The surface is idiomatic Node.js: synchronous helpers plus
Promise-returning async operations that never block the event loop.
Install
npm install @snapdir/snapdir
Prebuilt, zero build tools. The package ships precompiled native binaries
for Linux (glibc + musl) and macOS on x64 and arm64. There is no source build:
no Rust toolchain, no C compiler, no post-install step. The loader selects the
correct .node binary for your platform, so it runs on Alpine/musl out of the
box.
Usage
The store URI is always an argument, never an environment variable — use
file://$PWD/store for a no-setup local store, or gs://bucket/prefix for
Google Cloud Storage.
import { id, manifest, push, pull, fetch, diff, SnapdirError } from '@snapdir/snapdir'
const store = `file://${process.cwd()}/store`
// Snapshot ID — pure and deterministic, no store touched.
const snapshotId = await id('./my-dir')
console.log(snapshotId) // 64-char lowercase hex
// Full manifest: per-entry path, checksum, and size. size is always bigint.
const m = await manifest('./my-dir')
for (const entry of m.entries) {
console.log(entry.path, entry.size) // size: bigint (u64)
}
// Push to the store; returns the same 64-hex ID.
const sid = await push('./my-dir', store)
// Fetch into the local cache, or pull + materialize into a directory.
await fetch(sid, store)
await pull(sid, store, './restored')
// Diff two stores: { from, to } are arrays of store URIs.
// A pinned snapshot reference uses the store@id convention — the last '@'
// splits the store URI from the 64-hex snapshot ID.
const entries = await diff({ from: [store], to: [`${store}-next`] })
for (const e of entries) {
console.log(`${e.status}\t${e.path}`) // A / D / M / = — byte-identical to the CLI
}
For Google Cloud, swap the store URI for a gs:// URL:
const sid = await push('./my-dir', 'gs://my-bucket/snapshots')
Errors
Every failure is a SnapdirError (extends Error) whose .code is one of the
eight stable SCREAMING_SNAKE_CASE codes shared across all bindings and the
CLI.
import { push, SnapdirError } from '@snapdir/snapdir'
try {
await push('./missing', 'file://$PWD/store')
} catch (err) {
if (err instanceof SnapdirError) {
switch (err.code) {
case 'IO_ERROR': break // filesystem read/write failure
case 'HASH_MISMATCH': break // content did not match its expected checksum
case 'STORE_ERROR': break // object-store transport/backend failure
case 'IN_FLUX': break // a file changed while being read
case 'CATALOG_ERROR': break // manifest/catalog could not be parsed
case 'INVALID_ID': break // snapshot ID is not 64-hex
case 'INVALID_STORE': break // store URI is malformed or unsupported
case 'CONFLICT': break // a concurrent write conflicted
}
console.error(err.code, err.message)
}
}
Platform support
Every binding is CI-verified against its live public registry (npm) before release.
The Node.js binding runs on both glibc and musl. The only binding-wide exception to musl support is Java, which is glibc-only today (musl planned for 1.11.1); the Node.js binding has no such caveat. Windows is unsupported — snapdir is Unix-only.