evicting-cache 2.3.0 → 2.3.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 +252 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# Evicting Cache
|
|
2
|
-
JavaScript Cache using an LRU (Least Recently Used) algorithm
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/evicting-cache)
|
|
4
|
+
[](https://www.npmjs.com/package/evicting-cache)
|
|
5
|
+
[](https://opensource.org/licenses/ISC)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://github.com/D1g1talEntr0py/evicting-cache)
|
|
8
|
+
|
|
9
|
+
A lightweight, high-performance TypeScript implementation of an LRU (Least Recently Used) cache with automatic eviction.
|
|
10
|
+
|
|
11
|
+
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
12
|
|
|
6
13
|
## Installation
|
|
7
14
|
|
|
@@ -13,27 +20,256 @@ pnpm add evicting-cache
|
|
|
13
20
|
npm install evicting-cache
|
|
14
21
|
```
|
|
15
22
|
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- 🚀 **High Performance** - O(1) operations for get, put, delete, and evict
|
|
26
|
+
- 📦 **Lightweight** - Zero dependencies, small bundle size
|
|
27
|
+
- 🔄 **LRU Eviction** - Automatic removal of least recently used items
|
|
28
|
+
- 📊 **Statistics Tracking** - Built-in hit/miss ratio monitoring
|
|
29
|
+
- 🔢 **Batch Operations** - Efficient multi-key operations
|
|
30
|
+
- 🎯 **Type Safe** - Full TypeScript support with generics
|
|
31
|
+
- ✅ **100% Test Coverage** - Thoroughly tested and reliable
|
|
32
|
+
- 🌐 **Modern ES Modules** - Native ESM support
|
|
33
|
+
- 🔍 **Map-like API** - Familiar interface with additional features
|
|
34
|
+
|
|
16
35
|
## Usage
|
|
17
|
-
```javascript
|
|
18
|
-
import EvictingCache from 'evicting-cache';
|
|
19
36
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
### Basic Example
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { EvictingCache } from 'evicting-cache';
|
|
41
|
+
|
|
42
|
+
// Create a cache with capacity of 3 items (default is 100)
|
|
43
|
+
const cache = new EvictingCache<string, string>(3);
|
|
23
44
|
|
|
24
|
-
// Obviously a contrived example, but this is what you get with AI...
|
|
25
45
|
cache.put('key1', 'value1');
|
|
26
46
|
cache.put('key2', 'value2');
|
|
27
47
|
cache.put('key3', 'value3');
|
|
28
|
-
cache.put('key4', 'value4');
|
|
48
|
+
cache.put('key4', 'value4'); // 'key1' is evicted (LRU)
|
|
49
|
+
|
|
50
|
+
console.log(cache.get('key1')); // null (evicted)
|
|
51
|
+
console.log(cache.get('key2')); // 'value2'
|
|
52
|
+
console.log(cache.get('key3')); // 'value3'
|
|
53
|
+
console.log(cache.get('key4')); // 'value4'
|
|
54
|
+
|
|
55
|
+
cache.put('key5', 'value5'); // 'key2' is evicted
|
|
56
|
+
|
|
57
|
+
console.log(cache.get('key2')); // null (evicted)
|
|
58
|
+
console.log(cache.size); // 3
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### LRU Behavior
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const cache = new EvictingCache<string, number>(3);
|
|
65
|
+
|
|
66
|
+
cache.put('a', 1);
|
|
67
|
+
cache.put('b', 2);
|
|
68
|
+
cache.put('c', 3);
|
|
69
|
+
// Order: a, b, c (a is LRU)
|
|
70
|
+
|
|
71
|
+
cache.get('a'); // Access 'a', moves it to most recent
|
|
72
|
+
// Order: b, c, a (b is now LRU)
|
|
73
|
+
|
|
74
|
+
cache.put('d', 4); // Evicts 'b' (LRU)
|
|
75
|
+
// Order: c, a, d
|
|
76
|
+
|
|
77
|
+
console.log(cache.has('b')); // false (evicted)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Peek Without Affecting LRU
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const cache = new EvictingCache<string, string>(2);
|
|
84
|
+
|
|
85
|
+
cache.put('x', 'hello');
|
|
86
|
+
cache.put('y', 'world');
|
|
87
|
+
|
|
88
|
+
// peek() reads without updating LRU order
|
|
89
|
+
console.log(cache.peek('x')); // 'hello'
|
|
90
|
+
// 'x' remains LRU
|
|
91
|
+
|
|
92
|
+
cache.put('z', 'new'); // 'x' is evicted
|
|
93
|
+
console.log(cache.has('x')); // false
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Get or Compute
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const cache = new EvictingCache<string, number>(10);
|
|
100
|
+
|
|
101
|
+
// Get existing value or compute and cache it
|
|
102
|
+
const value = cache.getOrPut('userId:123', () => {
|
|
103
|
+
// Expensive computation only happens if key is missing
|
|
104
|
+
return fetchUserFromDatabase('123');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// If producer throws, cache remains unchanged
|
|
108
|
+
try {
|
|
109
|
+
cache.getOrPut('key', () => {
|
|
110
|
+
throw new Error('Failed to compute');
|
|
111
|
+
});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
// Cache state is unmodified
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Batch Operations
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const cache = new EvictingCache<string, number>(100);
|
|
121
|
+
|
|
122
|
+
// Add multiple entries at once
|
|
123
|
+
cache.putAll([
|
|
124
|
+
['a', 1],
|
|
125
|
+
['b', 2],
|
|
126
|
+
['c', 3]
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
// Can also use a Map
|
|
130
|
+
cache.putAll(new Map([['d', 4], ['e', 5]]));
|
|
131
|
+
|
|
132
|
+
// Get multiple values (returns Map, excludes missing keys)
|
|
133
|
+
const values = cache.getAll(['a', 'b', 'missing']);
|
|
134
|
+
console.log(values.size); // 2
|
|
135
|
+
console.log(values.get('a')); // 1
|
|
136
|
+
console.log(values.has('missing')); // false
|
|
137
|
+
|
|
138
|
+
// Delete multiple keys (returns count removed)
|
|
139
|
+
const removed = cache.deleteAll(['a', 'c', 'missing']);
|
|
140
|
+
console.log(removed); // 2
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Cache Statistics
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const cache = new EvictingCache<string, string>(10);
|
|
147
|
+
|
|
148
|
+
cache.put('a', 'value1');
|
|
149
|
+
cache.get('a'); // hit
|
|
150
|
+
cache.get('b'); // miss
|
|
151
|
+
cache.get('a'); // hit
|
|
152
|
+
|
|
153
|
+
const stats = cache.getStats();
|
|
154
|
+
console.log(stats.hits); // 2
|
|
155
|
+
console.log(stats.misses); // 1
|
|
156
|
+
console.log(stats.hitRate); // 0.667 (66.7%)
|
|
157
|
+
|
|
158
|
+
// Reset statistics
|
|
159
|
+
cache.resetStats();
|
|
160
|
+
console.log(cache.getStats().hits); // 0
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Iteration
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const cache = new EvictingCache<string, number>(3);
|
|
167
|
+
cache.put('a', 1);
|
|
168
|
+
cache.put('b', 2);
|
|
169
|
+
cache.put('c', 3);
|
|
170
|
+
|
|
171
|
+
// Iterate over entries (LRU to MRU order)
|
|
172
|
+
for (const [key, value] of cache) {
|
|
173
|
+
console.log(key, value);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Or use forEach
|
|
177
|
+
cache.forEach((value, key, cache) => {
|
|
178
|
+
console.log(key, value);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Get keys, values, or entries
|
|
182
|
+
console.log([...cache.keys()]); // ['a', 'b', 'c']
|
|
183
|
+
console.log([...cache.values()]); // [1, 2, 3]
|
|
184
|
+
console.log([...cache.entries()]); // [['a', 1], ['b', 2], ['c', 3]]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## API Reference
|
|
188
|
+
|
|
189
|
+
### Constructor
|
|
190
|
+
|
|
191
|
+
- `new EvictingCache<K, V>(capacity?: number)` - Creates a new cache with the specified capacity (default: 100)
|
|
192
|
+
|
|
193
|
+
### Core Methods
|
|
194
|
+
|
|
195
|
+
- `get(key: K): V | null` - Returns value and updates LRU order
|
|
196
|
+
- `peek(key: K): V | null` - Returns value without updating LRU order
|
|
197
|
+
- `put(key: K, value: V): void` - Adds or updates a key-value pair
|
|
198
|
+
- `delete(key: K): boolean` - Removes a key from the cache
|
|
199
|
+
- `has(key: K): boolean` - Checks if a key exists
|
|
200
|
+
- `getOrPut(key: K, producer: () => V): V` - Gets existing value or computes and stores new one
|
|
201
|
+
- `evict(): boolean` - Manually removes the LRU item
|
|
202
|
+
- `clear(): void` - Removes all items from the cache
|
|
203
|
+
|
|
204
|
+
### Batch Operations
|
|
205
|
+
|
|
206
|
+
- `putAll(entries: Iterable<[K, V]>): void` - Adds multiple entries
|
|
207
|
+
- `getAll(keys: Iterable<K>): Map<K, V>` - Gets multiple values
|
|
208
|
+
- `deleteAll(keys: Iterable<K>): number` - Removes multiple keys
|
|
209
|
+
|
|
210
|
+
### Statistics
|
|
211
|
+
|
|
212
|
+
- `getStats(): { hits: number, misses: number, hitRate: number }` - Returns cache statistics
|
|
213
|
+
- `resetStats(): void` - Resets statistics counters
|
|
214
|
+
|
|
215
|
+
### Iteration
|
|
216
|
+
|
|
217
|
+
- `keys(): IterableIterator<K>` - Returns an iterator over keys
|
|
218
|
+
- `values(): IterableIterator<V>` - Returns an iterator over values
|
|
219
|
+
- `entries(): IterableIterator<[K, V]>` - Returns an iterator over entries
|
|
220
|
+
- `forEach(callback: (value: V, key: K, cache: EvictingCache<K, V>) => void, thisArg?: unknown): void` - Executes callback for each entry
|
|
221
|
+
- `[Symbol.iterator]()` - Makes the cache iterable
|
|
222
|
+
|
|
223
|
+
### Properties
|
|
224
|
+
|
|
225
|
+
- `capacity: number` - Maximum number of items (read-only)
|
|
226
|
+
- `size: number` - Current number of items (read-only)
|
|
227
|
+
|
|
228
|
+
## Performance
|
|
229
|
+
|
|
230
|
+
All core operations have **O(1)** time complexity:
|
|
231
|
+
|
|
232
|
+
| Operation | Complexity | Updates LRU |
|
|
233
|
+
|-----------|------------|-------------|
|
|
234
|
+
| `get(key)` | O(1) | ✅ Yes |
|
|
235
|
+
| `peek(key)` | O(1) | ❌ No |
|
|
236
|
+
| `put(key, value)` | O(1) | ✅ Yes |
|
|
237
|
+
| `delete(key)` | O(1) | ❌ N/A |
|
|
238
|
+
| `evict()` | O(1) | ❌ N/A |
|
|
239
|
+
| `has(key)` | O(1) | ❌ No |
|
|
240
|
+
| `clear()` | O(1) | ❌ N/A |
|
|
241
|
+
|
|
242
|
+
**Space complexity:** O(n) where n is the capacity
|
|
243
|
+
|
|
244
|
+
## TypeScript Support
|
|
245
|
+
|
|
246
|
+
Full TypeScript support with generic types:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// Strongly typed cache
|
|
250
|
+
const userCache = new EvictingCache<number, User>(100);
|
|
251
|
+
const configCache = new EvictingCache<string, Config>(50);
|
|
252
|
+
|
|
253
|
+
// Works with any key/value types
|
|
254
|
+
const complexCache = new EvictingCache<{ id: number; tenant: string }, Promise<Data>>(25);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Browser Compatibility
|
|
258
|
+
|
|
259
|
+
Compatible with all modern browsers and Node.js environments that support ES2015+ features:
|
|
260
|
+
- Chrome/Edge: ✅ Latest
|
|
261
|
+
- Firefox: ✅ Latest
|
|
262
|
+
- Safari: ✅ 15+
|
|
263
|
+
- Node.js: ✅ 14+
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
ISC License - See [LICENSE](LICENSE) file for details
|
|
29
268
|
|
|
30
|
-
|
|
31
|
-
console.log(cache.get('key2')); // value2
|
|
32
|
-
console.log(cache.get('key3')); // value3
|
|
33
|
-
console.log(cache.get('key4')); // value4
|
|
269
|
+
## Contributing
|
|
34
270
|
|
|
35
|
-
|
|
271
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
36
272
|
|
|
37
|
-
|
|
273
|
+
## Changelog
|
|
38
274
|
|
|
39
|
-
|
|
275
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|