tiny-lru

A zero-dependency, ~2.2 KB LRU cache for JavaScript with O(1) operations and optional TTL expiration.

Library
npm
v13.0.0
184stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
66/100Good
Development Activity88
Maintenance44
Community52
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
88/100Excellent
Architecture85
Code Quality95
Innovation70
Learning Curve100

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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search