Atomically

Zero-dependency atomic file reads and writes for Node.js, with automatic retries and crash-safe temp files.

Library
npm
v2.1.1
180stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
44/100Fair
Development Activity40
Maintenance12
Community44
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
62/100Good
Architecture72
Code Quality70
Innovation82
Learning Curve25

Atomically is a small TypeScript library that provides drop-in replacements for Node’s readFile/writeFile with atomicity and reliability guarantees write-file-atomic doesn’t fully offer. Writes go through a temporary file that is renamed into place only once fully flushed, so a crash mid-write can never leave a corrupted or partially-written file behind, and writes to the same path are automatically queued so concurrent callers never interfere with each other.

Beyond atomicity, the library retries a wide set of transient filesystem errors (EMFILE, ENFILE, EBUSY, EACCES, and more) until a configurable timeout elapses, works around ENAMETOOLONG by truncating paths safely, and ignores platform-specific chmod/chown quirks (ENOSYS, and EINVAL/EPERM for non-root POSIX users) that would otherwise cause spurious failures. An opt-in fsyncWait: false mode skips waiting on the fsync syscall for roughly 10x faster writes while remaining nearly as crash-safe.

With zero third-party runtime dependencies beyond its own stubborn-fs and when-exit helper packages, it is meaningfully smaller than write-file-atomic while adding more reliability behavior, making it a straightforward substitute wherever files need to be written safely from Node.js.

What You Get

  • Atomic writeFile/writeFileSync that write to a temp file and rename into place, so files are never left partially written
  • Automatic retrying of transient errors (EMFILE, ENFILE, EBUSY, EACCES, EAGAIN, EPERM) on both reads and writes until a configurable timeout
  • Per-path write queuing so concurrent writes to the same file never interfere with each other
  • A fsyncWait: false mode for roughly 10x faster writes while remaining nearly as crash-safe
  • Automatic parent-directory creation, mode/ownership inheritance from the previous file version, and safe ENAMETOOLONG path truncation
  • Zero third-party runtime dependencies beyond the author’s own stubborn-fs and when-exit helper packages

Common Use Cases

  • Config file persistence - writing app config or state files where a crash mid-write must never corrupt the file
  • CLI tools and build scripts - safely rewriting lockfiles, caches, or generated artifacts that other processes may read concurrently
  • Database-adjacent local storage - implementing simple file-backed stores or journals that need write durability guarantees
  • Drop-in replacement for write-file-atomic - migrating existing projects to a smaller, faster library with the same guarantees plus extra reliability handling

Under The Hood

Architecture The library is a single src/index.ts module exposing overloaded readFile/readFileSync/writeFile/writeFileSync functions, backed by three small internal utilities: Scheduler (a per-path promise queue ensuring writes to the same file never overlap), Temp (generates, truncates, and purges temporary file paths, registering a whenExit hook so orphaned temp files are cleaned up on process exit), and lang (small type-guard helpers). The async write path is a single long function that resolves the real path, snapshots the old file’s mode/ownership when not explicitly overridden, opens and writes the temp file, optionally fsyncs, closes, applies chown/chmod, and finally renames into place with an ENAMETOOLONG fallback — a flat, procedural design rather than layered abstractions, so swapping the underlying retry primitive would touch every call site directly since stubborn-fs is used inline rather than behind an interface.

Tech Stack Written in TypeScript, published as pure ESM ("type": "module"), with exactly two runtime dependencies: stubborn-fs (retryable wrappers around low-level fs syscalls) and when-exit (cross-platform process-exit hooks), both maintained by the same author as sibling packages. Build/dev tooling is tsex for compiling and benchmarking, esbuild to bundle a CJS test target, and tap as the test runner — there is no web framework, ORM, or database involved since this is a low-level Node.js filesystem utility.

Code Quality The test suite (three files totaling roughly 950 lines: basic, concurrency, and integration) uses tap with require-inject to mock the fs module and exercise nearly every error code path (ENOOPEN, ENOWRITE, ENOFSYNC, ENOCHOWN, ENOSYS, EINVAL, EPERM, ENORENAME) alongside real concurrency scenarios. Error handling is explicit and typed via an isException guard that checks for a code property on thrown errors rather than swallowing failures. Naming and formatting follow an unusual but consistent spaced style (function readFile ( filePath: Path, ... )) used throughout the author’s other packages. No linter configuration or CI workflow file is present in the repository, so quality enforcement relies on the type checker and the manually-run test suite rather than automated gating.

API Design The public surface is deliberately minimal — four functions mirroring Node’s native fs.readFile/writeFile naming and calling conventions, positioned explicitly as a drop-in replacement for write-file-atomic. Sensible defaults mean zero configuration is needed for the common case, while power-user options (fsyncWait, schedule, tmpCreate, tmpPurge) are available without cluttering the basic path. TypeScript overloads correctly infer a string vs Buffer return type based on whether an encoding was supplied, and the README documents every option in a clear table alongside runnable usage examples.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search