close
Skip to main content
Works with
This package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers
This package works with Cloudflare Workers
This package works with Node.js
This package works with Deno
This package works with Bun
This package works with Browsers
JSR Score82%
Downloads7/wk
Published7 months ago (0.1.0)

Comprehensive Web Storage ponyfills and polyfiils for `localStorage`, `sessionStorage`, `StorageEvent`, and `Storage` interfaces. Includes manual and auto-shim support.

@nick/storage

Comprehensive ponyfills/polyfills for the standard Web Storage APIs.

Image Image


Overview

This package provides in-memory ponyfills for the Web Storage APIs Storage, localStorage, sessionStorage, and StorageEvent. It also provides optional polyfill support through the ./install and ./shim entry points.

Features

  • Complete Storage API: Full Storage interface with all standard methods defined by the Web Storage API specification.
  • localStorage + sessionStorage: In-memory ponyfills ensure both APIs are always available regardless of the runtime environment you're in.
  • Cross-platform compatibility: Works seamlessly in Deno, Bun, Node, Cloudflare Workers, browsers, and any other ES2015+ capable environment.
  • TypeScript support: Written in 100% TypeScript. Includes declarations for all public APIs, ensuring accurate type checking, intellisense, and autocompletion regardless of your IDE preference.
  • Zero dependencies: Entirely self-contained and dependency-free!
  • Standards compliant: Follows the Web Storage API specification to a T.
  • 🦄 Ponyfills! While this package does include polyfills on an opt-in basis, it primarily provides (and strongly recommends) 100% non-invasive ponyfills that don't modify the global scope unless explicitly requested.

    This ensures maximum compatibility, and guarantees the global scope will only be modified when you specifically ask for it. No surprises here!


Install

deno add jsr:@nick/storage
yarn add jsr:@nick/storage
pnpm add jsr:@nick/storage
bunx jsr add @nick/storage
npx jsr add @nick/storage

Usage

The recommended usage for this package is to import the ponyfill directly in your code and use it as needed. All APIs are exposed as named exports from the root entry point of the package.

Ponyfill

// import the ponyfill implementations as needed
import {
  localStorage,
  sessionStorage,
  Storage,
  type StorageEvent,
} from "@nick/storage";

// Use localStorage for persistent storage
localStorage.setItem("user", "john");
const user = localStorage.getItem("user"); // 'john'

// Use sessionStorage for session-based storage
sessionStorage.setItem("temp", "data");
const temp = sessionStorage.getItem("temp"); // 'data'

// Use the Storage constructor for custom storage instances
const customStorage = new Storage();

// Listen for storage events (in environments that support it)
if (typeof globalThis !== "undefined" && globalThis.addEventListener) {
  globalThis.addEventListener("storage", (event: StorageEvent) => {
    console.log(`Storage key changed: ${event.key}`);
    console.log(`Old value: ${event.oldValue}`);
    console.log(`New value: ${event.newValue}`);
    console.log(`URL of page that made the change: ${event.url}`);
    console.log(
      `Storage area: ${
        event.storageArea === localStorage ? "localStorage" : "sessionStorage"
      }`,
    );
  });
}
What is a Ponyfill?

A ponyfill is a polyfill that doesn't modify global objects. Instead, it provides the functionality as importable modules, giving you full control over how and when to use them.

Unlike polyfills that automatically modify global objects, ponyfills:

  • Don't pollute the global scope with opinionated implementations
  • Enable selective usage of features without affecting other code
  • Provide better testability, isolation, and modularity
  • Integrate well with future native implementations: you decide when to switch.
  • Avoid conflicts with external libraries

Polyfill (graceful global installation)

If you need the Storage APIs to be available globally (like in a browser), you can install them using the install functions exported from the ./install entry point. These functions will only install the APIs if they are not already available in the global scope.

import { install } from "@nick/storage/install";

const result = install();

