Caching expensive async work (DB/API calls) in-process, with size bounds and freshness control — and de-duplicating stampedes.
Overview
An in-memory cache that does the three things you actually want — bound the size (LRU), expire entries (TTL) and hide latency (stale-while-revalidate) — plus wrap()/memoize() that cache any async function and de-duplicate concurrent in-flight calls. 100 callers, one fetch.
Add `staleWhileRevalidate` to serve the cached value instantly after expiry while refreshing in the background.
`memoize(fn, { ttl })` wraps a function and keys by its arguments; use `.cache` for manual control.
Zero-dependency and isomorphic — safe to import on the server, in the browser, on the edge and in React Native. It tree-shakes, so you only ship what you import.
Watch out for
This is a per-process, in-memory cache — it doesn't share across serverless instances or survive restarts. For shared state, back it with Redis via your own store.
Cache keys are strings; for object args, `memoize` uses `JSON.stringify` by default — pass a custom `key` for stable hashing.