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

@ichicraft/caching

v1.0.2

Published

Part of the Widget Development Kit for building widgets for Ichicraft Boards. Comprehensive browser storage solution with unified interface for localStorage, sessionStorage, and IndexedDB

Readme

@ichicraft/caching

Scoped browser storage library providing a unified interface for localStorage, sessionStorage, and IndexedDB with automatic key prefixing, expiration management, and hierarchical sub-managers.

Features

  • Scoped Key Prefixing — All keys are automatically prefixed, preventing collisions between consumers
  • Hierarchical Sub-Managers — Create nested cache scopes via createSubManager()
  • Automatic Expiration — Items expire after 30 days by default, configurable per item
  • Auto Cleanup — Expired items are removed automatically after initialization
  • Two InterfacesSyncStore for localStorage/sessionStorage, AsyncStore for IndexedDB
  • Non-Fatal Writes — Storage quota errors are silently caught; cache writes never crash the app

Usage

import { CacheManager } from '@ichicraft/caching';

const cache = new CacheManager({
    prefix: 'com.myapp.user-123', // Required: root prefix for all keys
    enableLocal: true, // Optional (default: true)
    enableSession: true, // Optional (default: true)
    enableIndexedDB: true, // Optional (default: true)
    cleanupDelay: 1000, // Optional: ms before cleanup runs (default: 1000)
});

Storage Tiers

cache.local — SyncStore (localStorage)

Persistent storage that survives browser restarts. Synchronous API.

// Store with default 30-day expiration
cache.local.put('preferences', { theme: 'dark' });

// Store with custom expiration
cache.local.put('token', 'abc', { expire: dateAdd(new Date(), 'hour', 1) });

// Retrieve (returns null if missing or expired)
const prefs = cache.local.get<Preferences>('preferences');

// Delete
cache.local.delete('preferences');

cache.session — SyncStore (sessionStorage)

Storage that lasts for the current browser session. Same synchronous API as local.

cache.session.put('tempData', value);
const data = cache.session.get<TempData>('tempData');

cache.db — AsyncStore (IndexedDB)

Database storage for large or complex data. Asynchronous API.

// All operations return Promises
await cache.db.put('largeDataset', items, { expire: dateAdd(new Date(), 'day', 7) });
const items = await cache.db.get<Item[]>('largeDataset');
await cache.db.delete('largeDataset');

Sub-Managers

Create nested scopes that share the same underlying storage but extend the key prefix:

const rootCache = new CacheManager({ prefix: 'com.myapp.u-123' });

// Keys prefixed with "com.myapp.u-123.newsWidget."
const widgetCache = rootCache.createSubManager('newsWidget');

// Keys prefixed with "com.myapp.u-123.newsWidget.i-456."
const instanceCache = widgetCache.createSubManager('i-456');

// These are different keys in the same storage:
rootCache.local.put('setting', 'a'); // key: com.myapp.u-123.setting
widgetCache.local.put('setting', 'b'); // key: com.myapp.u-123.newsWidget.setting
instanceCache.local.put('setting', 'c'); // key: com.myapp.u-123.newsWidget.i-456.setting

API Reference

CacheManager

| Method | Description | | ----------------------------------- | ----------------------------------------------------------------------- | | local | SyncStore — synchronous localStorage access | | session | SyncStore — synchronous sessionStorage access | | db | AsyncStore — asynchronous IndexedDB access | | createSubManager(suffix) | Returns a new CacheManager with an extended key prefix | | deleteByPrefix(prefix) | Deletes all entries matching the prefix from all enabled stores (async) | | CacheManager.deleteDatabase(name) | Static. Deletes an entire IndexedDB database by name |

SyncStore (localStorage / sessionStorage)

| Method | Signature | Description | | ---------------- | --------------------------------------- | ------------------------------------------------- | | get | get<T>(key): T \| null | Returns value or null if missing/expired | | put | put(key, value, options?) | Stores a value. Silently fails if storage is full | | delete | delete(key) | Removes a value | | getOrPut | getOrPut<T>(key, getter, options?): T | Gets from cache or calls getter to populate | | deleteExpired | deleteExpired() | Removes all expired items | | deleteByPrefix | deleteByPrefix(prefix) | Removes all items whose key starts with prefix | | enabled | boolean | Whether the underlying storage is available |

AsyncStore (IndexedDB)

Same methods as SyncStore, but all return Promise:

| Method | Signature | | ---------------- | ------------------------------------------------ | | get | get<T>(key): Promise<T \| null> | | put | put(key, value, options?): Promise<void> | | delete | delete(key): Promise<void> | | getOrPut | getOrPut<T>(key, getter, options?): Promise<T> | | deleteExpired | deleteExpired(): Promise<void> | | deleteByPrefix | deleteByPrefix(prefix): Promise<void> |

PutOptions

interface PutOptions {
    /** Expiration date. Defaults to 30 days from now if omitted. */
    expire?: Date;
}

Utility: dateAdd

import { dateAdd } from '@ichicraft/caching';

dateAdd(new Date(), 'minute', 30); // 30 minutes from now
dateAdd(new Date(), 'hour', 2); // 2 hours from now
dateAdd(new Date(), 'day', 7); // 7 days from now

Supported intervals: year, quarter, month, week, day, hour, minute, second

Error Handling

  • put() never throws — If localStorage/sessionStorage quota is exceeded (QuotaExceededError), the write is silently skipped. IndexedDB errors are caught and logged to console.
  • get() returns null on error — Parse failures or storage errors return null rather than throwing.
  • Disabled stores — If a storage type is unavailable, enabled is false and operations are no-ops (returning null for reads).

IndexedDB vs localStorage/sessionStorage

| | localStorage / sessionStorage | IndexedDB | | ----------------- | ------------------------------------------------ | ------------------------------------------------- | | API | Synchronous (SyncStore) | Asynchronous (AsyncStore) | | Serialization | JSON (Dates become strings, Map/Set become {}) | Structured clone (preserves Date, Map, Set, Blob) | | Quota | ~5-10 MB | ~50 MB+ (browser-dependent) | | Use case | Small config, preferences, flags | Large datasets, complex objects |