@veekhere/just-cache-it 1.0.3
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/LICENSE.md +21 -0
- package/README.md +285 -0
- package/dist/index.d.ts +195 -0
- package/dist/index.js +237 -0
- package/package.json +41 -0
package/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Vitaliy Kuguenko
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,285 @@
|
|
1
|
+
# just-cache-it
|
2
|
+
|
3
|
+
A generic cache implementation with optional TTL support and subscriptions
|
4
|
+
|
5
|
+
- **Immutable** values
|
6
|
+
- **Lightweight** and "**zero-dependency**"
|
7
|
+
- **TTL** support
|
8
|
+
- **Cache update** subscriptions
|
9
|
+
- **Cache value update** subscriptions
|
10
|
+
- **Cache key** utilities
|
11
|
+
- **TypeScript** support
|
12
|
+
|
13
|
+
## Dependencies
|
14
|
+
|
15
|
+
**Runtime**
|
16
|
+
|
17
|
+
- [uuid](https://github.com/uuidjs/uuid) for UUID generation (zero-dependency)
|
18
|
+
- [lodash.clonedeep](https://github.com/lodash/lodash) for deep cloning (zero-dependency)
|
19
|
+
|
20
|
+
**Development**
|
21
|
+
|
22
|
+
- [jest](https://github.com/facebook/jest)
|
23
|
+
- [ts-jest](https://github.com/kulshekhar/ts-jest)
|
24
|
+
- [prettier](https://github.com/prettier/prettier)
|
25
|
+
- [typescript](https://github.com/microsoft/TypeScript)
|
26
|
+
|
27
|
+
## Quickstart
|
28
|
+
|
29
|
+
**1. Install**
|
30
|
+
|
31
|
+
```bash
|
32
|
+
npm i @veekhere/just-cache-it
|
33
|
+
```
|
34
|
+
|
35
|
+
**2. Create a cache**
|
36
|
+
|
37
|
+
```typescript
|
38
|
+
import { CacheIt } from '@veekhere/just-cache-it';
|
39
|
+
|
40
|
+
const cache = new CacheIt<string>();
|
41
|
+
|
42
|
+
const cacheValue: CacheValue<string> = cache.set('key', 'value');
|
43
|
+
|
44
|
+
cacheValue.unwrapPrevious(); // ⇨ undefined
|
45
|
+
cacheValue.unwrapCurrent(); // ⇨ 'value'
|
46
|
+
|
47
|
+
cacheValue.subscribe({
|
48
|
+
next: (value: CacheValue<string>) => {
|
49
|
+
const previousValue = value.unwrapPrevious();
|
50
|
+
const currentValue = value.unwrapCurrent();
|
51
|
+
|
52
|
+
// Do something with the previous and current values
|
53
|
+
},
|
54
|
+
complete: () => {
|
55
|
+
// Trigger something when the subscription is cancelled
|
56
|
+
},
|
57
|
+
});
|
58
|
+
```
|
59
|
+
|
60
|
+
## API Summary
|
61
|
+
|
62
|
+
**Main cache**
|
63
|
+
|
64
|
+
| | | |
|
65
|
+
| ------------------------------------------------------- | -------------------------------------------------- | --------------- |
|
66
|
+
| [`CacheIt`](#cacheit) | The main cache class | New in `v1.0.0` |
|
67
|
+
| [`CacheSubscriptionHandler`](#cachesubscriptionhandler) | Represents a handler to call when cache is updated | New in `v1.0.0` |
|
68
|
+
| [`CacheSubscription`](#cachesubscription) | Represents a subscription to cache changes | New in `v1.0.0` |
|
69
|
+
|
70
|
+
**Cache values**
|
71
|
+
|
72
|
+
| | | |
|
73
|
+
| ------------------------------------------------------------------- | --------------------------------------------------------------- | --------------- |
|
74
|
+
| [`CacheValue`](#cachevalue) | Represents a cached value that can be updated and subscribed to | New in `v1.0.0` |
|
75
|
+
| [`UnwrapHandlers`](#unwraphandlers) | Represents the handlers to call when unwrapping a cache value | New in `v1.0.0` |
|
76
|
+
| [`CacheValueSubscriptionHandlers`](#cachevaluesubscriptionhandlers) | Represents the handlers to call when cache value changes | New in `v1.0.0` |
|
77
|
+
| [`CacheValueSubscription`](#cachevaluesubscription) | Represents a subscription to a cache value | New in `v1.0.0` |
|
78
|
+
|
79
|
+
**Types and utilities**
|
80
|
+
|
81
|
+
| | | |
|
82
|
+
| ------------------------------- | ------------------------------------------------- | --------------- |
|
83
|
+
| [`CacheItUtils`](#cacheitutils) | Utility functions for working with cache entries | New in `v1.0.0` |
|
84
|
+
| [`Maybe`](#maybe) | Represents a value that may or may not be present | New in `v1.0.0` |
|
85
|
+
|
86
|
+
## API
|
87
|
+
|
88
|
+
### CacheIt
|
89
|
+
|
90
|
+
Main cache class. Returned values are immutable
|
91
|
+
|
92
|
+
**1. Primitive values**
|
93
|
+
|
94
|
+
```typescript
|
95
|
+
import { CacheIt } from '@veekhere/just-cache-it';
|
96
|
+
|
97
|
+
const cache = new CacheIt<string>(); // string cache
|
98
|
+
|
99
|
+
cache.set('key', 'value');
|
100
|
+
|
101
|
+
cache.get('key'); // ⇨ 'value'
|
102
|
+
```
|
103
|
+
|
104
|
+
**2. User defined values**
|
105
|
+
|
106
|
+
```typescript
|
107
|
+
import { CacheIt } from '@veekhere/just-cache-it';
|
108
|
+
|
109
|
+
class User {
|
110
|
+
constructor(public name: string) {}
|
111
|
+
|
112
|
+
whois(): string {
|
113
|
+
return `Hello, my name is ${this.name}`;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
const cache = new CacheIt<User>(); // User cache
|
118
|
+
|
119
|
+
cache.set('key', new User('John'));
|
120
|
+
|
121
|
+
cache.get('key')?.unwrapCurrent(); // ⇨ User { name: 'John' }
|
122
|
+
```
|
123
|
+
|
124
|
+
**3. Arrays**
|
125
|
+
|
126
|
+
```typescript
|
127
|
+
import { CacheIt } from '@veekhere/just-cache-it';
|
128
|
+
|
129
|
+
const cache = new CacheIt<string[]>(); // string[] cache
|
130
|
+
|
131
|
+
cache.set('key', ['value']);
|
132
|
+
|
133
|
+
cache.get('key'); // ⇨ ['value']
|
134
|
+
```
|
135
|
+
|
136
|
+
**And so on...**
|
137
|
+
|
138
|
+
### CacheSubscriptionHandler
|
139
|
+
|
140
|
+
Represents a handler to call when cache is updated
|
141
|
+
|
142
|
+
```typescript
|
143
|
+
import { CacheIt } from '@veekhere/just-cache-it';
|
144
|
+
|
145
|
+
const cache = new CacheIt<string>();
|
146
|
+
|
147
|
+
const handler: CacheSubscriptionHandler = () => {
|
148
|
+
// Trigger something when the cache is updated
|
149
|
+
};
|
150
|
+
|
151
|
+
const subscription = cache.subscribe(handler);
|
152
|
+
```
|
153
|
+
|
154
|
+
### CacheSubscription
|
155
|
+
|
156
|
+
Represents a subscription to cache changes
|
157
|
+
|
158
|
+
```typescript
|
159
|
+
import { CacheIt } from '@veekhere/just-cache-it';
|
160
|
+
|
161
|
+
const cache = new CacheIt<string>();
|
162
|
+
|
163
|
+
const subscription = cache.subscribe(() => {
|
164
|
+
// Trigger something when the cache changes
|
165
|
+
});
|
166
|
+
```
|
167
|
+
|
168
|
+
### CacheValue
|
169
|
+
|
170
|
+
Represents a cached value that can be updated and subscribed to
|
171
|
+
|
172
|
+
```typescript
|
173
|
+
import { CacheIt, CacheValue } from '@veekhere/just-cache-it';
|
174
|
+
|
175
|
+
const cache = new CacheIt<string>();
|
176
|
+
|
177
|
+
const cacheValue: CacheValue<string> = cache.set('key', 'value');
|
178
|
+
|
179
|
+
cacheValue.unwrapPrevious(); // ⇨ undefined
|
180
|
+
cacheValue.unwrapCurrent(); // ⇨ 'value'
|
181
|
+
|
182
|
+
cacheValue.subscribe({
|
183
|
+
next: (value: CacheValue<string>) => {
|
184
|
+
// Do something with the previous and current values
|
185
|
+
},
|
186
|
+
complete: () => {
|
187
|
+
// Trigger something when the subscription is cancelled
|
188
|
+
},
|
189
|
+
});
|
190
|
+
```
|
191
|
+
|
192
|
+
### UnwrapHandlers
|
193
|
+
|
194
|
+
Represents the handlers to call when unwrapping a cache value
|
195
|
+
|
196
|
+
```typescript
|
197
|
+
import { CacheIt, CacheValue } from '@veekhere/just-cache-it';
|
198
|
+
|
199
|
+
const cache = new CacheIt<string>();
|
200
|
+
|
201
|
+
const cacheValue: CacheValue<string> = cache.set('key', 'value');
|
202
|
+
|
203
|
+
const handlers: UnwrapHandlers<string> = {
|
204
|
+
onPresent: (value: string) => {
|
205
|
+
// Do something with the value
|
206
|
+
},
|
207
|
+
onAbsent: () => {
|
208
|
+
// Do something when the value is absent
|
209
|
+
},
|
210
|
+
};
|
211
|
+
|
212
|
+
cacheValue.unwrapPrevious(handlers);
|
213
|
+
cacheValue.unwrapCurrent(handlers);
|
214
|
+
```
|
215
|
+
|
216
|
+
### CacheValueSubscriptionHandlers
|
217
|
+
|
218
|
+
Represents the handlers to call when cache value changes
|
219
|
+
|
220
|
+
```typescript
|
221
|
+
import { CacheValue, CacheValueSubscriptionHandlers } from '@veekhere/just-cache-it';
|
222
|
+
|
223
|
+
const handlers: CacheValueSubscriptionHandlers<string> = {
|
224
|
+
next: (value: CacheValue<string>) => {
|
225
|
+
// Do something with the previous and current values
|
226
|
+
},
|
227
|
+
complete: () => {
|
228
|
+
// Trigger something when the subscription is cancelled
|
229
|
+
},
|
230
|
+
};
|
231
|
+
|
232
|
+
// register cache ...
|
233
|
+
|
234
|
+
cache.subscribe(handlers);
|
235
|
+
```
|
236
|
+
|
237
|
+
### CacheValueSubscription
|
238
|
+
|
239
|
+
Represents a subscription to a cache value
|
240
|
+
|
241
|
+
```typescript
|
242
|
+
import { CacheIt, CacheValue } from '@veekhere/just-cache-it';
|
243
|
+
|
244
|
+
const cache = new CacheIt<string>();
|
245
|
+
|
246
|
+
const cacheValue: CacheValue<string> = cache.set('key', 'value');
|
247
|
+
|
248
|
+
const subscription = cacheValue.subscribe({
|
249
|
+
// handlers
|
250
|
+
});
|
251
|
+
|
252
|
+
// some complex logic
|
253
|
+
|
254
|
+
subscription.unsubscribe();
|
255
|
+
```
|
256
|
+
|
257
|
+
### CacheItUtils
|
258
|
+
|
259
|
+
Utility functions for working with cache entries
|
260
|
+
|
261
|
+
```typescript
|
262
|
+
import { CacheItUtils } from '@veekhere/just-cache-it';
|
263
|
+
|
264
|
+
const keyGenerator = CacheItUtils.addBaseKey('BASE#KEY');
|
265
|
+
|
266
|
+
keyGenerator.generateKey('my', 'additional', 'parts'); // ⇨ '$CACHE-IT_BASE#KEY_my-additional-parts_2a4f54b2-700a-41b3-8008-ac641a550ee5'
|
267
|
+
```
|
268
|
+
|
269
|
+
### Maybe
|
270
|
+
|
271
|
+
Represents a value that may or may not be present
|
272
|
+
|
273
|
+
```typescript
|
274
|
+
import { Maybe } from '@veekhere/just-cache-it';
|
275
|
+
|
276
|
+
const maybe: Maybe<string> = getData(); // ⇨ string | undefined
|
277
|
+
```
|
278
|
+
|
279
|
+
## Contributing
|
280
|
+
|
281
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
282
|
+
|
283
|
+
## License
|
284
|
+
|
285
|
+
[MIT](https://github.com/veekhere/just-cache-it/blob/master/LICENSE.md)
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
/**
|
2
|
+
* Cache-It is a generic cache implementation with optional TTL support and subscriptions. It can be used to cache any value, including objects, arrays, and primitives.
|
3
|
+
* @template T The type of values stored in the cache
|
4
|
+
* @see https://github.com/veekhere/cache-it
|
5
|
+
* @author veekhere
|
6
|
+
* @license MIT
|
7
|
+
* @version 1.0.0
|
8
|
+
*/
|
9
|
+
/**
|
10
|
+
* Represents a value that may or may not be present
|
11
|
+
* @template T The type of the value
|
12
|
+
*/
|
13
|
+
type Maybe<T> = T | undefined;
|
14
|
+
/**
|
15
|
+
* Represents the handlers to call when unwrapping a cache value
|
16
|
+
* @template T The type of the value
|
17
|
+
*/
|
18
|
+
type UnwrapHandlers<T> = {
|
19
|
+
/**
|
20
|
+
* Called when the value is present
|
21
|
+
* @param value The value
|
22
|
+
*/
|
23
|
+
onPresent?: (value: T) => void;
|
24
|
+
/**
|
25
|
+
* Called when the value is absent
|
26
|
+
*/
|
27
|
+
onAbsent?: () => void;
|
28
|
+
};
|
29
|
+
/**
|
30
|
+
* Subscription to a cache value
|
31
|
+
*/
|
32
|
+
type CacheValueSubscription = {
|
33
|
+
/**
|
34
|
+
* Unsubscribes from the cache value
|
35
|
+
*/
|
36
|
+
unsubscribe: () => void;
|
37
|
+
};
|
38
|
+
/**
|
39
|
+
* Represents the handlers to call when cache value changes
|
40
|
+
*/
|
41
|
+
type CacheValueSubscriptionHandlers<T> = {
|
42
|
+
/**
|
43
|
+
* Called when the value changes
|
44
|
+
* @param value The new value of the cache
|
45
|
+
*/
|
46
|
+
next?: (value: Omit<CacheValue<T>, "subscribe" | "unsubscribeAll">) => void;
|
47
|
+
/**
|
48
|
+
* Called when the subscription is cancelled
|
49
|
+
*/
|
50
|
+
complete?: () => void;
|
51
|
+
};
|
52
|
+
/**
|
53
|
+
* Represents a value that can be cached and subscribed to
|
54
|
+
* @template T The type of the value
|
55
|
+
*/
|
56
|
+
declare class CacheValue<T> {
|
57
|
+
private readonly value;
|
58
|
+
private readonly subscribers;
|
59
|
+
/**
|
60
|
+
* Creates a new cache value
|
61
|
+
* @param ttl The time-to-live in milliseconds
|
62
|
+
*/
|
63
|
+
constructor(ttl?: number);
|
64
|
+
/**
|
65
|
+
* Updates the value in the cache
|
66
|
+
* @param value The new value to set
|
67
|
+
*/
|
68
|
+
update(value: Maybe<T>): void;
|
69
|
+
/**
|
70
|
+
* Returns the current value, or undefined if not present
|
71
|
+
*/
|
72
|
+
unwrapCurrent(): Maybe<T>;
|
73
|
+
/**
|
74
|
+
* Calls the provided handlers with the current value, or undefined if not present
|
75
|
+
* @param handlers The handlers to call
|
76
|
+
*/
|
77
|
+
unwrapCurrent(handlers: UnwrapHandlers<T>): void;
|
78
|
+
/**
|
79
|
+
* Returns the previous value, or undefined if not present
|
80
|
+
*/
|
81
|
+
unwrapPrevious(): Maybe<T>;
|
82
|
+
/**
|
83
|
+
* Calls the provided handlers with the previous value, or undefined if not present
|
84
|
+
* @param handlers The handlers to call
|
85
|
+
*/
|
86
|
+
unwrapPrevious(handlers: UnwrapHandlers<T>): void;
|
87
|
+
/**
|
88
|
+
* Removes the current and previous values
|
89
|
+
*/
|
90
|
+
clear(): void;
|
91
|
+
/**
|
92
|
+
* Subscribes to changes in the cache value
|
93
|
+
* @param handlers The handlers to call when the value changes
|
94
|
+
* @returns A subscription object that can be used to unsubscribe
|
95
|
+
*/
|
96
|
+
subscribe(handlers: CacheValueSubscriptionHandlers<T>): CacheValueSubscription;
|
97
|
+
/**
|
98
|
+
* Unsubscribes from all subscribers
|
99
|
+
*/
|
100
|
+
unsubscribeAll(): void;
|
101
|
+
private notifySubscribers;
|
102
|
+
}
|
103
|
+
/**
|
104
|
+
* Subscription to a cache changes
|
105
|
+
*/
|
106
|
+
type CacheSubscription = {
|
107
|
+
/**
|
108
|
+
* Unsubscribes from the cache changes
|
109
|
+
*/
|
110
|
+
unsubscribe: () => void;
|
111
|
+
};
|
112
|
+
/**
|
113
|
+
* Represents a handler to call when cache is updated
|
114
|
+
*/
|
115
|
+
type CacheSubscriptionHandler = () => void;
|
116
|
+
/**
|
117
|
+
* A generic cache implementation with optional TTL support
|
118
|
+
* @template T The type of values stored in the cache
|
119
|
+
*/
|
120
|
+
declare class CacheIt<T> {
|
121
|
+
private readonly cache;
|
122
|
+
private readonly subscribers;
|
123
|
+
/**
|
124
|
+
* Sets a value in the cache
|
125
|
+
* @param key The key to store the value under
|
126
|
+
* @param value The value to store
|
127
|
+
* @param ttl Optional time-to-live in milliseconds
|
128
|
+
*/
|
129
|
+
set(key: string, value: T, ttl?: number): CacheValue<T>;
|
130
|
+
/**
|
131
|
+
* Retrieves a value from the cache
|
132
|
+
* @param key The key to lookup
|
133
|
+
* @returns The stored value, or undefined if not found or expired
|
134
|
+
*/
|
135
|
+
get(key: string): CacheValue<T> | undefined;
|
136
|
+
/**
|
137
|
+
* Removes a value from the cache
|
138
|
+
* @param key The key to remove
|
139
|
+
* @returns true if the key was found and removed, false otherwise
|
140
|
+
*/
|
141
|
+
delete(key: string): boolean;
|
142
|
+
/**
|
143
|
+
* Removes all entries from the cache
|
144
|
+
*/
|
145
|
+
clear(): void;
|
146
|
+
/**
|
147
|
+
* Returns the number of entries in the cache
|
148
|
+
*/
|
149
|
+
size(): number;
|
150
|
+
/**
|
151
|
+
* Checks if a key exists in the cache and is not expired
|
152
|
+
* @param key The key to check
|
153
|
+
* @returns true if the key exists and is not expired
|
154
|
+
*/
|
155
|
+
has(key: string): boolean;
|
156
|
+
/**
|
157
|
+
* Subscribes to changes in the cache
|
158
|
+
* @param handler The handler to call when the cache changes
|
159
|
+
* @returns A subscription object that can be used to unsubscribe
|
160
|
+
*/
|
161
|
+
subscribe(handler: CacheSubscriptionHandler): CacheSubscription;
|
162
|
+
/**
|
163
|
+
* Unsubscribes from all subscribers without emitting any events
|
164
|
+
*/
|
165
|
+
unsubscribeAll(): void;
|
166
|
+
private notifySubscribers;
|
167
|
+
}
|
168
|
+
/**
|
169
|
+
* Represents a key generator for cache entries
|
170
|
+
*/
|
171
|
+
type CacheKeyGenerator = {
|
172
|
+
/**
|
173
|
+
* Generates a key with additional parts
|
174
|
+
* @param additional Additional parts to add to the key
|
175
|
+
*/
|
176
|
+
generateKey: (...additional: string[]) => string;
|
177
|
+
};
|
178
|
+
/**
|
179
|
+
* Utility functions for working with cache entries
|
180
|
+
*/
|
181
|
+
declare class CacheItUtils {
|
182
|
+
/**
|
183
|
+
* Adds a base key to a key generator
|
184
|
+
* @param baseKey The base key to add
|
185
|
+
* @returns A key generator with the base key added
|
186
|
+
*/
|
187
|
+
static addBaseKey(baseKey: string): CacheKeyGenerator;
|
188
|
+
/**
|
189
|
+
* Generates a key with additional parts
|
190
|
+
* @param additional Additional parts to add to the key
|
191
|
+
*/
|
192
|
+
static generateKey(...additional: string[]): string;
|
193
|
+
private static generateKeyWithBase;
|
194
|
+
}
|
195
|
+
export { CacheIt, CacheItUtils, CacheValue, CacheValueSubscription, UnwrapHandlers };
|
package/dist/index.js
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
import cloneDeep from 'lodash.clonedeep';
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
3
|
+
/**
|
4
|
+
* Represents a value that can be cached and subscribed to
|
5
|
+
* @template T The type of the value
|
6
|
+
*/
|
7
|
+
class CacheValue {
|
8
|
+
/**
|
9
|
+
* Creates a new cache value
|
10
|
+
* @param ttl The time-to-live in milliseconds
|
11
|
+
*/
|
12
|
+
constructor(ttl) {
|
13
|
+
this.value = {
|
14
|
+
current: undefined,
|
15
|
+
previous: undefined,
|
16
|
+
};
|
17
|
+
this.subscribers = new Map();
|
18
|
+
if (ttl != null) {
|
19
|
+
setTimeout(() => {
|
20
|
+
this.update(undefined);
|
21
|
+
}, ttl);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
/**
|
25
|
+
* Updates the value in the cache
|
26
|
+
* @param value The new value to set
|
27
|
+
*/
|
28
|
+
update(value) {
|
29
|
+
const oldValue = typeof this.value.current === 'object' && this.value.current != null
|
30
|
+
? cloneDeep(this.value.current)
|
31
|
+
: this.value.current;
|
32
|
+
const newValue = typeof value === 'object' && value != null
|
33
|
+
? cloneDeep(value)
|
34
|
+
: value;
|
35
|
+
this.value.previous = oldValue;
|
36
|
+
this.value.current = newValue;
|
37
|
+
this.notifySubscribers();
|
38
|
+
}
|
39
|
+
unwrapCurrent(handlers) {
|
40
|
+
var _a, _b;
|
41
|
+
if (handlers) {
|
42
|
+
if (this.value.current) {
|
43
|
+
(_a = handlers.onPresent) === null || _a === void 0 ? void 0 : _a.call(handlers, this.value.current);
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
(_b = handlers.onAbsent) === null || _b === void 0 ? void 0 : _b.call(handlers);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
else {
|
50
|
+
return this.value.current;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
unwrapPrevious(handlers) {
|
54
|
+
var _a, _b;
|
55
|
+
if (handlers) {
|
56
|
+
if (this.value.previous) {
|
57
|
+
(_a = handlers.onPresent) === null || _a === void 0 ? void 0 : _a.call(handlers, this.value.previous);
|
58
|
+
}
|
59
|
+
else {
|
60
|
+
(_b = handlers.onAbsent) === null || _b === void 0 ? void 0 : _b.call(handlers);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
else {
|
64
|
+
return this.value.previous;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
/**
|
68
|
+
* Removes the current and previous values
|
69
|
+
*/
|
70
|
+
clear() {
|
71
|
+
this.value.current = undefined;
|
72
|
+
this.value.previous = undefined;
|
73
|
+
this.notifySubscribers();
|
74
|
+
}
|
75
|
+
/**
|
76
|
+
* Subscribes to changes in the cache value
|
77
|
+
* @param handlers The handlers to call when the value changes
|
78
|
+
* @returns A subscription object that can be used to unsubscribe
|
79
|
+
*/
|
80
|
+
subscribe(handlers) {
|
81
|
+
const symbol = Symbol();
|
82
|
+
this.subscribers.set(symbol, handlers);
|
83
|
+
return {
|
84
|
+
unsubscribe: () => {
|
85
|
+
var _a;
|
86
|
+
(_a = handlers === null || handlers === void 0 ? void 0 : handlers.complete) === null || _a === void 0 ? void 0 : _a.call(handlers);
|
87
|
+
this.subscribers.delete(symbol);
|
88
|
+
},
|
89
|
+
};
|
90
|
+
}
|
91
|
+
/**
|
92
|
+
* Unsubscribes from all subscribers
|
93
|
+
*/
|
94
|
+
unsubscribeAll() {
|
95
|
+
this.notifySubscribers(true);
|
96
|
+
this.subscribers.clear();
|
97
|
+
}
|
98
|
+
notifySubscribers(isComplete) {
|
99
|
+
this.subscribers.forEach((handlers) => {
|
100
|
+
var _a, _b;
|
101
|
+
if (isComplete) {
|
102
|
+
(_a = handlers.complete) === null || _a === void 0 ? void 0 : _a.call(handlers);
|
103
|
+
}
|
104
|
+
else {
|
105
|
+
(_b = handlers.next) === null || _b === void 0 ? void 0 : _b.call(handlers, this);
|
106
|
+
}
|
107
|
+
});
|
108
|
+
}
|
109
|
+
}
|
110
|
+
/**
|
111
|
+
* A generic cache implementation with optional TTL support
|
112
|
+
* @template T The type of values stored in the cache
|
113
|
+
*/
|
114
|
+
class CacheIt {
|
115
|
+
constructor() {
|
116
|
+
this.cache = new Map();
|
117
|
+
this.subscribers = new Map();
|
118
|
+
}
|
119
|
+
/**
|
120
|
+
* Sets a value in the cache
|
121
|
+
* @param key The key to store the value under
|
122
|
+
* @param value The value to store
|
123
|
+
* @param ttl Optional time-to-live in milliseconds
|
124
|
+
*/
|
125
|
+
set(key, value, ttl) {
|
126
|
+
const cacheValue = new CacheValue(ttl);
|
127
|
+
cacheValue.update(value);
|
128
|
+
this.cache.set(key, cacheValue);
|
129
|
+
this.notifySubscribers();
|
130
|
+
return cacheValue;
|
131
|
+
}
|
132
|
+
/**
|
133
|
+
* Retrieves a value from the cache
|
134
|
+
* @param key The key to lookup
|
135
|
+
* @returns The stored value, or undefined if not found or expired
|
136
|
+
*/
|
137
|
+
get(key) {
|
138
|
+
const cacheValue = this.cache.get(key);
|
139
|
+
if (!cacheValue) {
|
140
|
+
return undefined;
|
141
|
+
}
|
142
|
+
return cacheValue;
|
143
|
+
}
|
144
|
+
/**
|
145
|
+
* Removes a value from the cache
|
146
|
+
* @param key The key to remove
|
147
|
+
* @returns true if the key was found and removed, false otherwise
|
148
|
+
*/
|
149
|
+
delete(key) {
|
150
|
+
const isDeleted = this.cache.delete(key);
|
151
|
+
if (isDeleted) {
|
152
|
+
this.notifySubscribers();
|
153
|
+
}
|
154
|
+
return isDeleted;
|
155
|
+
}
|
156
|
+
/**
|
157
|
+
* Removes all entries from the cache
|
158
|
+
*/
|
159
|
+
clear() {
|
160
|
+
this.cache.clear();
|
161
|
+
this.notifySubscribers();
|
162
|
+
}
|
163
|
+
/**
|
164
|
+
* Returns the number of entries in the cache
|
165
|
+
*/
|
166
|
+
size() {
|
167
|
+
return this.cache.size;
|
168
|
+
}
|
169
|
+
/**
|
170
|
+
* Checks if a key exists in the cache and is not expired
|
171
|
+
* @param key The key to check
|
172
|
+
* @returns true if the key exists and is not expired
|
173
|
+
*/
|
174
|
+
has(key) {
|
175
|
+
const cacheValue = this.cache.get(key);
|
176
|
+
if (!cacheValue) {
|
177
|
+
return false;
|
178
|
+
}
|
179
|
+
return true;
|
180
|
+
}
|
181
|
+
/**
|
182
|
+
* Subscribes to changes in the cache
|
183
|
+
* @param handler The handler to call when the cache changes
|
184
|
+
* @returns A subscription object that can be used to unsubscribe
|
185
|
+
*/
|
186
|
+
subscribe(handler) {
|
187
|
+
const symbol = Symbol();
|
188
|
+
this.subscribers.set(symbol, handler);
|
189
|
+
return {
|
190
|
+
unsubscribe: () => {
|
191
|
+
this.subscribers.delete(symbol);
|
192
|
+
},
|
193
|
+
};
|
194
|
+
}
|
195
|
+
/**
|
196
|
+
* Unsubscribes from all subscribers without emitting any events
|
197
|
+
*/
|
198
|
+
unsubscribeAll() {
|
199
|
+
this.subscribers.clear();
|
200
|
+
}
|
201
|
+
notifySubscribers() {
|
202
|
+
this.subscribers.forEach((handler) => handler());
|
203
|
+
}
|
204
|
+
}
|
205
|
+
/**
|
206
|
+
* Utility functions for working with cache entries
|
207
|
+
*/
|
208
|
+
class CacheItUtils {
|
209
|
+
/**
|
210
|
+
* Adds a base key to a key generator
|
211
|
+
* @param baseKey The base key to add
|
212
|
+
* @returns A key generator with the base key added
|
213
|
+
*/
|
214
|
+
static addBaseKey(baseKey) {
|
215
|
+
if (!baseKey) {
|
216
|
+
throw new Error('Base key cannot be empty');
|
217
|
+
}
|
218
|
+
return {
|
219
|
+
generateKey: (...additional) => CacheItUtils.generateKeyWithBase(baseKey, ...additional),
|
220
|
+
};
|
221
|
+
}
|
222
|
+
/**
|
223
|
+
* Generates a key with additional parts
|
224
|
+
* @param additional Additional parts to add to the key
|
225
|
+
*/
|
226
|
+
static generateKey(...additional) {
|
227
|
+
return CacheItUtils.generateKeyWithBase(undefined, ...additional);
|
228
|
+
}
|
229
|
+
static generateKeyWithBase(baseKey, ...additional) {
|
230
|
+
const baseKeyPart = baseKey != null ? `${baseKey}_` : '';
|
231
|
+
const additionalPart = additional != null && (additional === null || additional === void 0 ? void 0 : additional.length) > 0
|
232
|
+
? `${additional === null || additional === void 0 ? void 0 : additional.join('-')}_`
|
233
|
+
: '';
|
234
|
+
return `$CACHE-IT_${baseKeyPart}${additionalPart}${uuidv4()}`;
|
235
|
+
}
|
236
|
+
}
|
237
|
+
export { CacheIt, CacheItUtils, CacheValue };
|
package/package.json
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
{
|
2
|
+
"name": "@veekhere/just-cache-it",
|
3
|
+
"version": "1.0.3",
|
4
|
+
"description": "A generic cache implementation with optional TTL support and subscriptions",
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"types": "dist/index.d.ts",
|
7
|
+
"type": "module",
|
8
|
+
"scripts": {
|
9
|
+
"build": "tsc",
|
10
|
+
"test": "jest"
|
11
|
+
},
|
12
|
+
"keywords": [
|
13
|
+
"cache",
|
14
|
+
"ttl",
|
15
|
+
"subscriptions",
|
16
|
+
"immutable",
|
17
|
+
"zero-dependency",
|
18
|
+
"typescript"
|
19
|
+
],
|
20
|
+
"author": "veekhere",
|
21
|
+
"license": "MIT",
|
22
|
+
"files": [
|
23
|
+
"dist",
|
24
|
+
"LICENSE",
|
25
|
+
"README.md",
|
26
|
+
"package.json"
|
27
|
+
],
|
28
|
+
"devDependencies": {
|
29
|
+
"@types/jest": "^29.5.14",
|
30
|
+
"@types/node": "^22.13.4",
|
31
|
+
"jest": "^29.7.0",
|
32
|
+
"prettier": "^3.5.1",
|
33
|
+
"ts-jest": "^29.2.5",
|
34
|
+
"typescript": "^5.0.4"
|
35
|
+
},
|
36
|
+
"dependencies": {
|
37
|
+
"@types/lodash.clonedeep": "^4.5.9",
|
38
|
+
"lodash.clonedeep": "^4.5.0",
|
39
|
+
"uuid": "^11.0.0"
|
40
|
+
}
|
41
|
+
}
|