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

@sandsoftwaresolutions/json-store

v0.1.1

Published

A small JSON file store: atomic writes, ordered updates, rollback and versioned migrations. Never overwrites a file it does not understand.

Readme

@sandsoftwaresolutions/json-store

CI

A small database for the things that do not need a database: settings, state, a few records, in one JSON file. Safe writes, updates that never get lost, and migrations when the shape changes. No dependencies.

Install

npm install @sandsoftwaresolutions/json-store

Works with import and require(), and ships TypeScript types for both.

Usage

import { openStore } from "@sandsoftwaresolutions/json-store";

const store = await openStore({
  path: "/var/lib/myapp/state.json",
  defaults: { lastRun: 0, failures: 0 }
});

store.get();                                   // { lastRun: 0, failures: 0 } (a copy)
await store.update((state) => { state.failures += 1; });   // change in place...
await store.update((state) => ({ ...state, lastRun: Date.now() })); // ...or return a new value
await store.set({ lastRun: 0, failures: 0 });  // replace everything

What it takes care of

  • Atomic writes. The file is written to a temporary file, flushed, and renamed over the old one, so a crash or power cut never leaves half a file.
  • No lost updates. Updates run one after the other, in order, even when the callbacks are async. Fifty update calls at the same time give fifty changes.
  • Rollback. If your callback throws or the write fails, the data stays as it was.
  • What is in memory is what is on disk. The data goes through JSON on every change, so a Date becomes a string, undefined fields disappear and NaN/Infinity become null right away, not only after a restart. Map, Set and class instances are not supported.
  • Copies, not references. get() returns a copy, so changing it by accident changes nothing.
  • A private file. A new file is created with permissions 600, because stores tend to hold tokens. The permissions of an existing file are kept exactly.
  • It refuses to destroy what it does not understand. A corrupt file, a file that is not a store, or a file from a newer version of your program is never overwritten: you get an error that names the file. The same goes when a migration throws, whatever the error. A byte order mark at the start of the file is ignored.

Migrations

The file is { "version": 1, "data": { ... } }. When the shape of your data changes, raise version and add a migration:

const store = await openStore({
  path, version: 3, defaults: { first: "", last: "", tags: [] },
  migrations: {
    2: (old) => ({ first: old.name.split(" ")[0], last: old.name.split(" ").slice(1).join(" ") }),
    3: (old) => ({ ...old, tags: [] })
  }
});

Each migration upgrades from the version before it. The old file is kept as <path>.v1.bak before the first write of the new shape. A missing migration is an error at startup. backup: true also keeps <path>.bak of the previous content on every write.

Not for everything

The whole file is read once and written on every change. That is fine for kilobytes, not for megabytes or heavy traffic; for that use SQLite (@sandsoftwaresolutions/sqlite-kit). One process should own the file; for several, add @sandsoftwaresolutions/file-lock.