ioredis-toolkit 0.0.5 → 0.0.7
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 +772 -316
- package/dist/cache.d.ts +502 -6
- package/dist/cache.js +501 -5
- package/dist/client.d.ts +105 -0
- package/dist/client.js +105 -0
- package/dist/health.d.ts +141 -0
- package/dist/health.js +133 -0
- package/dist/lock.d.ts +55 -1
- package/dist/lock.js +12 -22
- package/dist/pubsub.d.ts +259 -7
- package/dist/pubsub.js +259 -7
- package/dist/ratelimiter.d.ts +279 -1
- package/dist/ratelimiter.js +250 -0
- package/dist/session/scripts/create.lua +18 -3
- package/dist/session/scripts/rotate-encrypted.lua +5 -2
- package/dist/session/scripts/rotate.lua +5 -2
- package/dist/types.js +0 -2
- package/package.json +1 -1
package/dist/cache.js
CHANGED
|
@@ -26,12 +26,21 @@ export class Cache {
|
|
|
26
26
|
/**
|
|
27
27
|
* Creates a cache bound to a Redis client.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* **Parameters:**
|
|
30
|
+
* - `client` - The underlying {@link RedisClientWrapper}. All cache operations
|
|
31
|
+
* delegate to this client.
|
|
32
|
+
* - `config` - Configuration object with the following fields:
|
|
33
|
+
* - `defaultTTL` (number, optional, default: `3600`) - Default TTL in seconds
|
|
34
|
+
* applied when no per-call TTL is specified.
|
|
35
|
+
* - `compressionThreshold` (number, optional, default: `1024`) - Byte threshold
|
|
36
|
+
* above which values are gzip-compressed transparently.
|
|
37
|
+
* - `logger` - Optional pino-compatible logger. Supports `trace/debug/info/warn/error/fatal`
|
|
38
|
+
* levels and `child()` for namespace logging. Defaults to `console`.
|
|
33
39
|
*
|
|
34
|
-
*
|
|
40
|
+
* **Type Parameters:**
|
|
41
|
+
* - `T` - The type of values stored/retrieved from the cache.
|
|
42
|
+
*
|
|
43
|
+
* **Example:**
|
|
35
44
|
* ```ts
|
|
36
45
|
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048 });
|
|
37
46
|
* ```
|
|
@@ -114,6 +123,44 @@ export class Cache {
|
|
|
114
123
|
* const token = await cache.get('token', 'auth');
|
|
115
124
|
* ```
|
|
116
125
|
*/
|
|
126
|
+
/**
|
|
127
|
+
* Reads a cached value.
|
|
128
|
+
*
|
|
129
|
+
* **Behavior:**
|
|
130
|
+
* - Objects are parsed from JSON when stored as JSON.
|
|
131
|
+
* - Values larger than `compressionThreshold` bytes are transparently
|
|
132
|
+
* decompressed (gzip) when reading.
|
|
133
|
+
* - Strings that are not JSON are returned as-is.
|
|
134
|
+
* - If the key does not exist, returns `null`.
|
|
135
|
+
*
|
|
136
|
+
* **Type Parameters:**
|
|
137
|
+
* - `T` - The expected return type. When the stored value is a JSON object/array,
|
|
138
|
+
* it will be parsed and returned as `T`. When it's a primitive (string/number/bool),
|
|
139
|
+
* it is returned as-is and typed as `T`.
|
|
140
|
+
*
|
|
141
|
+
* **Returns:**
|
|
142
|
+
* - The stored value, parsed as `T` when possible, or `null` when the key is missing.
|
|
143
|
+
*
|
|
144
|
+
* **Example:**
|
|
145
|
+
* ```ts
|
|
146
|
+
* // Store an object
|
|
147
|
+
* await cache.set('user:1', { name: 'alice', age: 30 });
|
|
148
|
+
*
|
|
149
|
+
* // Read it back with type coercion
|
|
150
|
+
* const user: { name: string; age: number } | null = await cache.get<UserProfile>('user:1');
|
|
151
|
+
* // user === { name: 'alice', age: 30 }
|
|
152
|
+
*
|
|
153
|
+
* // Read a string value
|
|
154
|
+
* const token = await cache.get('token'); // 'abc' | null
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* **Parameters:**
|
|
158
|
+
* - `key` - Cache key.
|
|
159
|
+
* - `namespace` - Optional namespace prefix (`namespace:key`). When provided,
|
|
160
|
+
* the key is internally transformed to `${namespace}:${key}`.
|
|
161
|
+
*
|
|
162
|
+
* @returns The stored value, or `null` when missing.
|
|
163
|
+
*/
|
|
117
164
|
async get(key, namespace) {
|
|
118
165
|
const fullKey = this.getKey(key, namespace);
|
|
119
166
|
const raw = await this.client.get(fullKey);
|
|
@@ -150,6 +197,47 @@ export class Cache {
|
|
|
150
197
|
* await cache.set('token', 'abc', { namespace: 'auth', compress: false });
|
|
151
198
|
* ```
|
|
152
199
|
*/
|
|
200
|
+
/**
|
|
201
|
+
* Stores a value in the cache.
|
|
202
|
+
*
|
|
203
|
+
* **Behavior:**
|
|
204
|
+
* - Values are JSON-serialized when they are objects, arrays, or booleans/strings/numbers
|
|
205
|
+
* are stored as-is.
|
|
206
|
+
* - Values larger than `compressionThreshold` bytes are gzip-compressed transparently.
|
|
207
|
+
* The compressed form is stored with metadata (`_compressed: true`, `_data: base64`) so
|
|
208
|
+
* it is transparently decompressed on read.
|
|
209
|
+
* - Set `compress: false` to disable compression for a single write, regardless of size.
|
|
210
|
+
* - Per-call TTL overrides the cache's `defaultTTL`.
|
|
211
|
+
* - Per-call `namespace` overrides the cache's configured namespace for that operation.
|
|
212
|
+
*
|
|
213
|
+
* **Type Parameters:**
|
|
214
|
+
* - `T` - The type of the value being stored. Can be any serializable JavaScript value.
|
|
215
|
+
*
|
|
216
|
+
* **Returns:**
|
|
217
|
+
* - `true` when the value was stored successfully (`result === 'OK'`).
|
|
218
|
+
*
|
|
219
|
+
* **Example:**
|
|
220
|
+
* ```ts
|
|
221
|
+
* // Store an object with a custom TTL
|
|
222
|
+
* await cache.set('user:1', { name: 'alice' }, { ttl: 300 });
|
|
223
|
+
*
|
|
224
|
+
* // Store with compression disabled
|
|
225
|
+
* await cache.set('token', 'abc123', { compress: false });
|
|
226
|
+
*
|
|
227
|
+
* // Store with a namespace
|
|
228
|
+
* await cache.set('token', 'abc', { namespace: 'auth' });
|
|
229
|
+
* ```
|
|
230
|
+
*
|
|
231
|
+
* **Parameters:**
|
|
232
|
+
* - `key` - Cache key.
|
|
233
|
+
* - `value` - Any serializable value (string, number, boolean, Buffer, or object).
|
|
234
|
+
* - `options` - Optional configuration:
|
|
235
|
+
* - `ttl` (number, optional) - TTL in seconds. Falls back to `defaultTTL`.
|
|
236
|
+
* - `namespace` (string, optional) - Namespace prefix. Falls back to cache config.
|
|
237
|
+
* - `compress` (boolean, optional) - Force compression or disable it. Defaults to `true`.
|
|
238
|
+
*
|
|
239
|
+
* @returns `true` when stored successfully.
|
|
240
|
+
*/
|
|
153
241
|
async set(key, value, options = {}) {
|
|
154
242
|
const fullKey = this.getKey(key, options.namespace);
|
|
155
243
|
const ttl = options.ttl || this.defaultTTL;
|
|
@@ -202,6 +290,36 @@ export class Cache {
|
|
|
202
290
|
* const claimed = await cache.setNX('job:1', 'worker-1', { ttl: 60 });
|
|
203
291
|
* ```
|
|
204
292
|
*/
|
|
293
|
+
/**
|
|
294
|
+
* Stores a value only if the key does not exist yet (`SETNX`).
|
|
295
|
+
*
|
|
296
|
+
* **Behavior:**
|
|
297
|
+
* - The value is stored atomically using Redis `SET key value EX ttl NX`.
|
|
298
|
+
* - Returns `true` only when the key did not exist and the value was set.
|
|
299
|
+
* - Per-call TTL overrides the cache's `defaultTTL`.
|
|
300
|
+
* - Per-call `namespace` is applied to the key.
|
|
301
|
+
*
|
|
302
|
+
* **Type Parameters:**
|
|
303
|
+
* - `T` - The type of the value being stored. Will be JSON-stringified if not a string.
|
|
304
|
+
*
|
|
305
|
+
* **Returns:**
|
|
306
|
+
* - `true` only when the value was actually stored (Redis SETNX returned `1`).
|
|
307
|
+
*
|
|
308
|
+
* **Example:**
|
|
309
|
+
* ```ts
|
|
310
|
+
* const claimed = await cache.setNX('job:1', 'worker-1', { ttl: 60 });
|
|
311
|
+
* // claimed === true (job was claimed by this worker)
|
|
312
|
+
* ```
|
|
313
|
+
*
|
|
314
|
+
* **Parameters:**
|
|
315
|
+
* - `key` - Cache key.
|
|
316
|
+
* - `value` - The value to store. String stored as-is; objects are JSON-stringified.
|
|
317
|
+
* - `options` - Optional configuration:
|
|
318
|
+
* - `ttl` (number, optional) - TTL in seconds. Falls back to `defaultTTL`.
|
|
319
|
+
* - `namespace` (string, optional) - Namespace prefix.
|
|
320
|
+
*
|
|
321
|
+
* @returns `true` only when the value was actually stored.
|
|
322
|
+
*/
|
|
205
323
|
async setNX(key, value, options = {}) {
|
|
206
324
|
const fullKey = this.getKey(key, options.namespace);
|
|
207
325
|
const ttl = options.ttl || this.defaultTTL;
|
|
@@ -229,6 +347,39 @@ export class Cache {
|
|
|
229
347
|
* const locked = await cache.setEXNX('lock:order:42', 'txn-id', { ttl: 30 });
|
|
230
348
|
* ```
|
|
231
349
|
*/
|
|
350
|
+
/**
|
|
351
|
+
* Stores a value only if the key does not exist yet, atomically with the TTL
|
|
352
|
+
* (`SET ... EX NX`).
|
|
353
|
+
*
|
|
354
|
+
* **Behavior:**
|
|
355
|
+
* - The value is stored atomically using Redis `SET key value EX ttl NX`.
|
|
356
|
+
* - This is the atomic equivalent of calling `SET key value EX ttl` followed by
|
|
357
|
+
* `SET key value NX` - but done in a single Redis call.
|
|
358
|
+
* - Returns `true` only when the key did not exist and the value was set with TTL.
|
|
359
|
+
* - Per-call TTL overrides the cache's `defaultTTL`.
|
|
360
|
+
* - Per-call `namespace` is applied to the key.
|
|
361
|
+
*
|
|
362
|
+
* **Type Parameters:**
|
|
363
|
+
* - `T` - The type of the value being stored. Will be JSON-stringified if not a string.
|
|
364
|
+
*
|
|
365
|
+
* **Returns:**
|
|
366
|
+
* - `true` only when the value was actually stored (Redis SET returned `OK`).
|
|
367
|
+
*
|
|
368
|
+
* **Example:**
|
|
369
|
+
* ```ts
|
|
370
|
+
* const locked = await cache.setEXNX('lock:order:42', 'txn-id', { ttl: 30 });
|
|
371
|
+
* // locked === true (lock was acquired with 30s TTL)
|
|
372
|
+
* ```
|
|
373
|
+
*
|
|
374
|
+
* **Parameters:**
|
|
375
|
+
* - `key` - Cache key.
|
|
376
|
+
* - `value` - The value to store. String stored as-is; objects are JSON-stringified.
|
|
377
|
+
* - `options` - Optional configuration:
|
|
378
|
+
* - `ttl` (number, optional) - TTL in seconds. Falls back to `defaultTTL`.
|
|
379
|
+
* - `namespace` (string, optional) - Namespace prefix.
|
|
380
|
+
*
|
|
381
|
+
* @returns `true` only when the value was actually stored.
|
|
382
|
+
*/
|
|
232
383
|
async setEXNX(key, value, options = {}) {
|
|
233
384
|
const fullKey = this.getKey(key, options.namespace);
|
|
234
385
|
const ttl = options.ttl || this.defaultTTL;
|
|
@@ -278,6 +429,37 @@ export class Cache {
|
|
|
278
429
|
* const [a, b] = await cache.mget(['user:1', 'user:2']);
|
|
279
430
|
* ```
|
|
280
431
|
*/
|
|
432
|
+
/**
|
|
433
|
+
* Reads multiple cache keys in one call.
|
|
434
|
+
*
|
|
435
|
+
* **Behavior:**
|
|
436
|
+
* - Cluster-safe: keys are grouped by hash slot under the hood, avoiding CROSS-SLOT errors.
|
|
437
|
+
* - Values are deserialized from JSON when stored as JSON. Strings/numbers/buffers
|
|
438
|
+
* are returned as-is.
|
|
439
|
+
* - Missing keys return `null` in the corresponding position.
|
|
440
|
+
*
|
|
441
|
+
* **Type Parameters:**
|
|
442
|
+
* - `T` - The expected type of each returned value. When the stored value is JSON,
|
|
443
|
+
* it will be parsed and coerced to `T`.
|
|
444
|
+
*
|
|
445
|
+
* **Returns:**
|
|
446
|
+
* - An array of values in the same order as the input `keys`. Each element is `T | null`.
|
|
447
|
+
* `null` indicates the key did not exist.
|
|
448
|
+
*
|
|
449
|
+
* **Example:**
|
|
450
|
+
* ```ts
|
|
451
|
+
* const [a, b] = await cache.mget(['user:1', 'user:2']);
|
|
452
|
+
* // a === { name: 'alice' }, b === { name: 'bob' }
|
|
453
|
+
* ```
|
|
454
|
+
*
|
|
455
|
+
* **Parameters:**
|
|
456
|
+
* - `keys` - Cache keys to read. Will have the namespace prefix applied automatically
|
|
457
|
+
* if a namespace is configured.
|
|
458
|
+
* - `namespace` - Optional namespace prefix applied to every key. When provided,
|
|
459
|
+
* each key is internally transformed to `${namespace}:${key}`.
|
|
460
|
+
*
|
|
461
|
+
* @returns Values in input order; `null` for missing keys.
|
|
462
|
+
*/
|
|
281
463
|
async mget(keys, namespace) {
|
|
282
464
|
const fullKeys = keys.map(k => this.getKey(k, namespace));
|
|
283
465
|
const raw = await this.client.mgetClusterAware(fullKeys);
|
|
@@ -336,6 +518,39 @@ export class Cache {
|
|
|
336
518
|
* await cache.mset({ 'user:1': alice, 'user:2': bob }, { ttl: 300 });
|
|
337
519
|
* ```
|
|
338
520
|
*/
|
|
521
|
+
/**
|
|
522
|
+
* Stores multiple key/value entries in one call.
|
|
523
|
+
*
|
|
524
|
+
* **Behavior:**
|
|
525
|
+
* - Cluster-safe: entries are grouped by hash slot, one pipeline per slot.
|
|
526
|
+
* This avoids CROSS-SLOT errors that would occur if keys spanned multiple slots in a
|
|
527
|
+
* single pipeline.
|
|
528
|
+
* - Values are JSON-serialized when they are objects; strings/buffers are stored as-is.
|
|
529
|
+
* - Per-call TTL overrides the cache's `defaultTTL`.
|
|
530
|
+
* - Per-call `namespace` is applied to all keys.
|
|
531
|
+
*
|
|
532
|
+
* **Type Parameters:**
|
|
533
|
+
* - `T` - The type of values being stored. Objects are JSON-stringified; strings/buffers
|
|
534
|
+
* are stored as-is.
|
|
535
|
+
*
|
|
536
|
+
* **Returns:**
|
|
537
|
+
* - `true` when every entry was stored successfully.
|
|
538
|
+
* - `false` if any entry failed to store.
|
|
539
|
+
*
|
|
540
|
+
* **Example:**
|
|
541
|
+
* ```ts
|
|
542
|
+
* await cache.mset({ 'user:1': alice, 'user:2': bob }, { ttl: 300 });
|
|
543
|
+
* // Both entries stored with a 5-minute TTL
|
|
544
|
+
* ```
|
|
545
|
+
*
|
|
546
|
+
* **Parameters:**
|
|
547
|
+
* - `entries` - Object mapping cache keys to values.
|
|
548
|
+
* - `options` - Optional configuration:
|
|
549
|
+
* - `ttl` (number, optional) - TTL in seconds. Falls back to `defaultTTL`.
|
|
550
|
+
* - `namespace` (string, optional) - Namespace prefix. Applied to all keys.
|
|
551
|
+
*
|
|
552
|
+
* @returns `true` when every entry was stored.
|
|
553
|
+
*/
|
|
339
554
|
async mset(entries, options = {}) {
|
|
340
555
|
const ttl = options.ttl || this.defaultTTL;
|
|
341
556
|
const namespace = options.namespace;
|
|
@@ -379,6 +594,28 @@ export class Cache {
|
|
|
379
594
|
* const removed = await cache.delete('user:1');
|
|
380
595
|
* ```
|
|
381
596
|
*/
|
|
597
|
+
/**
|
|
598
|
+
* Deletes a cache key.
|
|
599
|
+
*
|
|
600
|
+
* **Behavior:**
|
|
601
|
+
* - Deletes the full key (including any namespace prefix).
|
|
602
|
+
* - Returns `true` only when the key existed and was deleted (Redis DEL returned `1`).
|
|
603
|
+
*
|
|
604
|
+
* **Returns:**
|
|
605
|
+
* - `true` if the key existed and was deleted.
|
|
606
|
+
*
|
|
607
|
+
* **Example:**
|
|
608
|
+
* ```ts
|
|
609
|
+
* const removed = await cache.delete('user:1');
|
|
610
|
+
* // removed === true
|
|
611
|
+
* ```
|
|
612
|
+
*
|
|
613
|
+
* **Parameters:**
|
|
614
|
+
* - `key` - Cache key.
|
|
615
|
+
* - `namespace` - Optional namespace prefix.
|
|
616
|
+
*
|
|
617
|
+
* @returns `true` if the key existed and was deleted.
|
|
618
|
+
*/
|
|
382
619
|
async delete(key, namespace) {
|
|
383
620
|
const fullKey = this.getKey(key, namespace);
|
|
384
621
|
const result = await this.client.del(fullKey);
|
|
@@ -396,6 +633,25 @@ export class Cache {
|
|
|
396
633
|
* const cached = await cache.exists('user:1');
|
|
397
634
|
* ```
|
|
398
635
|
*/
|
|
636
|
+
/**
|
|
637
|
+
* Checks whether a cache key exists.
|
|
638
|
+
*
|
|
639
|
+
* **Returns:**
|
|
640
|
+
* - `true` if the key exists in Redis.
|
|
641
|
+
* - `false` if the key does not exist.
|
|
642
|
+
*
|
|
643
|
+
* **Example:**
|
|
644
|
+
* ```ts
|
|
645
|
+
* const cached = await cache.exists('user:1');
|
|
646
|
+
* // cached === true when 'user:1' has been set
|
|
647
|
+
* ```
|
|
648
|
+
*
|
|
649
|
+
* **Parameters:**
|
|
650
|
+
* - `key` - Cache key.
|
|
651
|
+
* - `namespace` - Optional namespace prefix.
|
|
652
|
+
*
|
|
653
|
+
* @returns `true` if the key exists.
|
|
654
|
+
*/
|
|
399
655
|
async exists(key, namespace) {
|
|
400
656
|
const fullKey = this.getKey(key, namespace);
|
|
401
657
|
const result = await this.client.exists(fullKey);
|
|
@@ -414,6 +670,26 @@ export class Cache {
|
|
|
414
670
|
* const extended = await cache.expire('session:42', 3600);
|
|
415
671
|
* ```
|
|
416
672
|
*/
|
|
673
|
+
/**
|
|
674
|
+
* Sets the TTL of an existing cache key.
|
|
675
|
+
*
|
|
676
|
+
* **Returns:**
|
|
677
|
+
* - `true` if the TTL was applied (Redis EXPIRE returned `1`).
|
|
678
|
+
* - `false` if the key did not exist.
|
|
679
|
+
*
|
|
680
|
+
* **Example:**
|
|
681
|
+
* ```ts
|
|
682
|
+
* const extended = await cache.expire('session:42', 3600);
|
|
683
|
+
* // extended === true (TTL was set to 1 hour)
|
|
684
|
+
* ```
|
|
685
|
+
*
|
|
686
|
+
* **Parameters:**
|
|
687
|
+
* - `key` - Cache key.
|
|
688
|
+
* - `ttl` - TTL in seconds.
|
|
689
|
+
* - `namespace` - Optional namespace prefix.
|
|
690
|
+
*
|
|
691
|
+
* @returns `true` if the TTL was applied.
|
|
692
|
+
*/
|
|
417
693
|
async expire(key, ttl, namespace) {
|
|
418
694
|
const fullKey = this.getKey(key, namespace);
|
|
419
695
|
const result = await this.client.expire(fullKey, ttl);
|
|
@@ -431,6 +707,26 @@ export class Cache {
|
|
|
431
707
|
* const secondsLeft = await cache.ttl('session:42');
|
|
432
708
|
* ```
|
|
433
709
|
*/
|
|
710
|
+
/**
|
|
711
|
+
* Returns the remaining TTL of a cache key in seconds.
|
|
712
|
+
*
|
|
713
|
+
* **Returns:**
|
|
714
|
+
* - The remaining TTL in seconds.
|
|
715
|
+
* - `-2` if the key does not exist.
|
|
716
|
+
* - `-1` if the key exists but has no TTL set.
|
|
717
|
+
*
|
|
718
|
+
* **Example:**
|
|
719
|
+
* ```ts
|
|
720
|
+
* const secondsLeft = await cache.ttl('session:42');
|
|
721
|
+
* // secondsLeft === 2500 (approximately 42 minutes remaining)
|
|
722
|
+
* ```
|
|
723
|
+
*
|
|
724
|
+
* **Parameters:**
|
|
725
|
+
* - `key` - Cache key.
|
|
726
|
+
* - `namespace` - Optional namespace prefix.
|
|
727
|
+
*
|
|
728
|
+
* @returns Remaining TTL in seconds (`-2` if missing, `-1` if no TTL).
|
|
729
|
+
*/
|
|
434
730
|
async ttl(key, namespace) {
|
|
435
731
|
const fullKey = this.getKey(key, namespace);
|
|
436
732
|
return this.client.ttl(fullKey);
|
|
@@ -448,6 +744,35 @@ export class Cache {
|
|
|
448
744
|
* const visits = await cache.increment('stats:visits');
|
|
449
745
|
* ```
|
|
450
746
|
*/
|
|
747
|
+
/**
|
|
748
|
+
* Atomically increments a cache counter.
|
|
749
|
+
*
|
|
750
|
+
* **Behavior:**
|
|
751
|
+
* - Uses Redis `INCR` command on the full key (including namespace if set).
|
|
752
|
+
* - The counter starts at `0` if the key does not exist, then increments to `1`.
|
|
753
|
+
* - The `by` parameter is passed to Redis but note: Redis `INCR` always increments
|
|
754
|
+
* by `1`. The `by` parameter is kept for API parity with other cache implementations
|
|
755
|
+
* but has no effect on the actual Redis command result.
|
|
756
|
+
*
|
|
757
|
+
* **Returns:**
|
|
758
|
+
* - The new counter value (the value after incrementing).
|
|
759
|
+
*
|
|
760
|
+
* **Example:**
|
|
761
|
+
* ```ts
|
|
762
|
+
* const visits = await cache.increment('stats:visits');
|
|
763
|
+
* // visits === 1 (first increment)
|
|
764
|
+
* const more = await cache.increment('stats:visits', 5); // by parameter ignored
|
|
765
|
+
* // more === 2
|
|
766
|
+
* ```
|
|
767
|
+
*
|
|
768
|
+
* **Parameters:**
|
|
769
|
+
* - `key` - Counter key.
|
|
770
|
+
* - `by` - Amount to increment by (default `1`). Note: Redis `INCR` always increments
|
|
771
|
+
* by `1`; this parameter is kept for API parity.
|
|
772
|
+
* - `namespace` - Optional namespace prefix.
|
|
773
|
+
*
|
|
774
|
+
* @returns The new counter value.
|
|
775
|
+
*/
|
|
451
776
|
async increment(key, by = 1, namespace) {
|
|
452
777
|
const fullKey = this.getKey(key, namespace);
|
|
453
778
|
return this.client.incr(fullKey);
|
|
@@ -465,6 +790,32 @@ export class Cache {
|
|
|
465
790
|
* const stock = await cache.decrement('inventory:sku-1');
|
|
466
791
|
* ```
|
|
467
792
|
*/
|
|
793
|
+
/**
|
|
794
|
+
* Atomically decrements a cache counter.
|
|
795
|
+
*
|
|
796
|
+
* **Behavior:**
|
|
797
|
+
* - Uses Redis `DECR` command on the full key (including namespace if set).
|
|
798
|
+
* - The `by` parameter is passed to Redis but note: Redis `DECR` always decrements
|
|
799
|
+
* by `1`. The `by` parameter is kept for API parity with other cache implementations
|
|
800
|
+
* but has no effect on the actual Redis command result.
|
|
801
|
+
*
|
|
802
|
+
* **Returns:**
|
|
803
|
+
* - The new counter value (the value after decrementing).
|
|
804
|
+
*
|
|
805
|
+
* **Example:**
|
|
806
|
+
* ```ts
|
|
807
|
+
* const stock = await cache.decrement('inventory:sku-1');
|
|
808
|
+
* // stock === 99 (started at 100, decremented by 1)
|
|
809
|
+
* ```
|
|
810
|
+
*
|
|
811
|
+
* **Parameters:**
|
|
812
|
+
* - `key` - Counter key.
|
|
813
|
+
* - `by` - Amount to decrement by (default `1`). Note: Redis `DECR` always decrements
|
|
814
|
+
* by `1`; this parameter is kept for API parity.
|
|
815
|
+
* - `namespace` - Optional namespace prefix.
|
|
816
|
+
*
|
|
817
|
+
* @returns The new counter value.
|
|
818
|
+
*/
|
|
468
819
|
async decrement(key, by = 1, namespace) {
|
|
469
820
|
const fullKey = this.getKey(key, namespace);
|
|
470
821
|
return this.client.decr(fullKey);
|
|
@@ -483,6 +834,30 @@ export class Cache {
|
|
|
483
834
|
* const name = await cache.hget('user:1', 'name');
|
|
484
835
|
* ```
|
|
485
836
|
*/
|
|
837
|
+
/**
|
|
838
|
+
* Reads a field from a hash-style cache key.
|
|
839
|
+
*
|
|
840
|
+
* **Behavior:**
|
|
841
|
+
* - The field value is retrieved from the Redis hash.
|
|
842
|
+
* - When the stored value is a JSON string, it is parsed and returned as the typed result.
|
|
843
|
+
* - When the stored value is not JSON, it is returned as a raw string, typed as `T`.
|
|
844
|
+
*
|
|
845
|
+
* **Returns:**
|
|
846
|
+
* - The field value, parsed as `T` when possible, or `null` when the key or field does not exist.
|
|
847
|
+
*
|
|
848
|
+
* **Example:**
|
|
849
|
+
* ```ts
|
|
850
|
+
* const name = await cache.hget('user:1', 'name');
|
|
851
|
+
* // name === 'alice' | null
|
|
852
|
+
* ```
|
|
853
|
+
*
|
|
854
|
+
* **Parameters:**
|
|
855
|
+
* - `key` - Cache key (hash key in Redis).
|
|
856
|
+
* - `field` - Hash field to read.
|
|
857
|
+
* - `namespace` - Optional namespace prefix. Applied to the key.
|
|
858
|
+
*
|
|
859
|
+
* @returns The field value, or `null`.
|
|
860
|
+
*/
|
|
486
861
|
async hget(key, field, namespace) {
|
|
487
862
|
const fullKey = this.getKey(key, namespace);
|
|
488
863
|
const result = await this.client.hget(fullKey, field);
|
|
@@ -509,6 +884,32 @@ export class Cache {
|
|
|
509
884
|
* await cache.hset('user:1', 'age', 30);
|
|
510
885
|
* ```
|
|
511
886
|
*/
|
|
887
|
+
/**
|
|
888
|
+
* Writes a field into a hash-style cache key.
|
|
889
|
+
*
|
|
890
|
+
* **Behavior:**
|
|
891
|
+
* - The value is JSON-stringified when it is not a string (objects, arrays, etc.).
|
|
892
|
+
* Strings are stored as-is.
|
|
893
|
+
* - Returns `true` only when a new field was created (Redis HSET returned `1`).
|
|
894
|
+
* If the field already exists, its value is overwritten and `true` is still returned.
|
|
895
|
+
*
|
|
896
|
+
* **Returns:**
|
|
897
|
+
* - `true` if a new field was created.
|
|
898
|
+
*
|
|
899
|
+
* **Example:**
|
|
900
|
+
* ```ts
|
|
901
|
+
* await cache.hset('user:1', 'age', 30);
|
|
902
|
+
* // Field 'age' set to '30' (stringified) in hash 'user:1'
|
|
903
|
+
* ```
|
|
904
|
+
*
|
|
905
|
+
* **Parameters:**
|
|
906
|
+
* - `key` - Cache key (hash key in Redis).
|
|
907
|
+
* - `field` - Hash field to write.
|
|
908
|
+
* - `value` - Any serializable value. Strings stored as-is; objects are JSON-stringified.
|
|
909
|
+
* - `namespace` - Optional namespace prefix. Applied to the key.
|
|
910
|
+
*
|
|
911
|
+
* @returns `true` if a new field was created.
|
|
912
|
+
*/
|
|
512
913
|
async hset(key, field, value, namespace) {
|
|
513
914
|
const fullKey = this.getKey(key, namespace);
|
|
514
915
|
const rawValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
@@ -527,6 +928,29 @@ export class Cache {
|
|
|
527
928
|
* const profile = await cache.hgetall('user:1');
|
|
528
929
|
* ```
|
|
529
930
|
*/
|
|
931
|
+
/**
|
|
932
|
+
* Returns every field of a hash-style cache key.
|
|
933
|
+
*
|
|
934
|
+
* **Behavior:**
|
|
935
|
+
* - Retrieves all fields and values from the Redis hash.
|
|
936
|
+
* - Each value is parsed from JSON when possible. Non-JSON values are returned as raw strings.
|
|
937
|
+
* - Returns an empty object `{}` when the key does not exist or has no fields.
|
|
938
|
+
*
|
|
939
|
+
* **Returns:**
|
|
940
|
+
* - An object mapping field names to their values, with values parsed as `T` when possible.
|
|
941
|
+
*
|
|
942
|
+
* **Example:**
|
|
943
|
+
* ```ts
|
|
944
|
+
* const profile = await cache.hgetall('user:1');
|
|
945
|
+
* // profile === { age: 30, name: 'alice' }
|
|
946
|
+
* ```
|
|
947
|
+
*
|
|
948
|
+
* **Parameters:**
|
|
949
|
+
* - `key` - Cache key (hash key in Redis).
|
|
950
|
+
* - `namespace` - Optional namespace prefix. Applied to the key.
|
|
951
|
+
*
|
|
952
|
+
* @returns Object mapping fields to values (JSON-parsed when possible).
|
|
953
|
+
*/
|
|
530
954
|
async hgetall(key, namespace) {
|
|
531
955
|
const fullKey = this.getKey(key, namespace);
|
|
532
956
|
const result = await this.client.hgetall(fullKey);
|
|
@@ -556,6 +980,31 @@ export class Cache {
|
|
|
556
980
|
* const removed = await cache.deletePattern('temp:*');
|
|
557
981
|
* ```
|
|
558
982
|
*/
|
|
983
|
+
/**
|
|
984
|
+
* Deletes every cache key matching a glob pattern.
|
|
985
|
+
*
|
|
986
|
+
* **Behavior:**
|
|
987
|
+
* - Cluster-safe: scans every node before deleting.
|
|
988
|
+
* - Uses `SCAN` iteratively to avoid blocking the Redis server on large datasets.
|
|
989
|
+
* - Each matching key is individually deleted via `DEL`.
|
|
990
|
+
* - The `batchSize` and `scanCount` options control the scan batching.
|
|
991
|
+
*
|
|
992
|
+
* **Returns:**
|
|
993
|
+
* - The number of deleted keys.
|
|
994
|
+
*
|
|
995
|
+
* **Example:**
|
|
996
|
+
* ```ts
|
|
997
|
+
* const removed = await cache.deletePattern('temp:*');
|
|
998
|
+
* // removed === number of keys matching 'temp:*' that were deleted
|
|
999
|
+
* ```
|
|
1000
|
+
*
|
|
1001
|
+
* **Parameters:**
|
|
1002
|
+
* - `pattern` - Glob pattern, e.g. `'temp:*'`.
|
|
1003
|
+
* - `namespace` - Optional namespace prefix. The pattern is transformed to
|
|
1004
|
+
* `${namespace}:${pattern}` before scanning.
|
|
1005
|
+
*
|
|
1006
|
+
* @returns The number of deleted keys.
|
|
1007
|
+
*/
|
|
559
1008
|
async deletePattern(pattern, namespace) {
|
|
560
1009
|
const fullPattern = namespace ? `${namespace}:${pattern}` : pattern;
|
|
561
1010
|
let deleted = 0;
|
|
@@ -580,6 +1029,31 @@ export class Cache {
|
|
|
580
1029
|
* const sessions = await cache.keys('session:*');
|
|
581
1030
|
* ```
|
|
582
1031
|
*/
|
|
1032
|
+
/**
|
|
1033
|
+
* Lists every cache key matching a glob pattern.
|
|
1034
|
+
*
|
|
1035
|
+
* **Behavior:**
|
|
1036
|
+
* - Cluster-safe: scans every node.
|
|
1037
|
+
* - Uses `SCAN` iteratively to avoid blocking the Redis server on large datasets.
|
|
1038
|
+
* - Returns all keys matching the glob pattern across all nodes (in cluster mode).
|
|
1039
|
+
*
|
|
1040
|
+
* **Returns:**
|
|
1041
|
+
* - An array of matching keys (relative format, without namespace prefix unless
|
|
1042
|
+
* one was provided in the options).
|
|
1043
|
+
*
|
|
1044
|
+
* **Example:**
|
|
1045
|
+
* ```ts
|
|
1046
|
+
* const sessions = await cache.keys('session:*');
|
|
1047
|
+
* // sessions === ['session:1', 'session:2', ...]
|
|
1048
|
+
* ```
|
|
1049
|
+
*
|
|
1050
|
+
* **Parameters:**
|
|
1051
|
+
* - `pattern` - Glob pattern, e.g. `'session:*'`.
|
|
1052
|
+
* - `namespace` - Optional namespace prefix. The pattern is transformed to
|
|
1053
|
+
* `${namespace}:${pattern}` before scanning.
|
|
1054
|
+
*
|
|
1055
|
+
* @returns Matching keys.
|
|
1056
|
+
*/
|
|
583
1057
|
async keys(pattern, namespace) {
|
|
584
1058
|
const fullPattern = namespace ? `${namespace}:${pattern}` : pattern;
|
|
585
1059
|
const keys = [];
|
|
@@ -600,6 +1074,28 @@ export class Cache {
|
|
|
600
1074
|
* const cleared = await cache.clearNamespace('sessions');
|
|
601
1075
|
* ```
|
|
602
1076
|
*/
|
|
1077
|
+
/**
|
|
1078
|
+
* Deletes every key inside a namespace.
|
|
1079
|
+
*
|
|
1080
|
+
* **Behavior:**
|
|
1081
|
+
* - Deletes all keys matching the pattern `*` within the specified namespace.
|
|
1082
|
+
* - Internally calls {@link deletePattern} with the pattern `*` and the given namespace.
|
|
1083
|
+
*
|
|
1084
|
+
* **Returns:**
|
|
1085
|
+
* - The number of deleted keys.
|
|
1086
|
+
*
|
|
1087
|
+
* **Example:**
|
|
1088
|
+
* ```ts
|
|
1089
|
+
* const cleared = await cache.clearNamespace('sessions');
|
|
1090
|
+
* // cleared === number of keys deleted in the 'sessions' namespace
|
|
1091
|
+
* ```
|
|
1092
|
+
*
|
|
1093
|
+
* **Parameters:**
|
|
1094
|
+
* - `namespace` - Namespace to wipe (e.g. `'sessions'`). Every key of the form
|
|
1095
|
+
* `namespace:*` will be deleted.
|
|
1096
|
+
*
|
|
1097
|
+
* @returns The number of deleted keys.
|
|
1098
|
+
*/
|
|
603
1099
|
async clearNamespace(namespace) {
|
|
604
1100
|
return this.deletePattern('*', namespace);
|
|
605
1101
|
}
|