@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 Interfaces —
SyncStorefor localStorage/sessionStorage,AsyncStorefor 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.settingAPI 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 nowSupported 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()returnsnullon error — Parse failures or storage errors returnnullrather than throwing.- Disabled stores — If a storage type is unavailable,
enabledisfalseand operations are no-ops (returningnullfor 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 |
