@winible/winible-typed 2.109.0 → 2.111.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.
|
@@ -26,6 +26,8 @@ class CacheService {
|
|
|
26
26
|
constructor() {
|
|
27
27
|
this.client = null;
|
|
28
28
|
this.errorHandler = null;
|
|
29
|
+
this.isConnected = false; // Track connection state
|
|
30
|
+
this.hasConnectedBefore = false; // Track if we've ever connected
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Set a custom error handler for capturing errors to Sentry or other services
|
|
@@ -68,13 +70,31 @@ class CacheService {
|
|
|
68
70
|
const username = process.env.ELASTICACHE_USERNAME;
|
|
69
71
|
const password = process.env.ELASTICACHE_PASSWORD;
|
|
70
72
|
if (!host) {
|
|
71
|
-
|
|
73
|
+
const error = new Error("ELASTICACHE_HOST environment variable is required");
|
|
74
|
+
this.handleError(error, null, {
|
|
75
|
+
method: "CacheService.initialize",
|
|
76
|
+
action: "validation_error",
|
|
77
|
+
additionalData: { message: "Missing ELASTICACHE_HOST" },
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
72
80
|
}
|
|
73
81
|
if (!username) {
|
|
74
|
-
|
|
82
|
+
const error = new Error("ELASTICACHE_USERNAME environment variable is required");
|
|
83
|
+
this.handleError(error, null, {
|
|
84
|
+
method: "CacheService.initialize",
|
|
85
|
+
action: "validation_error",
|
|
86
|
+
additionalData: { message: "Missing ELASTICACHE_USERNAME" },
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
75
89
|
}
|
|
76
90
|
if (!password) {
|
|
77
|
-
|
|
91
|
+
const error = new Error("ELASTICACHE_PASSWORD environment variable is required");
|
|
92
|
+
this.handleError(error, null, {
|
|
93
|
+
method: "CacheService.initialize",
|
|
94
|
+
action: "validation_error",
|
|
95
|
+
additionalData: { message: "Missing ELASTICACHE_PASSWORD" },
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
78
98
|
}
|
|
79
99
|
(0, logger_1.default)("Initializing cache client", {
|
|
80
100
|
level: "info",
|
|
@@ -88,55 +108,76 @@ class CacheService {
|
|
|
88
108
|
username,
|
|
89
109
|
password,
|
|
90
110
|
connectTimeout: 10000,
|
|
111
|
+
commandTimeout: 5000,
|
|
112
|
+
// enableOfflineQueue: false, // fail immediately when disconnected
|
|
113
|
+
maxRetriesPerRequest: 1,
|
|
91
114
|
retryStrategy: (times) => {
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
return null; // Stop retrying - this will cause the connection to fail
|
|
95
|
-
}
|
|
96
|
-
const delay = Math.min(times * 50, 2000);
|
|
115
|
+
// Retry with exponential backoff, max 30 seconds between attempts
|
|
116
|
+
const delay = Math.min(times * 1000, 30000);
|
|
97
117
|
return delay;
|
|
98
118
|
},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
? null
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.client.on("connect", () => {
|
|
106
|
-
(0, logger_1.default)("Cache client connected", {
|
|
107
|
-
level: "info",
|
|
108
|
-
method: "CacheService",
|
|
109
|
-
action: "connected",
|
|
110
|
-
});
|
|
119
|
+
tls: process.env.ELASTICACHE_SSL === "false" ||
|
|
120
|
+
process.env.ENVIRONMENT === "local"
|
|
121
|
+
? null
|
|
122
|
+
: {
|
|
123
|
+
rejectUnauthorized: true,
|
|
124
|
+
},
|
|
111
125
|
});
|
|
112
126
|
this.client.on("ready", () => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
// Only log when transitioning from disconnected to connected
|
|
128
|
+
if (!this.isConnected) {
|
|
129
|
+
this.isConnected = true;
|
|
130
|
+
// Only alert Sentry on Reconnection, not initial connection
|
|
131
|
+
if (this.hasConnectedBefore && this.errorHandler) {
|
|
132
|
+
const reconnectInfo = new Error("Cache reconnected successfully");
|
|
133
|
+
reconnectInfo.name = "CacheReconnected";
|
|
134
|
+
this.errorHandler(reconnectInfo, undefined, {
|
|
135
|
+
method: "CacheService",
|
|
136
|
+
action: "reconnected",
|
|
137
|
+
additionalData: { message: "Cache connection restored" },
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Mark that we've connected at least once
|
|
142
|
+
this.hasConnectedBefore = true;
|
|
143
|
+
(0, logger_1.default)("Cache client connected successfully", {
|
|
144
|
+
level: "info",
|
|
145
|
+
method: "CacheService",
|
|
146
|
+
action: "connected",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
118
150
|
});
|
|
119
151
|
this.client.on("error", (error) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
152
|
+
// Only report error when transitioning from connected to disconnected
|
|
153
|
+
if (this.isConnected) {
|
|
154
|
+
this.isConnected = false;
|
|
155
|
+
this.handleError(error, null, {
|
|
156
|
+
method: "CacheService",
|
|
157
|
+
action: "disconnected",
|
|
158
|
+
additionalData: {
|
|
159
|
+
message: "Cache connection lost",
|
|
160
|
+
error: error.message,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// Ignore repeated errors while already disconnected
|
|
126
165
|
});
|
|
127
166
|
this.client.on("close", () => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
167
|
+
// Only log when transitioning from connected to disconnected
|
|
168
|
+
if (this.isConnected) {
|
|
169
|
+
this.isConnected = false;
|
|
170
|
+
this.handleError(new Error("Cache Connection Closed"), null, {
|
|
171
|
+
method: "CacheService",
|
|
172
|
+
action: "disconnected",
|
|
173
|
+
additionalData: { message: "Cache client disconnected" },
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// Ignore repeated close events while already disconnected
|
|
133
177
|
});
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Connection timeout after 20 seconds")), 20000)),
|
|
138
|
-
]);
|
|
139
|
-
(0, logger_1.default)("Cache client connected successfully", {
|
|
178
|
+
await this.client.ping();
|
|
179
|
+
this.isConnected = true;
|
|
180
|
+
(0, logger_1.default)("Cache client initialized successfully", {
|
|
140
181
|
level: "info",
|
|
141
182
|
method: "CacheService.initialize",
|
|
142
183
|
action: "completed",
|
|
@@ -146,7 +187,9 @@ class CacheService {
|
|
|
146
187
|
this.handleError(error, null, {
|
|
147
188
|
method: "CacheService.initialize",
|
|
148
189
|
action: "error",
|
|
149
|
-
additionalData: {
|
|
190
|
+
additionalData: {
|
|
191
|
+
message: error.message || "Failed to initialize cache client",
|
|
192
|
+
},
|
|
150
193
|
});
|
|
151
194
|
}
|
|
152
195
|
}
|
|
@@ -156,6 +199,8 @@ class CacheService {
|
|
|
156
199
|
* @returns The cached value or null if not found
|
|
157
200
|
*/
|
|
158
201
|
async get(key) {
|
|
202
|
+
if (!this.isConnected)
|
|
203
|
+
return null;
|
|
159
204
|
try {
|
|
160
205
|
const value = await this.client.get(key);
|
|
161
206
|
if (value === null) {
|
|
@@ -179,6 +224,8 @@ class CacheService {
|
|
|
179
224
|
* @param ttl - Optional time-to-live in seconds
|
|
180
225
|
*/
|
|
181
226
|
async set(key, value, ttl) {
|
|
227
|
+
if (!this.isConnected)
|
|
228
|
+
return;
|
|
182
229
|
try {
|
|
183
230
|
const serialized = JSON.stringify(value);
|
|
184
231
|
if (ttl) {
|
|
@@ -201,6 +248,8 @@ class CacheService {
|
|
|
201
248
|
* @param key - The cache key to delete
|
|
202
249
|
*/
|
|
203
250
|
async delete(key) {
|
|
251
|
+
if (!this.isConnected)
|
|
252
|
+
return;
|
|
204
253
|
try {
|
|
205
254
|
await this.client.del(key);
|
|
206
255
|
}
|
|
@@ -219,6 +268,8 @@ class CacheService {
|
|
|
219
268
|
* @returns Array of matching keys
|
|
220
269
|
*/
|
|
221
270
|
async keys(pattern) {
|
|
271
|
+
if (!this.isConnected)
|
|
272
|
+
return [];
|
|
222
273
|
try {
|
|
223
274
|
const keys = [];
|
|
224
275
|
let cursor = "0";
|
|
@@ -244,6 +295,8 @@ class CacheService {
|
|
|
244
295
|
* @returns The total number of keys
|
|
245
296
|
*/
|
|
246
297
|
async dbsize() {
|
|
298
|
+
if (!this.isConnected)
|
|
299
|
+
return 0;
|
|
247
300
|
try {
|
|
248
301
|
const size = await this.client.dbsize();
|
|
249
302
|
return size;
|
|
@@ -263,6 +316,8 @@ class CacheService {
|
|
|
263
316
|
* @param pattern - Pattern to match (e.g., "user:*", "*:session")
|
|
264
317
|
*/
|
|
265
318
|
async clearPattern(pattern) {
|
|
319
|
+
if (!this.isConnected)
|
|
320
|
+
return;
|
|
266
321
|
try {
|
|
267
322
|
const keys = await this.keys(pattern);
|
|
268
323
|
if (keys.length > 0) {
|
|
@@ -282,7 +337,10 @@ class CacheService {
|
|
|
282
337
|
this.handleError(error, null, {
|
|
283
338
|
method: "CacheService.clearPattern",
|
|
284
339
|
action: "error",
|
|
285
|
-
additionalData: {
|
|
340
|
+
additionalData: {
|
|
341
|
+
message: "Failed to clear keys matching pattern",
|
|
342
|
+
pattern,
|
|
343
|
+
},
|
|
286
344
|
});
|
|
287
345
|
}
|
|
288
346
|
}
|
|
@@ -292,6 +350,8 @@ class CacheService {
|
|
|
292
350
|
* @returns true if the key exists, false otherwise
|
|
293
351
|
*/
|
|
294
352
|
async exists(key) {
|
|
353
|
+
if (!this.isConnected)
|
|
354
|
+
return false;
|
|
295
355
|
try {
|
|
296
356
|
const result = await this.client.exists(key);
|
|
297
357
|
return result === 1;
|
|
@@ -310,6 +370,8 @@ class CacheService {
|
|
|
310
370
|
* @param entries - Array of key-value-ttl tuples
|
|
311
371
|
*/
|
|
312
372
|
async mset(entries) {
|
|
373
|
+
if (!this.isConnected)
|
|
374
|
+
return;
|
|
313
375
|
try {
|
|
314
376
|
const pipeline = this.client.pipeline();
|
|
315
377
|
entries.forEach(({ key, value, ttl }) => {
|
|
@@ -327,7 +389,10 @@ class CacheService {
|
|
|
327
389
|
this.handleError(error, null, {
|
|
328
390
|
method: "CacheService.mset",
|
|
329
391
|
action: "error",
|
|
330
|
-
additionalData: {
|
|
392
|
+
additionalData: {
|
|
393
|
+
message: "Failed to set multiple values in cache",
|
|
394
|
+
count: entries.length,
|
|
395
|
+
},
|
|
331
396
|
});
|
|
332
397
|
}
|
|
333
398
|
}
|
|
@@ -337,15 +402,20 @@ class CacheService {
|
|
|
337
402
|
* @returns Array of cached values (null for missing keys)
|
|
338
403
|
*/
|
|
339
404
|
async mget(keys) {
|
|
405
|
+
if (!this.isConnected)
|
|
406
|
+
return keys.map(() => null);
|
|
340
407
|
try {
|
|
341
408
|
const values = await this.client.mget(...keys);
|
|
342
|
-
return values.map((value) => value ? JSON.parse(value) : null);
|
|
409
|
+
return values.map((value) => (value ? JSON.parse(value) : null));
|
|
343
410
|
}
|
|
344
411
|
catch (error) {
|
|
345
412
|
this.handleError(error, null, {
|
|
346
413
|
method: "CacheService.mget",
|
|
347
414
|
action: "error",
|
|
348
|
-
additionalData: {
|
|
415
|
+
additionalData: {
|
|
416
|
+
message: "Failed to get multiple values from cache",
|
|
417
|
+
count: keys.length,
|
|
418
|
+
},
|
|
349
419
|
});
|
|
350
420
|
return keys.map(() => null);
|
|
351
421
|
}
|
|
@@ -357,6 +427,8 @@ class CacheService {
|
|
|
357
427
|
* @returns The new value after increment
|
|
358
428
|
*/
|
|
359
429
|
async increment(key, increment = 1) {
|
|
430
|
+
if (!this.isConnected)
|
|
431
|
+
return 0;
|
|
360
432
|
try {
|
|
361
433
|
return await this.client.incrby(key, increment);
|
|
362
434
|
}
|
|
@@ -364,7 +436,11 @@ class CacheService {
|
|
|
364
436
|
this.handleError(error, null, {
|
|
365
437
|
method: "CacheService.increment",
|
|
366
438
|
action: "error",
|
|
367
|
-
additionalData: {
|
|
439
|
+
additionalData: {
|
|
440
|
+
message: "Failed to increment value in cache",
|
|
441
|
+
key,
|
|
442
|
+
increment,
|
|
443
|
+
},
|
|
368
444
|
});
|
|
369
445
|
return 0;
|
|
370
446
|
}
|
|
@@ -376,6 +452,8 @@ class CacheService {
|
|
|
376
452
|
* @returns The new value after decrement
|
|
377
453
|
*/
|
|
378
454
|
async decrement(key, decrement = 1) {
|
|
455
|
+
if (!this.isConnected)
|
|
456
|
+
return 0;
|
|
379
457
|
try {
|
|
380
458
|
return await this.client.decrby(key, decrement);
|
|
381
459
|
}
|
|
@@ -383,7 +461,11 @@ class CacheService {
|
|
|
383
461
|
this.handleError(error, null, {
|
|
384
462
|
method: "CacheService.decrement",
|
|
385
463
|
action: "error",
|
|
386
|
-
additionalData: {
|
|
464
|
+
additionalData: {
|
|
465
|
+
message: "Failed to decrement value in cache",
|
|
466
|
+
key,
|
|
467
|
+
decrement,
|
|
468
|
+
},
|
|
387
469
|
});
|
|
388
470
|
return 0;
|
|
389
471
|
}
|
|
@@ -395,6 +477,8 @@ class CacheService {
|
|
|
395
477
|
* @returns true if the timeout was set, false if key doesn't exist
|
|
396
478
|
*/
|
|
397
479
|
async expire(key, ttl) {
|
|
480
|
+
if (!this.isConnected)
|
|
481
|
+
return false;
|
|
398
482
|
try {
|
|
399
483
|
const result = await this.client.expire(key, ttl);
|
|
400
484
|
return result === 1;
|
|
@@ -403,7 +487,11 @@ class CacheService {
|
|
|
403
487
|
this.handleError(error, null, {
|
|
404
488
|
method: "CacheService.expire",
|
|
405
489
|
action: "error",
|
|
406
|
-
additionalData: {
|
|
490
|
+
additionalData: {
|
|
491
|
+
message: "Failed to set expiration on key",
|
|
492
|
+
key,
|
|
493
|
+
ttl,
|
|
494
|
+
},
|
|
407
495
|
});
|
|
408
496
|
return false;
|
|
409
497
|
}
|
|
@@ -414,6 +502,8 @@ class CacheService {
|
|
|
414
502
|
* @returns TTL in seconds, -1 if key exists but has no expire, -2 if key doesn't exist
|
|
415
503
|
*/
|
|
416
504
|
async ttl(key) {
|
|
505
|
+
if (!this.isConnected)
|
|
506
|
+
return -2;
|
|
417
507
|
try {
|
|
418
508
|
return await this.client.ttl(key);
|
|
419
509
|
}
|
|
@@ -426,6 +516,58 @@ class CacheService {
|
|
|
426
516
|
return -2;
|
|
427
517
|
}
|
|
428
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Atomically set a value only if the key doesn't exist (SETNX)
|
|
521
|
+
* Uses Redis SET NX EX command which is atomic - no race conditions possible.
|
|
522
|
+
*
|
|
523
|
+
* @param key - The cache key
|
|
524
|
+
* @param value - The value to cache (will be JSON serialized)
|
|
525
|
+
* @param ttl - Time-to-live in seconds (required for safety)
|
|
526
|
+
* @returns true if the value was set (key didn't exist), false if key already exists
|
|
527
|
+
*
|
|
528
|
+
* @example
|
|
529
|
+
* // Single-use token implementation
|
|
530
|
+
* const wasSet = await cacheService.setIfNotExists('magic_link:abc123', { userId: '123' }, 300);
|
|
531
|
+
* if (!wasSet) {
|
|
532
|
+
* throw new Error('Token already used');
|
|
533
|
+
* }
|
|
534
|
+
*
|
|
535
|
+
* @example
|
|
536
|
+
* // Distributed lock
|
|
537
|
+
* const lockAcquired = await cacheService.setIfNotExists('lock:resource_id', { owner: 'worker-1' }, 30);
|
|
538
|
+
* if (lockAcquired) {
|
|
539
|
+
* try {
|
|
540
|
+
* // Do work while holding lock
|
|
541
|
+
* } finally {
|
|
542
|
+
* await cacheService.delete('lock:resource_id');
|
|
543
|
+
* }
|
|
544
|
+
* }
|
|
545
|
+
*/
|
|
546
|
+
async setIfNotExists(key, value, ttl) {
|
|
547
|
+
if (!this.isConnected)
|
|
548
|
+
return false;
|
|
549
|
+
try {
|
|
550
|
+
const serialized = JSON.stringify(value);
|
|
551
|
+
// Atomic operation: SET key value EX ttl NX
|
|
552
|
+
// EX = Set expiry in seconds
|
|
553
|
+
// NX = Only set if key doesn't exist
|
|
554
|
+
// Returns "OK" if set successfully, null if key already exists
|
|
555
|
+
const result = await this.client.set(key, serialized, "EX", ttl, "NX");
|
|
556
|
+
return result === "OK";
|
|
557
|
+
}
|
|
558
|
+
catch (error) {
|
|
559
|
+
this.handleError(error, null, {
|
|
560
|
+
method: "CacheService.setIfNotExists",
|
|
561
|
+
action: "error",
|
|
562
|
+
additionalData: {
|
|
563
|
+
message: "Failed to set value atomically",
|
|
564
|
+
key,
|
|
565
|
+
ttl,
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
429
571
|
/**
|
|
430
572
|
* Disconnect the cache client
|
|
431
573
|
* Use this for graceful shutdown
|
|
@@ -440,8 +582,7 @@ class CacheService {
|
|
|
440
582
|
this.client.disconnect();
|
|
441
583
|
}
|
|
442
584
|
this.client = null;
|
|
443
|
-
|
|
444
|
-
// this.initPromise = null;
|
|
585
|
+
this.isConnected = false;
|
|
445
586
|
(0, logger_1.default)("Cache client disconnected", {
|
|
446
587
|
level: "info",
|
|
447
588
|
method: "CacheService.disconnect",
|
|
@@ -450,6 +591,8 @@ class CacheService {
|
|
|
450
591
|
}
|
|
451
592
|
}
|
|
452
593
|
async ping() {
|
|
594
|
+
if (!this.isConnected)
|
|
595
|
+
return "";
|
|
453
596
|
try {
|
|
454
597
|
const result = await this.client.ping();
|
|
455
598
|
return result;
|
|
@@ -460,6 +603,7 @@ class CacheService {
|
|
|
460
603
|
action: "error",
|
|
461
604
|
additionalData: { message: "Failed to ping cache client" },
|
|
462
605
|
});
|
|
606
|
+
return "";
|
|
463
607
|
}
|
|
464
608
|
}
|
|
465
609
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cacheService.js","sourceRoot":"","sources":["../../../utils/cacheService/cacheService.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cacheService.js","sourceRoot":"","sources":["../../../utils/cacheService/cacheService.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA4B;AAC5B,uDAA+B;AAe/B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,YAAY;IAAlB;QACU,WAAM,GAAiB,IAAI,CAAC;QAC5B,iBAAY,GAAQ,IAAI,CAAC;QACzB,gBAAW,GAAY,KAAK,CAAC,CAAC,yBAAyB;QACvD,uBAAkB,GAAY,KAAK,CAAC,CAAC,gCAAgC;IA0mB/E,CAAC;IAxmBC;;;OAGG;IACH,eAAe,CAAC,OAAqD;QACnE,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACK,WAAW,CACjB,KAAU,EACV,OAAe,EACf,OAIC;QAED,IAAI,IAAI,CAAC,YAAY,EAAE;YACrB,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;YAC5B,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;SAC9C;aAAM;YACL,gDAAgD;YAChD,IAAA,gBAAM,EAAC,OAAO,EAAE;gBACd,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAAI,cAAc;gBACzC,MAAM,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAAI,OAAO;gBAClC,KAAK;gBACL,cAAc,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,cAAc;aACxC,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,YAA2B;QAC1C,IAAI,YAAY,EAAE;YAChB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;SAClC;QACD,IAAI;YACF,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;YAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;YAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;YAClD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;YAElD,IAAI,CAAC,IAAI,EAAE;gBACT,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,mDAAmD,CACpD,CAAC;gBACF,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;oBAC5B,MAAM,EAAE,yBAAyB;oBACjC,MAAM,EAAE,kBAAkB;oBAC1B,cAAc,EAAE,EAAE,OAAO,EAAE,0BAA0B,EAAE;iBACxD,CAAC,CAAC;gBACH,OAAO;aACR;YACD,IAAI,CAAC,QAAQ,EAAE;gBACb,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,uDAAuD,CACxD,CAAC;gBACF,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;oBAC5B,MAAM,EAAE,yBAAyB;oBACjC,MAAM,EAAE,kBAAkB;oBAC1B,cAAc,EAAE,EAAE,OAAO,EAAE,8BAA8B,EAAE;iBAC5D,CAAC,CAAC;gBACH,OAAO;aACR;YACD,IAAI,CAAC,QAAQ,EAAE;gBACb,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,uDAAuD,CACxD,CAAC;gBACF,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;oBAC5B,MAAM,EAAE,yBAAyB;oBACjC,MAAM,EAAE,kBAAkB;oBAC1B,cAAc,EAAE,EAAE,OAAO,EAAE,8BAA8B,EAAE;iBAC5D,CAAC,CAAC;gBACH,OAAO;aACR;YAED,IAAA,gBAAM,EAAC,2BAA2B,EAAE;gBAClC,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,yBAAyB;gBACjC,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;aACzC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,GAAG,IAAI,iBAAK,CAAC;gBACtB,IAAI;gBACJ,IAAI;gBACJ,QAAQ;gBACR,QAAQ;gBACR,cAAc,EAAE,KAAK;gBACrB,cAAc,EAAE,IAAI;gBACpB,mEAAmE;gBACnE,oBAAoB,EAAE,CAAC;gBACvB,aAAa,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC/B,kEAAkE;oBAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;oBAC5C,OAAO,KAAK,CAAC;gBACf,CAAC;gBACD,GAAG,EACD,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO;oBACvC,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,OAAO;oBACjC,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC;wBACE,kBAAkB,EAAE,IAAI;qBACzB;aACR,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAC3B,6DAA6D;gBAC7D,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;oBACrB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBAExB,4DAA4D;oBAC5D,IAAI,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,YAAY,EAAE;wBAChD,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;wBAClE,aAAa,CAAC,IAAI,GAAG,kBAAkB,CAAC;wBAExC,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,SAAS,EAAE;4BAC1C,MAAM,EAAE,cAAc;4BACtB,MAAM,EAAE,aAAa;4BACrB,cAAc,EAAE,EAAE,OAAO,EAAE,2BAA2B,EAAE;yBACzD,CAAC,CAAC;qBACJ;yBAAM;wBACL,0CAA0C;wBAC1C,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;wBAE/B,IAAA,gBAAM,EAAC,qCAAqC,EAAE;4BAC5C,KAAK,EAAE,MAAM;4BACb,MAAM,EAAE,cAAc;4BACtB,MAAM,EAAE,WAAW;yBACpB,CAAC,CAAC;qBACJ;iBACF;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAChC,sEAAsE;gBACtE,IAAI,IAAI,CAAC,WAAW,EAAE;oBACpB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;oBACzB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;wBAC5B,MAAM,EAAE,cAAc;wBACtB,MAAM,EAAE,cAAc;wBACtB,cAAc,EAAE;4BACd,OAAO,EAAE,uBAAuB;4BAChC,KAAK,EAAE,KAAK,CAAC,OAAO;yBACrB;qBACF,CAAC,CAAC;iBACJ;gBACD,oDAAoD;YACtD,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAC3B,6DAA6D;gBAC7D,IAAI,IAAI,CAAC,WAAW,EAAE;oBACpB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;oBACzB,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE;wBAC3D,MAAM,EAAE,cAAc;wBACtB,MAAM,EAAE,cAAc;wBACtB,cAAc,EAAE,EAAE,OAAO,EAAE,2BAA2B,EAAE;qBACzD,CAAC,CAAC;iBACJ;gBACD,0DAA0D;YAC5D,CAAC,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YAExB,IAAA,gBAAM,EAAC,uCAAuC,EAAE;gBAC9C,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,yBAAyB;gBACjC,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;SACJ;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,yBAAyB;gBACjC,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,mCAAmC;iBAC9D;aACF,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,CAAI,GAAW;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAEnC,IAAI;YACF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1C,IAAI,KAAK,KAAK,IAAI,EAAE;gBAClB,OAAO,IAAI,CAAC;aACb;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC;SAC/B;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,kBAAkB;gBAC1B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,gCAAgC,EAAE,GAAG,EAAE;aACnE,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;SACb;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,GAAY;QAC9C,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAE9B,IAAI;YACF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,GAAG,EAAE;gBACP,MAAM,IAAI,CAAC,MAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;aAChD;iBAAM;gBACL,MAAM,IAAI,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;aACzC;SACF;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,kBAAkB;gBAC1B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,8BAA8B,EAAE,GAAG,EAAE,GAAG,EAAE;aACtE,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAE9B,IAAI;YACF,MAAM,IAAI,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SAC7B;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,qBAAqB;gBAC7B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,iCAAiC,EAAE,GAAG,EAAE;aACpE,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,IAAI,CAAC,OAAe;QACxB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,EAAE,CAAC;QAEjC,IAAI;YACF,MAAM,IAAI,GAAa,EAAE,CAAC;YAC1B,IAAI,MAAM,GAAG,GAAG,CAAC;YAEjB,GAAG;gBACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,IAAI,CACpC,MAAM,EACN,OAAO,EACP,OAAO,EACP,OAAO,EACP,GAAG,CACJ,CAAC;gBACF,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBACnB,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;aACzB,QAAQ,MAAM,KAAK,GAAG,EAAE;YAEzB,OAAO,IAAI,CAAC;SACb;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,mBAAmB;gBAC3B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,OAAO,EAAE;aACtE,CAAC,CAAC;YACH,OAAO,EAAE,CAAC;SACX;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,CAAC,CAAC;QAEhC,IAAI;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,MAAM,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;SACb;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,qBAAqB;gBAC7B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,6BAA6B,EAAE;aAC3D,CAAC,CAAC;YACH,OAAO,CAAC,CAAC;SACV;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,OAAe;QAChC,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAE9B,IAAI;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;gBACnB,qDAAqD;gBACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAO,CAAC,QAAQ,EAAE,CAAC;gBACzC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;gBACzC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACtB,IAAA,gBAAM,EAAC,+BAA+B,EAAE;oBACtC,KAAK,EAAE,MAAM;oBACb,MAAM,EAAE,2BAA2B;oBACnC,MAAM,EAAE,WAAW;oBACnB,cAAc,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;iBAChD,CAAC,CAAC;aACJ;SACF;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,2BAA2B;gBACnC,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,uCAAuC;oBAChD,OAAO;iBACR;aACF,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;QAEpC,IAAI;YACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC9C,OAAO,MAAM,KAAK,CAAC,CAAC;SACrB;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,qBAAqB;gBAC7B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,GAAG,EAAE;aAClE,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;SACd;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CACR,OAAyD;QAEzD,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAE9B,IAAI;YACF,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAO,CAAC,QAAQ,EAAE,CAAC;YACzC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE;gBACtC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBACzC,IAAI,GAAG,EAAE;oBACP,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;iBACtC;qBAAM;oBACL,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;iBAC/B;YACH,CAAC,CAAC,CAAC;YACH,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;SACvB;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,mBAAmB;gBAC3B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,wCAAwC;oBACjD,KAAK,EAAE,OAAO,CAAC,MAAM;iBACtB;aACF,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAI,IAAc;QAC1B,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAEnD,IAAI;YACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAChD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;SACzE;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,mBAAmB;gBAC3B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,0CAA0C;oBACnD,KAAK,EAAE,IAAI,CAAC,MAAM;iBACnB;aACF,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;SAC7B;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,YAAoB,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,CAAC,CAAC;QAEhC,IAAI;YACF,OAAO,MAAM,IAAI,CAAC,MAAO,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;SAClD;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,wBAAwB;gBAChC,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,oCAAoC;oBAC7C,GAAG;oBACH,SAAS;iBACV;aACF,CAAC,CAAC;YACH,OAAO,CAAC,CAAC;SACV;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,YAAoB,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,CAAC,CAAC;QAEhC,IAAI;YACF,OAAO,MAAM,IAAI,CAAC,MAAO,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;SAClD;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,wBAAwB;gBAChC,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,oCAAoC;oBAC7C,GAAG;oBACH,SAAS;iBACV;aACF,CAAC,CAAC;YACH,OAAO,CAAC,CAAC;SACV;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,GAAW;QACnC,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;QAEpC,IAAI;YACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACnD,OAAO,MAAM,KAAK,CAAC,CAAC;SACrB;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,qBAAqB;gBAC7B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,iCAAiC;oBAC1C,GAAG;oBACH,GAAG;iBACJ;aACF,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;SACd;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,CAAC,CAAC,CAAC;QAEjC,IAAI;YACF,OAAO,MAAM,IAAI,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACpC;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,kBAAkB;gBAC1B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,2BAA2B,EAAE,GAAG,EAAE;aAC9D,CAAC,CAAC;YACH,OAAO,CAAC,CAAC,CAAC;SACX;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,KAAK,CAAC,cAAc,CAClB,GAAW,EACX,KAAQ,EACR,GAAW;QAEX,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;QAEpC,IAAI;YACF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAEzC,4CAA4C;YAC5C,6BAA6B;YAC7B,qCAAqC;YACrC,+DAA+D;YAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAExE,OAAO,MAAM,KAAK,IAAI,CAAC;SACxB;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,6BAA6B;gBACrC,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE;oBACd,OAAO,EAAE,gCAAgC;oBACzC,GAAG;oBACH,GAAG;iBACJ;aACF,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;SACd;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,IAAI;gBACF,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;aAC1B;YAAC,OAAO,KAAK,EAAE;gBACd,iCAAiC;gBACjC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;aAC1B;YACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,IAAA,gBAAM,EAAC,2BAA2B,EAAE;gBAClC,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,yBAAyB;gBACjC,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;SACJ;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,EAAE,CAAC;QAEjC,IAAI;YACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,IAAI,EAAE,CAAC;YACzC,OAAO,MAAM,CAAC;SACf;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;gBAC5B,MAAM,EAAE,mBAAmB;gBAC3B,MAAM,EAAE,OAAO;gBACf,cAAc,EAAE,EAAE,OAAO,EAAE,6BAA6B,EAAE;aAC3D,CAAC,CAAC;YACH,OAAO,EAAE,CAAC;SACX;IACH,CAAC;CACF;AAED,4BAA4B;AACf,QAAA,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;AAE/C,+CAA+C;AAC/C,kBAAe,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -52,7 +52,7 @@ describe('CacheService', () => {
|
|
|
52
52
|
undefined,
|
|
53
53
|
expect.objectContaining({
|
|
54
54
|
method: 'CacheService.initialize',
|
|
55
|
-
action: '
|
|
55
|
+
action: 'validation_error',
|
|
56
56
|
additionalData: expect.objectContaining({
|
|
57
57
|
message: expect.any(String),
|
|
58
58
|
}),
|
|
@@ -73,7 +73,7 @@ describe('CacheService', () => {
|
|
|
73
73
|
undefined,
|
|
74
74
|
expect.objectContaining({
|
|
75
75
|
method: 'CacheService.initialize',
|
|
76
|
-
action: '
|
|
76
|
+
action: 'validation_error',
|
|
77
77
|
additionalData: expect.objectContaining({
|
|
78
78
|
message: expect.any(String),
|
|
79
79
|
}),
|
|
@@ -94,7 +94,7 @@ describe('CacheService', () => {
|
|
|
94
94
|
undefined,
|
|
95
95
|
expect.objectContaining({
|
|
96
96
|
method: 'CacheService.initialize',
|
|
97
|
-
action: '
|
|
97
|
+
action: 'validation_error',
|
|
98
98
|
additionalData: expect.objectContaining({
|
|
99
99
|
message: expect.any(String),
|
|
100
100
|
}),
|
|
@@ -446,6 +446,142 @@ describe('CacheService', () => {
|
|
|
446
446
|
});
|
|
447
447
|
});
|
|
448
448
|
|
|
449
|
+
describe('Atomic Operations', () => {
|
|
450
|
+
describe('setIfNotExists', () => {
|
|
451
|
+
it('should set value if key does not exist', async () => {
|
|
452
|
+
const wasSet = await cacheService.setIfNotExists('atomic:1', { data: 'value1' }, 300);
|
|
453
|
+
expect(wasSet).toBe(true);
|
|
454
|
+
|
|
455
|
+
const value = await cacheService.get('atomic:1');
|
|
456
|
+
expect(value).toEqual({ data: 'value1' });
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should return false if key already exists', async () => {
|
|
460
|
+
// Set initial value
|
|
461
|
+
await cacheService.set('atomic:2', { data: 'original' }, 300);
|
|
462
|
+
|
|
463
|
+
// Try to set with setIfNotExists
|
|
464
|
+
const wasSet = await cacheService.setIfNotExists('atomic:2', { data: 'new' }, 300);
|
|
465
|
+
expect(wasSet).toBe(false);
|
|
466
|
+
|
|
467
|
+
// Verify original value unchanged
|
|
468
|
+
const value = await cacheService.get('atomic:2');
|
|
469
|
+
expect(value).toEqual({ data: 'original' });
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('should set value with TTL', async () => {
|
|
473
|
+
const wasSet = await cacheService.setIfNotExists('atomic:ttl', { data: 'temp' }, 1);
|
|
474
|
+
expect(wasSet).toBe(true);
|
|
475
|
+
|
|
476
|
+
// Verify key exists immediately
|
|
477
|
+
let value = await cacheService.get('atomic:ttl');
|
|
478
|
+
expect(value).toEqual({ data: 'temp' });
|
|
479
|
+
|
|
480
|
+
// Wait for expiry
|
|
481
|
+
await new Promise(resolve => setTimeout(resolve, 1100));
|
|
482
|
+
|
|
483
|
+
// Verify key is gone
|
|
484
|
+
value = await cacheService.get('atomic:ttl');
|
|
485
|
+
expect(value).toBeNull();
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('should handle concurrent requests atomically', async () => {
|
|
489
|
+
const key = 'atomic:concurrent';
|
|
490
|
+
const promises = Array(10)
|
|
491
|
+
.fill(null)
|
|
492
|
+
.map((_, i) => cacheService.setIfNotExists(key, { request: i }, 300));
|
|
493
|
+
|
|
494
|
+
const results = await Promise.all(promises);
|
|
495
|
+
const successCount = results.filter(r => r === true).length;
|
|
496
|
+
|
|
497
|
+
// Only ONE request should succeed
|
|
498
|
+
expect(successCount).toBe(1);
|
|
499
|
+
|
|
500
|
+
// Verify a value was set
|
|
501
|
+
const value = await cacheService.get<{ request: number }>(key);
|
|
502
|
+
expect(value).toHaveProperty('request');
|
|
503
|
+
expect(typeof value!.request).toBe('number');
|
|
504
|
+
expect(value!.request).toBeGreaterThanOrEqual(0);
|
|
505
|
+
expect(value!.request).toBeLessThan(10);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('should allow setting after key expires', async () => {
|
|
509
|
+
// Set with 1 second TTL
|
|
510
|
+
const firstSet = await cacheService.setIfNotExists('atomic:expire', { data: 'first' }, 1);
|
|
511
|
+
expect(firstSet).toBe(true);
|
|
512
|
+
|
|
513
|
+
// Wait for expiry
|
|
514
|
+
await new Promise(resolve => setTimeout(resolve, 1100));
|
|
515
|
+
|
|
516
|
+
// Should be able to set again after expiration
|
|
517
|
+
const secondSet = await cacheService.setIfNotExists('atomic:expire', { data: 'second' }, 300);
|
|
518
|
+
expect(secondSet).toBe(true);
|
|
519
|
+
|
|
520
|
+
const value = await cacheService.get('atomic:expire');
|
|
521
|
+
expect(value).toEqual({ data: 'second' });
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('should work with string values', async () => {
|
|
525
|
+
const wasSet = await cacheService.setIfNotExists('atomic:string', 'simple-value', 300);
|
|
526
|
+
expect(wasSet).toBe(true);
|
|
527
|
+
|
|
528
|
+
const value = await cacheService.get<string>('atomic:string');
|
|
529
|
+
expect(value).toBe('simple-value');
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it('should work with numeric values', async () => {
|
|
533
|
+
const wasSet = await cacheService.setIfNotExists('atomic:number', 42, 300);
|
|
534
|
+
expect(wasSet).toBe(true);
|
|
535
|
+
|
|
536
|
+
const value = await cacheService.get<number>('atomic:number');
|
|
537
|
+
expect(value).toBe(42);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('should work with array values', async () => {
|
|
541
|
+
const arr = [1, 2, 3, 4, 5];
|
|
542
|
+
const wasSet = await cacheService.setIfNotExists('atomic:array', arr, 300);
|
|
543
|
+
expect(wasSet).toBe(true);
|
|
544
|
+
|
|
545
|
+
const value = await cacheService.get<number[]>('atomic:array');
|
|
546
|
+
expect(value).toEqual(arr);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('should work with complex nested objects', async () => {
|
|
550
|
+
const complex = {
|
|
551
|
+
userId: 'user-123',
|
|
552
|
+
metadata: {
|
|
553
|
+
usedAt: Date.now(),
|
|
554
|
+
source: 'mobile',
|
|
555
|
+
nested: {
|
|
556
|
+
deep: {
|
|
557
|
+
value: true,
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const wasSet = await cacheService.setIfNotExists('atomic:complex', complex, 300);
|
|
564
|
+
expect(wasSet).toBe(true);
|
|
565
|
+
|
|
566
|
+
const value = await cacheService.get('atomic:complex');
|
|
567
|
+
expect(value).toEqual(complex);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('should return false when trying to overwrite existing key with different TTL', async () => {
|
|
571
|
+
// Set initial value with 300s TTL
|
|
572
|
+
await cacheService.set('atomic:overwrite', { data: 'original' }, 300);
|
|
573
|
+
|
|
574
|
+
// Try to set with different TTL
|
|
575
|
+
const wasSet = await cacheService.setIfNotExists('atomic:overwrite', { data: 'new' }, 600);
|
|
576
|
+
expect(wasSet).toBe(false);
|
|
577
|
+
|
|
578
|
+
// Verify original value and TTL unchanged
|
|
579
|
+
const value = await cacheService.get('atomic:overwrite');
|
|
580
|
+
expect(value).toEqual({ data: 'original' });
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|
|
449
585
|
describe('Connection Management', () => {
|
|
450
586
|
it('should disconnect gracefully', async () => {
|
|
451
587
|
await cacheService.set('test', 'value');
|
|
@@ -33,12 +33,14 @@ type ErrorHandler = (
|
|
|
33
33
|
class CacheService {
|
|
34
34
|
private client: Redis | null = null;
|
|
35
35
|
private errorHandler: any = null;
|
|
36
|
+
private isConnected: boolean = false; // Track connection state
|
|
37
|
+
private hasConnectedBefore: boolean = false; // Track if we've ever connected
|
|
36
38
|
|
|
37
39
|
/**
|
|
38
40
|
* Set a custom error handler for capturing errors to Sentry or other services
|
|
39
41
|
* @param handler - Function to handle errors
|
|
40
42
|
*/
|
|
41
|
-
setErrorHandler(handler: any): void {
|
|
43
|
+
setErrorHandler(handler: (error: any, req: any, context: any) => void): void {
|
|
42
44
|
this.errorHandler = handler;
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -56,7 +58,7 @@ class CacheService {
|
|
|
56
58
|
}
|
|
57
59
|
): void {
|
|
58
60
|
if (this.errorHandler) {
|
|
59
|
-
error.name = context.method
|
|
61
|
+
error.name = context.method;
|
|
60
62
|
this.errorHandler(error, undefined, context);
|
|
61
63
|
} else {
|
|
62
64
|
// Fallback to logger if no error handler is set
|
|
@@ -85,13 +87,37 @@ class CacheService {
|
|
|
85
87
|
const password = process.env.ELASTICACHE_PASSWORD;
|
|
86
88
|
|
|
87
89
|
if (!host) {
|
|
88
|
-
|
|
90
|
+
const error = new Error(
|
|
91
|
+
"ELASTICACHE_HOST environment variable is required"
|
|
92
|
+
);
|
|
93
|
+
this.handleError(error, null, {
|
|
94
|
+
method: "CacheService.initialize",
|
|
95
|
+
action: "validation_error",
|
|
96
|
+
additionalData: { message: "Missing ELASTICACHE_HOST" },
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
89
99
|
}
|
|
90
100
|
if (!username) {
|
|
91
|
-
|
|
101
|
+
const error = new Error(
|
|
102
|
+
"ELASTICACHE_USERNAME environment variable is required"
|
|
103
|
+
);
|
|
104
|
+
this.handleError(error, null, {
|
|
105
|
+
method: "CacheService.initialize",
|
|
106
|
+
action: "validation_error",
|
|
107
|
+
additionalData: { message: "Missing ELASTICACHE_USERNAME" },
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
92
110
|
}
|
|
93
111
|
if (!password) {
|
|
94
|
-
|
|
112
|
+
const error = new Error(
|
|
113
|
+
"ELASTICACHE_PASSWORD environment variable is required"
|
|
114
|
+
);
|
|
115
|
+
this.handleError(error, null, {
|
|
116
|
+
method: "CacheService.initialize",
|
|
117
|
+
action: "validation_error",
|
|
118
|
+
additionalData: { message: "Missing ELASTICACHE_PASSWORD" },
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
95
121
|
}
|
|
96
122
|
|
|
97
123
|
logger("Initializing cache client", {
|
|
@@ -106,64 +132,85 @@ class CacheService {
|
|
|
106
132
|
port,
|
|
107
133
|
username,
|
|
108
134
|
password,
|
|
109
|
-
connectTimeout: 10000, //
|
|
135
|
+
connectTimeout: 10000, // 10s connection timeout
|
|
136
|
+
commandTimeout: 5000, // fail commands taking longer than 5s
|
|
137
|
+
// enableOfflineQueue: false, // fail immediately when disconnected
|
|
138
|
+
maxRetriesPerRequest: 1, // fail fast with only 1 retry attempt
|
|
110
139
|
retryStrategy: (times: number) => {
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
return null; // Stop retrying - this will cause the connection to fail
|
|
114
|
-
}
|
|
115
|
-
const delay = Math.min(times * 50, 2000);
|
|
140
|
+
// Retry with exponential backoff, max 30 seconds between attempts
|
|
141
|
+
const delay = Math.min(times * 1000, 30000);
|
|
116
142
|
return delay;
|
|
117
143
|
},
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.client.on("connect", () => {
|
|
126
|
-
logger("Cache client connected", {
|
|
127
|
-
level: "info",
|
|
128
|
-
method: "CacheService",
|
|
129
|
-
action: "connected",
|
|
130
|
-
});
|
|
144
|
+
tls:
|
|
145
|
+
process.env.ELASTICACHE_SSL === "false" ||
|
|
146
|
+
process.env.ENVIRONMENT === "local"
|
|
147
|
+
? null
|
|
148
|
+
: {
|
|
149
|
+
rejectUnauthorized: true,
|
|
150
|
+
},
|
|
131
151
|
});
|
|
132
152
|
|
|
133
153
|
this.client.on("ready", () => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
154
|
+
// Only log when transitioning from disconnected to connected
|
|
155
|
+
if (!this.isConnected) {
|
|
156
|
+
this.isConnected = true;
|
|
157
|
+
|
|
158
|
+
// Only alert Sentry on Reconnection, not initial connection
|
|
159
|
+
if (this.hasConnectedBefore && this.errorHandler) {
|
|
160
|
+
const reconnectInfo = new Error("Cache reconnected successfully");
|
|
161
|
+
reconnectInfo.name = "CacheReconnected";
|
|
162
|
+
|
|
163
|
+
this.errorHandler(reconnectInfo, undefined, {
|
|
164
|
+
method: "CacheService",
|
|
165
|
+
action: "reconnected",
|
|
166
|
+
additionalData: { message: "Cache connection restored" },
|
|
167
|
+
});
|
|
168
|
+
} else {
|
|
169
|
+
// Mark that we've connected at least once
|
|
170
|
+
this.hasConnectedBefore = true;
|
|
171
|
+
|
|
172
|
+
logger("Cache client connected successfully", {
|
|
173
|
+
level: "info",
|
|
174
|
+
method: "CacheService",
|
|
175
|
+
action: "connected",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
139
179
|
});
|
|
140
180
|
|
|
141
181
|
this.client.on("error", (error) => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
182
|
+
// Only report error when transitioning from connected to disconnected
|
|
183
|
+
if (this.isConnected) {
|
|
184
|
+
this.isConnected = false;
|
|
185
|
+
this.handleError(error, null, {
|
|
186
|
+
method: "CacheService",
|
|
187
|
+
action: "disconnected",
|
|
188
|
+
additionalData: {
|
|
189
|
+
message: "Cache connection lost",
|
|
190
|
+
error: error.message,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// Ignore repeated errors while already disconnected
|
|
148
195
|
});
|
|
149
196
|
|
|
150
197
|
this.client.on("close", () => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
// Only log when transitioning from connected to disconnected
|
|
199
|
+
if (this.isConnected) {
|
|
200
|
+
this.isConnected = false;
|
|
201
|
+
this.handleError(new Error("Cache Connection Closed"), null, {
|
|
202
|
+
method: "CacheService",
|
|
203
|
+
action: "disconnected",
|
|
204
|
+
additionalData: { message: "Cache client disconnected" },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// Ignore repeated close events while already disconnected
|
|
156
208
|
});
|
|
157
209
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.client.ping(),
|
|
161
|
-
new Promise((_, reject) =>
|
|
162
|
-
setTimeout(() => reject(new Error("Connection timeout after 20 seconds")), 20000)
|
|
163
|
-
),
|
|
164
|
-
]);
|
|
210
|
+
await this.client.ping();
|
|
211
|
+
this.isConnected = true;
|
|
165
212
|
|
|
166
|
-
logger("Cache client
|
|
213
|
+
logger("Cache client initialized successfully", {
|
|
167
214
|
level: "info",
|
|
168
215
|
method: "CacheService.initialize",
|
|
169
216
|
action: "completed",
|
|
@@ -172,7 +219,9 @@ class CacheService {
|
|
|
172
219
|
this.handleError(error, null, {
|
|
173
220
|
method: "CacheService.initialize",
|
|
174
221
|
action: "error",
|
|
175
|
-
additionalData: {
|
|
222
|
+
additionalData: {
|
|
223
|
+
message: error.message || "Failed to initialize cache client",
|
|
224
|
+
},
|
|
176
225
|
});
|
|
177
226
|
}
|
|
178
227
|
}
|
|
@@ -183,6 +232,8 @@ class CacheService {
|
|
|
183
232
|
* @returns The cached value or null if not found
|
|
184
233
|
*/
|
|
185
234
|
async get<T>(key: string): Promise<T | null> {
|
|
235
|
+
if (!this.isConnected) return null;
|
|
236
|
+
|
|
186
237
|
try {
|
|
187
238
|
const value = await this.client!.get(key);
|
|
188
239
|
if (value === null) {
|
|
@@ -206,6 +257,8 @@ class CacheService {
|
|
|
206
257
|
* @param ttl - Optional time-to-live in seconds
|
|
207
258
|
*/
|
|
208
259
|
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
|
260
|
+
if (!this.isConnected) return;
|
|
261
|
+
|
|
209
262
|
try {
|
|
210
263
|
const serialized = JSON.stringify(value);
|
|
211
264
|
if (ttl) {
|
|
@@ -227,6 +280,8 @@ class CacheService {
|
|
|
227
280
|
* @param key - The cache key to delete
|
|
228
281
|
*/
|
|
229
282
|
async delete(key: string): Promise<void> {
|
|
283
|
+
if (!this.isConnected) return;
|
|
284
|
+
|
|
230
285
|
try {
|
|
231
286
|
await this.client!.del(key);
|
|
232
287
|
} catch (error) {
|
|
@@ -245,6 +300,8 @@ class CacheService {
|
|
|
245
300
|
* @returns Array of matching keys
|
|
246
301
|
*/
|
|
247
302
|
async keys(pattern: string): Promise<string[]> {
|
|
303
|
+
if (!this.isConnected) return [];
|
|
304
|
+
|
|
248
305
|
try {
|
|
249
306
|
const keys: string[] = [];
|
|
250
307
|
let cursor = "0";
|
|
@@ -278,6 +335,8 @@ class CacheService {
|
|
|
278
335
|
* @returns The total number of keys
|
|
279
336
|
*/
|
|
280
337
|
async dbsize(): Promise<number> {
|
|
338
|
+
if (!this.isConnected) return 0;
|
|
339
|
+
|
|
281
340
|
try {
|
|
282
341
|
const size = await this.client!.dbsize();
|
|
283
342
|
return size;
|
|
@@ -297,6 +356,8 @@ class CacheService {
|
|
|
297
356
|
* @param pattern - Pattern to match (e.g., "user:*", "*:session")
|
|
298
357
|
*/
|
|
299
358
|
async clearPattern(pattern: string): Promise<void> {
|
|
359
|
+
if (!this.isConnected) return;
|
|
360
|
+
|
|
300
361
|
try {
|
|
301
362
|
const keys = await this.keys(pattern);
|
|
302
363
|
if (keys.length > 0) {
|
|
@@ -315,7 +376,10 @@ class CacheService {
|
|
|
315
376
|
this.handleError(error, null, {
|
|
316
377
|
method: "CacheService.clearPattern",
|
|
317
378
|
action: "error",
|
|
318
|
-
additionalData: {
|
|
379
|
+
additionalData: {
|
|
380
|
+
message: "Failed to clear keys matching pattern",
|
|
381
|
+
pattern,
|
|
382
|
+
},
|
|
319
383
|
});
|
|
320
384
|
}
|
|
321
385
|
}
|
|
@@ -326,6 +390,8 @@ class CacheService {
|
|
|
326
390
|
* @returns true if the key exists, false otherwise
|
|
327
391
|
*/
|
|
328
392
|
async exists(key: string): Promise<boolean> {
|
|
393
|
+
if (!this.isConnected) return false;
|
|
394
|
+
|
|
329
395
|
try {
|
|
330
396
|
const result = await this.client!.exists(key);
|
|
331
397
|
return result === 1;
|
|
@@ -346,6 +412,8 @@ class CacheService {
|
|
|
346
412
|
async mset(
|
|
347
413
|
entries: Array<{ key: string; value: any; ttl?: number }>
|
|
348
414
|
): Promise<void> {
|
|
415
|
+
if (!this.isConnected) return;
|
|
416
|
+
|
|
349
417
|
try {
|
|
350
418
|
const pipeline = this.client!.pipeline();
|
|
351
419
|
entries.forEach(({ key, value, ttl }) => {
|
|
@@ -361,7 +429,10 @@ class CacheService {
|
|
|
361
429
|
this.handleError(error, null, {
|
|
362
430
|
method: "CacheService.mset",
|
|
363
431
|
action: "error",
|
|
364
|
-
additionalData: {
|
|
432
|
+
additionalData: {
|
|
433
|
+
message: "Failed to set multiple values in cache",
|
|
434
|
+
count: entries.length,
|
|
435
|
+
},
|
|
365
436
|
});
|
|
366
437
|
}
|
|
367
438
|
}
|
|
@@ -372,16 +443,19 @@ class CacheService {
|
|
|
372
443
|
* @returns Array of cached values (null for missing keys)
|
|
373
444
|
*/
|
|
374
445
|
async mget<T>(keys: string[]): Promise<Array<T | null>> {
|
|
446
|
+
if (!this.isConnected) return keys.map(() => null);
|
|
447
|
+
|
|
375
448
|
try {
|
|
376
449
|
const values = await this.client!.mget(...keys);
|
|
377
|
-
return values.map((value) =>
|
|
378
|
-
value ? (JSON.parse(value) as T) : null
|
|
379
|
-
);
|
|
450
|
+
return values.map((value) => (value ? (JSON.parse(value) as T) : null));
|
|
380
451
|
} catch (error) {
|
|
381
452
|
this.handleError(error, null, {
|
|
382
453
|
method: "CacheService.mget",
|
|
383
454
|
action: "error",
|
|
384
|
-
additionalData: {
|
|
455
|
+
additionalData: {
|
|
456
|
+
message: "Failed to get multiple values from cache",
|
|
457
|
+
count: keys.length,
|
|
458
|
+
},
|
|
385
459
|
});
|
|
386
460
|
return keys.map(() => null);
|
|
387
461
|
}
|
|
@@ -394,13 +468,19 @@ class CacheService {
|
|
|
394
468
|
* @returns The new value after increment
|
|
395
469
|
*/
|
|
396
470
|
async increment(key: string, increment: number = 1): Promise<number> {
|
|
471
|
+
if (!this.isConnected) return 0;
|
|
472
|
+
|
|
397
473
|
try {
|
|
398
474
|
return await this.client!.incrby(key, increment);
|
|
399
475
|
} catch (error) {
|
|
400
476
|
this.handleError(error, null, {
|
|
401
477
|
method: "CacheService.increment",
|
|
402
478
|
action: "error",
|
|
403
|
-
additionalData: {
|
|
479
|
+
additionalData: {
|
|
480
|
+
message: "Failed to increment value in cache",
|
|
481
|
+
key,
|
|
482
|
+
increment,
|
|
483
|
+
},
|
|
404
484
|
});
|
|
405
485
|
return 0;
|
|
406
486
|
}
|
|
@@ -413,13 +493,19 @@ class CacheService {
|
|
|
413
493
|
* @returns The new value after decrement
|
|
414
494
|
*/
|
|
415
495
|
async decrement(key: string, decrement: number = 1): Promise<number> {
|
|
496
|
+
if (!this.isConnected) return 0;
|
|
497
|
+
|
|
416
498
|
try {
|
|
417
499
|
return await this.client!.decrby(key, decrement);
|
|
418
500
|
} catch (error) {
|
|
419
501
|
this.handleError(error, null, {
|
|
420
502
|
method: "CacheService.decrement",
|
|
421
503
|
action: "error",
|
|
422
|
-
additionalData: {
|
|
504
|
+
additionalData: {
|
|
505
|
+
message: "Failed to decrement value in cache",
|
|
506
|
+
key,
|
|
507
|
+
decrement,
|
|
508
|
+
},
|
|
423
509
|
});
|
|
424
510
|
return 0;
|
|
425
511
|
}
|
|
@@ -432,6 +518,8 @@ class CacheService {
|
|
|
432
518
|
* @returns true if the timeout was set, false if key doesn't exist
|
|
433
519
|
*/
|
|
434
520
|
async expire(key: string, ttl: number): Promise<boolean> {
|
|
521
|
+
if (!this.isConnected) return false;
|
|
522
|
+
|
|
435
523
|
try {
|
|
436
524
|
const result = await this.client!.expire(key, ttl);
|
|
437
525
|
return result === 1;
|
|
@@ -439,7 +527,11 @@ class CacheService {
|
|
|
439
527
|
this.handleError(error, null, {
|
|
440
528
|
method: "CacheService.expire",
|
|
441
529
|
action: "error",
|
|
442
|
-
additionalData: {
|
|
530
|
+
additionalData: {
|
|
531
|
+
message: "Failed to set expiration on key",
|
|
532
|
+
key,
|
|
533
|
+
ttl,
|
|
534
|
+
},
|
|
443
535
|
});
|
|
444
536
|
return false;
|
|
445
537
|
}
|
|
@@ -451,6 +543,8 @@ class CacheService {
|
|
|
451
543
|
* @returns TTL in seconds, -1 if key exists but has no expire, -2 if key doesn't exist
|
|
452
544
|
*/
|
|
453
545
|
async ttl(key: string): Promise<number> {
|
|
546
|
+
if (!this.isConnected) return -2;
|
|
547
|
+
|
|
454
548
|
try {
|
|
455
549
|
return await this.client!.ttl(key);
|
|
456
550
|
} catch (error) {
|
|
@@ -463,6 +557,64 @@ class CacheService {
|
|
|
463
557
|
}
|
|
464
558
|
}
|
|
465
559
|
|
|
560
|
+
/**
|
|
561
|
+
* Atomically set a value only if the key doesn't exist (SETNX)
|
|
562
|
+
* Uses Redis SET NX EX command which is atomic - no race conditions possible.
|
|
563
|
+
*
|
|
564
|
+
* @param key - The cache key
|
|
565
|
+
* @param value - The value to cache (will be JSON serialized)
|
|
566
|
+
* @param ttl - Time-to-live in seconds (required for safety)
|
|
567
|
+
* @returns true if the value was set (key didn't exist), false if key already exists
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* // Single-use token implementation
|
|
571
|
+
* const wasSet = await cacheService.setIfNotExists('magic_link:abc123', { userId: '123' }, 300);
|
|
572
|
+
* if (!wasSet) {
|
|
573
|
+
* throw new Error('Token already used');
|
|
574
|
+
* }
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* // Distributed lock
|
|
578
|
+
* const lockAcquired = await cacheService.setIfNotExists('lock:resource_id', { owner: 'worker-1' }, 30);
|
|
579
|
+
* if (lockAcquired) {
|
|
580
|
+
* try {
|
|
581
|
+
* // Do work while holding lock
|
|
582
|
+
* } finally {
|
|
583
|
+
* await cacheService.delete('lock:resource_id');
|
|
584
|
+
* }
|
|
585
|
+
* }
|
|
586
|
+
*/
|
|
587
|
+
async setIfNotExists<T>(
|
|
588
|
+
key: string,
|
|
589
|
+
value: T,
|
|
590
|
+
ttl: number
|
|
591
|
+
): Promise<boolean> {
|
|
592
|
+
if (!this.isConnected) return false;
|
|
593
|
+
|
|
594
|
+
try {
|
|
595
|
+
const serialized = JSON.stringify(value);
|
|
596
|
+
|
|
597
|
+
// Atomic operation: SET key value EX ttl NX
|
|
598
|
+
// EX = Set expiry in seconds
|
|
599
|
+
// NX = Only set if key doesn't exist
|
|
600
|
+
// Returns "OK" if set successfully, null if key already exists
|
|
601
|
+
const result = await this.client!.set(key, serialized, "EX", ttl, "NX");
|
|
602
|
+
|
|
603
|
+
return result === "OK";
|
|
604
|
+
} catch (error) {
|
|
605
|
+
this.handleError(error, null, {
|
|
606
|
+
method: "CacheService.setIfNotExists",
|
|
607
|
+
action: "error",
|
|
608
|
+
additionalData: {
|
|
609
|
+
message: "Failed to set value atomically",
|
|
610
|
+
key,
|
|
611
|
+
ttl,
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
466
618
|
/**
|
|
467
619
|
* Disconnect the cache client
|
|
468
620
|
* Use this for graceful shutdown
|
|
@@ -476,8 +628,7 @@ class CacheService {
|
|
|
476
628
|
this.client.disconnect();
|
|
477
629
|
}
|
|
478
630
|
this.client = null;
|
|
479
|
-
|
|
480
|
-
// this.initPromise = null;
|
|
631
|
+
this.isConnected = false;
|
|
481
632
|
logger("Cache client disconnected", {
|
|
482
633
|
level: "info",
|
|
483
634
|
method: "CacheService.disconnect",
|
|
@@ -487,6 +638,8 @@ class CacheService {
|
|
|
487
638
|
}
|
|
488
639
|
|
|
489
640
|
async ping(): Promise<string> {
|
|
641
|
+
if (!this.isConnected) return "";
|
|
642
|
+
|
|
490
643
|
try {
|
|
491
644
|
const result = await this.client!.ping();
|
|
492
645
|
return result;
|
|
@@ -496,6 +649,7 @@ class CacheService {
|
|
|
496
649
|
action: "error",
|
|
497
650
|
additionalData: { message: "Failed to ping cache client" },
|
|
498
651
|
});
|
|
652
|
+
return "";
|
|
499
653
|
}
|
|
500
654
|
}
|
|
501
655
|
}
|