@thi.ng/cache 2.1.105 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2024-03-07T20:40:47Z
3
+ - **Last updated**: 2024-03-11T10:09:24Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,25 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [2.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/cache@2.2.0) (2024-03-11)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add value update callback, update TLRUCache ([d2fed06](https://github.com/thi-ng/umbrella/commit/d2fed06))
17
+ - add CacheOpts.update
18
+ - update doSetEntry() in all impls
19
+ - refactor TLRUCache.getSet() as async fn
20
+ - update TLRUCache.prune() to return eviction count
21
+ - various other refactoring
22
+ - add tests
23
+ - add docs
24
+
25
+ ### [2.1.106](https://github.com/thi-ng/umbrella/tree/@thi.ng/cache@2.1.106) (2024-03-11)
26
+
27
+ #### 🩹 Bug fixes
28
+
29
+ - fix ICache.get() return type, add docs ([d9f98f7](https://github.com/thi-ng/umbrella/commit/d9f98f7))
30
+
12
31
  ### [2.1.89](https://github.com/thi-ng/umbrella/tree/@thi.ng/cache@2.1.89) (2024-01-26)
13
32
 
14
33
  #### 🩹 Bug fixes
package/README.md CHANGED
@@ -47,7 +47,8 @@ strategies](https://en.wikipedia.org/wiki/Cache_replacement_policies).
47
47
  - Supports any types for both keys & values
48
48
  - Customizable cache limits (no. of items / actual size)
49
49
  - Customizable key equality checks (@thi.ng/equiv by default)
50
- - Optional item release callbacks (to clean up resources when value is expunged)
50
+ - Optional item update & release callbacks (e.g. to clean up resources when
51
+ value is being updated or evicted)
51
52
 
52
53
  ## Status
53
54
 
@@ -79,7 +80,7 @@ For Node.js REPL:
79
80
  const cache = await import("@thi.ng/cache");
80
81
  ```
81
82
 
82
- Package sizes (brotli'd, pre-treeshake): ESM: 1.08 KB
83
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.06 KB
83
84
 
84
85
  ## Dependencies
85
86
 
@@ -119,6 +120,10 @@ interface CacheOpts<K, V> {
119
120
  * Eviction callback to clean up resources
120
121
  */
121
122
  release: (k: K, v: V) => void;
123
+ /**
124
+ * Update callback to clean up resources
125
+ */
126
+ update: (k: K, vold: V, vnew: V) => void;
122
127
  /**
123
128
  * Factory for ES6 Map compatible instance
124
129
  * to index cache entries
@@ -137,13 +142,14 @@ interface CacheOpts<K, V> {
137
142
 
138
143
  ### LRU
139
144
 
140
- Removes least recently used items if a new item is added, but would not satisfy cache limit. Every time a cached item is accessed, it's recency is updated.
145
+ Removes least recently used items if a new item is added, but would not satisfy
146
+ cache limit. Every time a cached item is accessed, it's recency is updated.
141
147
 
142
148
  ```ts
143
- import * as cache from "@thi.ng/cache";
149
+ import { LRUCache } from "@thi.ng/cache";
144
150
 
145
- // caches can be configured with maxLen, maxSize and sizing functions (see below)
146
- const lru = new cache.LRUCache<string, number>(null, { maxlen: 3 });
151
+ // caches can be configured with maxlen, maxsize and sizing functions (see below)
152
+ const lru = new LRUCache<string, number>(null, { maxlen: 3 });
147
153
  lru.set("foo", 23);
148
154
  lru.set("bar", 42);
149
155
  lru.set("baz", 66);
@@ -155,7 +161,7 @@ lru.get("foo");
155
161
  // 23
156
162
 
157
163
  // caches are fully iterable
158
- // largely intended for inspection, does not update recency
164
+ // largely intended for inspection only, does not update recency
159
165
  // btw. "foo" appears last since most recently accessed
160
166
  [...lru]
161
167
  // [ { k: 'bar', v: 42, s: 0 },
@@ -172,12 +178,12 @@ lru.delete("foo");
172
178
 
173
179
  // caches have a getSet() method to obtain & store a new value
174
180
  // if its key is not known. this process is asynchronous
175
- lru.getSet("boo", () => Promise.resolve(999)).then(console.log);
181
+ lru.getSet("boo", async () => 999).then(console.log);
176
182
  // 999
177
183
 
178
184
  // the given retrieval fn is only called if there's a cache miss
179
185
  // (not the case here). `getSet()` always returns a promise
180
- lru.getSet("boo", () => Promise.resolve(123)).then(console.log);
186
+ lru.getSet("boo", async () => 123).then(console.log);
181
187
  // 999
182
188
 
183
189
  // caches can be limited by size instead of (or in addition to)
@@ -187,13 +193,14 @@ lru.getSet("boo", () => Promise.resolve(123)).then(console.log);
187
193
  // we also provide a release hook for demo purposes
188
194
 
189
195
  // the first arg is an iterable of KV pairs to store (just as for Map)
190
- lru = new cache.LRUCache<string, number[]>(
196
+ lru = new LRUCache<string, number[]>(
191
197
  [ ["a", [1.0, 2.0]], ["b", [3.0, 4.0, 5.0]] ],
192
198
  {
193
199
  maxsize: 32,
194
200
  ksize: (k) => k.length,
195
201
  vsize: (v) => v.length * 8,
196
- release: (k, v) => console.log("release", k, v)
202
+ release: (k, v) => console.log("release", k, v),
203
+ update: (k, vold, vnew) => console.log("update", k, vold, "->", vnew)
197
204
  }
198
205
  );
199
206
  // release a [1, 2] ("a" is evicted due to maxsize constraint)
@@ -206,19 +213,18 @@ lru.size
206
213
 
207
214
  ### TLRU
208
215
 
209
- Time-aware LRU cache. Extends LRU strategy with TTL (time-to-live)
210
- values associated with each entry. `has()` will only return `true` and
211
- `get()` only returns a cached value if its TTL hasn't yet expired. When
212
- adding a new value to the cache, first removes expired entries and if
213
- there's still not sufficient space removes entries in LRU order. `set()`
214
- takes an optional entry specific `ttl` arg. If not given, uses the cache
215
- instance's default (provided via ctor option arg). If no instance TTL is
216
- given, TTL defaults to 1 hour.
216
+ Time-aware [LRU cache](#lru). Extends LRU strategy with TTL (time-to-live)
217
+ values associated with each entry. `has()` will only return `true` and `get()`
218
+ only returns a cached value if its TTL hasn't yet expired. When adding a new
219
+ value to the cache, first removes expired entries and if there's still not
220
+ sufficient space removes entries in LRU order. `set()` takes an optional entry
221
+ specific `ttl` arg. If not given, uses the cache instance's default (provided
222
+ via ctor option arg). If no instance TTL is given, TTL defaults to 1 hour.
217
223
 
218
224
  ```ts
219
225
  import { TLRUCache } from "@thi.ng/cache";
220
226
 
221
- // same opts as LRUCache, but here with custom TTL period (in ms)
227
+ // same opts as LRUCache, but here with custom default TTL period (in ms)
222
228
  tlru = new TLRUCache(null, { ttl: 10000 });
223
229
 
224
230
  // with item specific TTL (500ms)
@@ -227,7 +233,8 @@ tlru.set("foo", 42, 500)
227
233
 
228
234
  ### MRU
229
235
 
230
- Similar to LRU, but removes most recently accessed items first. [Wikipedia](https://en.wikipedia.org/wiki/Cache_replacement_policies#Most_recently_used_(MRU))
236
+ Similar to LRU, but removes most recently accessed items first.
237
+ [Wikipedia](https://en.wikipedia.org/wiki/Cache_replacement_policies#Most_recently_used_(MRU))
231
238
 
232
239
  ```ts
233
240
  import { MRUCache } from "@thi.ng/cache";
package/api.d.ts CHANGED
@@ -1,21 +1,94 @@
1
- import type { Fn, Fn0, Fn2, ICopy, IEmpty, ILength, IRelease } from "@thi.ng/api";
1
+ import type { Fn, Fn0, Fn2, Fn3, ICopy, IEmpty, ILength, IRelease } from "@thi.ng/api";
2
2
  export interface ICache<K, V> extends Iterable<Readonly<[K, CacheEntry<K, V>]>>, ICopy<ICache<K, V>>, IEmpty<ICache<K, V>>, ILength, IRelease {
3
3
  readonly size: number;
4
+ /**
5
+ * Returns true if the given `key` is currently in the cache.
6
+ *
7
+ * @param key
8
+ */
4
9
  has(key: K): boolean;
5
- get(key: K, notFound?: V): V;
10
+ /**
11
+ * Looks up value for given `key` and if cached returns it. For cache
12
+ * misses, returns the optional `notFound` value or else `undefined`.
13
+ *
14
+ * @param key
15
+ * @param notFound
16
+ */
17
+ get(key: K, notFound?: V): V | undefined;
18
+ /**
19
+ * Set or updates value for given `key` and updates cache internal
20
+ * statistics (depending on cache policy).
21
+ *
22
+ * @param key
23
+ * @param val
24
+ */
6
25
  set(key: K, val: V): V;
26
+ /**
27
+ * Combination of {@link ICache.get} and {@link ICache.set}. Looks up the
28
+ * value for given `key` and returns it. In case of a cache miss, calls
29
+ * given `fn` to provide a value for the `key` and then stores it in the
30
+ * cache before returning that value.
31
+ *
32
+ * @param key
33
+ * @param fn
34
+ */
7
35
  getSet(key: K, fn: Fn0<Promise<V>>): Promise<V>;
36
+ /**
37
+ * Evicts cache entry for given `key` and returns true if the `key` was
38
+ * still cached. If that's the case and if {@link CacheOpts.release} was
39
+ * given when the cache was created, also calls that user provided release
40
+ * handler to perform custom clean up tasks.
41
+ *
42
+ * @param key
43
+ */
8
44
  delete(key: K): boolean;
45
+ /**
46
+ * Returns an iterator of cache entries.
47
+ */
9
48
  entries(): IterableIterator<Readonly<[K, CacheEntry<K, V>]>>;
49
+ /**
50
+ * Returns an iterator of currently cached keys.
51
+ */
10
52
  keys(): IterableIterator<Readonly<K>>;
53
+ /**
54
+ * Returns an iterator of currently cached values.
55
+ */
11
56
  values(): IterableIterator<Readonly<V>>;
12
57
  }
13
58
  export interface CacheOpts<K, V> {
59
+ /**
60
+ * Function to compute the size of a given key in arbitrary user defined
61
+ * units (must be same unit as given to {@link CacheOpts.maxsize}).
62
+ */
14
63
  ksize: Fn<K, number>;
64
+ /**
65
+ * Function to compute the size of a given value in arbitrary user defined
66
+ * units (must be same unit as given to {@link CacheOpts.maxsize}).
67
+ */
15
68
  vsize: Fn<V, number>;
69
+ /**
70
+ * Callback function to perform custom tasks when an item gets evicted from
71
+ * the cache.
72
+ */
16
73
  release: Fn2<K, V, void>;
74
+ /**
75
+ * Callback function to perform custom tasks when an item gets updated in the cache.
76
+ * The function will be called with a key and its associated old and news values.
77
+ */
78
+ update: Fn3<K, V, V, void>;
79
+ /**
80
+ * Custom ES6 Map compatible implementation to use as the cache's backing
81
+ * store.
82
+ */
17
83
  map: Fn0<Map<K, any>>;
84
+ /**
85
+ * Max number of items in the cache.
86
+ */
18
87
  maxlen: number;
88
+ /**
89
+ * Cache max size in arbitrary user defined units (must be same unit as
90
+ * given to {@link CacheOpts.ksize} and/or {@link CacheOpts.vsize}).
91
+ */
19
92
  maxsize: number;
20
93
  }
21
94
  export interface CacheEntry<K, V> {
package/lru.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Fn0 } from "@thi.ng/api";
1
+ import type { Fn0, Nullable } from "@thi.ng/api";
2
2
  import type { ConsCell } from "@thi.ng/dcons";
3
3
  import { DCons } from "@thi.ng/dcons/dcons";
4
4
  import type { CacheEntry, CacheOpts, ICache } from "./api.js";
@@ -7,7 +7,7 @@ export declare class LRUCache<K, V> implements ICache<K, V> {
7
7
  protected items: DCons<CacheEntry<K, V>>;
8
8
  protected opts: CacheOpts<K, V>;
9
9
  protected _size: number;
10
- constructor(pairs?: Iterable<[K, V]> | null, opts?: Partial<CacheOpts<K, V>>);
10
+ constructor(pairs: Nullable<Iterable<[K, V]>>, opts?: Partial<CacheOpts<K, V>>);
11
11
  get length(): number;
12
12
  get size(): number;
13
13
  [Symbol.iterator](): IterableIterator<readonly [K, CacheEntry<K, V>]>;
@@ -18,7 +18,7 @@ export declare class LRUCache<K, V> implements ICache<K, V> {
18
18
  empty(): LRUCache<K, V>;
19
19
  release(): boolean;
20
20
  has(key: K): boolean;
21
- get(key: K, notFound?: any): any;
21
+ get(key: K, notFound?: V): V | undefined;
22
22
  set(key: K, value: V): V;
23
23
  into(pairs: Iterable<[K, V]>): this;
24
24
  getSet(key: K, retrieve: Fn0<Promise<V>>): Promise<V>;
package/lru.js CHANGED
@@ -6,16 +6,14 @@ class LRUCache {
6
6
  opts;
7
7
  _size;
8
8
  constructor(pairs, opts) {
9
- const _opts = Object.assign(
10
- {
11
- maxlen: Infinity,
12
- maxsize: Infinity,
13
- map: () => /* @__PURE__ */ new Map(),
14
- ksize: () => 0,
15
- vsize: () => 0
16
- },
17
- opts
18
- );
9
+ const _opts = {
10
+ maxlen: Infinity,
11
+ maxsize: Infinity,
12
+ map: () => /* @__PURE__ */ new Map(),
13
+ ksize: () => 0,
14
+ vsize: () => 0,
15
+ ...opts
16
+ };
19
17
  this.map = _opts.map();
20
18
  this.items = new DCons();
21
19
  this._size = 0;
@@ -73,10 +71,7 @@ class LRUCache {
73
71
  }
74
72
  get(key, notFound) {
75
73
  const e = this.map.get(key);
76
- if (e) {
77
- return this.resetEntry(e);
78
- }
79
- return notFound;
74
+ return e ? this.resetEntry(e) : notFound;
80
75
  }
81
76
  set(key, value) {
82
77
  const size = this.opts.ksize(key) + this.opts.vsize(value);
@@ -96,12 +91,9 @@ class LRUCache {
96
91
  }
97
92
  return this;
98
93
  }
99
- getSet(key, retrieve) {
94
+ async getSet(key, retrieve) {
100
95
  const e = this.map.get(key);
101
- if (e) {
102
- return Promise.resolve(this.resetEntry(e));
103
- }
104
- return retrieve().then((v) => this.set(key, v));
96
+ return e ? this.resetEntry(e) : this.set(key, await retrieve());
105
97
  }
106
98
  delete(key) {
107
99
  const e = this.map.get(key);
@@ -116,16 +108,13 @@ class LRUCache {
116
108
  return e.value.v;
117
109
  }
118
110
  ensureSize() {
119
- const release = this.opts.release;
120
- const maxs = this.opts.maxsize;
121
- const maxl = this.opts.maxlen;
122
- while (this._size > maxs || this.length >= maxl) {
111
+ const { release, maxsize, maxlen } = this.opts;
112
+ while (this._size > maxsize || this.length >= maxlen) {
123
113
  const e = this.items.drop();
124
- if (!e) {
114
+ if (!e)
125
115
  return false;
126
- }
127
116
  this.map.delete(e.k);
128
- release && release(e.k, e.v);
117
+ release?.(e.k, e.v);
129
118
  this._size -= e.s;
130
119
  }
131
120
  return true;
@@ -134,11 +123,12 @@ class LRUCache {
134
123
  const ee = e.value;
135
124
  this.map.delete(ee.k);
136
125
  this.items.remove(e);
137
- this.opts.release && this.opts.release(ee.k, ee.v);
126
+ this.opts.release?.(ee.k, ee.v);
138
127
  this._size -= ee.s;
139
128
  }
140
129
  doSetEntry(e, k, v, s) {
141
130
  if (e) {
131
+ this.opts.update?.(k, e.value.v, v);
142
132
  e.value.v = v;
143
133
  e.value.s = s;
144
134
  this.items.asTail(e);
package/mru.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { ConsCell } from "@thi.ng/dcons";
2
- import type { CacheEntry, CacheOpts } from "./api.js";
2
+ import type { CacheEntry } from "./api.js";
3
3
  import { LRUCache } from "./lru.js";
4
4
  export declare class MRUCache<K, V> extends LRUCache<K, V> {
5
- constructor(pairs?: Iterable<[K, V]> | null, opts?: Partial<CacheOpts<K, V>>);
6
5
  empty(): MRUCache<K, V>;
7
6
  protected resetEntry(e: ConsCell<CacheEntry<K, V>>): V;
8
7
  protected doSetEntry(e: ConsCell<CacheEntry<K, V>> | undefined, k: K, v: V, s: number): void;
package/mru.js CHANGED
@@ -1,8 +1,5 @@
1
1
  import { LRUCache } from "./lru.js";
2
2
  class MRUCache extends LRUCache {
3
- constructor(pairs, opts) {
4
- super(pairs, opts);
5
- }
6
3
  empty() {
7
4
  return new MRUCache(null, this.opts);
8
5
  }
@@ -12,6 +9,7 @@ class MRUCache extends LRUCache {
12
9
  }
13
10
  doSetEntry(e, k, v, s) {
14
11
  if (e) {
12
+ this.opts.update?.(k, e.value.v, v);
15
13
  e.value.v = v;
16
14
  e.value.s = s;
17
15
  this.items.asHead(e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/cache",
3
- "version": "2.1.105",
3
+ "version": "2.2.0",
4
4
  "description": "In-memory cache implementations with ES6 Map-like API and different eviction strategies",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -91,5 +91,5 @@
91
91
  ],
92
92
  "year": 2018
93
93
  },
94
- "gitHead": "69100942474942f7446ac645d59d91e7dfc352f9\n"
94
+ "gitHead": "8c206a7a1aa00822e5adedb67d922591648f7ced\n"
95
95
  }
package/tlru.d.ts CHANGED
@@ -1,8 +1,14 @@
1
- import type { Fn0 } from "@thi.ng/api";
1
+ import type { Fn0, Nullable } from "@thi.ng/api";
2
2
  import type { ConsCell, DCons } from "@thi.ng/dcons";
3
3
  import type { CacheEntry, CacheOpts } from "./api.js";
4
4
  import { LRUCache } from "./lru.js";
5
5
  export interface TLRUCacheOpts<K, V> extends CacheOpts<K, V> {
6
+ /**
7
+ * Default time-to-live (cache period in milliseconds) before an entry is
8
+ * considered expired.
9
+ *
10
+ * @defaultValue 3600000 (1 hour)
11
+ */
6
12
  ttl: number;
7
13
  }
8
14
  export interface TLRUCacheEntry<K, V> extends CacheEntry<K, V> {
@@ -26,14 +32,23 @@ export declare class TLRUCache<K, V> extends LRUCache<K, V> {
26
32
  protected opts: TLRUCacheOpts<K, V>;
27
33
  protected map: Map<K, ConsCell<TLRUCacheEntry<K, V>>>;
28
34
  protected items: DCons<TLRUCacheEntry<K, V>>;
29
- constructor(pairs?: Iterable<[K, V]> | null, opts?: Partial<TLRUCacheOpts<K, V>>);
35
+ constructor(pairs: Nullable<Iterable<[K, V]>>, opts?: Partial<TLRUCacheOpts<K, V>>);
30
36
  empty(): TLRUCache<K, V>;
31
37
  has(key: K): boolean;
32
- get(key: K, notFound?: any): any;
38
+ get(key: K, notFound?: V): V | undefined;
33
39
  set(key: K, value: V, ttl?: number): V;
34
40
  getSet(key: K, retrieve: Fn0<Promise<V>>, ttl?: number): Promise<V>;
35
- prune(): void;
41
+ /**
42
+ * Scans all cached entries and evicts any which are expired by now (based
43
+ * on their TTL). Does **not** modify last-accessed time of remaining
44
+ * entries. Returns number of entries evicted.
45
+ *
46
+ * @remarks
47
+ * For very large caches, it's recommended to call this function in a
48
+ * cron-like manner...
49
+ */
50
+ prune(): number;
36
51
  protected ensureSize(): boolean;
37
- protected doSetTlruEntry(e: ConsCell<TLRUCacheEntry<K, V>> | undefined, k: K, v: V, s: number, t: number): void;
52
+ protected doSetEntry(e: ConsCell<TLRUCacheEntry<K, V>> | undefined, k: K, v: V, s: number, ttl?: number): void;
38
53
  }
39
54
  //# sourceMappingURL=tlru.d.ts.map
package/tlru.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import { LRUCache } from "./lru.js";
2
2
  class TLRUCache extends LRUCache {
3
3
  constructor(pairs, opts) {
4
- opts = Object.assign({ ttl: 60 * 60 * 1e3 }, opts);
5
- super(pairs, opts);
4
+ super(pairs, { ttl: 60 * 60 * 1e3, ...opts });
6
5
  }
7
6
  empty() {
8
7
  return new TLRUCache(null, this.opts);
@@ -26,36 +25,43 @@ class TLRUCache extends LRUCache {
26
25
  const additionalSize = Math.max(0, size - (e ? e.value.s : 0));
27
26
  this._size += additionalSize;
28
27
  if (this.ensureSize()) {
29
- const t = Date.now() + ttl;
30
- this.doSetTlruEntry(e, key, value, size, t);
28
+ this.doSetEntry(e, key, value, size, ttl);
31
29
  } else {
32
30
  this._size -= additionalSize;
33
31
  }
34
32
  return value;
35
33
  }
36
- getSet(key, retrieve, ttl = this.opts.ttl) {
34
+ async getSet(key, retrieve, ttl = this.opts.ttl) {
37
35
  const e = this.get(key);
38
- if (e) {
39
- return Promise.resolve(e);
40
- }
41
- return retrieve().then((v) => this.set(key, v, ttl));
36
+ return e !== void 0 ? e : this.set(key, await retrieve(), ttl);
42
37
  }
38
+ /**
39
+ * Scans all cached entries and evicts any which are expired by now (based
40
+ * on their TTL). Does **not** modify last-accessed time of remaining
41
+ * entries. Returns number of entries evicted.
42
+ *
43
+ * @remarks
44
+ * For very large caches, it's recommended to call this function in a
45
+ * cron-like manner...
46
+ */
43
47
  prune() {
44
48
  const now = Date.now();
45
49
  let cell = this.items.head;
50
+ let count = 0;
46
51
  while (cell) {
47
52
  if (cell.value.t < now) {
48
53
  this.removeEntry(cell);
54
+ count++;
49
55
  }
50
56
  cell = cell.next;
51
57
  }
58
+ return count;
52
59
  }
53
60
  ensureSize() {
54
- const maxs = this.opts.maxsize;
55
- const maxl = this.opts.maxlen;
61
+ const { maxlen, maxsize } = this.opts;
56
62
  const now = Date.now();
57
63
  let cell = this.items.head;
58
- while (cell && (this._size > maxs || this.length >= maxl)) {
64
+ while (cell && (this._size > maxsize || this.length >= maxlen)) {
59
65
  if (cell.value.t < now) {
60
66
  this.removeEntry(cell);
61
67
  }
@@ -63,8 +69,10 @@ class TLRUCache extends LRUCache {
63
69
  }
64
70
  return super.ensureSize();
65
71
  }
66
- doSetTlruEntry(e, k, v, s, t) {
72
+ doSetEntry(e, k, v, s, ttl = this.opts.ttl) {
73
+ const t = Date.now() + ttl;
67
74
  if (e) {
75
+ this.opts.update?.(k, e.value.v, v);
68
76
  e.value.v = v;
69
77
  e.value.s = s;
70
78
  e.value.t = t;