mrkeyoor.com_
Sat 08 Aug 20:59 UTC
npmUtilsupdated 08 Aug 2026

lru_map

lru_map is a small bounded JavaScript map backed by a native Map and a doubly linked list. Reading with get marks an entry as most recently used, and adding beyond the numeric entry limit removes the least recently used pair. Its API resembles Map and adds find for a read that does not change recency, shift for explicit eviction, oldest and newest entry pointers, iterable ordering, JSON-friendly output, and bundled TypeScript declarations.

Verdict

Still understandable and useful for a bare entry-count cache in older module systems. New projects should usually install lru-cache or quick-lru for maintained ESM packaging, expiration, disposal, and fewer API surprises.

API stability3/5The current implementation and bundled declaration agree on the core constructor, set, get, find, has, delete, shift, clear, iterators, and inspection properties, and the package has changed little for years. Confidence drops because it remains at 0.4.1 and the README's main example contradicts source by showing set returning an evicted entry when the implementation returns this.
Docs4/5The README explains the linked-list design, recency order, complexity intent, module formats, JavaScript compatibility, TypeScript setup, the full API, iteration direction, entry hazards, and an eviction-cleanup override. It is held back by the stale set return-value example, a recommendation to copy source files, and limited discussion of resizing, invalid limits, mutation during iteration, or missing cache policies.
Maintenance2/5npm shows 0.4.1 published in October 2020 and registry metadata last modified in 2022. GitHub reports a later push in February 2025 and the repository is not archived, with seven open items including issues and pull requests, but there has been no package release in almost six years and the build metadata still targets early esbuild and TypeScript 3.9.
Ecosystem3/5lru_map recorded 5,245,748 downloads in the latest measured week and has 798 GitHub stars, while its UMD bundle covers CommonJS, AMD, and direct browser globals. It has no runtime dependencies and mimics much of Map. The broader caching ecosystem has moved to lru-cache and quick-lru, which supply modern ESM packaging and policies most applications eventually need.

Use it if

  • You need a tiny dependency-free cache limited only by entry count
  • Both recency-changing get and recency-neutral find are useful to your eviction policy
  • Iteration from least recently used to most recently used is useful for inspection or persistence
  • CommonJS, AMD, or a browser-global UMD build fits an older JavaScript codebase
Skip it if

Setup reality

`npm install lru_map` brings no runtime dependencies and loads `dist/lru.js`, a UMD bundle that supports CommonJS, AMD, and a browser global. There is no modern conditional exports map or native ESM entry. Node can use `const {LRUMap} = require('lru_map')`; ESM named-import interop depends on the runtime or bundler's CommonJS handling, so test it rather than assuming. Type declarations are included, but they were authored against TypeScript 3.9-era tooling. Capacity is an entry count only: a thousand one-byte values and a thousand hundred-megabyte buffers are equivalent to the cache. There is no TTL, max byte size, stale policy, automatic loader, promise coalescing, metrics, or built-in disposal event. `get` changes recency, while `find` and `has` do not. `set` returns the map for chaining and internally calls `shift()` when over capacity, discarding the evicted pair. The README suggests overriding shift for cleanup, which works because internal eviction calls it, but monkeypatching an instance method is a brittle lifecycle hook. Entry objects exposed by oldest and newest contain linked-list pointers behind symbols and are invalidated by mutation; the README says never store or modify them. The mutable public `limit` property is not a safe resize API because lowering it does not immediately trim all excess entries. The build requires modern JavaScript features such as Map, Symbol.iterator, const, and let; the README points ES5 users to an older v2 branch.

Patterns

Create an entry-count-limited cachecreate-bounded-cache

const {LRUMap} = require('lru_map');

const cache = new LRUMap(3);
cache.set('a', 1).set('b', 2).set('c', 3);
console.log(cache.size); // 3

The limit counts entries, not bytes. Values of radically different memory sizes count the same.

Read while refreshing recencyread-and-refresh

const cache = new LRUMap(2, [['a', 1], ['b', 2]]);

cache.get('a');      // a becomes newest
cache.set('c', 3);   // b is evicted
console.log(cache.has('a'), cache.has('b')); // true false

get changes cache state even though it looks like a read. Use find when inspection must not affect eviction.

Read without changing recencypeek-without-refresh

const cache = new LRUMap(2, [['a', 1], ['b', 2]]);

const value = cache.find('a');
cache.set('c', 3);
console.log(value, cache.has('a')); // 1 false

find leaves a as the oldest entry, so the next insertion evicts it.

Check membership without a cache hitcheck-without-refresh

if (cache.has(key)) {
  console.log('present but recency unchanged');
}

const value = cache.get(key); // this call refreshes recency

has does not mark an entry used. Calling has and then get performs two lookups; use get alone when undefined cannot be a stored value.

Remove the least recently used pairevict-oldest-manually

const removed = cache.shift();
if (removed) {
  const [key, value] = removed;
  dispose(key, value);
}

shift returns a [key, value] tuple or undefined. Automatic eviction from set calls shift but does not return that tuple to the caller.

Delete a key and receive its valuedelete-and-read-value

const removedValue = cache.delete('session-42');
if (removedValue !== undefined) {
  closeSession(removedValue);
}

Unlike native Map.delete, this returns the removed value rather than true or false. A stored undefined value cannot be distinguished from absence.

Seed the cache from entriesinitialize-from-entries

const cache = new LRUMap(3, [
  ['oldest', 1],
  ['middle', 2],
  ['newest', 3],
]);

Input order becomes oldest to newest. More seed entries than the numeric limit throws an overflow error during assignment.

Use the entry count as capacityinfer-limit-from-entries

const cache = new LRUMap([
  ['one', 1],
  ['two', 2],
]);
console.log(cache.limit); // 2

The iterable-only constructor fixes the limit to the initial size. Additions immediately evict unless limit is changed, and changing it is not a complete resize operation.

Iterate from oldest to newestiterate-by-recency

for (const [key, value] of cache) {
  console.log(key, value);
}

console.log([...cache.keys()]);
console.log([...cache.values()]);

Iteration order is eviction order, not insertion order after reads. Avoid mutating the cache while consuming an iterator.

Inspect oldest and newest entriesinspect-recency-ends

const oldestKey = cache.oldest?.key;
const newestKey = cache.newest?.key;
console.log({oldestKey, newestKey});

Entry objects are invalidated when the map changes. Read key and value immediately and never retain or modify the entry object.

Create JSON-friendly cache dataserialize-cache-order

const payload = JSON.stringify(cache.toJSON());
// [{"key":...,"value":...}] from oldest to newest

toJSON returns objects with key and value fields, not Map-style pairs. Complex keys may not round-trip through JSON.

Run cleanup whenever shift evictsdispose-on-eviction

const originalShift = cache.shift;
cache.shift = function () {
  const removed = originalShift.call(this);
  if (removed) dispose(removed[0], removed[1]);
  return removed;
};

The README documents this override because automatic eviction calls shift. It is instance monkeypatching, so lru-cache's native disposal hooks are safer for larger systems.

Alternatives

PackageRegistryPick it when
lru-cachenpmYou need actively maintained TTL, max-size calculation, disposal hooks, stale policies, or async fetch support
quick-lrunpmYou want a modern ESM cache with maxAge and a compact API for ordinary applications
mnemonistnpmYou need LRU caches alongside a broader collection of tested data structures