lru_map review
lru_map 0.4.1 is a synchronous, count-bounded JavaScript map that evicts the least recently read or written entry. A native `Map` provides key lookup, while a doubly linked list records oldest-to-newest order. `get()` promotes an entry; `find()` and `has()` inspect without changing recency. The package has no TTL, byte budget, async loader, stale-value policy, or built-in disposal callback. Version 0.4.1 is still the npm release from October 2020, and the repository publishes no GitHub releases. The README now recommends copying its source files for minimal use, which transfers maintenance responsibility to your project.
lru_map 0.4.1 installed in 0.8 seconds, occupied 1 MB, bundled to 1.3 KB gzipped, and had 0 audit findings in our sandbox. Its count-only cache is fine for stable legacy code, but a new service that needs TTL, async loading, or removal hooks should install lru-cache instead.
We installed it
| Install | ✓ · 0.8s | 1 package on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | 1.3 KB | gzipped (3.5 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does lru_map install cleanly?
Yes. In a fresh container with an empty cache, npm install lru_map finished in 0.8s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does lru_map add to a browser bundle?
1.3 KB gzipped (3.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does lru_map work with both ESM and CommonJS?
Yes. Both import 'lru_map' and require('lru_map') worked in Node 22 in our run. The package is published as CommonJS.
Does lru_map include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
lru_map or lru-cache: which should you use?
lru-cache: Use it when TTL, stale entries, size calculation, disposal hooks, or an async fetch method belongs in the cache itself. lru_map 0.4.1 installed in 0.8 seconds, occupied 1 MB, bundled to 1.3 KB gzipped, and had 0 audit findings in our sandbox.
When should you not use lru_map?
Cached values need expiration by time, stale-while-revalidate behavior, size calculation, or a total byte limit. lru_map only enforces a maximum number of entries.
Use it if
- You need a tiny in-process cache bounded only by entry count, with synchronous values and ordinary JavaScript keys.
- Reads should update recency, while a separate `find()` operation must inspect a value without keeping it alive.
- Oldest-to-newest iteration and manual `shift()` eviction are useful to the calling code.
- A legacy CommonJS application needs bundled TypeScript declarations and cannot adopt an ESM-only cache package.
- Cached values need expiration by time, stale-while-revalidate behavior, size calculation, or a total byte limit. lru_map only enforces a maximum number of entries.
- Cache misses start asynchronous work that must be deduplicated. The API stores values but has no fetch method, pending-promise policy, abort signal, or rejection handling.
- Every removal must close a resource or emit telemetry. Automatic capacity eviction calls `shift()`, but `delete()` and `clear()` follow separate paths, so overriding shift does not observe all removals.
- You want a current package release and documented support policy. npm 0.4.1 was published in 2020, there are no GitHub releases, and the README recommends vendoring the implementation.
- Your code assumes the native Map contract exactly. Here `get()` changes order, `delete()` returns the removed value instead of a boolean, and iteration follows recency rather than insertion history.
Setup reality
We installed lru_map 0.4.1 in a fresh Node 22 Bookworm sandbox. npm finished in 0.8 seconds and left 1 package using 1 MB on disk. The package is 48 KB unpacked, with 0 direct dependencies and 0 peer dependencies. npm audit reported 0 known vulnerabilities. TypeScript declarations are included.
The npm artifact is CommonJS and has no exports map. require() worked in our check, and Node's ESM loader also accepted import. There is no config file, native build, credential, or background process. The minified browser bundle measured 3.5 KB and 1.3 KB gzipped with esbuild, small enough that feature fit matters more than transfer size.
Pick a positive entry limit when constructing the cache. set() returns the cache for chaining and silently evicts through shift() after the count exceeds that limit. get() moves a hit to the newest position, while find() leaves order untouched. Missing entries return undefined, which is indistinguishable from a key deliberately storing undefined unless you check has() first.
Values live only inside the current process and have no automatic expiry. A cache around async work must decide whether to store the pending promise, how to remove rejected promises, and how to avoid duplicate loads. oldest and newest expose entry objects that the README says are invalidated by modification and must not be stored or changed. If you vendor the recommended source, copy lru.d.ts too and accept responsibility for fixes after the six-year-old npm release.
Patterns
Create a three-entry cache create-cache
const { LRUMap } = require('lru_map');
const cache = new LRUMap(3);
cache.set('a', 1).set('b', 2).set('c', 3);The numeric limit counts entries, not bytes. Adding a fourth distinct key evicts the oldest entry.
Read a value and refresh its recency read-and-promote
const value = cache.get('a');
if (value !== undefined) {
console.log(value);
}A successful `get()` moves the entry to newest. Use `has()` when `undefined` is a legitimate cached value.
Inspect a value without keeping it alive peek-without-promoting
const value = cache.find('a');`find()` returns the value without changing LRU order. That behavior differs from `get()`.
Separate a missing key from an undefined value distinguish-undefined
if (cache.has(key)) {
return cache.find(key);
}
return loadValue(key);Both `get()` and `find()` return `undefined` on a miss, so `has()` is required when the cache may store undefined.
Remove the least recently used entry evict-oldest
const evicted = cache.shift();
if (evicted) {
const [key, value] = evicted;
console.log({ key, value });
}`shift()` returns a `[key, value]` pair or undefined. It is also the path used by automatic count eviction.
Wrap automatic capacity eviction observe-capacity-eviction
const originalShift = cache.shift;
cache.shift = function () {
const entry = originalShift.call(this);
if (entry) onEvict(entry[0], entry[1]);
return entry;
};This sees `shift()` and limit-driven eviction. Direct `delete()` and `clear()` do not pass through the wrapper.
Delete one entry and receive its value delete-key
const removedValue = cache.delete('a');Unlike native Map.delete, this method returns the removed value or undefined, not a success boolean.
Iterate from oldest to newest iterate-by-recency
for (const [key, value] of cache) {
console.log(key, value);
}Iteration follows current LRU order. Calling `get()` before iteration can move an entry to the end.
Build a cache from ordered entries seed-from-entries
const cache = new LRUMap(2, [
['older', 1],
['newer', 2],
]);Input order becomes oldest to newest. Supplying more entries than the explicit limit makes `assign()` throw an overflow error.
Set capacity from the initial iterable derive-limit-from-input
const cache = new LRUMap([
['a', 1],
['b', 2],
]);
console.log(cache.limit); // 2The iterable-only constructor fixes the limit to the number of seeded entries after assignment.
Serialize entries in LRU order serialize-order
const payload = JSON.stringify(cache);
// [{"key":"older","value":1},{"key":"newer","value":2}]`toJSON()` emits objects with `key` and `value` properties, not the array-of-pairs format accepted by the constructor.
Store one pending promise per key cache-async-work
async function cachedLoad(key) {
if (cache.has(key)) return cache.get(key);
const pending = loadValue(key);
cache.set(key, pending);
try {
return await pending;
} catch (error) {
if (cache.find(key) === pending) cache.delete(key);
throw error;
}
}lru_map has no async fetch policy. Removing a rejected promise prevents a permanent cached failure, though capacity eviction can still allow duplicate in-flight work.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| lru-cache | npm | Use it when TTL, stale entries, size calculation, disposal hooks, or an async fetch method belongs in the cache itself. |
| quick-lru | npm | Use it for a small modern ESM cache with maxAge support and a simpler high-level API. |
| tiny-lru | npm | Use it when a compact current package with entry expiry is preferable to lru_map's Map-like surface. |
| mnemonist | npm | Use it when LRU caching is one of several specialized data structures the application needs. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

