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

@routier/browser-storage-plugin

v0.3.0

Published

Dexie plugin for routier

Readme

@routier/browser-storage-plugin

Routier storage backed by a DOM Storage object — localStorage, sessionStorage, or any object that implements the same interface.

import { DataStore } from "@routier/datastore";
import { BrowserStoragePlugin } from "@routier/browser-storage-plugin";

class AppStore extends DataStore {
  constructor() {
    super(new BrowserStoragePlugin("my-database", localStorage));
  }
}

The plugin takes the Storage object as an argument instead of reading a global. Pass sessionStorage for per-tab data, or a fake implementation in tests.

Contracts

Durability

localStorage survives a page reload and a browser restart. sessionStorage survives a reload and ends with the tab. The browser can evict either one under storage pressure.

Each collection is one storage key, named <database>__<collection>. A save serializes the whole collection into that key.

Tab boundary — single writer

Concurrent writers across tabs are not supported. Two tabs that write one database are independent read-modify-write owners of the same key, and the last save wins the whole collection. The plugin does not detect the conflict.

After the first read, the in-memory view is authoritative and storage is write-only. Rows another tab writes are never observed by this one.

If you need several tabs to write, elect one writer — a SharedWorker, a leader-election lock, or a BroadcastChannel protocol you own — and route every write through it.

Concurrency within one tab

Safe. One collection instance per (Storage object, database name, collection name) is shared process-wide, so every writer in the tab mutates the same view and each save is a superset of the last.

Quota

localStorage gives about 5 MB per origin, shared with everything else on that origin. The plugin stores compact JSON, not pretty-printed. A quota overflow fails the save with the browser's QuotaExceededError; nothing is written and the previous value stays.

Storage is synchronous, so a large collection blocks the main thread on every save. Use @routier/dexie-plugin for datasets above a few thousand rows.

Schema migration

None. The plugin stores whole entities as JSON. A schema change that renames or removes a property does not rewrite what is already stored, so read old values and write them back yourself if you need them converted.

Corrupt values

If a storage key does not hold valid JSON, every read of that collection fails with an error that names the key. The plugin never discards a value it cannot parse. Resetting to empty would turn unreadable data into deleted data.

To recover, inspect the key and remove it. The next read then starts from an empty collection.

An empty-string value is a valid empty collection, not corruption.

Disposal

Call store.destroyAsync() to remove this database's keys. The plugin holds no handles or timers, so a store that is never destroyed leaks nothing.

Failure semantics

A failed save leaves the stored value unchanged. The plugin reports the browser's error — quota, a security exception in a blocked third-party context, or a value that cannot be serialized.

Supported versions

Any browser with Storage. Node 18 or later with a Storage implementation supplied by the caller.

See also