# Changelog

## BREAKING MAJOR RELEASE v6.0.0 (2026-05-19)

Major release focused on correctness, type safety, and modernization. Includes breaking changes.

### 💥 Breaking changes

- Dropped legacy callback API (enableLegacyCallbacks option). Use the synchronous API directly.
- ttl(key, 0) now deletes the key to match the documented semantics. Previously it silently re-applied stdTTL.
- Storage switched from plain object to Map. If you accessed cache.data[key] directly (undocumented), use cache.data.get(key) / cache.data.size instead.
- take event signature: now emits (key, value) instead of (key), and no longer fires when the key is absent.
- Removed hand-written index.d.ts. Types are now generated by tsup from the source — they always match the implementation.
- Node.js ≥ 22 required (already declared, now relied upon: native structuredClone).

### 🐛 Bug fixes

- Fix double-cloning of every enumerable property in the deep-clone routine.
- Fix off-by-one in mset maxKeys check (>= → >); maxKeys=N now actually permits N keys.
- Fix prototype-pollution / collision surface: cache.get("toString") now returns undefined instead of `Object.prototype.toString`.
- Fix mget emitting miss with the full keys array instead of the missing key — now consistent with get.
- Fix set(key, undefined) silently turning into null. undefined now round-trips correctly.
- Fix take emitting take event for missing keys.
- Fix getTtl declaration mismatch between .d.ts and runtime behavior.

### ✨ New features

- fetchAsync(key, ttl?, fn) — async variant of fetch with built-in single-flight deduplication: concurrent misses on the same key share one fn() invocation.
- Typed events: cache.on('set', (key, value) => …) is now fully typed; same for del, expired, miss, take, flush, flush_stats.
- Generic get / mget / take / fetch preserve the caller-supplied type.
- Stricter input validation: NaN, non-number TTLs are rejected with ETTLTYPE.

### 🧹 Refactor & internals

- Replaced the 251-line bespoke deep-clone implementation with a 30-line wrapper around native structuredClone (faster, supports TypedArray/ArrayBuffer/circular refs out of the box; Promises/functions/errors pass through by reference).
- Centralized TTL handling in a single _resolveExpiry helper — eliminated duplicate ttl-resolution paths in set, ttl, and _wrap.
- Centralized "get + expiry check" path in _liveEntry, used by get/has/take/getTtl/mget.
- Collapsed the three-layer error factory (_ERRORS → _initErrors → ERRORS → _error) into a single helper.
- Removed the ~30-line bind block in the constructor.
- Replaced widespread any usage with unknown and proper WrappedValue / ErrorCode types.
- Fixed a race in fetchAsync between await resolution and cache population.

### 🚀 Migration guide

```js
// Before
const cache = new NodeCache({ enableLegacyCallbacks: true });
cache.get('key', (err, value) => { /* ... */ });
const raw = cache.data['key'];

// After
const cache = new NodeCache();
const value = cache.get('key');
const raw = cache.data.get('key');

// New: single-flight async fetch
const user = await cache.fetchAsync('user:42', 60, () => db.getUser(42));
```

## BREAKING MAJOR RELEASE v5.x

The recent 5.6.0 release:
* boundMethodCheck was removed as part of the legacy code
* Added Event on missed in get [#223](https://github.com/node-cache/node-cache/pull/223)
* Added Event on take in take

The recent 5.5.0 release:
* Clone pkg removed from the project

The recent 5.4.0 release:
* rewritten from Javascript -> Typescript

The recent 5.3.0 release:
* rewritten from ES5 -> ES6, IIFE -> CommonJS
* updated docs

The recent 5.2.0 release:
* dropped support for node versions before 18.x!
* rewritten from coffescript to javascripts 
* name changed node-cache -> node-internal-cache

The recent 5.x release:
* dropped support for node versions before 8.x!
* removed the callback-based api from all methods (you can re-enable them with the option `enableLegacyCallbacks`)
