cachify-function
v0.1.0
Published
A simple
Readme
cachify-function
A modular, zero-dependency, type-safe utility for caching sync or async functions in both Node.js and the browser (ES modules or CommonJS). You keep full type information while choosing the caching strategy that fits each call site: cache, stale-while-revalidate, or always-fresh.
Features
Fully type-safe – function generics propagate the exact argument and return types through every helper.
Modular – one small file, no third-party packages; tree-shakable ES export per helper.
Multiple run modes
runCache– standard “return if fresh, otherwise compute”.runStale– stale-while-revalidate with optional silent background errors.runFresh– force new execution, replacing the cache.runFreshCached– reuse an in-flight promise if one is already running.
Fine-grained invalidation – per-key (
invalidate) or global (invalidateAll).Perf-oriented – constant-time lookups, minimal timers, in-flight promise deduping.
Installation
npm i cachify-functionQuick start
import * as cf from 'cachify-function';
const delay = (ms: number) => new Promise<number>(res => setTimeout(() => res(ms), ms));
const delayCfc = cf.cachify(delay, {
max_age: 5000, // fresh for 5 s
max_age_auto_invalidate: 10000
});
(async () => {
// First call → takes a full second.
console.time('first');
await cf.runCache(delayCfc, 1000);
console.timeEnd('first'); // ≈1000 ms
// Cached call → instant.
console.time('cached');
await cf.runCache(delayCfc, 1000);
console.timeEnd('cached'); // <1 ms
// Force a fresh run.
console.time('fresh');
await cf.runFresh(delayCfc, 1000);
console.timeEnd('fresh'); // ≈1000 ms
// Deduping in-flight promises.
console.time('dedupe');
const p1 = cf.runFreshCached(delayCfc, 2000); // starts work
const p2 = cf.runFreshCached(delayCfc, 2000); // re-uses same promise
console.log('same promise:', p1 === p2); // true
await Promise.all([p1, p2]); // finishes once
console.timeEnd('dedupe'); // ≈2000 ms total
// After 6s the entry is stale but still present.
await delay(6000);
console.time('stale');
await cf.runStale(delayCfc, 1000); // returns immediately,
// then revalidates in background
console.timeEnd('stale'); // <1 ms
// Manual invalidation.
cf.invalidate(delayCfc, 1000); // remove that key
cf.invalidateAll(delayCfc); // or wipe everything
})();Why is it type safe?
Because each helper is generic over the original function:
// Type of `user` is still { id: string; name: string }
const user = await runCache(userCfc, 'u_123');No casts, no any, no surprise unknowns.
API surface (concise)
| Helper | Purpose |
| ------------------------------------------------- | --------------------------------------------------------- |
| cachify(fn, options) → CachifyFunctionContext | Wrap once, configure cache. |
| runCache(ctx, ...args) | Return cached value if fresh, else compute & cache. |
| runStale(ctx, ...args) | Return cached value even if stale; refresh in background. |
| runFresh(ctx, ...args) | Always compute; overwrites cache, dedupes any in-flight. |
| runFreshCached(ctx, ...args) | Use current in-flight promise or compute fresh. |
| invalidate(ctx, ...args) | Remove one cached entry. |
| invalidateAll(ctx) | Clear every entry for that context. |
Options for cachify(fn, options):
| Name | Default | Notes |
| ---------------------------------- | ----------------------- | ---------------------------------------------------------------------------- |
| max_age | 0 | Fresh-until age (ms). 0 = never stale. |
| max_age_auto_invalidate | 1.5 × max_age | Delete entry after this age (ms). 0/false disables. Must be ≥ max_age. |
| suppress_stale_revalidate_errors | true | Don’t surface background-refresh errors. |
| get_cache_key(...args) | serialize.generateKey | Custom key generator. (Recommended that you use this) |
Modular build usage
// ESM
import { runCache } from 'cachify-function/runCache.js';
// CommonJS
const { runCache } = require('cachify-function');License
MIT © 2025 VastBlast
