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

@waelio/ustore

v1.0.13

Published

Universal Storage

Readme

uStore

Universal storage for browser, Vue, reactive, and server-side runtimes.

Join the chat at https://discord.gg/tBZ2Fmdb7E CI NPM version NPM monthly downloads NPM total downloads Donate

uStore gives you one small CRUD-style API across multiple storage backends so you can switch between browser state, in-memory state, Vue integrations, and server storage without rewriting the same get / set / remove plumbing over and over.

Why use uStore

  • One familiar API across multiple storage adapters
  • Works with both uStore.<adapter> and direct named exports
  • Browser-friendly adapters with SSR-safe fallbacks where applicable
  • Includes encrypted memory, config-backed storage, server-side Keyv storage, and a messaging bridge
  • Ships ESM, Node ESM, CommonJS, UMD, and TypeScript declarations

Install

pnpm add @waelio/ustore

Or with npm:

npm install @waelio/ustore

The server entrypoint is included in the same package and is imported via @waelio/ustore/server.

Quick start

import { uStore } from "@waelio/ustore";

uStore.local.set("greeting", "hello");

const greeting = uStore.local.get("greeting");
const exists = uStore.local.has("greeting");

console.log(greeting); // "hello"
console.log(exists); // true

uStore.local.remove("greeting");

uStore is also attached to window.uStore in browsers and globalThis.uStore in non-browser runtimes.

Package entrypoints

| Import path | Purpose | | --- | --- | | @waelio/ustore | Main adapter exports, uStore, and the messaging bridge | | @waelio/ustore/server | Async server-side storage backed by Keyv |

Root exports

  • uStore
  • localStorage
  • sessionStorage
  • cookieStorage
  • memoryStorage
  • piniaStorage
  • vuexStorage
  • gunStorage
  • secureStorage
  • configStorage
  • signalStorage
  • createMessagingStore

Server exports

  • serverStorage
  • createServerStorage
  • createMongoServerStorage

Adapter overview

| Adapter | Access pattern | Runtime | Notes | | --- | --- | --- | --- | | local | uStore.local, localStorage | Browser / SSR-friendly | Local storage wrapper. | | session | uStore.session, sessionStorage | Browser / SSR-friendly | Session storage wrapper. | | cookie | uStore.cookie, cookieStorage | Browser / Node fallback | get() returns the raw key=value cookie entry. | | memory | uStore.memory, memoryStorage | Universal | Keyed in-memory storage. | | pinia | uStore.pinia, piniaStorage | Universal | Current compatibility layer behaves like an in-memory store. | | vuex | uStore.vuex, vuexStorage | Universal | Reads the latest committed value from a single slot. | | secure | uStore.secure, secureStorage | Universal | In-memory encrypted values via waelio-utils. | | config | uStore.config, configStorage | Universal | Supports nested keys such as client:apiUrl. | | signal | uStore.signal, signalStorage | Universal | Lightweight in-memory state for reactive-style workflows. | | gun | uStore.gun, gunStorage | Browser / CommonJS / direct import | Experimental, chain-oriented Gun integration. | | server | serverStorage, createServerStorage | Node / server | Async Keyv-backed storage from @waelio/ustore/server. |

The common shape

Most adapters expose aliases for the same basic operations:

type StoreLike = {
  get(key: string): unknown;
  getItem(key: string): unknown;
  set(key: string, value: unknown): unknown;
  setItem(key: string, value: unknown): unknown;
  has(key: string): unknown;
  hasItem(key: string): unknown;
  remove(key: string): unknown;
  removeItem(key: string): unknown;
};

That means you can swap adapters without changing the overall call style.

Browser adapters

Local storage

import { localStorage, uStore } from "@waelio/ustore";

uStore.local.set("profile", { name: "Wael" });
console.log(uStore.local.get("profile"));

localStorage.set("theme", "dark");
console.log(localStorage.has("theme")); // true

Session storage

import { sessionStorage, uStore } from "@waelio/ustore";

uStore.session.set("draft", { title: "Hello" });
console.log(uStore.session.get("draft"));

sessionStorage.set("token", "abc123");
sessionStorage.remove("token");

Cookie storage

import { cookieStorage, uStore } from "@waelio/ustore";

uStore.cookie.set("theme", "dark");
console.log(uStore.cookie.get("theme")); // "theme=dark"

cookieStorage.set("locale", "en-US");
console.log(cookieStorage.has("locale")); // true

Cookie values are written as simple cookie strings, so plain strings are usually the best fit.

In-memory and utility adapters

Memory storage

import { memoryStorage, uStore } from "@waelio/ustore";

uStore.memory.set("count", 42);
console.log(uStore.memory.get("count")); // 42

memoryStorage.setItem("feature", { enabled: true });
console.log(memoryStorage.getItem("feature"));

Secure storage

import { secureStorage, uStore } from "@waelio/ustore";

uStore.secure.set("secret", "hello world");
console.log(uStore.secure.getItem("secret"));

