v3.0.0
cacheables v3.0.0
v3 is a ground-up rewrite. The core idea is unchanged — wrap an async call, get caching for free — but the storage model, options shape, and method names are new.
What's new
- Multilayer storage via buckets. Stack a fast in-memory L1 with any L2/L3 you write (filesystem, Redis, S3, IndexedDB, …). Reads cascade L1 → Ln; on any hit, missing upper layers are back-filled. Writes fan out to every bucket in parallel.
- Bucket views (
cache.resolve).Cacheable<TView>is generic; buckets can publish a domain-specific projection — a local file URL, a presigned S3 link, anObjectURL— separately from the cached value.cache.resolve()returns the L1 view and shares the policy + dedup pipeline withremember(), so callers stay on a freshness-aware path instead of bypassing the policy with a raw bucket read. - Pluggable logger.
ILoggerinterface plus aconsoleLoggersingleton replaces the booleanlog/logTimingflags. One formatted line per call:Cacheable "<namespace>:<key>": HIT|MISS <Xms>. Omitloggerfor silent operation; assigncache.loggerat runtime to flip on/off. - Concurrency-safe dedup. Concurrent same-key callers share one cascade probe, one value read, and (for SWR) one background revalidation. Concurrent
remember+resolveon the same key still trigger only one producer call.
Migrating from v2
// v2
import { Cacheables } from 'cacheables'
const cache = new Cacheables({ log: true, logTiming: true })
await cache.cacheable(() => fetch(url), 'weather', {
cachePolicy: 'max-age',
maxAge: 5_000,
})// v3
import { Cacheable, MemoryBucket, consoleLogger } from 'cacheables'
const cache = new Cacheable('weather', {
buckets: [new MemoryBucket()],
policy: 'max-age',
maxAge: 5_000,
logger: consoleLogger,
})
await cache.remember(() => fetch(url).then((r) => r.json()), 'weather')Breaking changes
- Class renamed
Cacheables→Cacheable. - Method renamed
cache.cacheable(...)→cache.remember(...). - Cache policy moved to the constructor. No per-call options. Pass
policy(andmaxAgewhere required) once onnew Cacheable(namespace, { ... }). Field ispolicy, notcachePolicy. A single instance serves a single policy — split into multiple instances to mix. bucketsis required.new Cacheable('app', { buckets: [new MemoryBucket()] })reproduces the v2 default.namespaceis required and positional (first constructor argument). Prefixed onto every bucket key as${namespace}:.deleteandclearare async — they returnPromise<void>.log/logTimingreplaced bylogger. PassconsoleLoggerto restore default-on logging.- Removed:
enabledoption (callresource()directly to bypass),keys(),isCached(),Cacheables.key(...)(build keys with template literals or[...].join(':')). - Buckets can throw. Any throw from any bucket rejects
remember(). v2's in-memory store couldn't fail; this is a new error surface once you wire up a custom bucket.
Runtime
- Node ≥ 18 required.
sideEffects: falseso bundlers can tree-shake unused exports.- Dual ESM/CJS publish, browser + Node, no dependencies.