tiny-lru
A zero-dependency, ~2.2 KB LRU cache for JavaScript with O(1) operations and optional TTL expiration.
Repository Health
Technical Analysis
tiny-lru is a high-performance Least Recently Used (LRU) cache for JavaScript, implemented as a single doubly-linked list backed by a plain-object hash map so that get, set, delete, and eviction all run in O(1) time. It ships as both a validating factory function (lru()) and a raw constructor (LRU), supports optional TTL-based expiration with an opt-in reset-on-update mode, and compiles to dual ESM/CJS builds with full TypeScript declarations while pulling in zero runtime dependencies.
Beyond the core get/set/delete surface, it exposes batch helpers (getMany, hasAll, hasAny, keyed entries/values), TTL introspection (sizeByTTL, keysByTTL, valuesByTTL, expiresAt, cleanup), and eviction hooks (setWithEvicted, onEvict) that make it a drop-in building block for memoization, session caches, rate limiters, and API response caching where a small footprint and predictable performance matter more than a feature-rich cache framework.
What You Get
- A validating factory function (lru()) and a raw constructor (LRU) for creating cache instances, the former throwing TypeError on invalid max/ttl/resetTTL arguments
- O(1) get, set, delete, has, and evict operations backed by an internal doubly-linked list plus a null-prototype hash map
- Optional TTL expiration per cache instance, with an opt-in resetTTL flag to refresh expiry on every set() to an existing key
- Batch and introspection helpers — getMany, hasAll, hasAny, entries(keys), values(keys), sizeByTTL, keysByTTL, valuesByTTL, expiresAt, and cleanup() for pruning expired entries without touching LRU order
- Eviction visibility via setWithEvicted() (returns the evicted entry inline) and onEvict() (registers a callback fired on every eviction)
- Dual ESM/CJS builds with hand-authored TypeScript declarations (types/lru.d.ts) and zero runtime dependencies
Common Use Cases
- Memoizing expensive pure-function results (e.g. recursive computations) behind a bounded cache
- Caching API or database query responses for a fixed window to cut redundant network/DB round-trips
- Session or authentication token caches with TTL-based expiration and optional reset-on-touch semantics
- Rate limiting and request-throttling counters keyed by client/IP with automatic eviction of stale entries
- Caching LLM/API responses by prompt+params hash to avoid re-paying for identical calls within a time window
Under The Hood
Architecture
The implementation lives in a single ~670-line module (src/lru.js) that pairs a null-prototype hash map (this.items) for O(1) key lookup with a manually managed doubly-linked list (first/last pointers, per-node prev/next) to track recency order; the LRU class keeps bookkeeping state (#stats, #onEvict) as private fields and factors list surgery into small internal helpers (#unlink, moveToEnd, #rebuildList, #isExpired) so public methods like set, get, delete, evict, and cleanup stay declarative and each touch the list in exactly one place — meaning the eviction/reordering policy has a single, well-isolated point of change. Two constructors are exposed onto the same core: new LRU() for unchecked, minimal-overhead construction, and the lru() factory that adds parameter validation (throwing TypeError on bad max/ttl/resetTTL) for safer call sites.
Tech Stack
Pure, dependency-free JavaScript authored as ESM (src/lru.js) and compiled with Rollup (rollup.config.js, @rollup/plugin-terser) into dual CommonJS and ESM builds (dist/tiny-lru.cjs, dist/tiny-lru.js), with a hand-written TypeScript declaration file (types/lru.d.ts) covering both entry points. Linting and formatting run through oxlint and oxfmt (Rust-based tooling) rather than ESLint/Prettier, Husky wires these into pre-commit hooks, and the package targets Node.js >=14 or any ES Module-capable browser. A dedicated benchmarks/ suite using tinybench compares performance against lru-cache, quick-lru, and mnemonist.
Code Quality
The test suite (tests/unit/lru.test.js) contains roughly 150 cases run through Node’s built-in node:test runner — no external test framework dependency — and reaches 100% line and function coverage with 99.28% branch coverage per the checked-in coverage report. Error handling is explicit and typed: invalid constructor arguments raise TypeError from the validating factory rather than failing silently or producing NaN-poisoned state. Naming is consistent camelCase throughout, oxlint/oxfmt enforce style automatically via pre-commit hooks, and GitHub Actions CI runs the full suite on every push; CONTRIBUTING.md and a dedicated code-style guide document the conventions for outside contributors.
API Design
The public surface stays deliberately small: a single validating factory (lru(max, ttl, resetTtl)) covers most usage, and chainable methods (set().set().set()) avoid intermediate variables for common sequences. Batch helpers (getMany, hasAll, hasAny, keyed entries/values) let callers operate on many keys without hand-rolled loops, and TTL-aware introspection (sizeByTTL, keysByTTL, valuesByTTL, expiresAt) exposes cache internals that most minimal LRU implementations hide entirely. setWithEvicted() returning the evicted entry inline, and onEvict() registering a callback, give cache-instrumentation hooks (metrics, logging, cascading invalidation) without requiring a wrapper class. None of this is conceptually novel — LRU caching is a well-understood pattern — but the ergonomics (chaining, batch ops, TTL introspection, ~2.2 KB footprint) are more polished than a bare-minimum implementation would offer.
Used by 2 apps in this directory
Activepieces
Automation · AI Assistants
Open-source AI automation platform that converts 280+ workflow integrations into MCP servers for LLMs, with no-code builders and TypeScript extensibility.
GraphQL Hive
Developer Tools · Devops · Monitoring
Open-source GraphQL schema registry and observability platform with breaking change detection, federation support, and CI/CD integration for teams of any size.