btn-cache 1.0.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/dist/index.d.ts +242 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +507 -0
- package/package.json +47 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import EventEmitter from "events";
|
|
2
|
+
type BaseCacheOptions = {
|
|
3
|
+
evictionPolicy: "LRU" | "LFU" | "FIFO" | "RANDOM";
|
|
4
|
+
deleteOnExpire: boolean;
|
|
5
|
+
maxKeys: number;
|
|
6
|
+
useClones: boolean;
|
|
7
|
+
checkPeriod: number;
|
|
8
|
+
};
|
|
9
|
+
type invalidationPolicyOptions<T> = {
|
|
10
|
+
invalidationPolicy: "NONE";
|
|
11
|
+
} | {
|
|
12
|
+
invalidationPolicy: "TTL";
|
|
13
|
+
stdTTL: number;
|
|
14
|
+
} | {
|
|
15
|
+
invalidationPolicy: "EVENT";
|
|
16
|
+
predicate: (data: StoredData<T>) => boolean;
|
|
17
|
+
};
|
|
18
|
+
export type CacheOptions<T> = BaseCacheOptions & invalidationPolicyOptions<T>;
|
|
19
|
+
export type CacheOptionsInput<T> = Partial<BaseCacheOptions> & ({
|
|
20
|
+
invalidationPolicy?: "NONE";
|
|
21
|
+
} | {
|
|
22
|
+
invalidationPolicy?: "TTL";
|
|
23
|
+
stdTTL?: number;
|
|
24
|
+
} | {
|
|
25
|
+
invalidationPolicy?: "EVENT";
|
|
26
|
+
predicate?: (data: StoredData<T>) => boolean;
|
|
27
|
+
});
|
|
28
|
+
export type CacheStats = {
|
|
29
|
+
keys: number;
|
|
30
|
+
hits: number;
|
|
31
|
+
misses: number;
|
|
32
|
+
evictions: number;
|
|
33
|
+
ksize: number;
|
|
34
|
+
vsize: number;
|
|
35
|
+
};
|
|
36
|
+
export type StoredData<T> = {
|
|
37
|
+
value: T;
|
|
38
|
+
ttl?: number;
|
|
39
|
+
numberOfAccess: number;
|
|
40
|
+
lastAccessTimeStamp: number;
|
|
41
|
+
};
|
|
42
|
+
export type CacheEventMap<T> = {
|
|
43
|
+
set: [key: string | number, data: T];
|
|
44
|
+
get: [key: string | number, data: T];
|
|
45
|
+
del: [key: string | number, data?: T];
|
|
46
|
+
miss: [key: string | number];
|
|
47
|
+
expired: [key: string | number, cachedData: StoredData<T>];
|
|
48
|
+
error: [message: string];
|
|
49
|
+
evicted: [key: string | number, data?: T];
|
|
50
|
+
flush: [];
|
|
51
|
+
"flush-stats": [];
|
|
52
|
+
};
|
|
53
|
+
export declare class BTNCache<T = any> extends EventEmitter<CacheEventMap<T>> {
|
|
54
|
+
private data;
|
|
55
|
+
private options;
|
|
56
|
+
private stats;
|
|
57
|
+
private checkTimeout;
|
|
58
|
+
private queue;
|
|
59
|
+
/**
|
|
60
|
+
* Creates a new in memory cache using the options given to the constructor
|
|
61
|
+
* @param options the options object: \
|
|
62
|
+
* **evictionPolicy**: the type of eviction policy, one of RANDOM, LRU, LFU. (default: RANDOM)\
|
|
63
|
+
* **deleteOnExpire**: if the data gets deleted when it expires. \
|
|
64
|
+
* **maxKeys**: maximum amount of keys that can be stored in the cache, defaults to -1 (unlimited) \
|
|
65
|
+
* **useClones**: if the get and set operators copy the entered/received data or return a reference \
|
|
66
|
+
* **checkPeriod**: how often the cache runs an invalidation check. \
|
|
67
|
+
*
|
|
68
|
+
* **invalidationPolicy**: the type of invalidation policy, one of NONE, TTL or EVENT. (default: TTL) \
|
|
69
|
+
*
|
|
70
|
+
* When *invalidationPolicy == "TTL"*:
|
|
71
|
+
* **stdTTL**: the standard time to live on any data point unless specified other wise.
|
|
72
|
+
*
|
|
73
|
+
* When *invalidationPolicy == "EVENT"*:
|
|
74
|
+
* **predicate**: function that given a data point's state, returns if it should be considered invalid or not.
|
|
75
|
+
*/
|
|
76
|
+
constructor(options?: CacheOptionsInput<T>);
|
|
77
|
+
/**
|
|
78
|
+
* Retrieves a data point from the cache
|
|
79
|
+
* @emits "miss" Indicates that a cache miss happened
|
|
80
|
+
* @emits "get" Indicates that a cache hit happened
|
|
81
|
+
* @param key Key of the data to retrieve
|
|
82
|
+
*/
|
|
83
|
+
get(key: string | number): T | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Method to get many keys at once
|
|
86
|
+
* @emits "miss" Indicates that a cache miss happened
|
|
87
|
+
* @emits "get" Indicates that a cache hit happened
|
|
88
|
+
* @param keys Array of keys
|
|
89
|
+
* @returns
|
|
90
|
+
*/
|
|
91
|
+
many_get(keys: (string | number)[]): {
|
|
92
|
+
[key: string]: T;
|
|
93
|
+
[key: number]: T;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Function to set a certain key in the cache.
|
|
97
|
+
* @param key Key of the data to be set.
|
|
98
|
+
* @param data The data to be entered into the cache.
|
|
99
|
+
* @param ttl The TTL of the new data point, will be ignored if the invalidation policy isn't set to TTL
|
|
100
|
+
* @emits "set" Indicates that the data has been set successfully
|
|
101
|
+
* @returns
|
|
102
|
+
*/
|
|
103
|
+
set(key: string | number, data: T, ttl?: number): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Function to add multiple data points to the cache
|
|
106
|
+
* @param keyValueSet All of the keys that will be added, optional ttl argument will be ignored if not in TTL invalidation mode.
|
|
107
|
+
* @emits "set" Indicates that a value has been set correctly.
|
|
108
|
+
*/
|
|
109
|
+
many_set(keyValueSet: {
|
|
110
|
+
[key: string | number]: {
|
|
111
|
+
ttl?: number;
|
|
112
|
+
data: T;
|
|
113
|
+
};
|
|
114
|
+
}): void;
|
|
115
|
+
/**
|
|
116
|
+
* Function to get a key while removing it from the cache
|
|
117
|
+
* @param key Key to be taken out of the cache
|
|
118
|
+
* @returns value that was stored to that key
|
|
119
|
+
*/
|
|
120
|
+
take(key: string | number): T | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* Function to check if a key is present in the cache
|
|
123
|
+
* @param key key for the lookup
|
|
124
|
+
* @returns a boolean value specifying if the value is present.
|
|
125
|
+
*/
|
|
126
|
+
has(key: string | number): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Function to delete a certain set of keys from the cache
|
|
129
|
+
* @emits "del" Indicates that the data has been deleted successfully
|
|
130
|
+
* @param keys The array of keys to be deleted.
|
|
131
|
+
* @returns Number of elements deleted from the cache.
|
|
132
|
+
*/
|
|
133
|
+
del(keys: (string | number)[]): number;
|
|
134
|
+
/**
|
|
135
|
+
* Function to change the TTL of a given key in TTL mode
|
|
136
|
+
* @param key key to be changed
|
|
137
|
+
* @param newTTL the new TTL that will be assigned, if less than 0 key will be deleted.
|
|
138
|
+
* @returns boolean value representing if the change was successful
|
|
139
|
+
*/
|
|
140
|
+
ttl(key: string | number, newTTL?: number): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Function to get metadata about the key, such as TTL, time since last accessed, number of accesses
|
|
143
|
+
* @param key key for the lookup
|
|
144
|
+
* @returns metadata information about the key.
|
|
145
|
+
*/
|
|
146
|
+
getMeta(key: string | number): {
|
|
147
|
+
lastAccessTime: number;
|
|
148
|
+
numberOfAccesses: number;
|
|
149
|
+
ttl: number | undefined;
|
|
150
|
+
} | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* Function to get the data of the cache
|
|
153
|
+
* @returns the statistics of the cache
|
|
154
|
+
*/
|
|
155
|
+
getStats(): CacheStats;
|
|
156
|
+
/**
|
|
157
|
+
* Function to flush all the data and the statistics of the cache
|
|
158
|
+
*/
|
|
159
|
+
flushAll(): void;
|
|
160
|
+
/**
|
|
161
|
+
* Function to flush the statistics of the cache
|
|
162
|
+
*/
|
|
163
|
+
flushStats(): void;
|
|
164
|
+
/**
|
|
165
|
+
* Function to change some of the settings of the cache
|
|
166
|
+
* @param newOptions
|
|
167
|
+
*/
|
|
168
|
+
setSettings(newOptions: CacheOptionsInput<T>): void;
|
|
169
|
+
/**
|
|
170
|
+
* Function to close the cache.
|
|
171
|
+
*/
|
|
172
|
+
close(): void;
|
|
173
|
+
/**
|
|
174
|
+
* Function that returns all the present keys in the cache
|
|
175
|
+
* @returns List of keys present in the cache.
|
|
176
|
+
*/
|
|
177
|
+
keys(): (string | number)[];
|
|
178
|
+
/**
|
|
179
|
+
* Internal function to check the keys for expiration
|
|
180
|
+
* @param start decides if the next check will happen
|
|
181
|
+
*/
|
|
182
|
+
private _checkClock;
|
|
183
|
+
/**
|
|
184
|
+
* This function will evict a data point based on the set options
|
|
185
|
+
* @param numberOfEvictions number of keys that will get evicted, defaults to 1
|
|
186
|
+
* @emits "error" will emit an error if an undefined key is trying to be evicted.
|
|
187
|
+
*/
|
|
188
|
+
private _evictData;
|
|
189
|
+
/**
|
|
190
|
+
* Internal function that implements the RANDOM eviction policy
|
|
191
|
+
* @returns the key to be removed
|
|
192
|
+
*/
|
|
193
|
+
private _evictRandom;
|
|
194
|
+
/**
|
|
195
|
+
* Internal function that implements the LRU eviction policy
|
|
196
|
+
* @returns the key to be removed
|
|
197
|
+
*/
|
|
198
|
+
private _evictLRU;
|
|
199
|
+
/**
|
|
200
|
+
* Internal function that implements the LFU eviction policy
|
|
201
|
+
* @returns the key to be removed
|
|
202
|
+
*/
|
|
203
|
+
private _evictLFU;
|
|
204
|
+
/**
|
|
205
|
+
* Internal function that implements the FIFO eviction policy
|
|
206
|
+
* @returns the key to be removed
|
|
207
|
+
*/
|
|
208
|
+
private _evictFIFO;
|
|
209
|
+
/**
|
|
210
|
+
* Internal function to check if the cache is full, if so evict data according to the eviction policy
|
|
211
|
+
* @param [padding=0] How big the empty space should be.
|
|
212
|
+
*/
|
|
213
|
+
private _checkAndEvict;
|
|
214
|
+
/**
|
|
215
|
+
* Internal method to roughly calculate the size of the value
|
|
216
|
+
* @param value Value to process its size
|
|
217
|
+
*/
|
|
218
|
+
private _getDataLength;
|
|
219
|
+
/**
|
|
220
|
+
* Internal method to check if a data point is invalidated or not.
|
|
221
|
+
* @emits "expired" Indicates that some data has been invalidated.
|
|
222
|
+
* @param key The key of the checked data
|
|
223
|
+
* @param data The Stored data
|
|
224
|
+
* @returns If the data is invalidated or not
|
|
225
|
+
*/
|
|
226
|
+
private _checkData;
|
|
227
|
+
/**
|
|
228
|
+
* Internal method to standardize the returns of getters.
|
|
229
|
+
* @param data The data to be unwrapped
|
|
230
|
+
* @param asClone Option if to return a copy of the data or a reference
|
|
231
|
+
*/
|
|
232
|
+
private _unwrap;
|
|
233
|
+
/**
|
|
234
|
+
* Internal method to wrap any value into the stored value type.
|
|
235
|
+
* @param value Value that will be wrapped
|
|
236
|
+
* @param ttl Time to live, will be ignored if the invalidation policy is not set to "TTL"
|
|
237
|
+
* @param asClone If the wrapped value is a clone or a reference to the original
|
|
238
|
+
*/
|
|
239
|
+
private _wrap;
|
|
240
|
+
}
|
|
241
|
+
export {};
|
|
242
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BTNCache = void 0;
|
|
7
|
+
const events_1 = __importDefault(require("events"));
|
|
8
|
+
const clone_1 = __importDefault(require("clone"));
|
|
9
|
+
const DEFAULT_OPTIONS = {
|
|
10
|
+
evictionPolicy: "RANDOM",
|
|
11
|
+
deleteOnExpire: true,
|
|
12
|
+
maxKeys: -1,
|
|
13
|
+
invalidationPolicy: "TTL",
|
|
14
|
+
stdTTL: 500 * 1000,
|
|
15
|
+
useClones: true,
|
|
16
|
+
checkPeriod: 300 * 1000,
|
|
17
|
+
};
|
|
18
|
+
class BTNCache extends events_1.default {
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new in memory cache using the options given to the constructor
|
|
21
|
+
* @param options the options object: \
|
|
22
|
+
* **evictionPolicy**: the type of eviction policy, one of RANDOM, LRU, LFU. (default: RANDOM)\
|
|
23
|
+
* **deleteOnExpire**: if the data gets deleted when it expires. \
|
|
24
|
+
* **maxKeys**: maximum amount of keys that can be stored in the cache, defaults to -1 (unlimited) \
|
|
25
|
+
* **useClones**: if the get and set operators copy the entered/received data or return a reference \
|
|
26
|
+
* **checkPeriod**: how often the cache runs an invalidation check. \
|
|
27
|
+
*
|
|
28
|
+
* **invalidationPolicy**: the type of invalidation policy, one of NONE, TTL or EVENT. (default: TTL) \
|
|
29
|
+
*
|
|
30
|
+
* When *invalidationPolicy == "TTL"*:
|
|
31
|
+
* **stdTTL**: the standard time to live on any data point unless specified other wise.
|
|
32
|
+
*
|
|
33
|
+
* When *invalidationPolicy == "EVENT"*:
|
|
34
|
+
* **predicate**: function that given a data point's state, returns if it should be considered invalid or not.
|
|
35
|
+
*/
|
|
36
|
+
constructor(options = {}) {
|
|
37
|
+
super();
|
|
38
|
+
this.data = new Map();
|
|
39
|
+
this.queue = null;
|
|
40
|
+
this.options = {
|
|
41
|
+
...DEFAULT_OPTIONS,
|
|
42
|
+
...options,
|
|
43
|
+
};
|
|
44
|
+
if (this.options.invalidationPolicy == "EVENT" &&
|
|
45
|
+
typeof this.options.predicate !== "function") {
|
|
46
|
+
throw new Error("EVENT invalidation requires a predicate");
|
|
47
|
+
}
|
|
48
|
+
if (this.options.evictionPolicy == "FIFO") {
|
|
49
|
+
this.queue = [];
|
|
50
|
+
}
|
|
51
|
+
this.stats = {
|
|
52
|
+
keys: 0,
|
|
53
|
+
hits: 0,
|
|
54
|
+
misses: 0,
|
|
55
|
+
evictions: 0,
|
|
56
|
+
ksize: 0,
|
|
57
|
+
vsize: 0,
|
|
58
|
+
};
|
|
59
|
+
this._checkClock();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves a data point from the cache
|
|
63
|
+
* @emits "miss" Indicates that a cache miss happened
|
|
64
|
+
* @emits "get" Indicates that a cache hit happened
|
|
65
|
+
* @param key Key of the data to retrieve
|
|
66
|
+
*/
|
|
67
|
+
get(key) {
|
|
68
|
+
const found = this.data.get(key);
|
|
69
|
+
if (found && this._checkData(key, found)) {
|
|
70
|
+
this.stats.hits++;
|
|
71
|
+
found.numberOfAccess++;
|
|
72
|
+
found.lastAccessTimeStamp = Date.now();
|
|
73
|
+
this.emit("get", key, this._unwrap(found));
|
|
74
|
+
return this._unwrap(found);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
this.stats.misses++;
|
|
78
|
+
this.emit("miss", key);
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Method to get many keys at once
|
|
84
|
+
* @emits "miss" Indicates that a cache miss happened
|
|
85
|
+
* @emits "get" Indicates that a cache hit happened
|
|
86
|
+
* @param keys Array of keys
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
89
|
+
many_get(keys) {
|
|
90
|
+
let all_found = {};
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
const found = this.data.get(key);
|
|
93
|
+
if (found && this._checkData(key, found)) {
|
|
94
|
+
this.stats.hits++;
|
|
95
|
+
found.numberOfAccess++;
|
|
96
|
+
found.lastAccessTimeStamp = Date.now();
|
|
97
|
+
all_found[key] = this._unwrap(found);
|
|
98
|
+
this.emit("get", key, this._unwrap(found));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.stats.misses++;
|
|
102
|
+
this.emit("miss", key);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return all_found;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Function to set a certain key in the cache.
|
|
109
|
+
* @param key Key of the data to be set.
|
|
110
|
+
* @param data The data to be entered into the cache.
|
|
111
|
+
* @param ttl The TTL of the new data point, will be ignored if the invalidation policy isn't set to TTL
|
|
112
|
+
* @emits "set" Indicates that the data has been set successfully
|
|
113
|
+
* @returns
|
|
114
|
+
*/
|
|
115
|
+
set(key, data, ttl) {
|
|
116
|
+
this._checkAndEvict();
|
|
117
|
+
if (this.options.invalidationPolicy == "TTL" && !ttl) {
|
|
118
|
+
ttl = this.options.stdTTL;
|
|
119
|
+
}
|
|
120
|
+
let existent = false;
|
|
121
|
+
if (this.data.has(key)) {
|
|
122
|
+
existent = true;
|
|
123
|
+
this.stats.vsize -= this._getDataLength(data);
|
|
124
|
+
}
|
|
125
|
+
this.data.set(key, this._wrap(data, ttl));
|
|
126
|
+
this.stats.vsize += this._getDataLength(data);
|
|
127
|
+
if (this.options.evictionPolicy == "FIFO" && this.queue) {
|
|
128
|
+
this.queue.push(key);
|
|
129
|
+
}
|
|
130
|
+
if (!existent) {
|
|
131
|
+
this.stats.ksize += this._getDataLength(key);
|
|
132
|
+
this.stats.keys++;
|
|
133
|
+
}
|
|
134
|
+
this.emit("set", key, data);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Function to add multiple data points to the cache
|
|
139
|
+
* @param keyValueSet All of the keys that will be added, optional ttl argument will be ignored if not in TTL invalidation mode.
|
|
140
|
+
* @emits "set" Indicates that a value has been set correctly.
|
|
141
|
+
*/
|
|
142
|
+
many_set(keyValueSet) {
|
|
143
|
+
this._checkAndEvict(Object.keys(keyValueSet).length);
|
|
144
|
+
for (const keyValuePair of Object.entries(keyValueSet)) {
|
|
145
|
+
const [key, { ttl, data }] = keyValuePair;
|
|
146
|
+
this.set(key, data, ttl);
|
|
147
|
+
this.emit("set", key, data);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Function to get a key while removing it from the cache
|
|
152
|
+
* @param key Key to be taken out of the cache
|
|
153
|
+
* @returns value that was stored to that key
|
|
154
|
+
*/
|
|
155
|
+
take(key) {
|
|
156
|
+
const found = this.get(key);
|
|
157
|
+
if (found) {
|
|
158
|
+
this.del([key]);
|
|
159
|
+
}
|
|
160
|
+
return found;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Function to check if a key is present in the cache
|
|
164
|
+
* @param key key for the lookup
|
|
165
|
+
* @returns a boolean value specifying if the value is present.
|
|
166
|
+
*/
|
|
167
|
+
has(key) {
|
|
168
|
+
const found = this.data.get(key);
|
|
169
|
+
if (!found)
|
|
170
|
+
return false;
|
|
171
|
+
return this._checkData(key, found);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Function to delete a certain set of keys from the cache
|
|
175
|
+
* @emits "del" Indicates that the data has been deleted successfully
|
|
176
|
+
* @param keys The array of keys to be deleted.
|
|
177
|
+
* @returns Number of elements deleted from the cache.
|
|
178
|
+
*/
|
|
179
|
+
del(keys) {
|
|
180
|
+
let delCount = 0;
|
|
181
|
+
for (const key of keys) {
|
|
182
|
+
if (this.data.has(key)) {
|
|
183
|
+
const found = this.data.get(key);
|
|
184
|
+
this.stats.vsize -= this._getDataLength(found);
|
|
185
|
+
this.stats.ksize -= this._getDataLength(key);
|
|
186
|
+
this.stats.keys--;
|
|
187
|
+
delCount++;
|
|
188
|
+
this.data.delete(key);
|
|
189
|
+
if (this.options.evictionPolicy == "FIFO" && this.queue) {
|
|
190
|
+
this.queue = this.queue.filter((k) => key !== k);
|
|
191
|
+
}
|
|
192
|
+
this.emit("del", key, found?.value);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return delCount;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Function to change the TTL of a given key in TTL mode
|
|
199
|
+
* @param key key to be changed
|
|
200
|
+
* @param newTTL the new TTL that will be assigned, if less than 0 key will be deleted.
|
|
201
|
+
* @returns boolean value representing if the change was successful
|
|
202
|
+
*/
|
|
203
|
+
ttl(key, newTTL) {
|
|
204
|
+
if (this.options.invalidationPolicy !== "TTL")
|
|
205
|
+
return false;
|
|
206
|
+
if (!newTTL) {
|
|
207
|
+
newTTL = this.options.stdTTL;
|
|
208
|
+
}
|
|
209
|
+
const found = this.data.get(key);
|
|
210
|
+
if (found && this._checkData(key, found)) {
|
|
211
|
+
if (newTTL >= 0) {
|
|
212
|
+
this.data.set(key, this._wrap(found.value, newTTL, false));
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
this.del([key]);
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Function to get metadata about the key, such as TTL, time since last accessed, number of accesses
|
|
225
|
+
* @param key key for the lookup
|
|
226
|
+
* @returns metadata information about the key.
|
|
227
|
+
*/
|
|
228
|
+
getMeta(key) {
|
|
229
|
+
const found = this.data.get(key);
|
|
230
|
+
if (found && this._checkData(key, found)) {
|
|
231
|
+
return {
|
|
232
|
+
lastAccessTime: found.lastAccessTimeStamp,
|
|
233
|
+
numberOfAccesses: found.numberOfAccess,
|
|
234
|
+
ttl: found.ttl,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Function to get the data of the cache
|
|
240
|
+
* @returns the statistics of the cache
|
|
241
|
+
*/
|
|
242
|
+
getStats() {
|
|
243
|
+
return this.stats;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Function to flush all the data and the statistics of the cache
|
|
247
|
+
*/
|
|
248
|
+
flushAll() {
|
|
249
|
+
this.data = new Map();
|
|
250
|
+
this.stats = {
|
|
251
|
+
keys: 0,
|
|
252
|
+
vsize: 0,
|
|
253
|
+
evictions: 0,
|
|
254
|
+
ksize: 0,
|
|
255
|
+
hits: 0,
|
|
256
|
+
misses: 0,
|
|
257
|
+
};
|
|
258
|
+
this.emit("flush");
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Function to flush the statistics of the cache
|
|
262
|
+
*/
|
|
263
|
+
flushStats() {
|
|
264
|
+
this.stats = {
|
|
265
|
+
keys: 0,
|
|
266
|
+
vsize: 0,
|
|
267
|
+
ksize: 0,
|
|
268
|
+
evictions: 0,
|
|
269
|
+
hits: 0,
|
|
270
|
+
misses: 0,
|
|
271
|
+
};
|
|
272
|
+
this.emit("flush-stats");
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Function to change some of the settings of the cache
|
|
276
|
+
* @param newOptions
|
|
277
|
+
*/
|
|
278
|
+
setSettings(newOptions) {
|
|
279
|
+
this.options = {
|
|
280
|
+
...this.options,
|
|
281
|
+
...newOptions,
|
|
282
|
+
};
|
|
283
|
+
this.emit("settings-change", this.options);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Function to close the cache.
|
|
287
|
+
*/
|
|
288
|
+
close() {
|
|
289
|
+
if (this.checkTimeout)
|
|
290
|
+
clearTimeout(this.checkTimeout);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Function that returns all the present keys in the cache
|
|
294
|
+
* @returns List of keys present in the cache.
|
|
295
|
+
*/
|
|
296
|
+
keys() {
|
|
297
|
+
const keys = [];
|
|
298
|
+
for (const key of this.data.keys()) {
|
|
299
|
+
keys.push(key);
|
|
300
|
+
}
|
|
301
|
+
return keys;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Internal function to check the keys for expiration
|
|
305
|
+
* @param start decides if the next check will happen
|
|
306
|
+
*/
|
|
307
|
+
_checkClock(start = true) {
|
|
308
|
+
for (const [key, value] of this.data.entries()) {
|
|
309
|
+
this._checkData(key, value);
|
|
310
|
+
}
|
|
311
|
+
if (start && this.options.checkPeriod) {
|
|
312
|
+
this.checkTimeout = setTimeout(() => this._checkClock(start), this.options.checkPeriod);
|
|
313
|
+
this.checkTimeout.unref();
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* This function will evict a data point based on the set options
|
|
319
|
+
* @param numberOfEvictions number of keys that will get evicted, defaults to 1
|
|
320
|
+
* @emits "error" will emit an error if an undefined key is trying to be evicted.
|
|
321
|
+
*/
|
|
322
|
+
_evictData(numberOfEvictions = 1) {
|
|
323
|
+
const evictors = {
|
|
324
|
+
FIFO: () => this._evictFIFO(),
|
|
325
|
+
LRU: () => this._evictLRU(),
|
|
326
|
+
LFU: () => this._evictLFU(),
|
|
327
|
+
RANDOM: () => this._evictRandom(),
|
|
328
|
+
};
|
|
329
|
+
let evicted = undefined;
|
|
330
|
+
for (let i = 0; i < numberOfEvictions; i++) {
|
|
331
|
+
evicted = evictors[this.options.evictionPolicy]();
|
|
332
|
+
if (evicted === undefined) {
|
|
333
|
+
this.emit("error", "ERROR_UNDEFINED_EVICTION");
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
this.emit("evicted", evicted, this.get(evicted));
|
|
337
|
+
this.del([evicted]);
|
|
338
|
+
this.stats.evictions++;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Internal function that implements the RANDOM eviction policy
|
|
343
|
+
* @returns the key to be removed
|
|
344
|
+
*/
|
|
345
|
+
_evictRandom() {
|
|
346
|
+
const keys = this.keys();
|
|
347
|
+
if (keys.length === 0)
|
|
348
|
+
return undefined;
|
|
349
|
+
const evictedIndex = Math.floor(Math.random() * keys.length);
|
|
350
|
+
return keys[evictedIndex];
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Internal function that implements the LRU eviction policy
|
|
354
|
+
* @returns the key to be removed
|
|
355
|
+
*/
|
|
356
|
+
_evictLRU() {
|
|
357
|
+
const now = Date.now();
|
|
358
|
+
return this.keys().reduce((accumulator, current) => {
|
|
359
|
+
const meta = this.getMeta(current);
|
|
360
|
+
if (!meta)
|
|
361
|
+
return accumulator;
|
|
362
|
+
const difference = now - meta.lastAccessTime;
|
|
363
|
+
if (!accumulator || difference >= accumulator.difference) {
|
|
364
|
+
return {
|
|
365
|
+
key: current,
|
|
366
|
+
difference,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return accumulator;
|
|
370
|
+
}, undefined)?.key;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Internal function that implements the LFU eviction policy
|
|
374
|
+
* @returns the key to be removed
|
|
375
|
+
*/
|
|
376
|
+
_evictLFU() {
|
|
377
|
+
return this.keys().reduce((accumulator, current) => {
|
|
378
|
+
const meta = this.getMeta(current);
|
|
379
|
+
if (!meta)
|
|
380
|
+
return accumulator;
|
|
381
|
+
if (!accumulator || meta.numberOfAccesses <= accumulator.minimum) {
|
|
382
|
+
return {
|
|
383
|
+
key: current,
|
|
384
|
+
minimum: meta.numberOfAccesses,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return accumulator;
|
|
388
|
+
}, undefined)?.key;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Internal function that implements the FIFO eviction policy
|
|
392
|
+
* @returns the key to be removed
|
|
393
|
+
*/
|
|
394
|
+
_evictFIFO() {
|
|
395
|
+
if (this.queue) {
|
|
396
|
+
return this.queue.shift();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Internal function to check if the cache is full, if so evict data according to the eviction policy
|
|
401
|
+
* @param [padding=0] How big the empty space should be.
|
|
402
|
+
*/
|
|
403
|
+
_checkAndEvict(padding = 1) {
|
|
404
|
+
if (this.options.maxKeys < 0)
|
|
405
|
+
return;
|
|
406
|
+
const overflow = this.stats.keys + padding - this.options.maxKeys;
|
|
407
|
+
if (overflow > 0) {
|
|
408
|
+
this._evictData(overflow);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Internal method to roughly calculate the size of the value
|
|
413
|
+
* @param value Value to process its size
|
|
414
|
+
*/
|
|
415
|
+
_getDataLength(value) {
|
|
416
|
+
if (typeof value === "string") {
|
|
417
|
+
return value.length;
|
|
418
|
+
}
|
|
419
|
+
else if (typeof value === "number") {
|
|
420
|
+
return 8;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
return JSON.stringify(value).length;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Internal method to check if a data point is invalidated or not.
|
|
428
|
+
* @emits "expired" Indicates that some data has been invalidated.
|
|
429
|
+
* @param key The key of the checked data
|
|
430
|
+
* @param data The Stored data
|
|
431
|
+
* @returns If the data is invalidated or not
|
|
432
|
+
*/
|
|
433
|
+
_checkData(key, data) {
|
|
434
|
+
let returnValue = true;
|
|
435
|
+
const deleteAndEmit = () => {
|
|
436
|
+
if (this.options.deleteOnExpire) {
|
|
437
|
+
returnValue = false;
|
|
438
|
+
this.del([key]);
|
|
439
|
+
}
|
|
440
|
+
this.emit("expired", key, data);
|
|
441
|
+
};
|
|
442
|
+
if (this.options.invalidationPolicy === "TTL") {
|
|
443
|
+
if (data.ttl && data.ttl < Date.now()) {
|
|
444
|
+
deleteAndEmit();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else if (this.options.invalidationPolicy === "EVENT") {
|
|
448
|
+
const predicateResult = this.options.predicate(data);
|
|
449
|
+
if (predicateResult) {
|
|
450
|
+
deleteAndEmit();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return returnValue;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Internal method to standardize the returns of getters.
|
|
457
|
+
* @param data The data to be unwrapped
|
|
458
|
+
* @param asClone Option if to return a copy of the data or a reference
|
|
459
|
+
*/
|
|
460
|
+
_unwrap(data, asClone = true) {
|
|
461
|
+
if (!this.options.useClones) {
|
|
462
|
+
asClone = false;
|
|
463
|
+
}
|
|
464
|
+
if (asClone)
|
|
465
|
+
return (0, clone_1.default)(data.value);
|
|
466
|
+
else
|
|
467
|
+
return data.value;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Internal method to wrap any value into the stored value type.
|
|
471
|
+
* @param value Value that will be wrapped
|
|
472
|
+
* @param ttl Time to live, will be ignored if the invalidation policy is not set to "TTL"
|
|
473
|
+
* @param asClone If the wrapped value is a clone or a reference to the original
|
|
474
|
+
*/
|
|
475
|
+
_wrap(value, ttl, asClone = true) {
|
|
476
|
+
if (!this.options.useClones) {
|
|
477
|
+
asClone = false;
|
|
478
|
+
}
|
|
479
|
+
const now = Date.now();
|
|
480
|
+
let lifetime = -1;
|
|
481
|
+
if (this.options.invalidationPolicy === "TTL") {
|
|
482
|
+
const TTLMultiplication = 1;
|
|
483
|
+
if (ttl == 0) {
|
|
484
|
+
lifetime = 0;
|
|
485
|
+
}
|
|
486
|
+
else if (ttl) {
|
|
487
|
+
lifetime = now + ttl * TTLMultiplication;
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
if (this.options.stdTTL == 0) {
|
|
491
|
+
lifetime = 0;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
lifetime = now + this.options.stdTTL * TTLMultiplication;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const returned = {
|
|
499
|
+
value: asClone ? (0, clone_1.default)(value) : value,
|
|
500
|
+
ttl: lifetime,
|
|
501
|
+
numberOfAccess: 0,
|
|
502
|
+
lastAccessTimeStamp: now,
|
|
503
|
+
};
|
|
504
|
+
return returned;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
exports.BTNCache = BTNCache;
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "btn-cache",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Type safe implementation of in-memory caching inspired by node-cache",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"caching",
|
|
8
|
+
"in-memory",
|
|
9
|
+
"backend",
|
|
10
|
+
"server-side",
|
|
11
|
+
"server",
|
|
12
|
+
"library",
|
|
13
|
+
"lightweight",
|
|
14
|
+
"light",
|
|
15
|
+
"simple"
|
|
16
|
+
],
|
|
17
|
+
"author": "imaaann",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"main": "dist/index.js",
|
|
20
|
+
"types": "dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"require": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc -p tsconfig.json",
|
|
33
|
+
"test": "vitest",
|
|
34
|
+
"test:run": "vitest run",
|
|
35
|
+
"test:watch": "vitest --watch",
|
|
36
|
+
"test:coverage": "vitest run --coverage"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/clone": "^2.1.4",
|
|
40
|
+
"@types/node": "^25.0.3",
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"vitest": "^4.0.16"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"clone": "^2.1.2"
|
|
46
|
+
}
|
|
47
|
+
}
|