evicting-cache 2.3.0 → 3.0.1

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/README.md CHANGED
@@ -1,7 +1,16 @@
1
1
  # Evicting Cache
2
- JavaScript Cache using an LRU (Least Recently Used) algorithm
3
2
 
4
- The cache is backed by a LinkedMap, which is a Map that maintains insertion order. When the cache is full, the least recently used item is evicted.
3
+ [![npm version](https://img.shields.io/npm/v/@d1g1tal/evicting-cache?color=blue)](https://www.npmjs.com/package/@d1g1tal/evicting-cache)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@d1g1tal/evicting-cache)](https://www.npmjs.com/package/@d1g1tal/evicting-cache)
5
+ [![CI](https://github.com/D1g1talEntr0py/evicting-cache/actions/workflows/ci.yml/badge.svg)](https://github.com/D1g1talEntr0py/evicting-cache/actions/workflows/ci.yml)
6
+ [![codecov](https://codecov.io/gh/D1g1talEntr0py/evicting-cache/graph/badge.svg)](https://codecov.io/gh/D1g1talEntr0py/evicting-cache)
7
+ [![License: MIT](https://img.shields.io/github/license/D1g1talEntr0py/evicting-cache)](https://github.com/D1g1talEntr0py/evicting-cache/blob/main/LICENSE)
8
+ [![Node.js](https://img.shields.io/node/v/@d1g1tal/evicting-cache)](https://nodejs.org)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
10
+
11
+ A lightweight, high-performance TypeScript implementation of an LRU (Least Recently Used) cache with automatic eviction.
12
+
13
+ The cache is backed by JavaScript's native `Map`, leveraging its insertion order guarantee for efficient LRU semantics. When the cache reaches capacity, the least recently used item is automatically evicted.
5
14
 
6
15
  ## Installation
7
16
 
@@ -13,27 +22,256 @@ pnpm add evicting-cache
13
22
  npm install evicting-cache
14
23
  ```
15
24
 
25
+ ## Features
26
+
27
+ - 🚀 **High Performance** - O(1) operations for get, put, delete, and evict
28
+ - 📦 **Lightweight** - Zero dependencies, small bundle size
29
+ - 🔄 **LRU Eviction** - Automatic removal of least recently used items
30
+ - 📊 **Statistics Tracking** - Built-in hit/miss ratio monitoring
31
+ - 🔢 **Batch Operations** - Efficient multi-key operations
32
+ - 🎯 **Type Safe** - Full TypeScript support with generics
33
+ - ✅ **100% Test Coverage** - Thoroughly tested and reliable
34
+ - 🌐 **Modern ES Modules** - Native ESM support
35
+ - 🔍 **Map-like API** - Familiar interface with additional features
36
+
16
37
  ## Usage
17
- ```javascript
18
- import EvictingCache from 'evicting-cache';
19
38
 
20
- // Constructor accepts a number, which is the maximum number of items to store.
21
- // default is 100
22
- const cache = new EvictingCache(3);
39
+ ### Basic Example
40
+
41
+ ```typescript
42
+ import { EvictingCache } from 'evicting-cache';
43
+
44
+ // Create a cache with capacity of 3 items (default is 100)
45
+ const cache = new EvictingCache<string, string>(3);
23
46
 
24
- // Obviously a contrived example, but this is what you get with AI...
25
47
  cache.put('key1', 'value1');
26
48
  cache.put('key2', 'value2');
27
49
  cache.put('key3', 'value3');
28
- cache.put('key4', 'value4');
50
+ cache.put('key4', 'value4'); // 'key1' is evicted (LRU)
51
+
52
+ console.log(cache.get('key1')); // null (evicted)
53
+ console.log(cache.get('key2')); // 'value2'
54
+ console.log(cache.get('key3')); // 'value3'
55
+ console.log(cache.get('key4')); // 'value4'
56
+
57
+ cache.put('key5', 'value5'); // 'key2' is evicted
58
+
59
+ console.log(cache.get('key2')); // null (evicted)
60
+ console.log(cache.size); // 3
61
+ ```
62
+
63
+ ### LRU Behavior
64
+
65
+ ```typescript
66
+ const cache = new EvictingCache<string, number>(3);
67
+
68
+ cache.put('a', 1);
69
+ cache.put('b', 2);
70
+ cache.put('c', 3);
71
+ // Order: a, b, c (a is LRU)
72
+
73
+ cache.get('a'); // Access 'a', moves it to most recent
74
+ // Order: b, c, a (b is now LRU)
75
+
76
+ cache.put('d', 4); // Evicts 'b' (LRU)
77
+ // Order: c, a, d
78
+
79
+ console.log(cache.has('b')); // false (evicted)
80
+ ```
81
+
82
+ ### Peek Without Affecting LRU
83
+
84
+ ```typescript
85
+ const cache = new EvictingCache<string, string>(2);
86
+
87
+ cache.put('x', 'hello');
88
+ cache.put('y', 'world');
89
+
90
+ // peek() reads without updating LRU order
91
+ console.log(cache.peek('x')); // 'hello'
92
+ // 'x' remains LRU
93
+
94
+ cache.put('z', 'new'); // 'x' is evicted
95
+ console.log(cache.has('x')); // false
96
+ ```
97
+
98
+ ### Get or Compute
99
+
100
+ ```typescript
101
+ const cache = new EvictingCache<string, number>(10);
102
+
103
+ // Get existing value or compute and cache it
104
+ const value = cache.getOrPut('userId:123', () => {
105
+ // Expensive computation only happens if key is missing
106
+ return fetchUserFromDatabase('123');
107
+ });
108
+
109
+ // If producer throws, cache remains unchanged
110
+ try {
111
+ cache.getOrPut('key', () => {
112
+ throw new Error('Failed to compute');
113
+ });
114
+ } catch (error) {
115
+ // Cache state is unmodified
116
+ }
117
+ ```
118
+
119
+ ### Batch Operations
120
+
121
+ ```typescript
122
+ const cache = new EvictingCache<string, number>(100);
123
+
124
+ // Add multiple entries at once
125
+ cache.putAll([
126
+ ['a', 1],
127
+ ['b', 2],
128
+ ['c', 3]
129
+ ]);
130
+
131
+ // Can also use a Map
132
+ cache.putAll(new Map([['d', 4], ['e', 5]]));
133
+
134
+ // Get multiple values (returns Map, excludes missing keys)
135
+ const values = cache.getAll(['a', 'b', 'missing']);
136
+ console.log(values.size); // 2
137
+ console.log(values.get('a')); // 1
138
+ console.log(values.has('missing')); // false
139
+
140
+ // Delete multiple keys (returns count removed)
141
+ const removed = cache.deleteAll(['a', 'c', 'missing']);
142
+ console.log(removed); // 2
143
+ ```
144
+
145
+ ### Cache Statistics
146
+
147
+ ```typescript
148
+ const cache = new EvictingCache<string, string>(10);
149
+
150
+ cache.put('a', 'value1');
151
+ cache.get('a'); // hit
152
+ cache.get('b'); // miss
153
+ cache.get('a'); // hit
154
+
155
+ const stats = cache.getStats();
156
+ console.log(stats.hits); // 2
157
+ console.log(stats.misses); // 1
158
+ console.log(stats.hitRate); // 0.667 (66.7%)
159
+
160
+ // Reset statistics
161
+ cache.resetStats();
162
+ console.log(cache.getStats().hits); // 0
163
+ ```
164
+
165
+ ### Iteration
166
+
167
+ ```typescript
168
+ const cache = new EvictingCache<string, number>(3);
169
+ cache.put('a', 1);
170
+ cache.put('b', 2);
171
+ cache.put('c', 3);
172
+
173
+ // Iterate over entries (LRU to MRU order)
174
+ for (const [key, value] of cache) {
175
+ console.log(key, value);
176
+ }
177
+
178
+ // Or use forEach
179
+ cache.forEach((value, key, cache) => {
180
+ console.log(key, value);
181
+ });
182
+
183
+ // Get keys, values, or entries
184
+ console.log([...cache.keys()]); // ['a', 'b', 'c']
185
+ console.log([...cache.values()]); // [1, 2, 3]
186
+ console.log([...cache.entries()]); // [['a', 1], ['b', 2], ['c', 3]]
187
+ ```
188
+
189
+ ## API Reference
190
+
191
+ ### Constructor
192
+
193
+ - `new EvictingCache<K, V>(capacity?: number)` - Creates a new cache with the specified capacity (default: 100)
194
+
195
+ ### Core Methods
196
+
197
+ - `get(key: K): V | null` - Returns value and updates LRU order
198
+ - `peek(key: K): V | null` - Returns value without updating LRU order
199
+ - `put(key: K, value: V): void` - Adds or updates a key-value pair
200
+ - `delete(key: K): boolean` - Removes a key from the cache
201
+ - `has(key: K): boolean` - Checks if a key exists
202
+ - `getOrPut(key: K, producer: () => V): V` - Gets existing value or computes and stores new one
203
+ - `evict(): boolean` - Manually removes the LRU item
204
+ - `clear(): void` - Removes all items from the cache
205
+
206
+ ### Batch Operations
207
+
208
+ - `putAll(entries: Iterable<[K, V]>): void` - Adds multiple entries
209
+ - `getAll(keys: Iterable<K>): Map<K, V>` - Gets multiple values
210
+ - `deleteAll(keys: Iterable<K>): number` - Removes multiple keys
211
+
212
+ ### Statistics
213
+
214
+ - `getStats(): { hits: number, misses: number, hitRate: number }` - Returns cache statistics
215
+ - `resetStats(): void` - Resets statistics counters
216
+
217
+ ### Iteration
218
+
219
+ - `keys(): IterableIterator<K>` - Returns an iterator over keys
220
+ - `values(): IterableIterator<V>` - Returns an iterator over values
221
+ - `entries(): IterableIterator<[K, V]>` - Returns an iterator over entries
222
+ - `forEach(callback: (value: V, key: K, cache: EvictingCache<K, V>) => void, thisArg?: unknown): void` - Executes callback for each entry
223
+ - `[Symbol.iterator]()` - Makes the cache iterable
224
+
225
+ ### Properties
226
+
227
+ - `capacity: number` - Maximum number of items (read-only)
228
+ - `size: number` - Current number of items (read-only)
229
+
230
+ ## Performance
231
+
232
+ All core operations have **O(1)** time complexity:
233
+
234
+ | Operation | Complexity | Updates LRU |
235
+ |-----------|------------|-------------|
236
+ | `get(key)` | O(1) | ✅ Yes |
237
+ | `peek(key)` | O(1) | ❌ No |
238
+ | `put(key, value)` | O(1) | ✅ Yes |
239
+ | `delete(key)` | O(1) | ❌ N/A |
240
+ | `evict()` | O(1) | ❌ N/A |
241
+ | `has(key)` | O(1) | ❌ No |
242
+ | `clear()` | O(1) | ❌ N/A |
243
+
244
+ **Space complexity:** O(n) where n is the capacity
245
+
246
+ ## TypeScript Support
247
+
248
+ Full TypeScript support with generic types:
249
+
250
+ ```typescript
251
+ // Strongly typed cache
252
+ const userCache = new EvictingCache<number, User>(100);
253
+ const configCache = new EvictingCache<string, Config>(50);
254
+
255
+ // Works with any key/value types
256
+ const complexCache = new EvictingCache<{ id: number; tenant: string }, Promise<Data>>(25);
257
+ ```
258
+
259
+ ## Browser Compatibility
260
+
261
+ Compatible with all modern browsers and Node.js environments that support ES2015+ features:
262
+ - Chrome/Edge: ✅ Latest
263
+ - Firefox: ✅ Latest
264
+ - Safari: ✅ 15+
265
+ - Node.js: ✅ 14+
266
+
267
+ ## License
268
+
269
+ ISC License - See [LICENSE](LICENSE) file for details
29
270
 
30
- console.log(cache.get('key1')); // undefined
31
- console.log(cache.get('key2')); // value2
32
- console.log(cache.get('key3')); // value3
33
- console.log(cache.get('key4')); // value4
271
+ ## Contributing
34
272
 
35
- cache.put('key5', 'value5');
273
+ Contributions are welcome! Please feel free to submit a Pull Request.
36
274
 
37
- console.log(cache.get('key2')); // undefined
275
+ ## Changelog
38
276
 
39
- ```
277
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
package/package.json CHANGED
@@ -1,20 +1,37 @@
1
1
  {
2
2
  "name": "evicting-cache",
3
3
  "author": "D1g1talEntr0py",
4
- "version": "2.3.0",
4
+ "version": "3.0.1",
5
+ "packageManager": "pnpm@10.30.3",
5
6
  "license": "ISC",
6
7
  "description": "Cache implementation with an LRU evicting policy",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/D1g1talEntr0py/evicting-cache"
11
+ },
7
12
  "type": "module",
8
13
  "exports": {
9
14
  ".": {
10
15
  "types": "./dist/evicting-cache.d.ts",
11
- "import": "./src/evicting-cache.js"
16
+ "import": "./dist/evicting-cache.js"
12
17
  }
13
18
  },
14
19
  "files": [
15
20
  "dist/",
16
21
  "src/"
17
22
  ],
23
+ "scripts": {
24
+ "build": "tsbuild",
25
+ "build:watch": "tsbuild --watch",
26
+ "type-check": "tsbuild --noEmit",
27
+ "lint": "eslint",
28
+ "test": "vitest run",
29
+ "test:coverage": "vitest run --coverage",
30
+ "test:watch": "vitest",
31
+ "prepublish": "pnpm lint && pnpm test && pnpm -s build --minify --force",
32
+ "preversion": "pnpm lint && pnpm test",
33
+ "version": "node -e \"const fs=require('fs');const d=new Date().toISOString().split('T')[0];const v=require('./package.json').version;let c=fs.readFileSync('CHANGELOG.md','utf8');c=c.replace('## [Unreleased]','## ['+v+'] - '+d);fs.writeFileSync('CHANGELOG.md',c);\" && git add CHANGELOG.md"
34
+ },
18
35
  "keywords": [
19
36
  "cache",
20
37
  "evicting-cache",
@@ -23,36 +40,25 @@
23
40
  "evicting-lru-cache"
24
41
  ],
25
42
  "devDependencies": {
26
- "@eslint/compat": "^1.4.0",
27
- "@eslint/js": "^9.38.0",
43
+ "@d1g1tal/tsbuild": "^1.3.2",
44
+ "@eslint/compat": "^2.0.2",
45
+ "@eslint/js": "^10.0.1",
28
46
  "@types/eslint": "^9.6.1",
29
- "@types/node": "^24.9.1",
30
- "@typescript-eslint/eslint-plugin": "^8.46.2",
31
- "@typescript-eslint/parser": "^8.46.2",
32
- "@vitest/coverage-v8": "^4.0.4",
33
- "eslint": "^9.38.0",
34
- "eslint-plugin-compat": "^6.0.2",
35
- "eslint-plugin-jsdoc": "^61.1.9",
36
- "globals": "^16.4.0",
47
+ "@types/node": "^25.3.3",
48
+ "@typescript-eslint/eslint-plugin": "^8.56.1",
49
+ "@typescript-eslint/parser": "^8.56.1",
50
+ "@vitest/coverage-v8": "^4.0.18",
51
+ "eslint": "^10.0.2",
52
+ "eslint-plugin-compat": "^7.0.0",
53
+ "eslint-plugin-jsdoc": "^62.7.1",
54
+ "globals": "^17.4.0",
37
55
  "typescript": "^5.9.3",
38
- "typescript-eslint": "^8.46.2",
39
- "vitest": "^4.0.4"
56
+ "typescript-eslint": "^8.56.1",
57
+ "vitest": "^4.0.18"
40
58
  },
41
59
  "browserslist": [
42
60
  "defaults",
43
61
  "not ios_saf < 15",
44
62
  "not op_mini all"
45
- ],
46
- "scripts": {
47
- "build": "tsbuild",
48
- "build:watch": "tsbuild --watch",
49
- "type-check": "tsbuild --type-check",
50
- "lint": "eslint",
51
- "test": "vitest run",
52
- "test:coverage": "vitest run --coverage",
53
- "test:watch": "vitest",
54
- "prepublish": "pnpm lint && pnpm test && pnpm -s build --minify --force",
55
- "preversion": "pnpm lint && pnpm test",
56
- "version": "node -e \"const fs=require('fs');const d=new Date().toISOString().split('T')[0];const v=require('./package.json').version;let c=fs.readFileSync('CHANGELOG.md','utf8');c=c.replace('## [Unreleased]','## ['+v+'] - '+d);fs.writeFileSync('CHANGELOG.md',c);\" && git add CHANGELOG.md"
57
- }
58
- }
63
+ ]
64
+ }
@@ -1,181 +0,0 @@
1
- type CacheStats = {
2
- hits: number;
3
- misses: number;
4
- hitRate: number;
5
- };
6
- /** JavaScript implementation of a Least Recently Used(LRU) Cache using a Map. */
7
- declare class EvictingCache<K, V> {
8
- private readonly _capacity;
9
- private readonly cache;
10
- private hits;
11
- private misses;
12
- /**
13
- * Creates a new Evicting Cache with the given capacity.
14
- *
15
- * @param {number} [capacity=100] The maximum number of key-value pairs the cache can hold.
16
- */
17
- constructor(capacity?: number);
18
- /**
19
- * Returns the value associated with the given key from the cache and updates the LRU order.
20
- *
21
- * @param {K} key The key to get the value for.
22
- * @returns {V | null} The associated value if the key is in the cache, or null otherwise.
23
- */
24
- get(key: K): V | null;
25
- /**
26
- * Returns true if the given key is in the cache, false otherwise.
27
- *
28
- * @param {K} key The key to check.
29
- * @returns {boolean} True if the key is in the cache, false otherwise.
30
- */
31
- has(key: K): boolean;
32
- /**
33
- * Adds a new key-value pair to the cache and updates the LRU order.
34
- * If adding the new pair will exceed the capacity, removes the least recently used pair from the cache.
35
- *
36
- * @param {K} key The key to add.
37
- * @param {V} value The value to add.
38
- * @returns {void}
39
- */
40
- put(key: K, value: V): void;
41
- /**
42
- * Removes the specified key from the cache.
43
- *
44
- * @param {K} key The key to remove.
45
- * @returns {boolean} True if the key was in the cache and was removed, false otherwise.
46
- */
47
- delete(key: K): boolean;
48
- /**
49
- * Returns the value associated with the given key from the cache without updating the LRU order.
50
- *
51
- * @param {K} key The key to get the value for.
52
- * @returns {V | null} The associated value if the key is in the cache, or null otherwise.
53
- */
54
- peek(key: K): V | null;
55
- /**
56
- * Returns the value for the key if it exists in the cache. If not, put the key-value pair into the cache and return the value.
57
- * If the producer function throws an error, the cache state is not modified.
58
- *
59
- * @param {K} key The key.
60
- * @param {function(): V} producer The value to put if the key does not exist in the cache.
61
- * @returns {V} The value corresponding to the key.
62
- */
63
- getOrPut(key: K, producer: () => V): V;
64
- /**
65
- * Removes the least recently used key-value pair from the cache.
66
- * @returns {boolean} True if an item was removed, false otherwise.
67
- */
68
- evict(): boolean;
69
- /**
70
- * Clears the cache and the LRU list.
71
- * @returns {void}
72
- */
73
- clear(): void;
74
- /**
75
- * Executes a provided function once per each key/value pair in the cache, in insertion order.
76
- * @param {(value: V, key: K, cache: EvictingCache<K, V>) => void} callbackfn Function to execute for each element.
77
- * @param {unknown} [thisArg] Value to use as `this` when executing callback.
78
- * @returns {void}
79
- */
80
- forEach(callbackfn: (value: V, key: K, cache: EvictingCache<K, V>) => void, thisArg?: unknown): void;
81
- /**
82
- * Adds multiple key-value pairs to the cache.
83
- * Each pair is added individually, following the same LRU eviction rules as put().
84
- * @param {Iterable<[K, V]>} entries The entries to add.
85
- * @returns {void}
86
- */
87
- putAll(entries: Iterable<[K, V]>): void;
88
- /**
89
- * Gets multiple values from the cache.
90
- * Each get updates the LRU order for that key.
91
- *
92
- * @param {Iterable<K>} keys The keys to get values for.
93
- * @returns {Map<K, V>} A map of keys to their values (excludes missing keys).
94
- */
95
- getAll(keys: Iterable<K>): Map<K, V>;
96
- /**
97
- * Removes multiple keys from the cache.
98
- *
99
- * @param {Iterable<K>} keys The keys to remove.
100
- * @returns {number} The number of keys that were removed.
101
- */
102
- deleteAll(keys: Iterable<K>): number;
103
- /**
104
- * Gets cache statistics including hit/miss counts and hit rate.
105
- *
106
- * @returns {CacheStats} Cache statistics.
107
- */
108
- getStats(): CacheStats;
109
- /**
110
- * Resets cache statistics to zero.
111
- *
112
- * @returns {void}
113
- */
114
- resetStats(): void;
115
- /**
116
- * Gets the capacity of the cache.
117
- * This is the maximum number of key-value pairs the cache can hold.
118
- * This is not the number of key-value pairs in the cache.
119
- *
120
- * @readonly
121
- * @returns {number} The capacity of the cache.
122
- */
123
- get capacity(): number;
124
- /**
125
- * Gets the size of the cache.
126
- * This is the number of key-value pairs in the cache.
127
- * This is not the capacity of the cache.
128
- * The capacity is the maximum number of key-value pairs the cache can hold.
129
- * The size is the number of key-value pairs currently in the cache.
130
- * The size will be less than or equal to the capacity.
131
- *
132
- * @returns {number} The size of the cache.
133
- */
134
- get size(): number;
135
- /**
136
- * Returns an iterator over the keys in the cache.
137
- * The keys are returned in the order of least recently used to most recently used.
138
- *
139
- * @returns {IterableIterator<K>} An iterator over the keys in the cache.
140
- */
141
- keys(): IterableIterator<K>;
142
- /**
143
- * Returns an iterator over the values in the cache.
144
- * The values are returned in the order of least recently used to most recently used.
145
- *
146
- * @returns {IterableIterator<V>} An iterator over the values in the cache.
147
- */
148
- values(): IterableIterator<V>;
149
- /**
150
- * Returns an iterator over the entries in the cache.
151
- * The entries are returned in the order of least recently used to most recently used.
152
- *
153
- * @returns {IterableIterator<[K, V]>} An iterator over the entries in the cache.
154
- */
155
- entries(): IterableIterator<[K, V]>;
156
- /**
157
- * Returns an iterator over the entries in the cache.
158
- * The entries are returned in the order of least recently used to most recently used.
159
- *
160
- * @returns {IterableIterator<[K, V]>} An iterator over the entries in the cache.
161
- */
162
- [Symbol.iterator](): IterableIterator<[K, V]>;
163
- /**
164
- * Gets the description of the object.
165
- *
166
- * @override
167
- * @returns {string} The description of the object.
168
- */
169
- get [Symbol.toStringTag](): string;
170
- /**
171
- * Puts a key-value pair into the cache and evicts the least recently used item if necessary.
172
- * If the key already exists, the item is removed and re-added to update its position.
173
- * If the cache is full, the least recently used item is evicted and the new item is added.
174
- * @param {K} key The key to put.
175
- * @param {V} value The value to put.
176
- * @returns {V} The value that was put.
177
- */
178
- private putAndEvict;
179
- }
180
-
181
- export { EvictingCache };
@@ -1,2 +0,0 @@
1
- var r=class{_capacity;cache;hits=0;misses=0;constructor(e=100){if(e<1)throw new RangeError("capacity must be greater than 0");if(!Number.isInteger(e))throw new RangeError("capacity must be an integer");this._capacity=e,this.cache=new Map}get(e){let t=this.cache.get(e);return t===void 0?(this.misses++,null):(this.hits++,this.cache.delete(e),this.cache.set(e,t),t)}has(e){return this.cache.has(e)}put(e,t){this.putAndEvict(e,t)}delete(e){return this.cache.delete(e)}peek(e){return this.cache.get(e)??null}getOrPut(e,t){let s=this.get(e);if(s!==null)return s;let i=t();return this.putAndEvict(e,i)}evict(){let e=this.cache.keys().next();return e.done?!1:this.cache.delete(e.value)}clear(){this.cache.clear()}forEach(e,t){let s=t!==void 0?e.bind(t):e;this.cache.forEach((i,a)=>s(i,a,this))}putAll(e){for(let[t,s]of e)this.put(t,s)}getAll(e){let t=new Map;for(let s of e){let i=this.get(s);i!==null&&t.set(s,i)}return t}deleteAll(e){let t=0;for(let s of e)this.cache.delete(s)&&t++;return t}getStats(){let e=this.hits+this.misses;return{hits:this.hits,misses:this.misses,hitRate:e===0?0:this.hits/e}}resetStats(){this.hits=0,this.misses=0}get capacity(){return this._capacity}get size(){return this.cache.size}keys(){return this.cache.keys()}values(){return this.cache.values()}entries(){return this.cache.entries()}[Symbol.iterator](){return this.entries()}get[Symbol.toStringTag](){return"EvictingCache"}putAndEvict(e,t){return!this.cache.delete(e)&&this._capacity<=this.cache.size&&this.evict(),this.cache.set(e,t),t}};export{r as EvictingCache};
2
- //# sourceMappingURL=evicting-cache.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/evicting-cache.ts"],
4
- "sourcesContent": ["type CacheStats = {\n\thits: number;\n\tmisses: number;\n\thitRate: number;\n};\n\n/** JavaScript implementation of a Least Recently Used(LRU) Cache using a Map. */\nexport class EvictingCache<K, V> {\n\tprivate readonly _capacity: number;\n\tprivate readonly cache: Map<K, V>;\n\tprivate hits = 0;\n\tprivate misses = 0;\n\n\t/**\n\t * Creates a new Evicting Cache with the given capacity.\n\t *\n\t * @param {number} [capacity=100] The maximum number of key-value pairs the cache can hold.\n\t */\n\tconstructor(capacity: number = 100) {\n\t\tif (capacity < 1) { throw new RangeError('capacity must be greater than 0') }\n\t\tif (!Number.isInteger(capacity)) { throw new RangeError('capacity must be an integer') }\n\n\t\tthis._capacity = capacity;\n\t\tthis.cache = new Map();\n\t}\n\n\t/**\n\t * Returns the value associated with the given key from the cache and updates the LRU order.\n\t *\n\t * @param {K} key The key to get the value for.\n\t * @returns {V | null} The associated value if the key is in the cache, or null otherwise.\n\t */\n\tget(key: K): V | null {\n\t\tconst value = this.cache.get(key);\n\t\tif (value === undefined) {\n\t\t\tthis.misses++;\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.hits++;\n\t\tthis.cache.delete(key);\n\t\t// Move the accessed item to the end (most recently used)\n\t\tthis.cache.set(key, value);\n\n\t\treturn value;\n\t}\n\n\t/**\n\t * Returns true if the given key is in the cache, false otherwise.\n\t *\n\t * @param {K} key The key to check.\n\t * @returns {boolean} True if the key is in the cache, false otherwise.\n\t */\n\thas(key: K): boolean {\n\t\treturn this.cache.has(key);\n\t}\n\n\t/**\n\t * Adds a new key-value pair to the cache and updates the LRU order.\n\t * If adding the new pair will exceed the capacity, removes the least recently used pair from the cache.\n\t *\n\t * @param {K} key The key to add.\n\t * @param {V} value The value to add.\n\t * @returns {void}\n\t */\n\tput(key: K, value: V): void {\n\t\tthis.putAndEvict(key, value);\n\t}\n\n\t/**\n\t * Removes the specified key from the cache.\n\t *\n\t * @param {K} key The key to remove.\n\t * @returns {boolean} True if the key was in the cache and was removed, false otherwise.\n\t */\n\tdelete(key: K): boolean {\n\t\treturn this.cache.delete(key);\n\t}\n\n\t/**\n\t * Returns the value associated with the given key from the cache without updating the LRU order.\n\t *\n\t * @param {K} key The key to get the value for.\n\t * @returns {V | null} The associated value if the key is in the cache, or null otherwise.\n\t */\n\tpeek(key: K): V | null {\n\t\treturn this.cache.get(key) ?? null;\n\t}\n\n\t/**\n\t * Returns the value for the key if it exists in the cache. If not, put the key-value pair into the cache and return the value.\n\t * If the producer function throws an error, the cache state is not modified.\n\t *\n\t * @param {K} key The key.\n\t * @param {function(): V} producer The value to put if the key does not exist in the cache.\n\t * @returns {V} The value corresponding to the key.\n\t */\n\tgetOrPut(key: K, producer: () => V): V {\n\t\tconst existing = this.get(key);\n\t\tif (existing !== null) { return existing }\n\n\t\t// If producer throws, cache state remains unchanged\n\t\tconst value = producer();\n\t\treturn this.putAndEvict(key, value);\n\t}\n\n\t/**\n\t * Removes the least recently used key-value pair from the cache.\n\t * @returns {boolean} True if an item was removed, false otherwise.\n\t */\n\tevict(): boolean {\n\t\tconst firstEntry = this.cache.keys().next();\n\t\tif (firstEntry.done) { return false }\n\n\t\treturn this.cache.delete(firstEntry.value);\n\t}\n\n\t/**\n\t * Clears the cache and the LRU list.\n\t * @returns {void}\n\t */\n\tclear(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/**\n\t * Executes a provided function once per each key/value pair in the cache, in insertion order.\n\t * @param {(value: V, key: K, cache: EvictingCache<K, V>) => void} callbackfn Function to execute for each element.\n\t * @param {unknown} [thisArg] Value to use as `this` when executing callback.\n\t * @returns {void}\n\t */\n\tforEach(callbackfn: (value: V, key: K, cache: EvictingCache<K, V>) => void, thisArg?: unknown): void {\n\t\tconst boundCallback = thisArg !== undefined ? callbackfn.bind(thisArg) : callbackfn;\n\t\tthis.cache.forEach((value, key) => boundCallback(value, key, this));\n\t}\n\n\t/**\n\t * Adds multiple key-value pairs to the cache.\n\t * Each pair is added individually, following the same LRU eviction rules as put().\n\t * @param {Iterable<[K, V]>} entries The entries to add.\n\t * @returns {void}\n\t */\n\tputAll(entries: Iterable<[K, V]>): void {\n\t\tfor (const [key, value] of entries) { this.put(key, value) }\n\t}\n\n\t/**\n\t * Gets multiple values from the cache.\n\t * Each get updates the LRU order for that key.\n\t *\n\t * @param {Iterable<K>} keys The keys to get values for.\n\t * @returns {Map<K, V>} A map of keys to their values (excludes missing keys).\n\t */\n\tgetAll(keys: Iterable<K>): Map<K, V> {\n\t\tconst result = new Map<K, V>();\n\t\tfor (const key of keys) {\n\t\t\tconst value = this.get(key);\n\t\t\tif (value !== null) { result.set(key, value) }\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Removes multiple keys from the cache.\n\t *\n\t * @param {Iterable<K>} keys The keys to remove.\n\t * @returns {number} The number of keys that were removed.\n\t */\n\tdeleteAll(keys: Iterable<K>): number {\n\t\tlet count = 0;\n\t\tfor (const key of keys) {\n\t\t\tif (this.cache.delete(key)) { count++ }\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/**\n\t * Gets cache statistics including hit/miss counts and hit rate.\n\t *\n\t * @returns {CacheStats} Cache statistics.\n\t */\n\tgetStats(): CacheStats {\n\t\tconst total = this.hits + this.misses;\n\n\t\treturn { hits: this.hits, misses: this.misses, hitRate: total === 0 ? 0 : this.hits / total };\n\t}\n\n\t/**\n\t * Resets cache statistics to zero.\n\t *\n\t * @returns {void}\n\t */\n\tresetStats(): void {\n\t\tthis.hits = 0;\n\t\tthis.misses = 0;\n\t}\n\n\t/**\n\t * Gets the capacity of the cache.\n\t * This is the maximum number of key-value pairs the cache can hold.\n\t * This is not the number of key-value pairs in the cache.\n\t *\n\t * @readonly\n\t * @returns {number} The capacity of the cache.\n\t */\n\tget capacity(): number {\n\t\treturn this._capacity;\n\t}\n\n\t/**\n\t * Gets the size of the cache.\n\t * This is the number of key-value pairs in the cache.\n\t * This is not the capacity of the cache.\n\t * The capacity is the maximum number of key-value pairs the cache can hold.\n\t * The size is the number of key-value pairs currently in the cache.\n\t * The size will be less than or equal to the capacity.\n\t *\n\t * @returns {number} The size of the cache.\n\t */\n\tget size(): number {\n\t\treturn this.cache.size;\n\t}\n\n\t/**\n\t * Returns an iterator over the keys in the cache.\n\t * The keys are returned in the order of least recently used to most recently used.\n\t *\n\t * @returns {IterableIterator<K>} An iterator over the keys in the cache.\n\t */\n\tkeys(): IterableIterator<K> {\n\t\treturn this.cache.keys();\n\t}\n\n\t/**\n\t * Returns an iterator over the values in the cache.\n\t * The values are returned in the order of least recently used to most recently used.\n\t *\n\t * @returns {IterableIterator<V>} An iterator over the values in the cache.\n\t */\n\tvalues(): IterableIterator<V> {\n\t\treturn this.cache.values();\n\t}\n\n\t/**\n\t * Returns an iterator over the entries in the cache.\n\t * The entries are returned in the order of least recently used to most recently used.\n\t *\n\t * @returns {IterableIterator<[K, V]>} An iterator over the entries in the cache.\n\t */\n\tentries(): IterableIterator<[K, V]> {\n\t\treturn this.cache.entries();\n\t}\n\n\t/**\n\t * Returns an iterator over the entries in the cache.\n\t * The entries are returned in the order of least recently used to most recently used.\n\t *\n\t * @returns {IterableIterator<[K, V]>} An iterator over the entries in the cache.\n\t */\n\t[Symbol.iterator](): IterableIterator<[K, V]> {\n\t\treturn this.entries();\n\t}\n\n\t/**\n\t * Gets the description of the object.\n\t *\n\t * @override\n\t * @returns {string} The description of the object.\n\t */\n\tget [Symbol.toStringTag](): string {\n\t\treturn 'EvictingCache';\n\t}\n\n\t/**\n\t * Puts a key-value pair into the cache and evicts the least recently used item if necessary.\n\t * If the key already exists, the item is removed and re-added to update its position.\n\t * If the cache is full, the least recently used item is evicted and the new item is added.\n\t * @param {K} key The key to put.\n\t * @param {V} value The value to put.\n\t * @returns {V} The value that was put.\n\t */\n\tprivate putAndEvict(key: K, value: V): V {\n\t\tconst existed = this.cache.delete(key);\n\t\tif (!existed && this._capacity <= this.cache.size) { this.evict() }\n\n\t\tthis.cache.set(key, value);\n\n\t\treturn value;\n\t}\n}"],
5
- "mappings": "AAOO,IAAMA,EAAN,KAA0B,CACf,UACA,MACT,KAAO,EACP,OAAS,EAOjB,YAAYC,EAAmB,IAAK,CACnC,GAAIA,EAAW,EAAK,MAAM,IAAI,WAAW,iCAAiC,EAC1E,GAAI,CAAC,OAAO,UAAUA,CAAQ,EAAK,MAAM,IAAI,WAAW,6BAA6B,EAErF,KAAK,UAAYA,EACjB,KAAK,MAAQ,IAAI,GAClB,CAQA,IAAIC,EAAkB,CACrB,IAAMC,EAAQ,KAAK,MAAM,IAAID,CAAG,EAChC,OAAIC,IAAU,QACb,KAAK,SACE,OAGR,KAAK,OACL,KAAK,MAAM,OAAOD,CAAG,EAErB,KAAK,MAAM,IAAIA,EAAKC,CAAK,EAElBA,EACR,CAQA,IAAID,EAAiB,CACpB,OAAO,KAAK,MAAM,IAAIA,CAAG,CAC1B,CAUA,IAAIA,EAAQC,EAAgB,CAC3B,KAAK,YAAYD,EAAKC,CAAK,CAC5B,CAQA,OAAOD,EAAiB,CACvB,OAAO,KAAK,MAAM,OAAOA,CAAG,CAC7B,CAQA,KAAKA,EAAkB,CACtB,OAAO,KAAK,MAAM,IAAIA,CAAG,GAAK,IAC/B,CAUA,SAASA,EAAQE,EAAsB,CACtC,IAAMC,EAAW,KAAK,IAAIH,CAAG,EAC7B,GAAIG,IAAa,KAAQ,OAAOA,EAGhC,IAAMF,EAAQC,EAAS,EACvB,OAAO,KAAK,YAAYF,EAAKC,CAAK,CACnC,CAMA,OAAiB,CAChB,IAAMG,EAAa,KAAK,MAAM,KAAK,EAAE,KAAK,EAC1C,OAAIA,EAAW,KAAe,GAEvB,KAAK,MAAM,OAAOA,EAAW,KAAK,CAC1C,CAMA,OAAc,CACb,KAAK,MAAM,MAAM,CAClB,CAQA,QAAQC,EAAoEC,EAAyB,CACpG,IAAMC,EAAgBD,IAAY,OAAYD,EAAW,KAAKC,CAAO,EAAID,EACzE,KAAK,MAAM,QAAQ,CAACJ,EAAOD,IAAQO,EAAcN,EAAOD,EAAK,IAAI,CAAC,CACnE,CAQA,OAAOQ,EAAiC,CACvC,OAAW,CAACR,EAAKC,CAAK,IAAKO,EAAW,KAAK,IAAIR,EAAKC,CAAK,CAC1D,CASA,OAAOQ,EAA8B,CACpC,IAAMC,EAAS,IAAI,IACnB,QAAWV,KAAOS,EAAM,CACvB,IAAMR,EAAQ,KAAK,IAAID,CAAG,EACtBC,IAAU,MAAQS,EAAO,IAAIV,EAAKC,CAAK,CAC5C,CAEA,OAAOS,CACR,CAQA,UAAUD,EAA2B,CACpC,IAAIE,EAAQ,EACZ,QAAWX,KAAOS,EACb,KAAK,MAAM,OAAOT,CAAG,GAAKW,IAG/B,OAAOA,CACR,CAOA,UAAuB,CACtB,IAAMC,EAAQ,KAAK,KAAO,KAAK,OAE/B,MAAO,CAAE,KAAM,KAAK,KAAM,OAAQ,KAAK,OAAQ,QAASA,IAAU,EAAI,EAAI,KAAK,KAAOA,CAAM,CAC7F,CAOA,YAAmB,CAClB,KAAK,KAAO,EACZ,KAAK,OAAS,CACf,CAUA,IAAI,UAAmB,CACtB,OAAO,KAAK,SACb,CAYA,IAAI,MAAe,CAClB,OAAO,KAAK,MAAM,IACnB,CAQA,MAA4B,CAC3B,OAAO,KAAK,MAAM,KAAK,CACxB,CAQA,QAA8B,CAC7B,OAAO,KAAK,MAAM,OAAO,CAC1B,CAQA,SAAoC,CACnC,OAAO,KAAK,MAAM,QAAQ,CAC3B,CAQA,CAAC,OAAO,QAAQ,GAA8B,CAC7C,OAAO,KAAK,QAAQ,CACrB,CAQA,IAAK,OAAO,WAAW,GAAY,CAClC,MAAO,eACR,CAUQ,YAAYZ,EAAQC,EAAa,CAExC,MAAI,CADY,KAAK,MAAM,OAAOD,CAAG,GACrB,KAAK,WAAa,KAAK,MAAM,MAAQ,KAAK,MAAM,EAEhE,KAAK,MAAM,IAAIA,EAAKC,CAAK,EAElBA,CACR,CACD",
6
- "names": ["EvictingCache", "capacity", "key", "value", "producer", "existing", "firstEntry", "callbackfn", "thisArg", "boundCallback", "entries", "keys", "result", "count", "total"]
7
- }