mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmUtilsupdated 22 Sept 2026

weak-lru-cache review

weak-lru-cache 1.2.2 is an in-process JavaScript cache with two retention phases. A least-recently and frequently used expirer first holds values strongly. After an object leaves that window, a `WeakRef` can still return it until garbage collection reclaims it; a `FinalizationRegistry` later removes the dead entry. This preserves object identity while memory allows, but it offers no TTL, persistence, cross-process sharing, byte ceiling, or predictable eviction time. Primitive values cannot enter the weak phase and disappear when the strong expirer removes them. The current version remains the February 2022 release.

Verdict

weak-lru-cache 1.2.2 installed in 0.5 seconds, used 1 MB, passed npm audit, and bundled to 1.5 KB gzipped in our sandbox. Use it only for recreatable object identities that may vanish whenever GC chooses; ordinary application caches should prefer maintained TTL and capacity controls from `lru-cache`.

We installed it

Lab card: what happened when we installed weak-lru-cacheScreenshot of weak-lru-cache documentation
Install✓ · 0.5s1 package on disk · 1 MB
ImportESM import works · require() works · ESM package with exports map
Browser1.5 KBgzipped (3.7 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does weak-lru-cache install cleanly?

Yes. In a fresh container with an empty cache, npm install weak-lru-cache finished in 0.5s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does weak-lru-cache add to a browser bundle?

1.5 KB gzipped (3.7 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does weak-lru-cache work with both ESM and CommonJS?

Yes. Both import 'weak-lru-cache' and require('weak-lru-cache') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does weak-lru-cache include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

weak-lru-cache or lru-cache: which should you use?

lru-cache: Use it for maintained deterministic limits, TTLs, disposal hooks, and async fetch support. weak-lru-cache 1.2.2 installed in 0.5 seconds, used 1 MB, passed npm audit, and bundled to 1.5 KB gzipped in our sandbox.

When should you not use weak-lru-cache?

Expiry must happen after a known duration. This package has no TTL or maximum age; garbage collection timing belongs to the runtime.

API stability3/5Version 1.2.2 has a small intended surface around `WeakLRUCache`, `LRFUExpirer`, `getValue`, and `setValue`, with explicit ESM and CommonJS entries. The source has not changed since 2022, so existing behavior is frozen. The public shape is still deceptive: Map inheritance exposes entry-level methods, and the bundled declaration says `setValue` returns `void` even though runtime code returns a `CacheEntry`. Consumers should avoid that undocumented return.
Docs3/5The README returned HTTP 200 and explains strong LRFU retention followed by weak references, object identity, primitive handling, expiration priority, pinning, the 32,768 default cache size, the 16,777,216 maximum, shared or disabled expirer modes, and why `get` differs from `getValue`. Its opening sample contains an undefined `myValue` variable and non-JavaScript arrow notation, while deletion, clearing, counters, type drift, and deterministic testing receive little attention.
Maintenance1/5npm published 1.2.2 on February 2, 2022, and GitHub records the last push on April 2, 2022. The repository is unarchived, has 39 stars and 2 open issues and pull requests, but shows no later commits, releases, runtime matrix updates, or declaration correction. WeakRef and finalization behavior depends on JavaScript engines, so 4 years without visible compatibility work matters more than it would for a pure string helper.
Ecosystem3/5The npm endpoint counted 5,224,754 downloads in the week ending August 24, 2026. Our check found 0 dependencies, both module entry paths, declarations, and a 1.5 KB gzipped browser build. Direct community activity is small at 39 stars, and there are no framework adapters, persistence stores, metrics plugins, or fetch helpers. High installs likely come through dependency trees rather than teams deliberately standardizing on weak cache semantics.

Use it if

  • Cached values are recreatable objects and a miss at any later read is always safe.
  • Callers benefit from receiving the same object identity while another reference or the cache keeps that object alive.
  • The runtime supports `WeakRef` and `FinalizationRegistry`, and nondeterministic garbage collection is part of the design.
  • Memory pressure should reclaim cold objects without a wall-clock TTL or application-managed disposal schedule.
Skip it if

Setup reality

We installed weak-lru-cache 1.2.2 in a fresh Node 22 Bookworm sandbox in 0.5 seconds. It left 1 package and 1 MB on disk. npm audit found 0 known vulnerabilities. The ESM package has no direct or peer dependencies, is 64 KB unpacked, ships TypeScript declarations, and uses an exports map. Both CommonJS require() and ESM import worked on Node 22.23.2. Our browser build measured 3.7 KB minified and 1.5 KB gzipped.

There are no credentials, native builds, services, or configuration files. Runtime support is the first gate: the README specifies Node 14.10 or newer because construction uses WeakRef and FinalizationRegistry. WeakLRUCache extends Map, but its values are internal cache entries. Use setValue() and getValue() for application data. Iteration and inherited get() expose entries; inherited set() expects an entry. The bundled declaration says setValue returns void, while the 1.2.2 source returns the created CacheEntry.

Objects begin with a strong reference in the shared LRFU expirer. Once expired there, the entry keeps a weak reference and getValue() may still recover the object until GC runs. The default cacheSize is 32,768 and the documented maximum is 16,777,216, but this sizes the strong retention machinery rather than setting an exact number of live objects. A higher expirationPriority makes an entry leave sooner; -1 pins it in strong memory. Pinning defeats the memory-release reason for this cache.

The default expirer is shared across cache instances, so heavily used caches compete differently from isolated ones. Pass expirer: false for weak-only behavior or construct LRFUExpirer when isolation is intentional. Tests must never require GC to run by a deadline. A read can miss even after an earlier write, and concurrent misses can duplicate reconstruction work. Use explicit delete() for invalidation and clear() for teardown; both remove entries from the expirer instead of waiting for finalization.

Patterns

Store and retrieve one object cache-object

import { WeakLRUCache } from 'weak-lru-cache';

const cache = new WeakLRUCache();
cache.setValue('user:42', { id: 42, name: 'Ada' });
const user = cache.getValue('user:42');

The object starts strongly retained, then becomes weakly reachable after LRFU expiry and may disappear after garbage collection.

Rebuild data after a miss fill-on-miss

async function getUser(id) {
  const key = `user:${id}`;
  let user = cache.getValue(key);
  if (user === undefined) {
    user = await database.loadUser(id);
    cache.setValue(key, user);
  }
  return user;
}

A miss can occur after any earlier write. Two concurrent misses may issue 2 database reads because loading is not coalesced.

Reuse an object's identity intern-object

function internNode(id, fields) {
  const existing = cache.getValue(id);
  if (existing) return existing;
  const node = Object.freeze({ id, ...fields });
  cache.setValue(id, node);
  return node;
}

Identity lasts only while the object remains alive. After GC, the same key can produce a newly constructed object.

Tune strong retention set-cache-size

const cache = new WeakLRUCache({
  cacheSize: 4096,
});

A 4,096 entry setting sizes the LRFU structure. It is not an exact cap on weakly reachable objects or bytes.

Expire a large value sooner weight-large-object

const estimatedBytes = Buffer.byteLength(JSON.stringify(document));
cache.setValue(document.id, document, estimatedBytes >> 10);

Higher priority expires sooner. The README suggests approximate kilobytes as a relative weight, not byte-accurate memory accounting.

Pin one object strongly pin-value

cache.setValue('schema', compiledSchema, -1);

A negative priority pins the object until the entry changes. One pin removes GC's ability to reclaim that cached value.

Return a pin to normal retention unpin-value

const schema = cache.getValue('schema');
if (schema) cache.setValue('schema', schema, 0);

Reading a pinned entry keeps it pinned. Replacing it with priority 0 sends it back through normal expiration.

Disable the strong expirer use-weak-only

const weakOnly = new WeakLRUCache({
  expirer: false,
});
weakOnly.setValue('preview', previewObject);

Without strong retention, the value may become collectible as soon as outside references disappear. Primitives have no useful weak phase.

Create a private retention policy isolate-expirer

import { LRFUExpirer, WeakLRUCache } from 'weak-lru-cache';

const expirer = new LRFUExpirer({
  lruSize: 2048,
  cleanupInterval: 30_000,
});
const isolated = new WeakLRUCache({ expirer });

The default expirer is shared. A private 2,048-slot generation isolates competition but gives up cross-cache prioritization.

Invalidate one key immediately delete-entry

const removed = cache.delete('user:42');
console.log({ removed });

`delete` removes the Map entry and its LRFU reference. Use it for correctness instead of waiting for GC.

Remove every cached entry clear-cache

cache.clear();

`clear` walks all entries and detaches them from the expirer. Other application references can still keep those objects alive.

Access entry metadata deliberately inspect-entry

const entry = cache.get('user:42');
const value = entry?.deref ? entry.deref() : entry?.value;

Inherited `get()` returns a `CacheEntry`, not the application value. Ordinary reads should call `getValue()` instead.

Alternatives

PackageRegistryPick it when
lru-cachenpmUse it for maintained deterministic limits, TTLs, disposal hooks, and async fetch support.
quick-lrunpmUse it for a small ESM LRU with a predictable maximum entry count.
mnemonistnpmUse it when a broader collection package with fixed-capacity LRU structures is useful.

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.