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

process-wal

v1.5.1

Published

A zero-dependency write-ahead log for Node.js process-restart durability

Readme

process-wal

CI npm Node.js 22+ TypeScript Runtime dependencies Modules License: MIT OpenSSF Scorecard

Durability across Node.js process restarts — without SQLite, Redis, a broker, or native binaries. Pure TypeScript, zero runtime dependencies, one clear job: append accepted work before you acknowledge it, replay whatever was not checkpointed, compact when convenient.

import { createWal } from "process-wal";

const wal = createWal<{ jobId: string }>({ dir: "./data" });

const seq = wal.append({ jobId: "job-42" }); // persisted before this returns
acknowledgeJob(seq); // now it is safe to say "got it"

for (const entry of wal.replay()) {
  // on startup, redo what was not done
  await processJob(entry.value);
  wal.checkpoint(entry.seq);
}
docker restart my-service   # the process dies; its persistent disk survives

If a service acknowledges work before that work is recoverable, a restart can silently drop it. process-wal closes exactly that gap, and nothing else. It is intentionally local, synchronous, and single-writer. That smallness is the design boundary, not an incomplete queue.

Documentation

  • API — every method, option and error code
  • Examples — each method in the situation it exists for, and a complete service
  • Durability — what survives what, recovery on open, and the repair procedure
  • Benchmarks — measured on NTFS and ext4, with the methodology
  • Alternatives — when SQLite, an outbox or a broker is the better boundary
  • Internals — design decisions, cost profile, source layout

Install

Requires Node.js 22 or newer.

npm install process-wal

Published with provenance over OIDC, so the npm artifact is cryptographically linked to the commit and workflow that built it. No publish token exists in this repository.

How it works

Sequence diagram: work is appended and only then acknowledged; the side effect runs, and only then is the sequence checkpointed. Anything not checkpointed replays after a restart.

append, appendMany, checkpoint, compact, and close are synchronous by design. A successful append means the complete record reached the selected durability boundary before its sequence number was returned.

Durability model

A normal synchronous write reaches the kernel page cache. The cache outlives a process crash, a deploy, a container restart, or SIGKILL while the host stays up. It does not guarantee survival if the host loses power.

| Mode | Process restart | Host or power loss | Measured mean append | | ------------------------ | --------------- | ------------------------------------------ | -------------------: | | fsync: false (default) | Recoverable | Not guaranteed | 0.0027–0.0067 ms | | fsync: true | Recoverable | Requests an OS storage flush before return | 0.47–1.49 ms |

The page cache is a receptionist; storage is a fireproof safe. Handing over a note is fast and survives you leaving the building. Waiting for the safe costs more, but is the appropriate boundary if the building itself may fail.

Each range spans NTFS and ext4 on one machine and one device: the fsync: true cost triples between them, which is the point — that number is a property of your filesystem, not of this library. Methodology, percentiles and the full recovery contract are in benchmarks and durability.

At-least-once, not exactly-once

Append before acknowledging incoming work, then checkpoint only after its side effect succeeds. A crash between the two replays work that may already have happened, so consumers must be idempotent — deduplicate on an identity from your payload, never on the sequence number. Exactly-once delivery needs transactional coordination with the downstream system and is deliberately outside this library.

How it compares

| | Survives process restart | Survives host power loss | Dependencies | What you still have to build | | -------------------------------- | :----------------------: | :----------------------: | ----------------- | ---------------------------------------------------------------------------------- | | process-wal | ✅ | opt-in via fsync | none | nothing — append/replay/checkpoint is the API | | node:sqlite | ✅ | opt-in via pragma | none (in-runtime) | schema, insert/delete cycle, checkpoint semantics, compaction, torn-write recovery | | better-sqlite3 | ✅ | opt-in via pragma | native binding | the same, plus a build toolchain and prebuild matrix | | lowdb, steno, node-persist | ⚠️ last full write only | ❌ | 1–3 | append semantics, replay, checkpointing — these rewrite whole documents | | BullMQ, RabbitMQ, Kafka | ✅ | ✅ | a running service | operating that service |

When it fits

Anywhere a single process accepts work it must not lose across its own restart, on storage that outlives that restart: a webhook receiver surviving deploys, an ingestion buffer flushing downstream, a desktop app holding drafts and pending uploads, a resumable CLI or ETL, an edge agent buffering telemetry through a flaky uplink.

When not to use it

  • Serverless or otherwise ephemeral filesystems.
  • Multiple processes writing to the same WAL directory.
  • Replicated or distributed durability.
  • Work that requires transactions, queries, priorities, leases, or routing.
  • A system that already owns the relevant data in a transactional database — use an outbox in that database instead.

Development

npm ci
npm run lint
npm test
npm run coverage
npm run bench
npm run lint:package

CI runs lint, coverage, package validation, and the durability suite on Node 22 and 24 across Linux, macOS, and Windows, plus one leg on the current Node release. Package validation installs the packed tarball and imports it, rather than only inspecting it. A release is a merged version bump followed by a vX.Y.Z tag, which publishes to npm over OIDC.

process-wal is stable at 1.x, so the API and the durability contract above are a commitment: breaking either is a major version, not a patch.

License

MIT