secureStorage.set("token", "signed-value", { salt: "demo-salt" });
console.log(secureStorage.getItem("token", { salt: "demo-salt" }));

Config storage

import { configStorage, uStore } from "@waelio/ustore";

uStore.config.set("client:apiUrl", "https://api.example.com");
console.log(uStore.config.get("client:apiUrl"));

configStorage.set("featureFlags", { dashboard: true });
console.log(configStorage.get("featureFlags"));
console.log(configStorage.client());
console.log(configStorage.server());
console.log(configStorage.dev());

Signal storage

import { signalStorage, uStore } from "@waelio/ustore";

uStore.signal.set("online", true);
console.log(uStore.signal.get("online")); // true

signalStorage.set("users", ["alice", "bob"]);
console.log(signalStorage.has("users")); // true
signalStorage.remove("users");

Vue adapters

Pinia adapter

import { piniaStorage, uStore } from "@waelio/ustore";

uStore.pinia.set("theme", "dark");
console.log(uStore.pinia.get("theme"));

piniaStorage.setItem("language", "en");
console.log(piniaStorage.hasItem("language"));

The current Pinia adapter is a compatibility layer backed by in-memory state, which makes it predictable in SSR and test environments.

Vuex adapter

import { vuexStorage, uStore } from "@waelio/ustore";

uStore.vuex.set("status", "ready");
console.log(uStore.vuex.get());
console.log(uStore.vuex.has());

vuexStorage.setItem("message", { text: "hello" });
console.log(vuexStorage.getItem());

The current Vuex adapter stores the latest committed value in a single slot, so reads do not need a key.

Gun adapter

import { gunStorage } from "@waelio/ustore";

await gunStorage.set("room", { hello: "world" });
const roomRef = await gunStorage.get("room");

console.log(roomRef);

The Gun adapter is experimental and chain-oriented, so it behaves differently from the purely synchronous adapters.

Native Node ESM note

In the native Node ESM entry, uStore.gun intentionally throws to avoid eager-import issues. If you need Gun in that environment:

  • import gunStorage directly from @waelio/ustore
  • or use CommonJS / a bundler-based environment

Server-side storage

Use @waelio/ustore/server when you want async storage backed by Keyv.

import { createServerStorage } from "@waelio/ustore/server";

const store = createServerStorage({
  namespace: "sessions",
  ttl: 1000 * 60 * 60,
});

await store.set("session:waelio", {
  role: "owner",
  authenticated: true,
});

console.log(await store.get("session:waelio"));
console.log(await store.has("session:waelio"));

await store.remove("session:waelio");

There is also a ready-to-use singleton:

import { serverStorage } from "@waelio/ustore/server";

await serverStorage.set("healthcheck", "ok");
console.log(await serverStorage.get("healthcheck"));

Mongo-backed server storage

import { createMongoServerStorage } from "@waelio/ustore/server";

const store = createMongoServerStorage("mongodb://127.0.0.1:27017/ustore", {
  namespace: "waelio-auth",
});

await store.set("user:1", { role: "admin" });
console.log(await store.getItem("user:1"));

Messaging bridge

createMessagingStore is a client-side bridge between uStore adapters and a Socket.io-compatible messaging backend.

Install a Socket.io client in the consuming app if you do not already have one:

pnpm add socket.io-client

Then wire it up:

import { io } from "socket.io-client";
import { createMessagingStore } from "@waelio/ustore";

const socket = io("https://waelio-messaging.onrender.com");
const store = createMessagingStore(socket, {
  historyLimit: 200,
  storagePrefix: "wm",
});

const unsubscribe = store.onMessage((message) => {
  console.log("incoming", message.senderId, message.payload);
});

store.send("userId-abc", "hello!");
store.broadcast({ text: "hello everyone" });

const history = await store.loadHistory();
console.log(history.length);
console.log(store.getUnread());

unsubscribe();
store.destroy();

The messaging bridge uses:

  • localStorage for cached history
  • sessionStorage for session identity
  • signalStorage for unread count, user list, and connection state

Runtime notes

  • Browser-oriented adapters such as local, session, and cookie storage include fallbacks where applicable so they can still behave safely during SSR or non-browser execution.
  • uStore.gun is intentionally unavailable in native Node ESM via the uStore object, but gunStorage remains exportable.
  • idbStorage and webqlStorage exist in the source tree as planned adapters and are not currently exported from the public package.

Test coverage

The test suite currently covers:

  • localStorage
  • sessionStorage
  • cookieStorage
  • memoryStorage
  • piniaStorage
  • vuexStorage
  • secureStorage
  • configStorage
  • signalStorage
  • createServerStorage
  • cross-adapter isolation
  • alias consistency such as get / getItem and set / setItem
  • messaging behavior and Node runtime behavior

Development

pnpm test -- e2e.test.ts
pnpm test
pnpm build

Publishing

Typical local release flow:

pnpm test
pnpm build
npm publish --access public

References