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.

Platform Supported
Linux glibc (x64 + arm64) yes
Linux musl / Alpine (x64 + arm64) yes
macOS yes
Windows no

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.