if (result.type === "success") {
  console.log("Storage APIs installed successfully");
  // Now you can use localStorage and sessionStorage globally
  localStorage.setItem("key", "value");
  sessionStorage.setItem("temp", "data");
} else if (result.type === "skipped") {
  console.log("Storage APIs already available");
} else {
  console.error("Failed to install Storage APIs:", result.error);
}

Shim (auto-install)

For convenience, you can auto-install the global APIs by importing the shim entry point of this package via a side-effect import. This is a convenience module that provides no exports — importing it is equivalent to calling the install function from the ./install entry point.

import "@nick/storage/shim";

// Storage APIs are now available globally
localStorage.setItem("key", "value");
sessionStorage.setItem("temp", "data");
Caution

The shim entry point automatically installs the Storage APIs globally if they are not already present. Use with care — modifying the global scope could lead to conflicts with other libraries/future native implementations.

CDN Usage (via esm.sh)

JavaScript/TypeScript Modules (HTTPS imports)

import {
  localStorage,
  sessionStorage,
  Storage,
  type StorageEvent,
} from "https://esm.sh/jsr/@nick/storage";

// Use the Storage APIs as needed
import "https://esm.sh/jsr/@nick/storage/shim";

// Storage APIs are now available globally

HTML

<script type="module">
  import {
    localStorage,
    sessionStorage,
    Storage,
    type StorageEvent,
  } from "https://esm.sh/jsr/@nick/storage";

  // Use the Storage APIs as needed
</script>
<script src="https://esm.sh/jsr/@nick/storage/shim"></script>

<!-- Storage APIs are now available globally -->

API

Storage API

The Storage class implements the complete Web Storage API interface.

class Storage {
  readonly length: number;

  clear(): void;
  getItem(key: string): string | null;
  key(index: number): string | null;
  removeItem(key: string): void;
  setItem(key: string, value: string): void;

  [index: number]: string;
}

All storage instances support these methods:

clear(): void

Removes all key-value pairs from storage.

import { Storage } from "@nick/storage";

const storage = new Storage();

storage.setItem("foo", "bar");

storage.clear(); // all items removed

getItem(key: string): string | null

Returns the value associated with the key, or null if the key doesn't exist.

import { localStorage } from "@nick/storage";

const value = localStorage.getItem("username");

console.log(`Username: ${value}`);

key(index: number): string | null

Returns the key at the specified index, or null if the index is out of range.

import { sessionStorage } from "@nick/storage";

const value = sessionStorage.getItem("token");

console.log(`Token: ${value}`);

removeItem(key: string): void

Removes the key-value pair from storage.

import { localStorage } from "@nick/storage";

localStorage.removeItem("theme");

setItem(key: string, value: string): void

Sets the value for the given key. The value is always stored as a string.

import { localStorage } from "@nick/storage";

localStorage.setItem("theme", "dark");

localStorage

Persistent storage that maintains data across sessions. In browser environments, this data persists even after the browser is closed and reopened.

Important

This ponyfill has no built-in persistence mechanism, and therefore both its localStorage and sessionStorage implementations are in-memory only. They do not persist data across application restarts.

import { localStorage } from "@nick/storage";
import assert from "node:assert";

// store data
localStorage.setItem("username", "alice");
localStorage.setItem("preferences", JSON.stringify({ theme: "dark" }));

// retrieve data
const username = localStorage.getItem("username"); // 'alice'
const prefs = JSON.parse(localStorage.getItem("preferences") || "{}");

assert.strictEqual(username, "alice");
assert.deepStrictEqual(prefs, { theme: "dark" });

// remove specific item
localStorage.removeItem("username");

// clear all data
localStorage.clear();

assert.strictEqual(localStorage.length, 0);
import { localStorage } from "@nick/storage";
import assert from "node:assert";

localStorage.setItem("session_id", "xyz789");
localStorage.setItem("auth_token", "token_123");

// check storage length
console.log(
  `${localStorage.length} items in local storage`, // 2
);

// get key by index
const firstKey = localStorage.key(0); // 'session_id'
assert.strictEqual(firstKey, "session_id");

// indexed property access per specification
const sessionId = localStorage["session_id"] ||= "xyz789"; // 'xyz789'

