@sebspark/promise-cache 3.3.3 → 3.4.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/README.md +126 -8
- package/dist/index.d.mts +221 -2
- package/dist/index.d.ts +221 -2
- package/dist/index.js +283 -36
- package/dist/index.mjs +284 -35
- package/package.json +5 -1
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
1
2
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
3
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
4
|
}) : x)(function(x) {
|
|
4
5
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
6
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
7
|
});
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
7
12
|
|
|
8
13
|
// src/promiseCache.ts
|
|
9
14
|
import { randomUUID } from "node:crypto";
|
|
@@ -20,7 +25,7 @@ var LocalStorage = class {
|
|
|
20
25
|
}
|
|
21
26
|
set(key, value, options) {
|
|
22
27
|
this.client.set(key, value);
|
|
23
|
-
if (options
|
|
28
|
+
if (options?.PX) {
|
|
24
29
|
setTimeout(() => {
|
|
25
30
|
this.client.delete(key);
|
|
26
31
|
}, options.PX);
|
|
@@ -57,8 +62,8 @@ var fixESM = __require("fix-esm");
|
|
|
57
62
|
var superjson = fixESM.require("superjson");
|
|
58
63
|
var CACHE_CLIENT = createClient;
|
|
59
64
|
var isTestRunning = process.env.NODE_ENV === "test";
|
|
60
|
-
function toMillis(
|
|
61
|
-
return
|
|
65
|
+
function toMillis(seconds2) {
|
|
66
|
+
return seconds2 * 1e3;
|
|
62
67
|
}
|
|
63
68
|
var Persistor = class {
|
|
64
69
|
client = null;
|
|
@@ -90,17 +95,15 @@ var Persistor = class {
|
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
async startConnection() {
|
|
93
|
-
var _a;
|
|
94
98
|
try {
|
|
95
99
|
await new Promise((resolve, reject) => {
|
|
96
|
-
var _a2, _b, _c, _d, _e;
|
|
97
100
|
this.client = CACHE_CLIENT({
|
|
98
|
-
url:
|
|
99
|
-
username:
|
|
100
|
-
password:
|
|
101
|
-
pingInterval:
|
|
101
|
+
url: this.redis?.url,
|
|
102
|
+
username: this.redis?.username,
|
|
103
|
+
password: this.redis?.password,
|
|
104
|
+
pingInterval: this.redis?.pingInterval || void 0,
|
|
102
105
|
socket: {
|
|
103
|
-
...
|
|
106
|
+
...this.redis?.socket,
|
|
104
107
|
reconnectStrategy: (retries, cause) => {
|
|
105
108
|
console.error(cause);
|
|
106
109
|
return 1e3 * 2 ** retries;
|
|
@@ -113,17 +116,15 @@ var Persistor = class {
|
|
|
113
116
|
this.onSuccess();
|
|
114
117
|
resolve(true);
|
|
115
118
|
}).on("reconnecting", () => {
|
|
116
|
-
|
|
117
|
-
(_a3 = this.logger) == null ? void 0 : _a3.info("reconnecting...", this.clientId);
|
|
119
|
+
this.logger?.info("reconnecting...", this.clientId);
|
|
118
120
|
}).on("end", () => {
|
|
119
|
-
|
|
120
|
-
(_a3 = this.logger) == null ? void 0 : _a3.info("end...", this.clientId);
|
|
121
|
+
this.logger?.info("end...", this.clientId);
|
|
121
122
|
});
|
|
122
123
|
this.client.connect();
|
|
123
124
|
});
|
|
124
125
|
} catch (ex) {
|
|
125
126
|
this.onError(`${ex}`);
|
|
126
|
-
|
|
127
|
+
this.logger?.error(ex);
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
async size() {
|
|
@@ -136,8 +137,7 @@ var Persistor = class {
|
|
|
136
137
|
return this.clientId;
|
|
137
138
|
}
|
|
138
139
|
getIsClientConnected() {
|
|
139
|
-
|
|
140
|
-
return !!((_a = this.client) == null ? void 0 : _a.isReady);
|
|
140
|
+
return !!this.client?.isReady;
|
|
141
141
|
}
|
|
142
142
|
createOptions(ttl) {
|
|
143
143
|
if (ttl !== null && ttl !== void 0) {
|
|
@@ -153,9 +153,8 @@ var Persistor = class {
|
|
|
153
153
|
* @param object.timestamp Timestamp
|
|
154
154
|
*/
|
|
155
155
|
async set(key, { value, timestamp = Date.now(), ttl }) {
|
|
156
|
-
var _a, _b;
|
|
157
156
|
if (!this.client || !this.client.isReady) {
|
|
158
|
-
|
|
157
|
+
this.logger?.error("Client not ready");
|
|
159
158
|
return;
|
|
160
159
|
}
|
|
161
160
|
try {
|
|
@@ -167,7 +166,7 @@ var Persistor = class {
|
|
|
167
166
|
const options = this.createOptions(ttl);
|
|
168
167
|
await this.client.set(key, serializedData, options);
|
|
169
168
|
} catch (error) {
|
|
170
|
-
|
|
169
|
+
this.logger?.error(`Error setting data in redis: ${error}`);
|
|
171
170
|
throw new Error(`Error setting data in redis: ${error}`);
|
|
172
171
|
}
|
|
173
172
|
}
|
|
@@ -177,9 +176,8 @@ var Persistor = class {
|
|
|
177
176
|
* @returns GetType<T> value
|
|
178
177
|
*/
|
|
179
178
|
async get(key) {
|
|
180
|
-
var _a, _b;
|
|
181
179
|
if (!this.client) {
|
|
182
|
-
|
|
180
|
+
this.logger?.error("Client not ready");
|
|
183
181
|
return null;
|
|
184
182
|
}
|
|
185
183
|
try {
|
|
@@ -189,7 +187,7 @@ var Persistor = class {
|
|
|
189
187
|
}
|
|
190
188
|
return superjson.parse(data);
|
|
191
189
|
} catch (error) {
|
|
192
|
-
|
|
190
|
+
this.logger?.error(`Error getting data in redis: ${error}`);
|
|
193
191
|
throw new Error(`Error getting data from redis: ${error}`);
|
|
194
192
|
}
|
|
195
193
|
}
|
|
@@ -198,15 +196,14 @@ var Persistor = class {
|
|
|
198
196
|
* @param key Cache key
|
|
199
197
|
*/
|
|
200
198
|
async delete(key) {
|
|
201
|
-
var _a, _b;
|
|
202
199
|
if (!this.client || !this.client.isReady) {
|
|
203
|
-
|
|
200
|
+
this.logger?.error("Client not ready");
|
|
204
201
|
return;
|
|
205
202
|
}
|
|
206
203
|
try {
|
|
207
204
|
await this.client.del(key);
|
|
208
205
|
} catch (error) {
|
|
209
|
-
|
|
206
|
+
this.logger?.error(`Error deleting data from redis: ${error}`);
|
|
210
207
|
throw new Error(`Error deleting data from redis: ${error}`);
|
|
211
208
|
}
|
|
212
209
|
}
|
|
@@ -221,20 +218,20 @@ var getPersistor = ({
|
|
|
221
218
|
onSuccess,
|
|
222
219
|
clientId
|
|
223
220
|
}) => {
|
|
224
|
-
const connectionName = redis ?
|
|
221
|
+
const connectionName = redis ? redis?.name || "default" : "local";
|
|
225
222
|
if (!persistors[connectionName]) {
|
|
226
223
|
persistors[connectionName] = new Persistor({
|
|
227
224
|
redis,
|
|
228
225
|
onError: (error) => {
|
|
229
|
-
onError
|
|
230
|
-
logger
|
|
231
|
-
`\u274C REDIS | Client Error | ${connectionName} | ${redis
|
|
226
|
+
onError?.(error);
|
|
227
|
+
logger?.error(
|
|
228
|
+
`\u274C REDIS | Client Error | ${connectionName} | ${redis?.url}: ${error}`
|
|
232
229
|
);
|
|
233
230
|
},
|
|
234
231
|
onSuccess: () => {
|
|
235
|
-
onSuccess
|
|
236
|
-
logger
|
|
237
|
-
`\u{1F4E6} REDIS | Connection Ready | ${connectionName} | ${redis
|
|
232
|
+
onSuccess?.();
|
|
233
|
+
logger?.info(
|
|
234
|
+
`\u{1F4E6} REDIS | Connection Ready | ${connectionName} | ${redis?.url}`
|
|
238
235
|
);
|
|
239
236
|
},
|
|
240
237
|
clientId,
|
|
@@ -302,7 +299,7 @@ var PromiseCache = class {
|
|
|
302
299
|
*/
|
|
303
300
|
async find(key) {
|
|
304
301
|
const result = await this.persistor.get(key);
|
|
305
|
-
return
|
|
302
|
+
return result?.value ?? null;
|
|
306
303
|
}
|
|
307
304
|
/**
|
|
308
305
|
* A simple promise cache wrapper.
|
|
@@ -339,7 +336,259 @@ var PromiseCache = class {
|
|
|
339
336
|
return response;
|
|
340
337
|
}
|
|
341
338
|
};
|
|
339
|
+
|
|
340
|
+
// src/serializer.ts
|
|
341
|
+
var fixESM2 = __require("fix-esm");
|
|
342
|
+
var superjson2 = fixESM2.require("superjson");
|
|
343
|
+
var serialize = (data) => {
|
|
344
|
+
return superjson2.stringify(data);
|
|
345
|
+
};
|
|
346
|
+
var deserialize = (serialized) => {
|
|
347
|
+
if (serialized === void 0 || serialized === null) return serialized;
|
|
348
|
+
return superjson2.parse(serialized);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// src/setOptions.ts
|
|
352
|
+
var toSetOptions = (expiry) => {
|
|
353
|
+
const options = {};
|
|
354
|
+
if (expiry !== void 0) {
|
|
355
|
+
if (typeof expiry === "number") {
|
|
356
|
+
options.PX = expiry;
|
|
357
|
+
} else if (expiry instanceof Date && !Number.isNaN(expiry.getTime())) {
|
|
358
|
+
const timestamp = expiry.getTime();
|
|
359
|
+
if (timestamp > Date.now()) {
|
|
360
|
+
options.PXAT = timestamp;
|
|
361
|
+
} else {
|
|
362
|
+
options.PX = 1;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (!options.PX && !options.PXAT) {
|
|
367
|
+
options.EX = 1;
|
|
368
|
+
}
|
|
369
|
+
return options;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// src/cache.ts
|
|
373
|
+
var createCache = (persistor, prefix) => {
|
|
374
|
+
const pendingPromises = /* @__PURE__ */ new Map();
|
|
375
|
+
const cache = {
|
|
376
|
+
persistor,
|
|
377
|
+
wrap: (delegate, options) => {
|
|
378
|
+
return async (...args) => {
|
|
379
|
+
let key = typeof options.key === "string" ? options.key : options.key(...args);
|
|
380
|
+
if (prefix) {
|
|
381
|
+
key = `${prefix}:${key}`;
|
|
382
|
+
}
|
|
383
|
+
if (pendingPromises.has(key)) {
|
|
384
|
+
return pendingPromises.get(key);
|
|
385
|
+
}
|
|
386
|
+
const resultPromise = (async () => {
|
|
387
|
+
try {
|
|
388
|
+
const cached = await persistor.get(key);
|
|
389
|
+
if (cached !== null) {
|
|
390
|
+
return deserialize(cached);
|
|
391
|
+
}
|
|
392
|
+
const result = await delegate(...args);
|
|
393
|
+
const expiry = typeof options.expiry === "function" ? options.expiry(args, result) : options.expiry;
|
|
394
|
+
const serialized = serialize(result);
|
|
395
|
+
const setOptions = toSetOptions(expiry);
|
|
396
|
+
await persistor.set(key, serialized, setOptions);
|
|
397
|
+
return result;
|
|
398
|
+
} finally {
|
|
399
|
+
pendingPromises.delete(key);
|
|
400
|
+
}
|
|
401
|
+
})();
|
|
402
|
+
pendingPromises.set(key, resultPromise);
|
|
403
|
+
return resultPromise;
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
return cache;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// src/inMemoryPersistor.ts
|
|
411
|
+
var InMemoryPersistor = class {
|
|
412
|
+
/**
|
|
413
|
+
* Internal key-value store for caching string values.
|
|
414
|
+
* @private
|
|
415
|
+
*/
|
|
416
|
+
store;
|
|
417
|
+
/**
|
|
418
|
+
* Tracks active timeouts for expiring keys.
|
|
419
|
+
* Each key maps to a `setTimeout` reference that deletes the key when triggered.
|
|
420
|
+
* @private
|
|
421
|
+
*/
|
|
422
|
+
expirations;
|
|
423
|
+
/**
|
|
424
|
+
* Stores absolute expiration timestamps (in milliseconds since epoch) for each key.
|
|
425
|
+
* Used to compute remaining TTL.
|
|
426
|
+
* @private
|
|
427
|
+
*/
|
|
428
|
+
expiryTimestamps;
|
|
429
|
+
/**
|
|
430
|
+
* Creates a new instance of `InMemoryPersistor`.
|
|
431
|
+
* Initializes an empty store, expiration map, and TTL tracker.
|
|
432
|
+
*/
|
|
433
|
+
constructor() {
|
|
434
|
+
this.store = /* @__PURE__ */ new Map();
|
|
435
|
+
this.expirations = /* @__PURE__ */ new Map();
|
|
436
|
+
this.expiryTimestamps = /* @__PURE__ */ new Map();
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Stores a key-value pair with optional expiration settings.
|
|
440
|
+
* If an expiration is provided (`EX`, `PX`, `EXAT`, `PXAT`), the key is automatically removed when TTL expires.
|
|
441
|
+
*
|
|
442
|
+
* @param {string} key - The key to store.
|
|
443
|
+
* @param {string} value - The string value to associate with the key.
|
|
444
|
+
* @param {SetOptions} [options] - Optional Redis-style expiration settings.
|
|
445
|
+
* @returns {Promise<'OK' | null>} Resolves to `'OK'` on success, or `null` if a conditional set (`NX`) fails.
|
|
446
|
+
*/
|
|
447
|
+
async set(key, value, options) {
|
|
448
|
+
this.store.set(key, value);
|
|
449
|
+
if (options?.EX !== void 0) {
|
|
450
|
+
this.setExpiration(key, options.EX * 1e3);
|
|
451
|
+
} else if (options?.PX !== void 0) {
|
|
452
|
+
this.setExpiration(key, options.PX);
|
|
453
|
+
} else if (options?.EXAT !== void 0) {
|
|
454
|
+
const timeToExpire = options.EXAT * 1e3 - Date.now();
|
|
455
|
+
this.setExpiration(key, Math.max(0, timeToExpire));
|
|
456
|
+
} else if (options?.PXAT !== void 0) {
|
|
457
|
+
const timeToExpire = options.PXAT - Date.now();
|
|
458
|
+
this.setExpiration(key, Math.max(0, timeToExpire));
|
|
459
|
+
}
|
|
460
|
+
return "OK";
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Retrieves the value associated with a key.
|
|
464
|
+
*
|
|
465
|
+
* @param {string} key - The key to retrieve.
|
|
466
|
+
* @returns {Promise<string | null>} Resolves to the string value, or `null` if the key does not exist.
|
|
467
|
+
*/
|
|
468
|
+
async get(key) {
|
|
469
|
+
return this.store.get(key) ?? null;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Deletes a key from the store.
|
|
473
|
+
* If the key exists, it is removed along with any associated expiration.
|
|
474
|
+
*
|
|
475
|
+
* @param {string} key - The key to delete.
|
|
476
|
+
* @returns {Promise<number>} Resolves to `1` if the key was deleted, or `0` if the key did not exist.
|
|
477
|
+
*/
|
|
478
|
+
async del(key) {
|
|
479
|
+
const existed = this.store.has(key);
|
|
480
|
+
if (existed) {
|
|
481
|
+
this.store.delete(key);
|
|
482
|
+
this.clearExpiration(key);
|
|
483
|
+
}
|
|
484
|
+
return existed ? 1 : 0;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Sets a time-to-live (TTL) in seconds for a key.
|
|
488
|
+
* If the key exists, it will be deleted after the specified duration.
|
|
489
|
+
*
|
|
490
|
+
* @param {string} key - The key to set an expiration on.
|
|
491
|
+
* @param {number} seconds - The TTL in seconds.
|
|
492
|
+
* @returns {Promise<number>} Resolves to `1` if the TTL was set, or `0` if the key does not exist.
|
|
493
|
+
*/
|
|
494
|
+
async expire(key, seconds2) {
|
|
495
|
+
if (!this.store.has(key)) return false;
|
|
496
|
+
this.setExpiration(key, seconds2 * 1e3);
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Retrieves the remaining time-to-live (TTL) of a key in seconds.
|
|
501
|
+
*
|
|
502
|
+
* @param {string} key - The key to check.
|
|
503
|
+
* @returns {Promise<number>} Resolves to:
|
|
504
|
+
* - Remaining TTL in **seconds** if the key exists and has an expiration.
|
|
505
|
+
* - `-1` if the key exists but has no expiration.
|
|
506
|
+
* - `-2` if the key does not exist.
|
|
507
|
+
*/
|
|
508
|
+
async ttl(key) {
|
|
509
|
+
if (!this.store.has(key)) return -2;
|
|
510
|
+
if (!this.expiryTimestamps.has(key)) return -1;
|
|
511
|
+
const timeLeft = this.expiryTimestamps.get(key) - Date.now();
|
|
512
|
+
return timeLeft > 0 ? Math.ceil(timeLeft / 1e3) : -2;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Removes all keys from the store and clears all active expirations.
|
|
516
|
+
*
|
|
517
|
+
* @returns {Promise<'OK'>} Resolves to `'OK'` after all data is cleared.
|
|
518
|
+
*/
|
|
519
|
+
async flushAll() {
|
|
520
|
+
this.store.clear();
|
|
521
|
+
for (const timeout of this.expirations.values()) {
|
|
522
|
+
clearTimeout(timeout);
|
|
523
|
+
}
|
|
524
|
+
this.expirations.clear();
|
|
525
|
+
this.expiryTimestamps.clear();
|
|
526
|
+
return "OK";
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Sets an expiration timeout for a key.
|
|
530
|
+
* Cancels any existing expiration before setting a new one.
|
|
531
|
+
*
|
|
532
|
+
* @private
|
|
533
|
+
* @param {string} key - The key to expire.
|
|
534
|
+
* @param {number} ttlMs - Time-to-live in milliseconds.
|
|
535
|
+
*/
|
|
536
|
+
setExpiration(key, ttlMs) {
|
|
537
|
+
this.clearExpiration(key);
|
|
538
|
+
const expiryTimestamp = Date.now() + ttlMs;
|
|
539
|
+
this.expiryTimestamps.set(key, expiryTimestamp);
|
|
540
|
+
const timeout = setTimeout(() => {
|
|
541
|
+
this.store.delete(key);
|
|
542
|
+
this.expirations.delete(key);
|
|
543
|
+
this.expiryTimestamps.delete(key);
|
|
544
|
+
}, ttlMs);
|
|
545
|
+
this.expirations.set(key, timeout);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Cancels an active expiration timeout for a key and removes its TTL record.
|
|
549
|
+
*
|
|
550
|
+
* @private
|
|
551
|
+
* @param {string} key - The key whose expiration should be cleared.
|
|
552
|
+
*/
|
|
553
|
+
clearExpiration(key) {
|
|
554
|
+
if (this.expirations.has(key)) {
|
|
555
|
+
clearTimeout(this.expirations.get(key));
|
|
556
|
+
this.expirations.delete(key);
|
|
557
|
+
this.expiryTimestamps.delete(key);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// src/time.ts
|
|
563
|
+
var time_exports = {};
|
|
564
|
+
__export(time_exports, {
|
|
565
|
+
DAY: () => DAY,
|
|
566
|
+
HOUR: () => HOUR,
|
|
567
|
+
MINUTE: () => MINUTE,
|
|
568
|
+
SECOND: () => SECOND,
|
|
569
|
+
WEEK: () => WEEK,
|
|
570
|
+
add: () => add,
|
|
571
|
+
days: () => days,
|
|
572
|
+
hours: () => hours,
|
|
573
|
+
minutes: () => minutes,
|
|
574
|
+
seconds: () => seconds,
|
|
575
|
+
weeks: () => weeks
|
|
576
|
+
});
|
|
577
|
+
import { add } from "date-fns";
|
|
578
|
+
var SECOND = 1e3;
|
|
579
|
+
var MINUTE = 60 * SECOND;
|
|
580
|
+
var HOUR = 60 * MINUTE;
|
|
581
|
+
var DAY = 24 * HOUR;
|
|
582
|
+
var WEEK = 7 * DAY;
|
|
583
|
+
var seconds = (num) => num * SECOND;
|
|
584
|
+
var minutes = (num) => num * MINUTE;
|
|
585
|
+
var hours = (num) => num * HOUR;
|
|
586
|
+
var days = (num) => num * DAY;
|
|
587
|
+
var weeks = (num) => num * WEEK;
|
|
342
588
|
export {
|
|
589
|
+
InMemoryPersistor,
|
|
343
590
|
Persistor,
|
|
344
|
-
PromiseCache
|
|
591
|
+
PromiseCache,
|
|
592
|
+
createCache,
|
|
593
|
+
time_exports as time
|
|
345
594
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebspark/promise-cache",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -13,12 +13,16 @@
|
|
|
13
13
|
"dev": "tsc --watch --noEmit",
|
|
14
14
|
"lint": "biome check .",
|
|
15
15
|
"test": "vitest run --passWithNoTests --coverage",
|
|
16
|
+
"test:e2e": "vitest --config vitest.config.e2e.ts --run",
|
|
16
17
|
"typecheck": "vitest --typecheck.only --passWithNoTests"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
20
|
+
"@testcontainers/redis": "^10.17.2",
|
|
21
|
+
"testcontainers": "^10.17.2",
|
|
19
22
|
"tsconfig": "*"
|
|
20
23
|
},
|
|
21
24
|
"dependencies": {
|
|
25
|
+
"date-fn": "^0.0.2",
|
|
22
26
|
"fix-esm": "1.0.1",
|
|
23
27
|
"redis": "4.7.0",
|
|
24
28
|
"superjson": "2.2.2"
|