@sebspark/promise-cache 6.1.2 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-C6wwvPpM.mjs +18 -0
- package/dist/index.d.mts +940 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1229 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +11 -5
- package/dist/index.d.ts +0 -895
- package/dist/index.js +0 -1323
- package/dist/index.js.map +0 -1
package/dist/index.js
DELETED
|
@@ -1,1323 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
// src/serializer.ts
|
|
8
|
-
var serializer_exports = {};
|
|
9
|
-
__export(serializer_exports, {
|
|
10
|
-
deserialize: () => deserialize,
|
|
11
|
-
serialize: () => serialize
|
|
12
|
-
});
|
|
13
|
-
import superjson from "superjson";
|
|
14
|
-
var serialize = (data) => {
|
|
15
|
-
return superjson.stringify(data);
|
|
16
|
-
};
|
|
17
|
-
var deserialize = (serialized) => {
|
|
18
|
-
if (serialized === void 0 || serialized === null) return serialized;
|
|
19
|
-
return superjson.parse(serialized);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// src/setOptions.ts
|
|
23
|
-
var toSetOptions = (expiry) => {
|
|
24
|
-
const options = {};
|
|
25
|
-
if (expiry !== void 0) {
|
|
26
|
-
if (typeof expiry === "number") {
|
|
27
|
-
options.PX = expiry;
|
|
28
|
-
} else if (expiry instanceof Date && !Number.isNaN(expiry.getTime())) {
|
|
29
|
-
const timestamp = expiry.getTime();
|
|
30
|
-
if (timestamp > Date.now()) {
|
|
31
|
-
options.PXAT = timestamp;
|
|
32
|
-
} else {
|
|
33
|
-
options.PX = 1;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (!options.PX && !options.PXAT) {
|
|
38
|
-
options.EX = 1;
|
|
39
|
-
}
|
|
40
|
-
return options;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// src/cache.ts
|
|
44
|
-
var createCache = (persistor, prefix) => {
|
|
45
|
-
const pendingPromises = /* @__PURE__ */ new Map();
|
|
46
|
-
const cache = {
|
|
47
|
-
persistor,
|
|
48
|
-
wrap: (delegate, options) => {
|
|
49
|
-
return async (...args) => {
|
|
50
|
-
let key = typeof options.key === "string" ? options.key : options.key(...args);
|
|
51
|
-
if (prefix) {
|
|
52
|
-
key = `${prefix}:${key}`;
|
|
53
|
-
}
|
|
54
|
-
if (pendingPromises.has(key)) {
|
|
55
|
-
return pendingPromises.get(key);
|
|
56
|
-
}
|
|
57
|
-
const resultPromise = (async () => {
|
|
58
|
-
try {
|
|
59
|
-
await ensurePersistorIsReady(persistor);
|
|
60
|
-
const cached = deserialize(await persistor.get(key));
|
|
61
|
-
if (cached !== null) {
|
|
62
|
-
return cached;
|
|
63
|
-
}
|
|
64
|
-
const result = await delegate(...args);
|
|
65
|
-
const expiry = typeof options.expiry === "function" ? options.expiry(args, result) : options.expiry;
|
|
66
|
-
await ensurePersistorIsReady(persistor);
|
|
67
|
-
const serialized = serialize(result);
|
|
68
|
-
const setOptions = toSetOptions(expiry);
|
|
69
|
-
await persistor.set(key, serialized, setOptions);
|
|
70
|
-
return result;
|
|
71
|
-
} finally {
|
|
72
|
-
pendingPromises.delete(key);
|
|
73
|
-
}
|
|
74
|
-
})();
|
|
75
|
-
pendingPromises.set(key, resultPromise);
|
|
76
|
-
return resultPromise;
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
return cache;
|
|
81
|
-
};
|
|
82
|
-
var ensurePersistorIsReady = async (persistor) => {
|
|
83
|
-
if (persistor.isReady) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
if (persistor.isOpen) {
|
|
87
|
-
await new Promise((resolve) => {
|
|
88
|
-
if (persistor.isReady) return resolve();
|
|
89
|
-
persistor.once("ready", resolve);
|
|
90
|
-
return;
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
await persistor.connect();
|
|
95
|
-
} catch (err) {
|
|
96
|
-
if (err.message === "Socket already opened") {
|
|
97
|
-
await new Promise((resolve) => {
|
|
98
|
-
if (persistor.isReady) return resolve();
|
|
99
|
-
persistor.once("ready", resolve);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// src/inMemoryPersistor.ts
|
|
106
|
-
var InMemoryPersistor = class {
|
|
107
|
-
/**
|
|
108
|
-
* Internal key-value store for caching string values.
|
|
109
|
-
* @private
|
|
110
|
-
*/
|
|
111
|
-
store;
|
|
112
|
-
/**
|
|
113
|
-
* Tracks active timeouts for expiring keys.
|
|
114
|
-
* Each key maps to a `setTimeout` reference that deletes the key when triggered.
|
|
115
|
-
* @private
|
|
116
|
-
*/
|
|
117
|
-
expirations;
|
|
118
|
-
/**
|
|
119
|
-
* Stores absolute expiration timestamps (in milliseconds since epoch) for each key.
|
|
120
|
-
* Used to compute remaining TTL.
|
|
121
|
-
* @private
|
|
122
|
-
*/
|
|
123
|
-
expiryTimestamps;
|
|
124
|
-
/**
|
|
125
|
-
* Creates a new instance of `InMemoryPersistor`.
|
|
126
|
-
* Initializes an empty store, expiration map, and TTL tracker.
|
|
127
|
-
*/
|
|
128
|
-
constructor() {
|
|
129
|
-
this.store = /* @__PURE__ */ new Map();
|
|
130
|
-
this.expirations = /* @__PURE__ */ new Map();
|
|
131
|
-
this.expiryTimestamps = /* @__PURE__ */ new Map();
|
|
132
|
-
}
|
|
133
|
-
async connect() {
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
get isReady() {
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
get isOpen() {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
once() {
|
|
143
|
-
return this;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Stores a key-value pair with optional expiration settings.
|
|
147
|
-
* If an expiration is provided (`EX`, `PX`, `EXAT`, `PXAT`), the key is automatically removed when TTL expires.
|
|
148
|
-
*
|
|
149
|
-
* @param {string} key - The key to store.
|
|
150
|
-
* @param {string} value - The string value to associate with the key.
|
|
151
|
-
* @param {SetOptions} [options] - Optional Redis-style expiration settings.
|
|
152
|
-
* @returns {Promise<'OK' | null>} Resolves to `'OK'` on success, or `null` if a conditional set (`NX`) fails.
|
|
153
|
-
*/
|
|
154
|
-
async set(key, value, options) {
|
|
155
|
-
if (options?.NX && this.store.has(key)) {
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
if (options?.XX && !this.store.has(key)) {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
this.store.set(key, value);
|
|
162
|
-
if (options?.EX !== void 0) {
|
|
163
|
-
this.setExpiration(key, options.EX * 1e3);
|
|
164
|
-
} else if (options?.PX !== void 0) {
|
|
165
|
-
this.setExpiration(key, options.PX);
|
|
166
|
-
} else if (options?.EXAT !== void 0) {
|
|
167
|
-
const timeToExpire = options.EXAT * 1e3 - Date.now();
|
|
168
|
-
this.setExpiration(key, Math.max(0, timeToExpire));
|
|
169
|
-
} else if (options?.PXAT !== void 0) {
|
|
170
|
-
const timeToExpire = options.PXAT - Date.now();
|
|
171
|
-
this.setExpiration(key, Math.max(0, timeToExpire));
|
|
172
|
-
}
|
|
173
|
-
return "OK";
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Stores a key-value pair with an expiration time in seconds.
|
|
177
|
-
* If the key already exists, it will be overwritten.
|
|
178
|
-
*
|
|
179
|
-
* @param key - The storage key.
|
|
180
|
-
* @param seconds - Expiration time in seconds.
|
|
181
|
-
* @param value - The string value to store.
|
|
182
|
-
* @returns Resolves to `'OK'` on success.
|
|
183
|
-
*/
|
|
184
|
-
async setEx(key, seconds2, value) {
|
|
185
|
-
this.store.set(key, value);
|
|
186
|
-
await this.expire(key, seconds2);
|
|
187
|
-
return "OK";
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Stores a key-value pair with an expiration time in milliseconds.
|
|
191
|
-
* If the key already exists, it will be overwritten.
|
|
192
|
-
*
|
|
193
|
-
* @param key - The storage key.
|
|
194
|
-
* @param milliseconds - Expiration time in milliseconds.
|
|
195
|
-
* @param value - The string value to store.
|
|
196
|
-
* @returns Resolves to `'OK'` on success.
|
|
197
|
-
*/
|
|
198
|
-
async pSetEx(key, milliseconds, value) {
|
|
199
|
-
return this.setEx(key, milliseconds / 1e3, value);
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Stores a key-value pair **only if the key does not already exist**.
|
|
203
|
-
* If the key exists, the operation fails and returns `false`.
|
|
204
|
-
*
|
|
205
|
-
* @param key - The storage key.
|
|
206
|
-
* @param value - The string value to store.
|
|
207
|
-
* @returns Resolves to `true` if the key was set, or `false` if the key already exists.
|
|
208
|
-
*/
|
|
209
|
-
async setNX(key, value) {
|
|
210
|
-
if (this.store.has(key)) return 0;
|
|
211
|
-
this.store.set(key, value);
|
|
212
|
-
return 1;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Retrieves the value associated with a key.
|
|
216
|
-
*
|
|
217
|
-
* @param {string} key - The key to retrieve.
|
|
218
|
-
* @returns {Promise<string | null>} Resolves to the string value, or `null` if the key does not exist.
|
|
219
|
-
*/
|
|
220
|
-
async get(key) {
|
|
221
|
-
return this.store.get(key) ?? null;
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Deletes a key from the store.
|
|
225
|
-
* If the key exists, it is removed along with any associated expiration.
|
|
226
|
-
*
|
|
227
|
-
* @param {string} key - The key to delete.
|
|
228
|
-
* @returns {Promise<number>} Resolves to `1` if the key was deleted, or `0` if the key did not exist.
|
|
229
|
-
*/
|
|
230
|
-
async del(key) {
|
|
231
|
-
const existed = this.store.has(key);
|
|
232
|
-
if (existed) {
|
|
233
|
-
this.store.delete(key);
|
|
234
|
-
this.clearExpiration(key);
|
|
235
|
-
}
|
|
236
|
-
return existed ? 1 : 0;
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Sets a time-to-live (TTL) in seconds for a key.
|
|
240
|
-
* If the key exists, it will be deleted after the specified duration.
|
|
241
|
-
*
|
|
242
|
-
* @param {string} key - The key to set an expiration on.
|
|
243
|
-
* @param {number} seconds - The TTL in seconds.
|
|
244
|
-
* @returns {Promise<number>} Resolves to `1` if the TTL was set, or `0` if the key does not exist.
|
|
245
|
-
*/
|
|
246
|
-
async expire(key, seconds2) {
|
|
247
|
-
if (!this.store.has(key)) return 0;
|
|
248
|
-
this.setExpiration(key, seconds2 * 1e3);
|
|
249
|
-
return 1;
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Retrieves the remaining time-to-live (TTL) of a key in seconds.
|
|
253
|
-
*
|
|
254
|
-
* @param {string} key - The key to check.
|
|
255
|
-
* @returns {Promise<number>} Resolves to:
|
|
256
|
-
* - Remaining TTL in **seconds** if the key exists and has an expiration.
|
|
257
|
-
* - `-1` if the key exists but has no expiration.
|
|
258
|
-
* - `-2` if the key does not exist.
|
|
259
|
-
*/
|
|
260
|
-
async ttl(key) {
|
|
261
|
-
if (!this.store.has(key)) return -2;
|
|
262
|
-
if (!this.expiryTimestamps.has(key)) return -1;
|
|
263
|
-
const timeLeft = this.expiryTimestamps.get(key) - Date.now();
|
|
264
|
-
return timeLeft > 0 ? Math.ceil(timeLeft / 1e3) : -2;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Checks if one or more keys exist in the store.
|
|
268
|
-
*
|
|
269
|
-
* @param {string | string[]} keys - A single key or an array of keys to check.
|
|
270
|
-
* @returns {Promise<number>} Resolves to the number of keys that exist.
|
|
271
|
-
*/
|
|
272
|
-
async exists(keys) {
|
|
273
|
-
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
274
|
-
return keyArray.reduce(
|
|
275
|
-
(count, key) => this.store.has(key) ? count + 1 : count,
|
|
276
|
-
0
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Increments a numeric value stored at a key by 1.
|
|
281
|
-
* If the key does not exist, it is set to `1`.
|
|
282
|
-
*
|
|
283
|
-
* @param {string} key - The key to increment.
|
|
284
|
-
* @returns {Promise<number>} Resolves to the new value after increment.
|
|
285
|
-
*/
|
|
286
|
-
async incr(key) {
|
|
287
|
-
const current = Number(this.store.get(key)) || 0;
|
|
288
|
-
const newValue = current + 1;
|
|
289
|
-
this.store.set(key, newValue.toString());
|
|
290
|
-
return newValue;
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Increments a numeric value stored at a key by a specified amount.
|
|
294
|
-
* If the key does not exist, it is set to the increment value.
|
|
295
|
-
*
|
|
296
|
-
* @param {string} key - The key to increment.
|
|
297
|
-
* @param {number} increment - The amount to increase by.
|
|
298
|
-
* @returns {Promise<number>} Resolves to the new value after increment.
|
|
299
|
-
*/
|
|
300
|
-
async incrBy(key, increment) {
|
|
301
|
-
const current = Number(this.store.get(key)) || 0;
|
|
302
|
-
const newValue = current + increment;
|
|
303
|
-
this.store.set(key, newValue.toString());
|
|
304
|
-
return newValue;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Decrements a numeric value stored at a key by 1.
|
|
308
|
-
* If the key does not exist, it is set to `-1`.
|
|
309
|
-
*
|
|
310
|
-
* @param {string} key - The key to decrement.
|
|
311
|
-
* @returns {Promise<number>} Resolves to the new value after decrement.
|
|
312
|
-
*/
|
|
313
|
-
async decr(key) {
|
|
314
|
-
const current = Number(this.store.get(key)) || 0;
|
|
315
|
-
const newValue = current - 1;
|
|
316
|
-
this.store.set(key, newValue.toString());
|
|
317
|
-
return newValue;
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Decrements a numeric value stored at a key by a specified amount.
|
|
321
|
-
* If the key does not exist, it is set to the negative decrement value.
|
|
322
|
-
*
|
|
323
|
-
* @param {string} key - The key to decrement.
|
|
324
|
-
* @param {number} decrement - The amount to decrease by.
|
|
325
|
-
* @returns {Promise<number>} Resolves to the new value after decrement.
|
|
326
|
-
*/
|
|
327
|
-
async decrBy(key, decrement) {
|
|
328
|
-
const current = Number(this.store.get(key)) || 0;
|
|
329
|
-
const newValue = current - decrement;
|
|
330
|
-
this.store.set(key, newValue.toString());
|
|
331
|
-
return newValue;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Sets a field in a hash.
|
|
335
|
-
* If the field already exists, its value is updated.
|
|
336
|
-
*
|
|
337
|
-
* @param key - The hash key.
|
|
338
|
-
* @param field - The field name.
|
|
339
|
-
* @param value - The value to store.
|
|
340
|
-
* @returns Resolves to `1` if a new field was added, `0` if an existing field was updated.
|
|
341
|
-
*/
|
|
342
|
-
async hSet(key, field, value) {
|
|
343
|
-
const existingHash = JSON.parse(this.store.get(key) ?? "{}");
|
|
344
|
-
const isNewField = !Object.hasOwn(existingHash, field);
|
|
345
|
-
existingHash[field] = value;
|
|
346
|
-
this.store.set(key, JSON.stringify(existingHash));
|
|
347
|
-
return isNewField ? 1 : 0;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Retrieves a field from a hash.
|
|
351
|
-
*
|
|
352
|
-
* @param key - The hash key.
|
|
353
|
-
* @param field - The field name to retrieve.
|
|
354
|
-
* @returns Resolves to the field value, or `null` if the field does not exist.
|
|
355
|
-
*/
|
|
356
|
-
async hGet(key, field) {
|
|
357
|
-
const hash = JSON.parse(this.store.get(key) ?? "{}");
|
|
358
|
-
return hash[field] ?? null;
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Pushes elements to the left (head) of a list.
|
|
362
|
-
*
|
|
363
|
-
* @param key - The list key.
|
|
364
|
-
* @param values - One or more values to add.
|
|
365
|
-
* @returns Resolves to the length of the list after the operation.
|
|
366
|
-
*/
|
|
367
|
-
async lPush(key, values) {
|
|
368
|
-
const list = JSON.parse(this.store.get(key) ?? "[]");
|
|
369
|
-
const newValues = Array.isArray(values) ? values : [values];
|
|
370
|
-
const updatedList = [...newValues.reverse(), ...list];
|
|
371
|
-
this.store.set(key, JSON.stringify(updatedList));
|
|
372
|
-
return updatedList.length;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Pushes elements to the right (tail) of a list.
|
|
376
|
-
*
|
|
377
|
-
* @param key - The list key.
|
|
378
|
-
* @param values - One or more values to add.
|
|
379
|
-
* @returns Resolves to the length of the list after the operation.
|
|
380
|
-
*/
|
|
381
|
-
async rPush(key, values) {
|
|
382
|
-
const list = JSON.parse(this.store.get(key) ?? "[]");
|
|
383
|
-
const newValues = Array.isArray(values) ? values : [values];
|
|
384
|
-
const updatedList = [...list, ...newValues];
|
|
385
|
-
this.store.set(key, JSON.stringify(updatedList));
|
|
386
|
-
return updatedList.length;
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Removes and returns the first element from a list.
|
|
390
|
-
*
|
|
391
|
-
* @param key - The list key.
|
|
392
|
-
* @returns Resolves to the removed element, or `null` if the list is empty.
|
|
393
|
-
*/
|
|
394
|
-
async lPop(key) {
|
|
395
|
-
const list = JSON.parse(this.store.get(key) ?? "[]");
|
|
396
|
-
if (list.length === 0) return null;
|
|
397
|
-
const value = list.shift();
|
|
398
|
-
if (list.length > 0) {
|
|
399
|
-
this.store.set(key, JSON.stringify(list));
|
|
400
|
-
} else {
|
|
401
|
-
this.store.delete(key);
|
|
402
|
-
}
|
|
403
|
-
return value;
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Removes and returns the last element from a list.
|
|
407
|
-
*
|
|
408
|
-
* @param key - The list key.
|
|
409
|
-
* @returns Resolves to the removed element, or `null` if the list is empty.
|
|
410
|
-
*/
|
|
411
|
-
async rPop(key) {
|
|
412
|
-
const list = JSON.parse(this.store.get(key) ?? "[]");
|
|
413
|
-
if (list.length === 0) return null;
|
|
414
|
-
const value = list.pop();
|
|
415
|
-
if (list.length > 0) {
|
|
416
|
-
this.store.set(key, JSON.stringify(list));
|
|
417
|
-
} else {
|
|
418
|
-
this.store.delete(key);
|
|
419
|
-
}
|
|
420
|
-
return value;
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Retrieves a range of elements from a list.
|
|
424
|
-
*
|
|
425
|
-
* @param key - The list key.
|
|
426
|
-
* @param start - The starting index.
|
|
427
|
-
* @param stop - The stopping index.
|
|
428
|
-
* @returns Resolves to an array containing the requested range.
|
|
429
|
-
*/
|
|
430
|
-
async lRange(key, start, stop) {
|
|
431
|
-
const list = JSON.parse(this.store.get(key) ?? "[]");
|
|
432
|
-
const normalizedStop = stop === -1 ? list.length : stop + 1;
|
|
433
|
-
return list.slice(start, normalizedStop);
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* Adds elements to a set.
|
|
437
|
-
*
|
|
438
|
-
* @param key - The set key.
|
|
439
|
-
* @param values - One or more values to add.
|
|
440
|
-
* @returns Resolves to the number of new elements added.
|
|
441
|
-
*/
|
|
442
|
-
async sAdd(key, values) {
|
|
443
|
-
const set = new Set(JSON.parse(this.store.get(key) ?? "[]"));
|
|
444
|
-
const newValues = Array.isArray(values) ? values : [values];
|
|
445
|
-
const initialSize = set.size;
|
|
446
|
-
for (const value of newValues) {
|
|
447
|
-
set.add(value);
|
|
448
|
-
}
|
|
449
|
-
this.store.set(key, JSON.stringify([...set]));
|
|
450
|
-
return set.size - initialSize;
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Removes elements from a set.
|
|
454
|
-
*
|
|
455
|
-
* @param key - The set key.
|
|
456
|
-
* @param values - One or more values to remove.
|
|
457
|
-
* @returns Resolves to the number of elements removed.
|
|
458
|
-
*/
|
|
459
|
-
async sRem(key, values) {
|
|
460
|
-
const set = new Set(JSON.parse(this.store.get(key) ?? "[]"));
|
|
461
|
-
const valuesToRemove = Array.isArray(values) ? values : [values];
|
|
462
|
-
const initialSize = set.size;
|
|
463
|
-
for (const value of valuesToRemove) {
|
|
464
|
-
set.delete(value);
|
|
465
|
-
}
|
|
466
|
-
this.store.set(key, JSON.stringify([...set]));
|
|
467
|
-
return initialSize - set.size;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Retrieves all elements from a set.
|
|
471
|
-
*
|
|
472
|
-
* @param key - The set key.
|
|
473
|
-
* @returns Resolves to an array of all set members.
|
|
474
|
-
*/
|
|
475
|
-
async sMembers(key) {
|
|
476
|
-
return JSON.parse(this.store.get(key) ?? "[]");
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Adds members to a sorted set with scores.
|
|
480
|
-
*
|
|
481
|
-
* @param key - The sorted set key.
|
|
482
|
-
* @param members - An array of objects containing `{ score, value }`.
|
|
483
|
-
* @returns Resolves to the number of new elements added.
|
|
484
|
-
*/
|
|
485
|
-
async zAdd(key, members) {
|
|
486
|
-
const sortedSet = JSON.parse(
|
|
487
|
-
this.store.get(key) ?? "[]"
|
|
488
|
-
);
|
|
489
|
-
const initialSize = sortedSet.length;
|
|
490
|
-
for (const { score, value } of members) {
|
|
491
|
-
const existingIndex = sortedSet.findIndex(
|
|
492
|
-
(entry) => entry.value === value
|
|
493
|
-
);
|
|
494
|
-
if (existingIndex !== -1) {
|
|
495
|
-
sortedSet[existingIndex].score = score;
|
|
496
|
-
} else {
|
|
497
|
-
sortedSet.push({ score, value });
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
sortedSet.sort((a, b) => a.score - b.score);
|
|
501
|
-
this.store.set(key, JSON.stringify(sortedSet));
|
|
502
|
-
return sortedSet.length - initialSize;
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* Retrieves a range of elements from a sorted set.
|
|
506
|
-
*
|
|
507
|
-
* @param key - The sorted set key.
|
|
508
|
-
* @param start - The starting index.
|
|
509
|
-
* @param stop - The stopping index.
|
|
510
|
-
* @returns Resolves to an array of sorted set values in the range.
|
|
511
|
-
*/
|
|
512
|
-
async zRange(key, start, stop) {
|
|
513
|
-
const sortedSet = JSON.parse(
|
|
514
|
-
this.store.get(key) ?? "[]"
|
|
515
|
-
);
|
|
516
|
-
const normalizedStop = stop === -1 ? sortedSet.length : stop + 1;
|
|
517
|
-
return sortedSet.slice(start, normalizedStop).map((entry) => entry.value);
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Removes elements from a sorted set.
|
|
521
|
-
*
|
|
522
|
-
* @param key - The sorted set key.
|
|
523
|
-
* @param members - One or more values to remove.
|
|
524
|
-
* @returns Resolves to the number of elements removed.
|
|
525
|
-
*/
|
|
526
|
-
async zRem(key, members) {
|
|
527
|
-
const sortedSet = JSON.parse(
|
|
528
|
-
this.store.get(key) ?? "[]"
|
|
529
|
-
);
|
|
530
|
-
const valuesToRemove = Array.isArray(members) ? members : [members];
|
|
531
|
-
const initialSize = sortedSet.length;
|
|
532
|
-
this.store.set(
|
|
533
|
-
key,
|
|
534
|
-
JSON.stringify(
|
|
535
|
-
sortedSet.filter((entry) => !valuesToRemove.includes(entry.value))
|
|
536
|
-
)
|
|
537
|
-
);
|
|
538
|
-
return initialSize - JSON.parse(this.store.get(key) ?? "[]").length;
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Removes all keys from the store and clears all active expirations.
|
|
542
|
-
*
|
|
543
|
-
* @returns {Promise<'OK'>} Resolves to `'OK'` after all data is cleared.
|
|
544
|
-
*/
|
|
545
|
-
async flushAll() {
|
|
546
|
-
this.store.clear();
|
|
547
|
-
for (const timeout of this.expirations.values()) {
|
|
548
|
-
clearTimeout(timeout);
|
|
549
|
-
}
|
|
550
|
-
this.expirations.clear();
|
|
551
|
-
this.expiryTimestamps.clear();
|
|
552
|
-
return "OK";
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Creates a new multi-command batch instance.
|
|
556
|
-
* Commands queued in this batch will be executed together when `exec()` is called.
|
|
557
|
-
*
|
|
558
|
-
* @returns A new `IPersistorMulti` instance for batching commands.
|
|
559
|
-
*/
|
|
560
|
-
multi() {
|
|
561
|
-
return new InMemoryMulti(this);
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Sets an expiration timeout for a key.
|
|
565
|
-
* Cancels any existing expiration before setting a new one.
|
|
566
|
-
*
|
|
567
|
-
* @private
|
|
568
|
-
* @param {string} key - The key to expire.
|
|
569
|
-
* @param {number} ttlMs - Time-to-live in milliseconds.
|
|
570
|
-
*/
|
|
571
|
-
setExpiration(key, ttlMs) {
|
|
572
|
-
this.clearExpiration(key);
|
|
573
|
-
const expiryTimestamp = Date.now() + ttlMs;
|
|
574
|
-
this.expiryTimestamps.set(key, expiryTimestamp);
|
|
575
|
-
const timeout = setTimeout(() => {
|
|
576
|
-
this.store.delete(key);
|
|
577
|
-
this.expirations.delete(key);
|
|
578
|
-
this.expiryTimestamps.delete(key);
|
|
579
|
-
}, ttlMs);
|
|
580
|
-
this.expirations.set(key, timeout);
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Cancels an active expiration timeout for a key and removes its TTL record.
|
|
584
|
-
*
|
|
585
|
-
* @private
|
|
586
|
-
* @param {string} key - The key whose expiration should be cleared.
|
|
587
|
-
*/
|
|
588
|
-
clearExpiration(key) {
|
|
589
|
-
if (this.expirations.has(key)) {
|
|
590
|
-
clearTimeout(this.expirations.get(key));
|
|
591
|
-
this.expirations.delete(key);
|
|
592
|
-
this.expiryTimestamps.delete(key);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
var InMemoryMulti = class {
|
|
597
|
-
persistor;
|
|
598
|
-
commands = /* @__PURE__ */ new Set();
|
|
599
|
-
constructor(persistor) {
|
|
600
|
-
this.persistor = persistor;
|
|
601
|
-
}
|
|
602
|
-
/**
|
|
603
|
-
* Queues a `SET` command to store a key-value pair with optional expiration settings.
|
|
604
|
-
* The command will be executed when `exec()` is called.
|
|
605
|
-
*
|
|
606
|
-
* @param key - The storage key.
|
|
607
|
-
* @param value - The string value to store.
|
|
608
|
-
* @param options - Optional expiration settings.
|
|
609
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
610
|
-
*/
|
|
611
|
-
set(key, value, options) {
|
|
612
|
-
this.commands.add(() => this.persistor.set(key, value, options));
|
|
613
|
-
return this;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Queues a `SETEX` command to store a key-value pair with an expiration time in seconds.
|
|
617
|
-
* The command will be executed when `exec()` is called.
|
|
618
|
-
*
|
|
619
|
-
* @param key - The storage key.
|
|
620
|
-
* @param seconds - Expiration time in seconds.
|
|
621
|
-
* @param value - The string value to store.
|
|
622
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
623
|
-
*/
|
|
624
|
-
setEx(key, seconds2, value) {
|
|
625
|
-
this.commands.add(() => this.persistor.setEx(key, seconds2, value));
|
|
626
|
-
return this;
|
|
627
|
-
}
|
|
628
|
-
/**
|
|
629
|
-
* Queues a `PSETEX` command to store a key-value pair with an expiration time in milliseconds.
|
|
630
|
-
* The command will be executed when `exec()` is called.
|
|
631
|
-
*
|
|
632
|
-
* @param key - The storage key.
|
|
633
|
-
* @param milliseconds - Expiration time in milliseconds.
|
|
634
|
-
* @param value - The string value to store.
|
|
635
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
636
|
-
*/
|
|
637
|
-
pSetEx(key, milliseconds, value) {
|
|
638
|
-
this.commands.add(() => this.persistor.pSetEx(key, milliseconds, value));
|
|
639
|
-
return this;
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Queues a `SETNX` command to store a key-value pair **only if the key does not already exist**.
|
|
643
|
-
* The command will be executed when `exec()` is called.
|
|
644
|
-
*
|
|
645
|
-
* @param key - The storage key.
|
|
646
|
-
* @param value - The string value to store.
|
|
647
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
648
|
-
*/
|
|
649
|
-
setNX(key, value) {
|
|
650
|
-
this.commands.add(() => this.persistor.setNX(key, value));
|
|
651
|
-
return this;
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* Queues a `GET` command to retrieve the value associated with a key.
|
|
655
|
-
* The command will be executed when `exec()` is called.
|
|
656
|
-
*
|
|
657
|
-
* @param key - The storage key to retrieve.
|
|
658
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
659
|
-
*/
|
|
660
|
-
get(key) {
|
|
661
|
-
this.commands.add(() => this.persistor.get(key));
|
|
662
|
-
return this;
|
|
663
|
-
}
|
|
664
|
-
/**
|
|
665
|
-
* Queues a `DEL` command to delete a key from the store.
|
|
666
|
-
* The command will be executed when `exec()` is called.
|
|
667
|
-
*
|
|
668
|
-
* @param key - The storage key to delete.
|
|
669
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
670
|
-
*/
|
|
671
|
-
del(key) {
|
|
672
|
-
this.commands.add(() => this.persistor.del(key));
|
|
673
|
-
return this;
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Queues an `EXPIRE` command to set a time-to-live (TTL) in seconds for a key.
|
|
677
|
-
* The command will be executed when `exec()` is called.
|
|
678
|
-
*
|
|
679
|
-
* @param key - The storage key.
|
|
680
|
-
* @param seconds - TTL in seconds.
|
|
681
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
682
|
-
*/
|
|
683
|
-
expire(key, seconds2) {
|
|
684
|
-
this.commands.add(() => this.persistor.expire(key, seconds2));
|
|
685
|
-
return this;
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Queues a `TTL` command to get the remaining time-to-live (TTL) of a key in seconds.
|
|
689
|
-
* The command will be executed when `exec()` is called.
|
|
690
|
-
*
|
|
691
|
-
* @param key - The storage key to check.
|
|
692
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
693
|
-
*/
|
|
694
|
-
ttl(key) {
|
|
695
|
-
this.commands.add(() => this.persistor.ttl(key));
|
|
696
|
-
return this;
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Queues an `exists` operation in the batch.
|
|
700
|
-
* @param key - The key(s) to check existence.
|
|
701
|
-
* @returns The multi instance for method chaining.
|
|
702
|
-
*/
|
|
703
|
-
exists(key) {
|
|
704
|
-
this.commands.add(() => this.persistor.exists(key));
|
|
705
|
-
return this;
|
|
706
|
-
}
|
|
707
|
-
/**
|
|
708
|
-
* Queues an `incr` operation in the batch.
|
|
709
|
-
* @param key - The key to increment.
|
|
710
|
-
* @returns The multi instance for method chaining.
|
|
711
|
-
*/
|
|
712
|
-
incr(key) {
|
|
713
|
-
this.commands.add(() => this.persistor.incr(key));
|
|
714
|
-
return this;
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* Queues an `incrBy` operation in the batch.
|
|
718
|
-
* @param key - The key to increment.
|
|
719
|
-
* @param increment - The amount to increment by.
|
|
720
|
-
* @returns The multi instance for method chaining.
|
|
721
|
-
*/
|
|
722
|
-
incrBy(key, increment) {
|
|
723
|
-
this.commands.add(() => this.persistor.incrBy(key, increment));
|
|
724
|
-
return this;
|
|
725
|
-
}
|
|
726
|
-
/**
|
|
727
|
-
* Queues a `decr` operation in the batch.
|
|
728
|
-
* @param key - The key to decrement.
|
|
729
|
-
* @returns The multi instance for method chaining.
|
|
730
|
-
*/
|
|
731
|
-
decr(key) {
|
|
732
|
-
this.commands.add(() => this.persistor.decr(key));
|
|
733
|
-
return this;
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Queues a `decrBy` operation in the batch.
|
|
737
|
-
* @param key - The key to decrement.
|
|
738
|
-
* @param decrement - The amount to decrement by.
|
|
739
|
-
* @returns The multi instance for method chaining.
|
|
740
|
-
*/
|
|
741
|
-
decrBy(key, decrement) {
|
|
742
|
-
this.commands.add(() => this.persistor.decrBy(key, decrement));
|
|
743
|
-
return this;
|
|
744
|
-
}
|
|
745
|
-
/**
|
|
746
|
-
* Queues a `flushAll` operation in the batch.
|
|
747
|
-
* @returns The multi instance for method chaining.
|
|
748
|
-
*/
|
|
749
|
-
flushAll() {
|
|
750
|
-
this.commands.add(() => this.persistor.flushAll());
|
|
751
|
-
return this;
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* Queues an `hSet` command to store a field-value pair in a hash.
|
|
755
|
-
* The command will be executed when `exec()` is called.
|
|
756
|
-
*
|
|
757
|
-
* @param key - The hash key.
|
|
758
|
-
* @param field - The field name.
|
|
759
|
-
* @param value - The value to store.
|
|
760
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
761
|
-
*/
|
|
762
|
-
hSet(key, field, value) {
|
|
763
|
-
this.commands.add(() => this.persistor.hSet(key, field, value));
|
|
764
|
-
return this;
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Queues an `hGet` command to retrieve a field value from a hash.
|
|
768
|
-
* The command will be executed when `exec()` is called.
|
|
769
|
-
*
|
|
770
|
-
* @param key - The hash key.
|
|
771
|
-
* @param field - The field to retrieve.
|
|
772
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
773
|
-
*/
|
|
774
|
-
hGet(key, field) {
|
|
775
|
-
this.commands.add(() => this.persistor.hGet(key, field));
|
|
776
|
-
return this;
|
|
777
|
-
}
|
|
778
|
-
/**
|
|
779
|
-
* Queues an `lPush` command to add elements to the left (head) of a list.
|
|
780
|
-
* The command will be executed when `exec()` is called.
|
|
781
|
-
*
|
|
782
|
-
* @param key - The list key.
|
|
783
|
-
* @param values - The values to add.
|
|
784
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
785
|
-
*/
|
|
786
|
-
lPush(key, values) {
|
|
787
|
-
this.commands.add(() => this.persistor.lPush(key, values));
|
|
788
|
-
return this;
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Queues an `rPush` command to add elements to the right (tail) of a list.
|
|
792
|
-
* The command will be executed when `exec()` is called.
|
|
793
|
-
*
|
|
794
|
-
* @param key - The list key.
|
|
795
|
-
* @param values - The values to add.
|
|
796
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
797
|
-
*/
|
|
798
|
-
rPush(key, values) {
|
|
799
|
-
this.commands.add(() => this.persistor.rPush(key, values));
|
|
800
|
-
return this;
|
|
801
|
-
}
|
|
802
|
-
/**
|
|
803
|
-
* Queues an `lPop` command to remove and return the first element of a list.
|
|
804
|
-
* The command will be executed when `exec()` is called.
|
|
805
|
-
*
|
|
806
|
-
* @param key - The list key.
|
|
807
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
808
|
-
*/
|
|
809
|
-
lPop(key) {
|
|
810
|
-
this.commands.add(() => this.persistor.lPop(key));
|
|
811
|
-
return this;
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Queues an `rPop` command to remove and return the last element of a list.
|
|
815
|
-
* The command will be executed when `exec()` is called.
|
|
816
|
-
*
|
|
817
|
-
* @param key - The list key.
|
|
818
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
819
|
-
*/
|
|
820
|
-
rPop(key) {
|
|
821
|
-
this.commands.add(() => this.persistor.rPop(key));
|
|
822
|
-
return this;
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* Queues an `lRange` command to retrieve a range of elements from a list.
|
|
826
|
-
* The command will be executed when `exec()` is called.
|
|
827
|
-
*
|
|
828
|
-
* @param key - The list key.
|
|
829
|
-
* @param start - The start index.
|
|
830
|
-
* @param stop - The stop index (inclusive).
|
|
831
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
832
|
-
*/
|
|
833
|
-
lRange(key, start, stop) {
|
|
834
|
-
this.commands.add(() => this.persistor.lRange(key, start, stop));
|
|
835
|
-
return this;
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Queues an `sAdd` command to add elements to a set.
|
|
839
|
-
* The command will be executed when `exec()` is called.
|
|
840
|
-
*
|
|
841
|
-
* @param key - The set key.
|
|
842
|
-
* @param values - The values to add.
|
|
843
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
844
|
-
*/
|
|
845
|
-
sAdd(key, values) {
|
|
846
|
-
this.commands.add(() => this.persistor.sAdd(key, values));
|
|
847
|
-
return this;
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Queues an `sRem` command to remove elements from a set.
|
|
851
|
-
* The command will be executed when `exec()` is called.
|
|
852
|
-
*
|
|
853
|
-
* @param key - The set key.
|
|
854
|
-
* @param values - The values to remove.
|
|
855
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
856
|
-
*/
|
|
857
|
-
sRem(key, values) {
|
|
858
|
-
this.commands.add(() => this.persistor.sRem(key, values));
|
|
859
|
-
return this;
|
|
860
|
-
}
|
|
861
|
-
/**
|
|
862
|
-
* Queues an `sMembers` command to retrieve all members of a set.
|
|
863
|
-
* The command will be executed when `exec()` is called.
|
|
864
|
-
*
|
|
865
|
-
* @param key - The set key.
|
|
866
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
867
|
-
*/
|
|
868
|
-
sMembers(key) {
|
|
869
|
-
this.commands.add(() => this.persistor.sMembers(key));
|
|
870
|
-
return this;
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* Queues a `zAdd` command to add elements to a sorted set with scores.
|
|
874
|
-
* The command will be executed when `exec()` is called.
|
|
875
|
-
*
|
|
876
|
-
* @param key - The sorted set key.
|
|
877
|
-
* @param members - An array of objects with `score` and `value`.
|
|
878
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
879
|
-
*/
|
|
880
|
-
zAdd(key, members) {
|
|
881
|
-
this.commands.add(() => this.persistor.zAdd(key, members));
|
|
882
|
-
return this;
|
|
883
|
-
}
|
|
884
|
-
/**
|
|
885
|
-
* Queues a `zRange` command to retrieve a range of elements from a sorted set.
|
|
886
|
-
* The command will be executed when `exec()` is called.
|
|
887
|
-
*
|
|
888
|
-
* @param key - The sorted set key.
|
|
889
|
-
* @param start - The start index.
|
|
890
|
-
* @param stop - The stop index (inclusive).
|
|
891
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
892
|
-
*/
|
|
893
|
-
zRange(key, start, stop) {
|
|
894
|
-
this.commands.add(() => this.persistor.zRange(key, start, stop));
|
|
895
|
-
return this;
|
|
896
|
-
}
|
|
897
|
-
/**
|
|
898
|
-
* Queues a `zRem` command to remove elements from a sorted set.
|
|
899
|
-
* The command will be executed when `exec()` is called.
|
|
900
|
-
*
|
|
901
|
-
* @param key - The sorted set key.
|
|
902
|
-
* @param members - The members to remove.
|
|
903
|
-
* @returns The `IPersistorMulti` instance to allow method chaining.
|
|
904
|
-
*/
|
|
905
|
-
zRem(key, members) {
|
|
906
|
-
this.commands.add(() => this.persistor.zRem(key, members));
|
|
907
|
-
return this;
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Executes multiple commands in a batch operation.
|
|
911
|
-
* Each command is executed in sequence, and results are collected in an array.
|
|
912
|
-
*
|
|
913
|
-
* @returns Resolves to an array containing the results of each queued command.
|
|
914
|
-
*/
|
|
915
|
-
async exec() {
|
|
916
|
-
return Promise.all([...this.commands].map((cmd) => cmd()));
|
|
917
|
-
}
|
|
918
|
-
};
|
|
919
|
-
|
|
920
|
-
// src/persistor.ts
|
|
921
|
-
import { getLogger } from "@sebspark/otel";
|
|
922
|
-
import { createClient } from "redis";
|
|
923
|
-
import superjson2 from "superjson";
|
|
924
|
-
|
|
925
|
-
// src/localMemory.ts
|
|
926
|
-
var LocalStorage = class {
|
|
927
|
-
client = /* @__PURE__ */ new Map();
|
|
928
|
-
isReady = false;
|
|
929
|
-
get(key) {
|
|
930
|
-
return this.client.get(key);
|
|
931
|
-
}
|
|
932
|
-
set(key, value, options) {
|
|
933
|
-
this.client.set(key, value);
|
|
934
|
-
if (options?.PX) {
|
|
935
|
-
setTimeout(() => {
|
|
936
|
-
this.client.delete(key);
|
|
937
|
-
}, options.PX);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
del(key) {
|
|
941
|
-
this.client.delete(key);
|
|
942
|
-
}
|
|
943
|
-
clear() {
|
|
944
|
-
this.client.clear();
|
|
945
|
-
}
|
|
946
|
-
async DBSIZE() {
|
|
947
|
-
return Promise.resolve(this.client.size);
|
|
948
|
-
}
|
|
949
|
-
// This is just for testing
|
|
950
|
-
on(event, callback) {
|
|
951
|
-
if (event === "ready" && callback) {
|
|
952
|
-
this.isReady = true;
|
|
953
|
-
callback("ready");
|
|
954
|
-
}
|
|
955
|
-
return this;
|
|
956
|
-
}
|
|
957
|
-
connect() {
|
|
958
|
-
return Promise.resolve(this);
|
|
959
|
-
}
|
|
960
|
-
};
|
|
961
|
-
var localStorage = new LocalStorage();
|
|
962
|
-
var createLocalMemoryClient = () => {
|
|
963
|
-
return localStorage;
|
|
964
|
-
};
|
|
965
|
-
|
|
966
|
-
// src/persistor.ts
|
|
967
|
-
var CACHE_CLIENT = createClient;
|
|
968
|
-
var isTestRunning = process.env.NODE_ENV === "test";
|
|
969
|
-
function toMillis(seconds2) {
|
|
970
|
-
return seconds2 * 1e3;
|
|
971
|
-
}
|
|
972
|
-
var Persistor = class {
|
|
973
|
-
client = null;
|
|
974
|
-
clientId;
|
|
975
|
-
onError;
|
|
976
|
-
onSuccess;
|
|
977
|
-
logger;
|
|
978
|
-
redis;
|
|
979
|
-
constructor({
|
|
980
|
-
redis,
|
|
981
|
-
redisClient,
|
|
982
|
-
clientId,
|
|
983
|
-
onSuccess,
|
|
984
|
-
onError
|
|
985
|
-
}) {
|
|
986
|
-
this.logger = getLogger("Persistor");
|
|
987
|
-
this.logger.warn(
|
|
988
|
-
"Persistor class is deprecated. Use InMemoryPersistor or redis: createClient instead"
|
|
989
|
-
);
|
|
990
|
-
this.onError = onError || (() => {
|
|
991
|
-
});
|
|
992
|
-
this.onSuccess = onSuccess || (() => {
|
|
993
|
-
});
|
|
994
|
-
this.clientId = clientId;
|
|
995
|
-
if (redisClient) {
|
|
996
|
-
this.client = redisClient;
|
|
997
|
-
} else if (redis && !isTestRunning) {
|
|
998
|
-
this.redis = redis;
|
|
999
|
-
} else {
|
|
1000
|
-
CACHE_CLIENT = createLocalMemoryClient;
|
|
1001
|
-
}
|
|
1002
|
-
if (!this.client || !this.client.isReady) {
|
|
1003
|
-
this.startConnection();
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
async startConnection() {
|
|
1007
|
-
try {
|
|
1008
|
-
await new Promise((resolve, reject) => {
|
|
1009
|
-
this.client = CACHE_CLIENT({
|
|
1010
|
-
url: this.redis?.url,
|
|
1011
|
-
username: this.redis?.username,
|
|
1012
|
-
password: this.redis?.password,
|
|
1013
|
-
pingInterval: this.redis?.pingInterval || void 0,
|
|
1014
|
-
socket: {
|
|
1015
|
-
...this.redis?.socket,
|
|
1016
|
-
reconnectStrategy: (retries, cause) => {
|
|
1017
|
-
this.logger.error(cause);
|
|
1018
|
-
return 1e3 * 2 ** retries;
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
}).on("error", (err) => {
|
|
1022
|
-
this.onError(err);
|
|
1023
|
-
reject(err);
|
|
1024
|
-
}).on("ready", () => {
|
|
1025
|
-
this.onSuccess();
|
|
1026
|
-
resolve(true);
|
|
1027
|
-
}).on("reconnecting", () => {
|
|
1028
|
-
this.logger.info(`reconnecting... ${this.clientId}`);
|
|
1029
|
-
}).on("end", () => {
|
|
1030
|
-
this.logger.info(`end... ${this.clientId}`);
|
|
1031
|
-
});
|
|
1032
|
-
this.client.connect();
|
|
1033
|
-
});
|
|
1034
|
-
} catch (err) {
|
|
1035
|
-
this.onError(`${err}`);
|
|
1036
|
-
this.logger.error(err);
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
async size() {
|
|
1040
|
-
if (!this.client) {
|
|
1041
|
-
throw new Error("Client not initialized");
|
|
1042
|
-
}
|
|
1043
|
-
return await this.client.DBSIZE();
|
|
1044
|
-
}
|
|
1045
|
-
getClientId() {
|
|
1046
|
-
return this.clientId;
|
|
1047
|
-
}
|
|
1048
|
-
getIsClientConnected() {
|
|
1049
|
-
return !!this.client?.isReady;
|
|
1050
|
-
}
|
|
1051
|
-
createOptions(ttl) {
|
|
1052
|
-
if (ttl !== null && ttl !== void 0 && ttl > 0) {
|
|
1053
|
-
return { PX: Math.round(toMillis(ttl)) };
|
|
1054
|
-
}
|
|
1055
|
-
return {};
|
|
1056
|
-
}
|
|
1057
|
-
/**
|
|
1058
|
-
* Set a value in the cache.
|
|
1059
|
-
* @param key Cache key.
|
|
1060
|
-
* @param object.value Value to set in the cache.
|
|
1061
|
-
* @param object.ttl Time to live in seconds.
|
|
1062
|
-
* @param object.timestamp Timestamp
|
|
1063
|
-
*/
|
|
1064
|
-
async set(key, { value, timestamp = Date.now(), ttl }) {
|
|
1065
|
-
if (!this.client || !this.client.isReady) {
|
|
1066
|
-
this.logger.error("Client not ready");
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
try {
|
|
1070
|
-
const serializedData = superjson2.stringify({
|
|
1071
|
-
value,
|
|
1072
|
-
ttl,
|
|
1073
|
-
timestamp
|
|
1074
|
-
});
|
|
1075
|
-
const options = this.createOptions(ttl);
|
|
1076
|
-
await this.client.set(key, serializedData, options);
|
|
1077
|
-
} catch (error) {
|
|
1078
|
-
this.logger.error("Error setting data in redis", error);
|
|
1079
|
-
throw new Error(`Error setting data in redis: ${error}`);
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Get a value from the cache.
|
|
1084
|
-
* @param key Cache key.
|
|
1085
|
-
* @returns GetType<T> value
|
|
1086
|
-
*/
|
|
1087
|
-
async get(key) {
|
|
1088
|
-
if (!this.client) {
|
|
1089
|
-
this.logger.error("Client not ready");
|
|
1090
|
-
return null;
|
|
1091
|
-
}
|
|
1092
|
-
try {
|
|
1093
|
-
const data = await this.client.get(key);
|
|
1094
|
-
if (!data) {
|
|
1095
|
-
return null;
|
|
1096
|
-
}
|
|
1097
|
-
return superjson2.parse(data);
|
|
1098
|
-
} catch (error) {
|
|
1099
|
-
this.logger.error(`Error getting data in redis: ${error}`);
|
|
1100
|
-
throw new Error(`Error getting data from redis: ${error}`);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
/**
|
|
1104
|
-
* Delete a value from the cache.
|
|
1105
|
-
* @param key Cache key
|
|
1106
|
-
*/
|
|
1107
|
-
async delete(key) {
|
|
1108
|
-
if (!this.client || !this.client.isReady) {
|
|
1109
|
-
this.logger.error("Client not ready");
|
|
1110
|
-
return;
|
|
1111
|
-
}
|
|
1112
|
-
try {
|
|
1113
|
-
await this.client.del(key);
|
|
1114
|
-
} catch (error) {
|
|
1115
|
-
this.logger.error(`Error deleting data from redis: ${error}`);
|
|
1116
|
-
throw new Error(`Error deleting data from redis: ${error}`);
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
|
|
1121
|
-
// src/promiseCache.ts
|
|
1122
|
-
import { randomUUID } from "crypto";
|
|
1123
|
-
import { getLogger as getLogger2 } from "@sebspark/otel";
|
|
1124
|
-
var persistors = {};
|
|
1125
|
-
var getPersistor = ({
|
|
1126
|
-
redis,
|
|
1127
|
-
onError,
|
|
1128
|
-
onSuccess,
|
|
1129
|
-
clientId
|
|
1130
|
-
}) => {
|
|
1131
|
-
const logger = getLogger2("PromiseCache persistor");
|
|
1132
|
-
const connectionName = redis ? redis?.name || "default" : "local";
|
|
1133
|
-
if (!persistors[connectionName]) {
|
|
1134
|
-
persistors[connectionName] = new Persistor({
|
|
1135
|
-
redis,
|
|
1136
|
-
onError: (error) => {
|
|
1137
|
-
onError?.(error);
|
|
1138
|
-
logger?.error(
|
|
1139
|
-
`\u274C REDIS | Client Error | ${connectionName} | ${redis?.url}: ${error}`
|
|
1140
|
-
);
|
|
1141
|
-
},
|
|
1142
|
-
onSuccess: () => {
|
|
1143
|
-
onSuccess?.();
|
|
1144
|
-
logger?.info(
|
|
1145
|
-
`\u{1F4E6} REDIS | Connection Ready | ${connectionName} | ${redis?.url}`
|
|
1146
|
-
);
|
|
1147
|
-
},
|
|
1148
|
-
clientId
|
|
1149
|
-
});
|
|
1150
|
-
}
|
|
1151
|
-
return persistors[connectionName];
|
|
1152
|
-
};
|
|
1153
|
-
var PromiseCache = class {
|
|
1154
|
-
persistor;
|
|
1155
|
-
clientId = randomUUID();
|
|
1156
|
-
caseSensitive;
|
|
1157
|
-
fallbackToFunction;
|
|
1158
|
-
// If true, the cache will fallback to the delegate function if there is an error retrieving the cache.
|
|
1159
|
-
ttl;
|
|
1160
|
-
// Time to live in milliseconds.
|
|
1161
|
-
logger;
|
|
1162
|
-
/**
|
|
1163
|
-
* Initialize a new PromiseCache.
|
|
1164
|
-
* @param ttlInSeconds Default cache TTL.
|
|
1165
|
-
* @param caseSensitive Set to true if you want to differentiate between keys with different casing.
|
|
1166
|
-
*/
|
|
1167
|
-
constructor({
|
|
1168
|
-
ttlInSeconds,
|
|
1169
|
-
caseSensitive = false,
|
|
1170
|
-
redis,
|
|
1171
|
-
fallbackToFunction = false,
|
|
1172
|
-
onSuccess,
|
|
1173
|
-
onError
|
|
1174
|
-
}) {
|
|
1175
|
-
this.logger = getLogger2("PromiseCache");
|
|
1176
|
-
this.logger.warn(
|
|
1177
|
-
"PromiseCache class is deprecated. Use createCache instead"
|
|
1178
|
-
);
|
|
1179
|
-
this.persistor = getPersistor({
|
|
1180
|
-
redis,
|
|
1181
|
-
onError,
|
|
1182
|
-
onSuccess,
|
|
1183
|
-
clientId: this.clientId
|
|
1184
|
-
});
|
|
1185
|
-
this.caseSensitive = caseSensitive;
|
|
1186
|
-
this.fallbackToFunction = fallbackToFunction;
|
|
1187
|
-
if (ttlInSeconds) {
|
|
1188
|
-
this.ttl = ttlInSeconds;
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Cache size.
|
|
1193
|
-
* @returns The number of entries in the cache.
|
|
1194
|
-
*/
|
|
1195
|
-
async size() {
|
|
1196
|
-
return await this.persistor.size();
|
|
1197
|
-
}
|
|
1198
|
-
/**
|
|
1199
|
-
* Override a value in the cache.
|
|
1200
|
-
* @param key Cache key.
|
|
1201
|
-
* @param value Cache value.
|
|
1202
|
-
* @param ttlInSeconds Time to live in seconds.
|
|
1203
|
-
*/
|
|
1204
|
-
async override(key, value, ttlInSeconds) {
|
|
1205
|
-
const effectiveKey = this.caseSensitive ? key : key.toLowerCase();
|
|
1206
|
-
const effectiveTTL = ttlInSeconds !== void 0 ? ttlInSeconds : this.ttl;
|
|
1207
|
-
await this.persistor.set(effectiveKey, {
|
|
1208
|
-
value,
|
|
1209
|
-
timestamp: Date.now(),
|
|
1210
|
-
ttl: effectiveTTL
|
|
1211
|
-
});
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* Get a value from the cache.
|
|
1215
|
-
* @param key Cache key.
|
|
1216
|
-
*/
|
|
1217
|
-
async find(key) {
|
|
1218
|
-
const result = await this.persistor.get(key);
|
|
1219
|
-
return result?.value ?? null;
|
|
1220
|
-
}
|
|
1221
|
-
/**
|
|
1222
|
-
* A simple promise cache wrapper.
|
|
1223
|
-
* @param key Cache key.
|
|
1224
|
-
* @param delegate The function to execute if the key is not in the cache.
|
|
1225
|
-
* @param ttlInSeconds Time to live in seconds.
|
|
1226
|
-
* @param ttlKeyInSeconds The key in the response object that contains the TTL.
|
|
1227
|
-
* @returns The result of the delegate function.
|
|
1228
|
-
*/
|
|
1229
|
-
async wrap(key, delegate, ttlInSeconds, ttlKeyInSeconds) {
|
|
1230
|
-
const now = Date.now();
|
|
1231
|
-
const effectiveKey = this.caseSensitive ? key : key.toLowerCase();
|
|
1232
|
-
let effectiveTTL = ttlInSeconds ?? this.ttl;
|
|
1233
|
-
try {
|
|
1234
|
-
const cached = await this.persistor.get(effectiveKey);
|
|
1235
|
-
if (cached) {
|
|
1236
|
-
if (!ttlKeyInSeconds && cached.ttl !== effectiveTTL) {
|
|
1237
|
-
this.logger?.error(
|
|
1238
|
-
"WARNING: TTL mismatch for key. It is recommended to use the same TTL for the same key."
|
|
1239
|
-
);
|
|
1240
|
-
}
|
|
1241
|
-
return cached.value;
|
|
1242
|
-
}
|
|
1243
|
-
} catch (err) {
|
|
1244
|
-
const error = err;
|
|
1245
|
-
if (!this.fallbackToFunction) {
|
|
1246
|
-
throw error;
|
|
1247
|
-
}
|
|
1248
|
-
this.logger?.error(
|
|
1249
|
-
"redis error, falling back to function execution",
|
|
1250
|
-
error
|
|
1251
|
-
);
|
|
1252
|
-
}
|
|
1253
|
-
const response = await delegate();
|
|
1254
|
-
if (ttlKeyInSeconds) {
|
|
1255
|
-
const responseDict = response;
|
|
1256
|
-
const responseTTL = Number(responseDict[ttlKeyInSeconds]);
|
|
1257
|
-
effectiveTTL = responseTTL || effectiveTTL;
|
|
1258
|
-
}
|
|
1259
|
-
try {
|
|
1260
|
-
await this.persistor.set(effectiveKey, {
|
|
1261
|
-
value: response,
|
|
1262
|
-
timestamp: now,
|
|
1263
|
-
ttl: effectiveTTL
|
|
1264
|
-
});
|
|
1265
|
-
} catch (err) {
|
|
1266
|
-
const error = err;
|
|
1267
|
-
console.error("failed to cache result", error.message);
|
|
1268
|
-
}
|
|
1269
|
-
return response;
|
|
1270
|
-
}
|
|
1271
|
-
};
|
|
1272
|
-
|
|
1273
|
-
// src/time.ts
|
|
1274
|
-
var time_exports = {};
|
|
1275
|
-
__export(time_exports, {
|
|
1276
|
-
DAY: () => DAY,
|
|
1277
|
-
HOUR: () => HOUR,
|
|
1278
|
-
MINUTE: () => MINUTE,
|
|
1279
|
-
SECOND: () => SECOND,
|
|
1280
|
-
WEEK: () => WEEK,
|
|
1281
|
-
add: () => add,
|
|
1282
|
-
days: () => days,
|
|
1283
|
-
hours: () => hours,
|
|
1284
|
-
minutes: () => minutes,
|
|
1285
|
-
seconds: () => seconds,
|
|
1286
|
-
sub: () => sub,
|
|
1287
|
-
today: () => today,
|
|
1288
|
-
tomorrow: () => tomorrow,
|
|
1289
|
-
weeks: () => weeks
|
|
1290
|
-
});
|
|
1291
|
-
import { add, sub } from "date-fns";
|
|
1292
|
-
var SECOND = 1e3;
|
|
1293
|
-
var MINUTE = 60 * SECOND;
|
|
1294
|
-
var HOUR = 60 * MINUTE;
|
|
1295
|
-
var DAY = 24 * HOUR;
|
|
1296
|
-
var WEEK = 7 * DAY;
|
|
1297
|
-
var seconds = (num) => num * SECOND;
|
|
1298
|
-
var minutes = (num) => num * MINUTE;
|
|
1299
|
-
var hours = (num) => num * HOUR;
|
|
1300
|
-
var days = (num) => num * DAY;
|
|
1301
|
-
var weeks = (num) => num * WEEK;
|
|
1302
|
-
var today = (hours2 = 0, minutes2 = 0, seconds2 = 0) => {
|
|
1303
|
-
const local = /* @__PURE__ */ new Date();
|
|
1304
|
-
const utc = Date.UTC(
|
|
1305
|
-
local.getUTCFullYear(),
|
|
1306
|
-
local.getUTCMonth(),
|
|
1307
|
-
local.getUTCDate(),
|
|
1308
|
-
hours2,
|
|
1309
|
-
minutes2,
|
|
1310
|
-
seconds2
|
|
1311
|
-
);
|
|
1312
|
-
return new Date(utc);
|
|
1313
|
-
};
|
|
1314
|
-
var tomorrow = (hours2 = 0, minutes2 = 0, seconds2 = 0) => add(today(hours2, minutes2, seconds2), { days: 1 });
|
|
1315
|
-
export {
|
|
1316
|
-
InMemoryPersistor,
|
|
1317
|
-
Persistor,
|
|
1318
|
-
PromiseCache,
|
|
1319
|
-
createCache,
|
|
1320
|
-
serializer_exports as serializer,
|
|
1321
|
-
time_exports as time
|
|
1322
|
-
};
|
|
1323
|
-
//# sourceMappingURL=index.js.map
|