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 🙏

© 2025 – Pkg Stats / Ryan Hefner

iframe-shared-storage

v1.0.34

Published

Tiny hub/client helper that lets you proxy `localStorage` and `indexedDB` (via [`idb-keyval`](https://github.com/jakearchibald/idb-keyval)) calls into a cross-origin iframe. The hub runs on a storage-friendly origin, while the client injects a hidden ifra

Readme

iframe-shared-storage

Tiny hub/client helper that lets you proxy localStorage and indexedDB (via idb-keyval) calls into a cross-origin iframe. The hub runs on a storage-friendly origin, while the client injects a hidden iframe, performs a readiness handshake, and then proxies storage calls through postmsg-rpc.

Highlights

  • Drop-in storage facade – use the returned client just like window.localStorage, but every call is executed inside the hub origin.
  • Origin isolation – keep your application origin locked down (COEP/COOP, CSP, etc.) while delegating storage access to a lightweight hub page.
  • Automatic readiness checks – the client pings the iframe until the hub replies, enforcing a configurable timeout instead of hanging forever.
  • Optional diagnostics – enable structured logging per-domain (client, hub, or both) to inspect RPC traffic when debugging.
  • Browser + bundler friendly – ship dist/browser.js for <script> tags or import from the published package when bundling.

How it works

  1. Hub (initHub): expose localStorage and selected idb-keyval APIs to postmsg-rpc. Every method is wrapped with logging hooks and runs inside the iframe's origin.
  2. Client (constructClient): either attach to an existing iframe or inject one that points at the hub URL. It keeps the iframe hidden, performs a handshake via postMessage, and only issues RPCs after the hub reports it is ready.
  3. Messaging options: optional metadata is appended to every RPC so both sides can toggle logging without custom wire formats.

The repository also contains client.html / hub.html demo pages plus Playwright harnesses that emulate restrictive headers to ensure the handshake behaves under COEP/CORP variations.

Installation

npm install iframe-shared-storage

Building the standalone bundle

npm run build           # emits dist/index.js + dist/browser.js

Use dist/browser.js for <script> based integrations; it registers a global IframeStorage with constructClient and initHub.

Quick start

Hub page

<!-- hub.html -->
<script src="/dist/browser.js"></script>
<script>
  IframeStorage.initHub();
</script>

Host this file on the origin that is allowed to use the storage APIs you care about.

Client application

import { constructClient } from "iframe-shared-storage";

const storage = constructClient({
  iframe: {
    src: "https://storage-origin.example.com/hub.html",
    messagingOptions: { enableLog: "client" },
    iframeReadyTimeoutMs: 1500,
    methodCallTimeoutMs: 2000,
    methodCallRetries: 2,
  },
});

await storage.localStorage.setItem("foo", "bar");
const value = await storage.localStorage.getItem("foo");
await storage.indexedDBKeyval?.set("heavy", JSON.stringify({ ... }));

For non-bundled apps, the same API is available via the IframeStorage global that dist/browser.js defines:

<script src="https://cdn.example.com/iframe-shared-storage/dist/browser.js"></script>
<script>
  const storage = IframeStorage.constructClient({ iframe: { src: "…" } });
</script>

API

initHub(): void

Call this once inside the hub iframe. The hub must have a parent window (i.e. it cannot run as a top-level page). It registers handlers for:

  • localStorage.setItem/getItem/removeItem/clear/key
  • indexedDBKeyval.set/get/del

constructClient(options: { iframe: … }): Client

  • Pass { iframe: { src: string } } to inject a hidden iframe that points to your hub URL. The iframe receives an auto-generated iframe-storage-hub id.
  • Pass { iframe: { id: string } } to bind to an already-rendered <iframe> (useful when you control markup separately).
  • iframeReadyTimeoutMs (default 1000) caps how long the client will wait for the handshake before every RPC.
  • methodCallTimeoutMs (default 1000) caps how long each RPC waits for a reply before rejecting, so hung hubs fail fast instead of stalling tests forever.
  • methodCallRetries (default 0) retries RPCs that ended with a timeout. Each retry performs the same readiness check and timeout, so a methodCallRetries of 2 with methodCallTimeoutMs of 1000 can run for up to ~3 seconds before failing.
  • messagingOptions.enableLog accepts "client" | "hub" | "both". When set, both sides console.log contextual events (method names, payloads, and responses).

The returned object exposes:

type Client = {
  localStorage: {
    setItem(key, value): Promise<void>;
    getItem(key): Promise<string | null>;
    removeItem(key): Promise<void>;
    clear(): Promise<void>;
    key(index): Promise<string | null>;
  };
  indexedDBKeyval?: {
    set(key, value): Promise<void>;
    get(key): Promise<string | undefined>;
    del(key): Promise<void>;
  };
};

Cross-origin requirements

  • Framing – the hub page must be embeddable from the client origin. Avoid X-Frame-Options: DENY and ensure CSP frame-ancestors allows the client.
  • Embedder policies – if the client enforces Cross-Origin-Embedder-Policy, make sure the hub responds with compatible headers (e.g. COEP: require-corp plus Cross-Origin-Resource-Policy: cross-origin). The Playwright suite (npm run test:e2e) exercises several combinations.
  • Handshake visibility – the readiness ping uses postMessage("*") while the iframe is still loading about:blank, then switches to the actual origin. Keep that in mind if you monitor CSP reports.

Local development

npm install
npm run build           # compile TypeScript + browser bundle
npm run build:watch     # concurrent module + browser watch (POSIX shells)
npm run serve:hub       # serve hub.html at http://127.0.0.1:5101
npm run serve:client    # serve client.html at http://127.0.0.1:5100

client.html is wired to load the production hub hosted on Vercel by default; uncomment the local URL in that file while iterating.

Testing

  • npm run test runs the Jest suite (unit tests for handshake, logging, timeout helpers, and a client↔hub integration sandbox).
  • npm run test:e2e launches the Playwright scenario that spins up two Express servers with configurable COEP/CORP headers and validates the handshake logic.
  • npm run verify or npm run test:all performs type checking, builds, unit tests, and e2e tests in sequence.

Repository layout

  • src/ – TypeScript sources (client, hub, utilities, and tests).
  • dist/ – build artifacts consumed by npm and the in-browser demo.
  • client.html / hub.html – runnable demo pair.
  • e2e/ – Playwright harness and Express servers for header/handshake testing.

Feel free to file issues or PRs if you need more storage methods exposed or would like to cover additional browser restrictions.