assert.strictEqual(sessionId, localStorage["session_id"]); // 'xyz789'
Tip

If you absolutely need persistence, consider using the native localStorage API which is supported by the latest versions of Node.js, Bun, and Deno.


sessionStorage

Session-based storage that clears when the session ends. In browser environments, this data is cleared when the tab is closed.

import { sessionStorage } from "@nick/storage";
import assert from "node:assert";

// store temporary data
sessionStorage.setItem("csrf_token", "abc123");
sessionStorage.setItem("form_data", JSON.stringify({ step: 2 }));

// retrieve data
const token = sessionStorage.getItem("csrf_token"); // 'abc123'
const formData = JSON.parse(sessionStorage.getItem("form_data") || "{}");

assert.strictEqual(token, "abc123");
assert.deepStrictEqual(formData, { step: 2 });

// check storage length
console.log(`${sessionStorage.length} items in session storage`);

// get key by index
const firstKey = sessionStorage.key(0);
assert.strictEqual(firstKey, "csrf_token");

// clear session data
sessionStorage.clear();
assert.strictEqual(sessionStorage.length, 0);

StorageEvent

This package also includes a StorageEvent implementation to match the Web Storage specification. StorageEvents are fired when changes are made to Storage instances like localStorage or sessionStorage in another context (e.g. a different browser tab/window/frame that shares the same origin).

Basic Usage

In browser environments that support it, StorageEvent is fired automatically when storage changes in a different window or tab, or from other contexts like iframes or service workers.

Creating StorageEvent Manually
import { StorageEvent } from "@nick/storage";

const event = new StorageEvent("storage", {
  key: "theme",
  oldValue: "light",
  newValue: "dark",
  url: "https://example.com",
  storageArea: localStorage,
});

// If in a browser environment, you could dispatch it
if (
  typeof globalThis.navigator !== "undefined" &&
  !globalThis.navigator.userAgent?.includes("Deno") &&
  typeof globalThis.dispatchEvent === "function"
) globalThis.dispatchEvent(event);
Listening for Storage Events
import "@nick/storage/shim";

// Add a listener for storage events
addEventListener("storage", (event) => {
  console.log(`Storage key changed: ${event.key}`);
  console.log(`Old value: ${event.oldValue}`);
  console.log(`New value: ${event.newValue}`);
  console.log(`URL of page that made the change: ${event.url}`);
  console.log(
    `Storage area: ${
      event.storageArea === localStorage ? "localStorage" : "sessionStorage"
    }`,
  );
});

In Deno or other non-browser environments, storage events are simulated when modifications are made to the storage objects. These events are only dispatched in the same context where the change was made.

Creating StorageEvent Manually

import { StorageEvent } from "@nick/storage";

const event = new StorageEvent("storage", {
  key: "user-preferences",
  oldValue: '{"theme":"light"}',
  newValue: '{"theme":"dark"}',
  url: "https://example.com",
  storageArea: localStorage,
});

// If in a browser environment, you could dispatch it
if (
  typeof globalThis.navigator !== "undefined" &&
  !globalThis.navigator.userAgent?.includes("Deno") &&
  typeof globalThis.dispatchEvent === "function"
) {
  globalThis.dispatchEvent(event);
}

StorageEvent Properties

  • key: The key being changed (string)
  • oldValue: The previous value, or null for new items
  • newValue: The new value, or null if the key was removed
  • url: The URL of the page where the change occurred
  • storageArea: The storage object that was modified (localStorage or sessionStorage)

StorageEvent Methods

  • initStorageEvent(): Legacy method to initialize the event (similar to Event.initEvent)

Environment Compatibility

This package is fully supported in the following environments:

Contributing

Contributions are always welcome! Please open an issue to discuss any proposed changes before submitting a Pull Request.


MIT © Nicholas Berlette. All rights reserved.

github · issues · docs · jsr · esm.sh · @nick

Report package

Please provide a reason for reporting this package. We will review your report and take appropriate action.

Please review the JSR usage policy before submitting a report.