# 2.1.1 / 2026-09-11 - Override tsup's esbuild dependency to the patched 0.28.2 release, removing the older Windows development-server vulnerability. - Update eleven direct dependencies within their existing major versions. Enforce a seven-day minimum release age for future pnpm resolution; verify all resolved versions against npm publication timestamps. - Treat expired keys as absent in `setIfAbsent()`, `incr()`, and `decr()` even with `allowStale` enabled, so counters start a new TTL window. - Preserve local LRU order and TTL on `peek()` and `has()`; `has()` no longer reports a stale local entry as present. - Reduce cold `fetch()` from three IPC requests to two, and follower polling from two requests per cycle to one. Followers now populate L1 from the claim response. - Fix false IPC failures under backpressure and handle asynchronous send errors. Queued mutations are sent once and wait for their response. - Validate nested IPC error causes so malformed or cyclic payloads cannot crash a worker. - Keep hot L1 entries within the primary expiration deadline, including when local sliding TTL or stale reads are enabled. - Return remaining TTLs with cache values, reducing a cold L1 batch read from one request plus a TTL request per value to one request total. - Support `mGet()` batches larger than JavaScript's argument limit and avoid copying fully warm batch results. - Strengthen L1 race and cross-worker invalidation tests to exercise retained, warm instances. Replace timeout-based fuzz checks with valid-response checks and verify exact errors. # 2.1.0 / 2026-05-03 ## Features - **Local L1 cache** -- new `localL1` option adds a per-worker LRU cache in front of the primary-owned shared cache. Off by default. Requires `experimental: true` in v2.1 as an explicit opt-in acknowledgement of the eventual-consistency model. ```ts new LRUCacheClustered({ namespace: 'users', max: 50_000, ttl: 60_000, localL1: { enabled: true, experimental: true, ttl: 2_000 }, }); ``` - **Per-call bypass options** -- `get`, `has`, `peek`, `mGet`, `fetch`, and `memoize` accept `{ bypassL1: true }` to skip the local cache on a single call. `set` accepts `{ updateL1: true }` to populate the caller's L1 after a successful write; bulk writes clear the local L1. - **Method-level L1 controls** -- `localL1.methods` can restrict L1 to specific read families (`get`, `has`, and `fetch`). When `methods` is provided, omitted method keys are disabled. `memoize()` delegates to `fetch()`, so its L1 behavior follows the `fetch` setting. - **Invalidation mode control** -- the default `localL1.invalidation: 'broadcast'` subscribes same-process and cross-worker invalidation pushes. `localL1.invalidation: 'ttl-only'` is available for callers that intentionally want to rely only on the local TTL window. - **New methods**: - `clearLocal()` -- flush this worker's L1 for the namespace without touching the primary. - `invalidateLocal(key)` -- drop a single key from this worker's L1. - `localStats()` -- returns `{ enabled, hits, misses, sets, invalidations, evictions, staleHits, size, ipcAvoided }`. - `withoutLocal()` -- returns a bypass view that routes all reads through the primary; the original instance is unaffected. - **New events**: `l1:hit`, `l1:miss`, `l1:set`, `l1:invalidate`, `l1:evict`, and `l1:stale-hit`. Each carries `{ namespace, key }`; namespace-wide invalidations use `key: '*'`. - **Wire format** -- IPC responses now carry an optional `version` field; push (invalidation broadcast) messages were added. Backward compatible: a v2.0 worker talking to a v2.1 primary ignores the unknown field; a v2.1 worker talking to a v2.0 primary simply receives no invalidation broadcasts and relies on TTL expiry. ## Fixes and validation - Fixed `fetch(..., { bypassL1: true })` so followers do not observe or repopulate stale L1 state while preserving primary-side single-flight coordination. - Tightened L1 method-option semantics so provided `methods` objects consistently disable omitted read families. - Hardened local invalidation handling for nullish or unencodable keys, repeated unsubscribe calls, worker destroy, bulk operations, TTL changes, capacity changes, and primary load/purge paths. - Added regression coverage for L1 TTL clamping, per-entry TTL handling, local stats/events, same-process invalidation, worker invalidation broadcasts, and fetch bypass behavior. - Test coverage now reaches 100% statements, branches, functions, and lines for `src/**/*.ts`. # 2.0.0 / 2026-04-28 ## Breaking changes — TypeScript rewrite - Rewritten in TypeScript with full IPC type safety via discriminated unions - Adopted `lru-cache@11` native API (was a translated layer over `lru-cache@6`) - Renamed methods: `del → delete`, `reset → clear`, `prune → purgeStale`, `length`/`itemCount → size`, `stale → allowStale` - Renamed options: `maxAge → ttl`, `stale → allowStale` - Removed `parse` / `stringify` options and `setObject` / `getObject` / `mGetObjects` / `mSetObjects` — callers handle serialization - Removed `prune: ` option — schedule `purgeStale()` from your own scheduler - Removed `execute(method, ...args)` — call methods directly - Renamed `master` to `primary` everywhere (matches `cluster.isPrimary`) - Internal IPC method renamed `sendToMaster` → `sendToPrimary` ## Features and infrastructure - Canonical package name is now `@0xdoublesharp/lru-cache-clustered`; `lru-cache-for-clusters-as-promised` is published from the same build at the same version - Renamed canonical class to `LRUCacheClustered`; `LRUCacheForClustersAsPromised` is retained as a backward-compatible alias (both are exported) - Repository moved to `github.com/doublesharp/lru-cache-clustered` - Publish workflow (`scripts/prepare-publish.mjs` + `.github/workflows/npm-publish.yml`) builds once, prepares scoped and legacy package directories, and publishes both at the same version with npm provenance via GitHub Actions OIDC, triggered by `v*` tags - Publish prep validates required build artifacts before creating package directories - Scoped and legacy package copies share primary state in one process to avoid duplicate IPC listeners during migration - Dual ESM + CJS publish via `package.json` `exports` - Generic types: `LRUCacheClustered` - Node `>=22` required - Added `destroy()` to tear down namespace caches, stats, and fetch coordination state on the primary - Added explicit startup controls via `LRUCacheClustered.bootstrap()` and `cache.healthCheck()` - Added size-bounded cache support (`maxSize`, `maxEntrySize`, and `size` on write paths) - `fetch()` now uses primary-side single-flight coordination across workers; `memoize()` is exported as a top-level helper that delegates to it - Added `wrap()` for transparent encode/decode of cached values (gzip, MessagePack, custom symmetric codecs) - Structured error transport over IPC: rejected promises carry `name`, `message`, `code`, `stack`, and the `cause` chain - Dropped runtime deps: `cron`, `uuid` — request IDs use a per-process monotonic counter - pnpm for development; node:test + c8 for tests/coverage; tsup + tsc for build - husky `pre-commit` and `pre-push` hooks; lint-staged on staged files - 100% line/statement/function coverage - Property-based fuzz tests for cache, IPC, and worker layers - CI: Node 24 test workflow, Node 22 coverage workflow for Codecov, Doublcov HTML artifacts, and GitHub Pages coverage publishing from `main`, plus a Quality workflow running lint, typecheck, knip, type-coverage, build, and size-limit - New `assets/`: logo (`LRUCacheClustered.png`) and topology SVG diagram - New `examples/`: six runnable clustered server examples (users / memoize, rate-limit / incr, sessions, idempotency / setIfAbsent, compressed documents / wrap, multilayer LRU + Redis) - `incr` / `decr` retained (race-safe across workers) - `failsafe` and `timeout` retained for worker IPC ## Migration See README "Migrating from older releases" for the full mapping. # 1.7.1 / 2021-03-25 - Added `static getInstance(options)` to asynchronously return an `LRUCacheClustered` once the underlying `LRUCache` is guaranteed to exist. - Added `static getAllCaches()` to return all underlying `LRUCache` instances keyed by namespace. _Use only when `cluster.isMaster === true`._ - Added `getCache()` to return underlying `LRUCache` instance. _Use only when `cluster.isMaster === true`._ - Added test coverage via GitHub Actions - Bug fixes for namespaces - Bug fixes for prune cron jobs - Refactoring for maintainability - Updated tests - Updated dependencies # 1.7.0 / 2021-03-25 - Refactoring for maintainability - Update dependencies - More reliable test coverage # 1.6.1 / 2021-03-20 - Update types # 1.6.0 / 2021-03-20 - Refactor codebase to be more maintainable - Support for external `parse` and `stringify` functions, used for object caching, more efficient on large objects - Update tests, 100% code coverage - Update documentation - Update dependencies - npm audit fix # 1.5.25 / 2021-03-02 - Update dependencies # 1.5.24 / 2020-07-02 - Update dependencies - npm audit fix # 1.5.23 / 2020-06-10 - Typescript support (thanks @hanspeter1!) - Update dependencies # 1.5.22 / 2020-05-01 - Remove yarn.lock # 1.5.21 / 2020-05-01 - Update dependencies # 1.5.20 / 2019-09-29 - Update dependencies # 1.5.19 / 2019-08-03 - Update dependencies - Fix coverage badges with nyc # 1.5.18 / 2019-08-01 - Update dependencies - Use nyc for coverage # 1.5.17 / 2019-04-25 - Update dependencies - Lint code # 1.5.16 / 2017-11-21 - Update dependencies # 1.5.15 / 2017-10-14 - Update dependencies # 1.5.14 / 2017-09-11 - Update dependencies # 1.5.13 / 2017-08-04 - Update dependencies # 1.5.12 / 2017-07-06 - Update dependencies # 1.5.11 / 2017-05-07 - Update dependencies # 1.5.10 / 2017-05-07 - Deduplicate common methods - fixes CodeClimate rating to 4.0 # 1.5.9 / 2017-05-06 - Remove `npm-shrinkwrap.json` - Update dependencies # 1.5.8 / 2017-03-03 - Use Google ESLint config - Update dependencies # 1.5.7 / 2017-02-02 - Use `yarn` for installs - Update dependencies and dev dependencies # 1.5.6 / 2016-12-14 - Update dependencies # 1.5.5 / 2016-12-14 - Update dependencies # 1.5.4 / 2016-12-05 - Update dependencies # 1.5.3 / 2016-11-19 - Update `uuid` and `developer-tools` # 1.5.2 / 2016-11-17 - Improved debug logging # 1.5.1 / 2016-11-17 - Add support for `mSetObjects()` and `mGetObjects()` - Pass namespace through to pruning job for debugging # 1.5.0 / 2016-11-16 - Add support for `mGet([key])`, `mSet({key: value}, maxAge)`, and `mDel([key])`. # 1.4.6 / 2016-11-16 - Always use a shared cache for consistent behavior # 1.4.5 / 2016-11-15 - Bug fix for null objects # 1.4.4 / 2016-11-15 - Support for getting and setting objects + test coverage Update deps Bump version # 1.4.3 / 2016-11-12 - Update ESLint settings Remove duplicated code Update deps Bump version # 1.4.2 / 2016-11-08 - Update dependencies Bump version # 1.4.1 / 2016-10-26 - bump version - fix the tests, provide more coverage fix for case when no options are passed in. - Give longer to create cache Overwrite settings if they are different (allow workers to just use name, for example). Better properties on create return options Bugfix, cache master caches by namespace. # 1.4.0 / 2016-10-21 - Use cron to optionally prune cache on the master Support for `maxAge` parameter on `set()` Updated debugging Updated test coverage Updated deps Bumped version - use web sequence diagrams # 1.3.1 / 2016-10-11 - bump version - Update code climate config - dedupe keys test - dedupe tests - Dedupe test code - fix hello test - Refactor tests to improve coverage and remove duplicated code. - Downgrade version of `eslint-plugin-import` - Update deps Add pre-push testing Allow code climate to calc duplication # 1.3.0 / 2016-10-03 - When `cluster.isMaster===true` and the `caches[namespace]` is populated, use that instead of creating a `new LRUCache()` - the master thread will now act on the same cache as the workers. Support for updating the max, maxAge, stale of the cache. Update README for new features. Update tests to provide 100% coverage. Bump version - use a long random string as the lru key value to give the promise time to call the failsafe with reject() - update eslint config for code climate - disable duplication check - Fix `import/no-extraneous-dependencies` value # 1.2.0 / 2016-09-30 - Bump version - Add support and tests for `incr()`/`decr()` Update readme. Syntax cleanup. # 1.1.0 / 2016-09-29 - bump version - Update to failsafe with `resolve(undefined)` and `reject(Error)` via `options.failsafe=reject`, update docs # 1.0.6 / 2016-09-29 - Update readme, bump version - bump version # 1.0.5 / 2016-09-29 - Export `init` method stub Some refactoring Add tests for timeout coverage - 100% covered! Update README for `init()` # 1.0.4 / 2016-09-26 - Update dependencies, new lint rules, start worker for each cpu core and then call mocha done(), don’t resolve a value if the callback has timed out, set timeout in options, update README, bump version # 1.0.3 / 2016-09-21 - Bump version, remove code climate issue count since it won’t update - Update standards/linting Update main filename Update tests - update developer-tools confs lint code - update code climate config - use .eslintrc instead of .eslintrc.js - ignore eslintrc.js in code climate, update config file to standards - check in code climate file # 1.0.2 / 2016-08-25 - fix link in readme, bump version # 1.0.1 / 2016-08-25 - add header, eslint config, clean up code, bump version to 1.0.1 # 1.0.0 / 2016-08-25 - fix typo, more example updates - fix example - more comments - add more comments - Update README.md update with info about `namespace` option. - Update README.md update with api and options, fix example - Update README.md add description, install, usage, patterns. - fix for properties on promisified lru-cache - Update readme and add git repo to package - Update to create coverage badge - Only fork twice for tests - First version, linted with test coverage. - Initial commit