@sebspark/promise-cache 4.0.0 → 4.0.2

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.js CHANGED
@@ -29,369 +29,20 @@ __export(index_exports, {
29
29
  });
30
30
  module.exports = __toCommonJS(index_exports);
31
31
 
32
- // src/promiseCache.ts
33
- var import_node_crypto = require("crypto");
34
-
35
- // src/persistor.ts
36
- var import_redis = require("redis");
37
-
38
- // src/localMemory.ts
39
- var LocalStorage = class {
40
- client = /* @__PURE__ */ new Map();
41
- isReady = false;
42
- get(key) {
43
- return this.client.get(key);
44
- }
45
- set(key, value, options) {
46
- this.client.set(key, value);
47
- if (options?.PX) {
48
- setTimeout(() => {
49
- this.client.delete(key);
50
- }, options.PX);
51
- }
52
- }
53
- del(key) {
54
- this.client.delete(key);
55
- }
56
- clear() {
57
- this.client.clear();
58
- }
59
- async DBSIZE() {
60
- return Promise.resolve(this.client.size);
61
- }
62
- // This is just for testing
63
- on(event, callback) {
64
- if (event === "ready" && callback) {
65
- this.isReady = true;
66
- callback("ready");
67
- }
68
- return this;
69
- }
70
- connect() {
71
- return Promise.resolve(this);
72
- }
73
- };
74
- var localStorage = new LocalStorage();
75
- var createLocalMemoryClient = () => {
76
- return localStorage;
77
- };
78
-
79
- // src/persistor.ts
80
- var fixESM = require("fix-esm");
81
- var superjson = fixESM.require("superjson");
82
- var CACHE_CLIENT = import_redis.createClient;
83
- var isTestRunning = process.env.NODE_ENV === "test";
84
- function toMillis(seconds2) {
85
- return seconds2 * 1e3;
86
- }
87
- var Persistor = class {
88
- client = null;
89
- clientId;
90
- onError;
91
- onSuccess;
92
- logger;
93
- redis;
94
- constructor({
95
- redis,
96
- clientId,
97
- onSuccess,
98
- onError,
99
- logger
100
- }) {
101
- this.onError = onError || (() => {
102
- });
103
- this.onSuccess = onSuccess || (() => {
104
- });
105
- this.clientId = clientId;
106
- this.logger = logger;
107
- if (redis && !isTestRunning) {
108
- this.redis = redis;
109
- } else {
110
- CACHE_CLIENT = createLocalMemoryClient;
111
- }
112
- if (!this.client || !this.client.isReady) {
113
- this.startConnection();
114
- }
115
- }
116
- async startConnection() {
117
- try {
118
- await new Promise((resolve, reject) => {
119
- this.client = CACHE_CLIENT({
120
- url: this.redis?.url,
121
- username: this.redis?.username,
122
- password: this.redis?.password,
123
- pingInterval: this.redis?.pingInterval || void 0,
124
- socket: {
125
- ...this.redis?.socket,
126
- reconnectStrategy: (retries, cause) => {
127
- this.logger?.error(cause);
128
- return 1e3 * 2 ** retries;
129
- }
130
- }
131
- }).on("error", (err) => {
132
- this.onError(err);
133
- reject(err);
134
- }).on("ready", () => {
135
- this.onSuccess();
136
- resolve(true);
137
- }).on("reconnecting", () => {
138
- this.logger?.info("reconnecting...", this.clientId);
139
- }).on("end", () => {
140
- this.logger?.info("end...", this.clientId);
141
- });
142
- this.client.connect();
143
- });
144
- } catch (ex) {
145
- this.onError(`${ex}`);
146
- this.logger?.error(ex);
147
- }
148
- }
149
- async size() {
150
- if (!this.client) {
151
- throw new Error("Client not initialized");
152
- }
153
- return await this.client.DBSIZE();
154
- }
155
- getClientId() {
156
- return this.clientId;
157
- }
158
- getIsClientConnected() {
159
- return !!this.client?.isReady;
160
- }
161
- createOptions(ttl) {
162
- if (ttl !== null && ttl !== void 0) {
163
- return { PX: Math.round(toMillis(ttl)) };
164
- }
165
- return {};
166
- }
167
- /**
168
- * Set a value in the cache.
169
- * @param key Cache key.
170
- * @param object.value Value to set in the cache.
171
- * @param object.ttl Time to live in seconds.
172
- * @param object.timestamp Timestamp
173
- */
174
- async set(key, { value, timestamp = Date.now(), ttl }) {
175
- if (!this.client || !this.client.isReady) {
176
- this.logger?.error("Client not ready");
177
- return;
178
- }
179
- try {
180
- const serializedData = superjson.stringify({
181
- value,
182
- ttl,
183
- timestamp
184
- });
185
- const options = this.createOptions(ttl);
186
- await this.client.set(key, serializedData, options);
187
- } catch (error) {
188
- this.logger?.error(`Error setting data in redis: ${error}`);
189
- throw new Error(`Error setting data in redis: ${error}`);
190
- }
191
- }
192
- /**
193
- * Get a value from the cache.
194
- * @param key Cache key.
195
- * @returns GetType<T> value
196
- */
197
- async get(key) {
198
- if (!this.client) {
199
- this.logger?.error("Client not ready");
200
- return null;
201
- }
202
- try {
203
- const data = await this.client.get(key);
204
- if (!data) {
205
- return null;
206
- }
207
- return superjson.parse(data);
208
- } catch (error) {
209
- this.logger?.error(`Error getting data in redis: ${error}`);
210
- throw new Error(`Error getting data from redis: ${error}`);
211
- }
212
- }
213
- /**
214
- * Delete a value from the cache.
215
- * @param key Cache key
216
- */
217
- async delete(key) {
218
- if (!this.client || !this.client.isReady) {
219
- this.logger?.error("Client not ready");
220
- return;
221
- }
222
- try {
223
- await this.client.del(key);
224
- } catch (error) {
225
- this.logger?.error(`Error deleting data from redis: ${error}`);
226
- throw new Error(`Error deleting data from redis: ${error}`);
227
- }
228
- }
229
- };
230
-
231
- // src/promiseCache.ts
232
- var persistors = {};
233
- var getPersistor = ({
234
- redis,
235
- logger,
236
- onError,
237
- onSuccess,
238
- clientId
239
- }) => {
240
- const connectionName = redis ? redis?.name || "default" : "local";
241
- if (!persistors[connectionName]) {
242
- persistors[connectionName] = new Persistor({
243
- redis,
244
- onError: (error) => {
245
- onError?.(error);
246
- logger?.error(
247
- `\u274C REDIS | Client Error | ${connectionName} | ${redis?.url}: ${error}`
248
- );
249
- },
250
- onSuccess: () => {
251
- onSuccess?.();
252
- logger?.info(
253
- `\u{1F4E6} REDIS | Connection Ready | ${connectionName} | ${redis?.url}`
254
- );
255
- },
256
- clientId,
257
- logger
258
- });
259
- }
260
- return persistors[connectionName];
261
- };
262
- var PromiseCache = class {
263
- persistor;
264
- clientId = (0, import_node_crypto.randomUUID)();
265
- caseSensitive;
266
- fallbackToFunction;
267
- // If true, the cache will fallback to the delegate function if there is an error retrieving the cache.
268
- ttl;
269
- // Time to live in milliseconds.
270
- logger;
271
- /**
272
- * Initialize a new PromiseCache.
273
- * @param ttlInSeconds Default cache TTL.
274
- * @param caseSensitive Set to true if you want to differentiate between keys with different casing.
275
- */
276
- constructor({
277
- ttlInSeconds,
278
- caseSensitive = false,
279
- redis,
280
- fallbackToFunction = false,
281
- onSuccess,
282
- onError,
283
- logger
284
- }) {
285
- this.logger = logger;
286
- this.persistor = getPersistor({
287
- redis,
288
- onError,
289
- onSuccess,
290
- clientId: this.clientId,
291
- logger: this.logger
292
- });
293
- this.caseSensitive = caseSensitive;
294
- this.fallbackToFunction = fallbackToFunction;
295
- if (ttlInSeconds) {
296
- this.ttl = ttlInSeconds;
297
- }
298
- }
299
- /**
300
- * Cache size.
301
- * @returns The number of entries in the cache.
302
- */
303
- async size() {
304
- return await this.persistor.size();
305
- }
306
- /**
307
- * Override a value in the cache.
308
- * @param key Cache key.
309
- * @param value Cache value.
310
- * @param ttlInSeconds Time to live in seconds.
311
- */
312
- async override(key, value, ttlInSeconds) {
313
- const effectiveKey = this.caseSensitive ? key : key.toLowerCase();
314
- const effectiveTTL = ttlInSeconds !== void 0 ? ttlInSeconds : this.ttl;
315
- await this.persistor.set(effectiveKey, {
316
- value,
317
- timestamp: Date.now(),
318
- ttl: effectiveTTL
319
- });
320
- }
321
- /**
322
- * Get a value from the cache.
323
- * @param key Cache key.
324
- */
325
- async find(key) {
326
- const result = await this.persistor.get(key);
327
- return result?.value ?? null;
328
- }
329
- /**
330
- * A simple promise cache wrapper.
331
- * @param key Cache key.
332
- * @param delegate The function to execute if the key is not in the cache.
333
- * @param ttlInSeconds Time to live in seconds.
334
- * @param ttlKeyInSeconds The key in the response object that contains the TTL.
335
- * @returns The result of the delegate function.
336
- */
337
- async wrap(key, delegate, ttlInSeconds, ttlKeyInSeconds) {
338
- const now = Date.now();
339
- const effectiveKey = this.caseSensitive ? key : key.toLowerCase();
340
- let effectiveTTL = ttlInSeconds ?? this.ttl;
341
- try {
342
- const cached = await this.persistor.get(effectiveKey);
343
- if (cached) {
344
- if (!ttlKeyInSeconds && cached.ttl !== effectiveTTL) {
345
- this.logger?.error(
346
- "WARNING: TTL mismatch for key. It is recommended to use the same TTL for the same key."
347
- );
348
- }
349
- return cached.value;
350
- }
351
- } catch (err) {
352
- const error = err;
353
- if (!this.fallbackToFunction) {
354
- throw error;
355
- }
356
- this.logger?.error(
357
- "redis error, falling back to function execution",
358
- error instanceof Error ? error.message : String(error)
359
- );
360
- }
361
- const response = await delegate();
362
- if (ttlKeyInSeconds) {
363
- const responseDict = response;
364
- const responseTTL = Number(responseDict[ttlKeyInSeconds]);
365
- effectiveTTL = responseTTL || effectiveTTL;
366
- }
367
- try {
368
- await this.persistor.set(effectiveKey, {
369
- value: response,
370
- timestamp: now,
371
- ttl: effectiveTTL
372
- });
373
- } catch (err) {
374
- const error = err;
375
- console.error("failed to cache result", error.message);
376
- }
377
- return response;
378
- }
379
- };
380
-
381
32
  // src/serializer.ts
382
33
  var serializer_exports = {};
383
34
  __export(serializer_exports, {
384
35
  deserialize: () => deserialize,
385
36
  serialize: () => serialize
386
37
  });
387
- var fixESM2 = require("fix-esm");
388
- var superjson2 = fixESM2.require("superjson");
38
+ var fixESM = require("fix-esm");
39
+ var superjson = fixESM.require("superjson");
389
40
  var serialize = (data) => {
390
- return superjson2.stringify(data);
41
+ return superjson.stringify(data);
391
42
  };
392
43
  var deserialize = (serialized) => {
393
44
  if (serialized === void 0 || serialized === null) return serialized;
394
- return superjson2.parse(serialized);
45
+ return superjson.parse(serialized);
395
46
  };
396
47
 
397
48
  // src/setOptions.ts
@@ -716,10 +367,7 @@ var InMemoryPersistor = class {
716
367
  */
717
368
  async hSet(key, field, value) {
718
369
  const existingHash = JSON.parse(this.store.get(key) ?? "{}");
719
- const isNewField = !Object.prototype.hasOwnProperty.call(
720
- existingHash,
721
- field
722
- );
370
+ const isNewField = !Object.hasOwn(existingHash, field);
723
371
  existingHash[field] = value;
724
372
  this.store.set(key, JSON.stringify(existingHash));
725
373
  return isNewField ? 1 : 0;
@@ -1195,103 +843,450 @@ var InMemoryMulti = class {
1195
843
  * @param key - The list key.
1196
844
  * @returns The `IPersistorMulti` instance to allow method chaining.
1197
845
  */
1198
- rPop(key) {
1199
- this.commands.add(() => this.persistor.rPop(key));
1200
- return this;
846
+ rPop(key) {
847
+ this.commands.add(() => this.persistor.rPop(key));
848
+ return this;
849
+ }
850
+ /**
851
+ * Queues an `lRange` command to retrieve a range of elements from a list.
852
+ * The command will be executed when `exec()` is called.
853
+ *
854
+ * @param key - The list key.
855
+ * @param start - The start index.
856
+ * @param stop - The stop index (inclusive).
857
+ * @returns The `IPersistorMulti` instance to allow method chaining.
858
+ */
859
+ lRange(key, start, stop) {
860
+ this.commands.add(() => this.persistor.lRange(key, start, stop));
861
+ return this;
862
+ }
863
+ /**
864
+ * Queues an `sAdd` command to add elements to a set.
865
+ * The command will be executed when `exec()` is called.
866
+ *
867
+ * @param key - The set key.
868
+ * @param values - The values to add.
869
+ * @returns The `IPersistorMulti` instance to allow method chaining.
870
+ */
871
+ sAdd(key, values) {
872
+ this.commands.add(() => this.persistor.sAdd(key, values));
873
+ return this;
874
+ }
875
+ /**
876
+ * Queues an `sRem` command to remove elements from a set.
877
+ * The command will be executed when `exec()` is called.
878
+ *
879
+ * @param key - The set key.
880
+ * @param values - The values to remove.
881
+ * @returns The `IPersistorMulti` instance to allow method chaining.
882
+ */
883
+ sRem(key, values) {
884
+ this.commands.add(() => this.persistor.sRem(key, values));
885
+ return this;
886
+ }
887
+ /**
888
+ * Queues an `sMembers` command to retrieve all members of a set.
889
+ * The command will be executed when `exec()` is called.
890
+ *
891
+ * @param key - The set key.
892
+ * @returns The `IPersistorMulti` instance to allow method chaining.
893
+ */
894
+ sMembers(key) {
895
+ this.commands.add(() => this.persistor.sMembers(key));
896
+ return this;
897
+ }
898
+ /**
899
+ * Queues a `zAdd` command to add elements to a sorted set with scores.
900
+ * The command will be executed when `exec()` is called.
901
+ *
902
+ * @param key - The sorted set key.
903
+ * @param members - An array of objects with `score` and `value`.
904
+ * @returns The `IPersistorMulti` instance to allow method chaining.
905
+ */
906
+ zAdd(key, members) {
907
+ this.commands.add(() => this.persistor.zAdd(key, members));
908
+ return this;
909
+ }
910
+ /**
911
+ * Queues a `zRange` command to retrieve a range of elements from a sorted set.
912
+ * The command will be executed when `exec()` is called.
913
+ *
914
+ * @param key - The sorted set key.
915
+ * @param start - The start index.
916
+ * @param stop - The stop index (inclusive).
917
+ * @returns The `IPersistorMulti` instance to allow method chaining.
918
+ */
919
+ zRange(key, start, stop) {
920
+ this.commands.add(() => this.persistor.zRange(key, start, stop));
921
+ return this;
922
+ }
923
+ /**
924
+ * Queues a `zRem` command to remove elements from a sorted set.
925
+ * The command will be executed when `exec()` is called.
926
+ *
927
+ * @param key - The sorted set key.
928
+ * @param members - The members to remove.
929
+ * @returns The `IPersistorMulti` instance to allow method chaining.
930
+ */
931
+ zRem(key, members) {
932
+ this.commands.add(() => this.persistor.zRem(key, members));
933
+ return this;
934
+ }
935
+ /**
936
+ * Executes multiple commands in a batch operation.
937
+ * Each command is executed in sequence, and results are collected in an array.
938
+ *
939
+ * @returns Resolves to an array containing the results of each queued command.
940
+ */
941
+ async exec() {
942
+ return Promise.all([...this.commands].map((cmd) => cmd()));
943
+ }
944
+ };
945
+
946
+ // src/persistor.ts
947
+ var import_redis = require("redis");
948
+
949
+ // src/localMemory.ts
950
+ var LocalStorage = class {
951
+ client = /* @__PURE__ */ new Map();
952
+ isReady = false;
953
+ get(key) {
954
+ return this.client.get(key);
955
+ }
956
+ set(key, value, options) {
957
+ this.client.set(key, value);
958
+ if (options?.PX) {
959
+ setTimeout(() => {
960
+ this.client.delete(key);
961
+ }, options.PX);
962
+ }
963
+ }
964
+ del(key) {
965
+ this.client.delete(key);
966
+ }
967
+ clear() {
968
+ this.client.clear();
969
+ }
970
+ async DBSIZE() {
971
+ return Promise.resolve(this.client.size);
972
+ }
973
+ // This is just for testing
974
+ on(event, callback) {
975
+ if (event === "ready" && callback) {
976
+ this.isReady = true;
977
+ callback("ready");
978
+ }
979
+ return this;
980
+ }
981
+ connect() {
982
+ return Promise.resolve(this);
983
+ }
984
+ };
985
+ var localStorage = new LocalStorage();
986
+ var createLocalMemoryClient = () => {
987
+ return localStorage;
988
+ };
989
+
990
+ // src/persistor.ts
991
+ var fixESM2 = require("fix-esm");
992
+ var superjson2 = fixESM2.require("superjson");
993
+ var CACHE_CLIENT = import_redis.createClient;
994
+ var isTestRunning = process.env.NODE_ENV === "test";
995
+ function toMillis(seconds2) {
996
+ return seconds2 * 1e3;
997
+ }
998
+ var Persistor = class {
999
+ client = null;
1000
+ clientId;
1001
+ onError;
1002
+ onSuccess;
1003
+ logger;
1004
+ redis;
1005
+ constructor({
1006
+ redis,
1007
+ clientId,
1008
+ onSuccess,
1009
+ onError,
1010
+ logger
1011
+ }) {
1012
+ this.onError = onError || (() => {
1013
+ });
1014
+ this.onSuccess = onSuccess || (() => {
1015
+ });
1016
+ this.clientId = clientId;
1017
+ this.logger = logger;
1018
+ if (redis && !isTestRunning) {
1019
+ this.redis = redis;
1020
+ } else {
1021
+ CACHE_CLIENT = createLocalMemoryClient;
1022
+ }
1023
+ if (!this.client || !this.client.isReady) {
1024
+ this.startConnection();
1025
+ }
1026
+ }
1027
+ async startConnection() {
1028
+ try {
1029
+ await new Promise((resolve, reject) => {
1030
+ this.client = CACHE_CLIENT({
1031
+ url: this.redis?.url,
1032
+ username: this.redis?.username,
1033
+ password: this.redis?.password,
1034
+ pingInterval: this.redis?.pingInterval || void 0,
1035
+ socket: {
1036
+ ...this.redis?.socket,
1037
+ reconnectStrategy: (retries, cause) => {
1038
+ this.logger?.error(cause);
1039
+ return 1e3 * 2 ** retries;
1040
+ }
1041
+ }
1042
+ }).on("error", (err) => {
1043
+ this.onError(err);
1044
+ reject(err);
1045
+ }).on("ready", () => {
1046
+ this.onSuccess();
1047
+ resolve(true);
1048
+ }).on("reconnecting", () => {
1049
+ this.logger?.info("reconnecting...", this.clientId);
1050
+ }).on("end", () => {
1051
+ this.logger?.info("end...", this.clientId);
1052
+ });
1053
+ this.client.connect();
1054
+ });
1055
+ } catch (ex) {
1056
+ this.onError(`${ex}`);
1057
+ this.logger?.error(ex);
1058
+ }
1059
+ }
1060
+ async size() {
1061
+ if (!this.client) {
1062
+ throw new Error("Client not initialized");
1063
+ }
1064
+ return await this.client.DBSIZE();
1065
+ }
1066
+ getClientId() {
1067
+ return this.clientId;
1068
+ }
1069
+ getIsClientConnected() {
1070
+ return !!this.client?.isReady;
1071
+ }
1072
+ createOptions(ttl) {
1073
+ if (ttl !== null && ttl !== void 0 && ttl > 0) {
1074
+ return { PX: Math.round(toMillis(ttl)) };
1075
+ }
1076
+ return {};
1077
+ }
1078
+ /**
1079
+ * Set a value in the cache.
1080
+ * @param key Cache key.
1081
+ * @param object.value Value to set in the cache.
1082
+ * @param object.ttl Time to live in seconds.
1083
+ * @param object.timestamp Timestamp
1084
+ */
1085
+ async set(key, { value, timestamp = Date.now(), ttl }) {
1086
+ if (!this.client || !this.client.isReady) {
1087
+ this.logger?.error("Client not ready");
1088
+ return;
1089
+ }
1090
+ try {
1091
+ const serializedData = superjson2.stringify({
1092
+ value,
1093
+ ttl,
1094
+ timestamp
1095
+ });
1096
+ const options = this.createOptions(ttl);
1097
+ await this.client.set(key, serializedData, options);
1098
+ } catch (error) {
1099
+ this.logger?.error(`Error setting data in redis: ${error}`);
1100
+ throw new Error(`Error setting data in redis: ${error}`);
1101
+ }
1201
1102
  }
1202
1103
  /**
1203
- * Queues an `lRange` command to retrieve a range of elements from a list.
1204
- * The command will be executed when `exec()` is called.
1205
- *
1206
- * @param key - The list key.
1207
- * @param start - The start index.
1208
- * @param stop - The stop index (inclusive).
1209
- * @returns The `IPersistorMulti` instance to allow method chaining.
1104
+ * Get a value from the cache.
1105
+ * @param key Cache key.
1106
+ * @returns GetType<T> value
1210
1107
  */
1211
- lRange(key, start, stop) {
1212
- this.commands.add(() => this.persistor.lRange(key, start, stop));
1213
- return this;
1108
+ async get(key) {
1109
+ if (!this.client) {
1110
+ this.logger?.error("Client not ready");
1111
+ return null;
1112
+ }
1113
+ try {
1114
+ const data = await this.client.get(key);
1115
+ if (!data) {
1116
+ return null;
1117
+ }
1118
+ return superjson2.parse(data);
1119
+ } catch (error) {
1120
+ this.logger?.error(`Error getting data in redis: ${error}`);
1121
+ throw new Error(`Error getting data from redis: ${error}`);
1122
+ }
1214
1123
  }
1215
1124
  /**
1216
- * Queues an `sAdd` command to add elements to a set.
1217
- * The command will be executed when `exec()` is called.
1218
- *
1219
- * @param key - The set key.
1220
- * @param values - The values to add.
1221
- * @returns The `IPersistorMulti` instance to allow method chaining.
1125
+ * Delete a value from the cache.
1126
+ * @param key Cache key
1222
1127
  */
1223
- sAdd(key, values) {
1224
- this.commands.add(() => this.persistor.sAdd(key, values));
1225
- return this;
1128
+ async delete(key) {
1129
+ if (!this.client || !this.client.isReady) {
1130
+ this.logger?.error("Client not ready");
1131
+ return;
1132
+ }
1133
+ try {
1134
+ await this.client.del(key);
1135
+ } catch (error) {
1136
+ this.logger?.error(`Error deleting data from redis: ${error}`);
1137
+ throw new Error(`Error deleting data from redis: ${error}`);
1138
+ }
1226
1139
  }
1227
- /**
1228
- * Queues an `sRem` command to remove elements from a set.
1229
- * The command will be executed when `exec()` is called.
1230
- *
1231
- * @param key - The set key.
1232
- * @param values - The values to remove.
1233
- * @returns The `IPersistorMulti` instance to allow method chaining.
1234
- */
1235
- sRem(key, values) {
1236
- this.commands.add(() => this.persistor.sRem(key, values));
1237
- return this;
1140
+ };
1141
+
1142
+ // src/promiseCache.ts
1143
+ var import_node_crypto = require("crypto");
1144
+ var persistors = {};
1145
+ var getPersistor = ({
1146
+ redis,
1147
+ logger,
1148
+ onError,
1149
+ onSuccess,
1150
+ clientId
1151
+ }) => {
1152
+ const connectionName = redis ? redis?.name || "default" : "local";
1153
+ if (!persistors[connectionName]) {
1154
+ persistors[connectionName] = new Persistor({
1155
+ redis,
1156
+ onError: (error) => {
1157
+ onError?.(error);
1158
+ logger?.error(
1159
+ `\u274C REDIS | Client Error | ${connectionName} | ${redis?.url}: ${error}`
1160
+ );
1161
+ },
1162
+ onSuccess: () => {
1163
+ onSuccess?.();
1164
+ logger?.info(
1165
+ `\u{1F4E6} REDIS | Connection Ready | ${connectionName} | ${redis?.url}`
1166
+ );
1167
+ },
1168
+ clientId,
1169
+ logger
1170
+ });
1238
1171
  }
1172
+ return persistors[connectionName];
1173
+ };
1174
+ var PromiseCache = class {
1175
+ persistor;
1176
+ clientId = (0, import_node_crypto.randomUUID)();
1177
+ caseSensitive;
1178
+ fallbackToFunction;
1179
+ // If true, the cache will fallback to the delegate function if there is an error retrieving the cache.
1180
+ ttl;
1181
+ // Time to live in milliseconds.
1182
+ logger;
1239
1183
  /**
1240
- * Queues an `sMembers` command to retrieve all members of a set.
1241
- * The command will be executed when `exec()` is called.
1242
- *
1243
- * @param key - The set key.
1244
- * @returns The `IPersistorMulti` instance to allow method chaining.
1184
+ * Initialize a new PromiseCache.
1185
+ * @param ttlInSeconds Default cache TTL.
1186
+ * @param caseSensitive Set to true if you want to differentiate between keys with different casing.
1245
1187
  */
1246
- sMembers(key) {
1247
- this.commands.add(() => this.persistor.sMembers(key));
1248
- return this;
1188
+ constructor({
1189
+ ttlInSeconds,
1190
+ caseSensitive = false,
1191
+ redis,
1192
+ fallbackToFunction = false,
1193
+ onSuccess,
1194
+ onError,
1195
+ logger
1196
+ }) {
1197
+ this.logger = logger;
1198
+ this.persistor = getPersistor({
1199
+ redis,
1200
+ onError,
1201
+ onSuccess,
1202
+ clientId: this.clientId,
1203
+ logger: this.logger
1204
+ });
1205
+ this.caseSensitive = caseSensitive;
1206
+ this.fallbackToFunction = fallbackToFunction;
1207
+ if (ttlInSeconds) {
1208
+ this.ttl = ttlInSeconds;
1209
+ }
1249
1210
  }
1250
1211
  /**
1251
- * Queues a `zAdd` command to add elements to a sorted set with scores.
1252
- * The command will be executed when `exec()` is called.
1253
- *
1254
- * @param key - The sorted set key.
1255
- * @param members - An array of objects with `score` and `value`.
1256
- * @returns The `IPersistorMulti` instance to allow method chaining.
1212
+ * Cache size.
1213
+ * @returns The number of entries in the cache.
1257
1214
  */
1258
- zAdd(key, members) {
1259
- this.commands.add(() => this.persistor.zAdd(key, members));
1260
- return this;
1215
+ async size() {
1216
+ return await this.persistor.size();
1261
1217
  }
1262
1218
  /**
1263
- * Queues a `zRange` command to retrieve a range of elements from a sorted set.
1264
- * The command will be executed when `exec()` is called.
1265
- *
1266
- * @param key - The sorted set key.
1267
- * @param start - The start index.
1268
- * @param stop - The stop index (inclusive).
1269
- * @returns The `IPersistorMulti` instance to allow method chaining.
1219
+ * Override a value in the cache.
1220
+ * @param key Cache key.
1221
+ * @param value Cache value.
1222
+ * @param ttlInSeconds Time to live in seconds.
1270
1223
  */
1271
- zRange(key, start, stop) {
1272
- this.commands.add(() => this.persistor.zRange(key, start, stop));
1273
- return this;
1224
+ async override(key, value, ttlInSeconds) {
1225
+ const effectiveKey = this.caseSensitive ? key : key.toLowerCase();
1226
+ const effectiveTTL = ttlInSeconds !== void 0 ? ttlInSeconds : this.ttl;
1227
+ await this.persistor.set(effectiveKey, {
1228
+ value,
1229
+ timestamp: Date.now(),
1230
+ ttl: effectiveTTL
1231
+ });
1274
1232
  }
1275
1233
  /**
1276
- * Queues a `zRem` command to remove elements from a sorted set.
1277
- * The command will be executed when `exec()` is called.
1278
- *
1279
- * @param key - The sorted set key.
1280
- * @param members - The members to remove.
1281
- * @returns The `IPersistorMulti` instance to allow method chaining.
1234
+ * Get a value from the cache.
1235
+ * @param key Cache key.
1282
1236
  */
1283
- zRem(key, members) {
1284
- this.commands.add(() => this.persistor.zRem(key, members));
1285
- return this;
1237
+ async find(key) {
1238
+ const result = await this.persistor.get(key);
1239
+ return result?.value ?? null;
1286
1240
  }
1287
1241
  /**
1288
- * Executes multiple commands in a batch operation.
1289
- * Each command is executed in sequence, and results are collected in an array.
1290
- *
1291
- * @returns Resolves to an array containing the results of each queued command.
1242
+ * A simple promise cache wrapper.
1243
+ * @param key Cache key.
1244
+ * @param delegate The function to execute if the key is not in the cache.
1245
+ * @param ttlInSeconds Time to live in seconds.
1246
+ * @param ttlKeyInSeconds The key in the response object that contains the TTL.
1247
+ * @returns The result of the delegate function.
1292
1248
  */
1293
- async exec() {
1294
- return Promise.all([...this.commands].map((cmd) => cmd()));
1249
+ async wrap(key, delegate, ttlInSeconds, ttlKeyInSeconds) {
1250
+ const now = Date.now();
1251
+ const effectiveKey = this.caseSensitive ? key : key.toLowerCase();
1252
+ let effectiveTTL = ttlInSeconds ?? this.ttl;
1253
+ try {
1254
+ const cached = await this.persistor.get(effectiveKey);
1255
+ if (cached) {
1256
+ if (!ttlKeyInSeconds && cached.ttl !== effectiveTTL) {
1257
+ this.logger?.error(
1258
+ "WARNING: TTL mismatch for key. It is recommended to use the same TTL for the same key."
1259
+ );
1260
+ }
1261
+ return cached.value;
1262
+ }
1263
+ } catch (err) {
1264
+ const error = err;
1265
+ if (!this.fallbackToFunction) {
1266
+ throw error;
1267
+ }
1268
+ this.logger?.error(
1269
+ "redis error, falling back to function execution",
1270
+ error instanceof Error ? error.message : String(error)
1271
+ );
1272
+ }
1273
+ const response = await delegate();
1274
+ if (ttlKeyInSeconds) {
1275
+ const responseDict = response;
1276
+ const responseTTL = Number(responseDict[ttlKeyInSeconds]);
1277
+ effectiveTTL = responseTTL || effectiveTTL;
1278
+ }
1279
+ try {
1280
+ await this.persistor.set(effectiveKey, {
1281
+ value: response,
1282
+ timestamp: now,
1283
+ ttl: effectiveTTL
1284
+ });
1285
+ } catch (err) {
1286
+ const error = err;
1287
+ console.error("failed to cache result", error.message);
1288
+ }
1289
+ return response;
1295
1290
  }
1296
1291
  };
1297
1292