ioredis-toolkit 0.0.8 → 0.0.10
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 +556 -1
- package/dist/cache.d.ts +39 -36
- package/dist/cache.js +77 -64
- package/dist/client.d.ts +4 -1
- package/dist/client.js +3 -4
- package/dist/lock.d.ts +1 -16
- package/dist/lock.js +43 -0
- package/dist/session/scripts/rotate-encrypted.lua +53 -14
- package/dist/session/scripts/rotate.lua +54 -9
- package/dist/session/session-config.d.ts +20 -0
- package/dist/session/session-config.js +9 -0
- package/dist/session/session-keys.d.ts +13 -0
- package/dist/session/session-keys.js +15 -0
- package/dist/session/session-metrics.d.ts +9 -1
- package/dist/session/session-metrics.js +23 -0
- package/dist/session/session-repository.d.ts +23 -0
- package/dist/session/session-repository.js +102 -22
- package/dist/session/session-serializer.d.ts +1 -1
- package/dist/session/session-serializer.js +19 -0
- package/dist/session/session-service.d.ts +20 -1
- package/dist/session/session-service.js +60 -1
- package/dist/session/session-types.d.ts +28 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.js +1 -0
- package/package.json +1 -1
package/dist/cache.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { RedisClientWrapper } from
|
|
2
|
-
import { CacheOptions, CacheInputConfig } from
|
|
3
|
-
import { LoggerLike } from
|
|
1
|
+
import { RedisClientWrapper } from "./client.js";
|
|
2
|
+
import { CacheOptions, CacheInputConfig } from "./types.js";
|
|
3
|
+
import { LoggerLike } from "./logger.js";
|
|
4
4
|
/**
|
|
5
5
|
* Cache layer on top of {@link RedisClientWrapper} with JSON serialization,
|
|
6
6
|
* optional gzip compression and namespace support.
|
|
@@ -20,6 +20,7 @@ export declare class Cache {
|
|
|
20
20
|
private logger;
|
|
21
21
|
private defaultTTL;
|
|
22
22
|
private compressionThreshold;
|
|
23
|
+
private namespace;
|
|
23
24
|
/**
|
|
24
25
|
* Creates a cache bound to a Redis client.
|
|
25
26
|
*
|
|
@@ -31,6 +32,7 @@ export declare class Cache {
|
|
|
31
32
|
* applied when no per-call TTL is specified.
|
|
32
33
|
* - `compressionThreshold` (number, optional, default: `1024`) - Byte threshold
|
|
33
34
|
* above which values are gzip-compressed transparently.
|
|
35
|
+
* - `namespace` (string, optional, default: `''`) - Namespace prefix for all keys.
|
|
34
36
|
* - `logger` - Optional pino-compatible logger. Supports `trace/debug/info/warn/error/fatal`
|
|
35
37
|
* levels and `child()` for namespace logging. Defaults to `console`.
|
|
36
38
|
*
|
|
@@ -39,12 +41,13 @@ export declare class Cache {
|
|
|
39
41
|
*
|
|
40
42
|
* **Example:**
|
|
41
43
|
* ```ts
|
|
42
|
-
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048 });
|
|
44
|
+
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048, namespace: 'myapp' });
|
|
43
45
|
* ```
|
|
44
46
|
*/
|
|
45
47
|
constructor(client: RedisClientWrapper, config: CacheInputConfig, logger?: LoggerLike);
|
|
46
48
|
private serialize;
|
|
47
49
|
private deserialize;
|
|
50
|
+
private get getNamespace();
|
|
48
51
|
private getKey;
|
|
49
52
|
/**
|
|
50
53
|
* Reads a cached value.
|
|
@@ -132,7 +135,7 @@ export declare class Cache {
|
|
|
132
135
|
*
|
|
133
136
|
* **Type Parameters:**
|
|
134
137
|
* - `T` - The type of the value being stored. Can be any serializable JavaScript value.
|
|
135
|
-
|
|
138
|
+
*
|
|
136
139
|
* **Returns:**
|
|
137
140
|
* - `true` when the value was stored successfully (`result === 'OK'`).
|
|
138
141
|
*
|
|
@@ -266,36 +269,36 @@ export declare class Cache {
|
|
|
266
269
|
* ```
|
|
267
270
|
*/
|
|
268
271
|
/**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
*
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
272
|
+
* Reads multiple cache keys in one call.
|
|
273
|
+
*
|
|
274
|
+
* **Behavior:**
|
|
275
|
+
* - Cluster-safe: keys are grouped by hash slot under the hood, avoiding CROSS-SLOT errors.
|
|
276
|
+
* - Values are deserialized from JSON when stored as JSON. Strings/numbers/buffers
|
|
277
|
+
* are returned as-is.
|
|
278
|
+
* - Missing keys return `null` in the corresponding position.
|
|
279
|
+
*
|
|
280
|
+
* **Type Parameters:**
|
|
281
|
+
* - `T` - The expected type of each returned value. When the stored value is JSON,
|
|
282
|
+
* it will be parsed and coerced to `T`.
|
|
283
|
+
*
|
|
284
|
+
* **Returns:**
|
|
285
|
+
* - An array of values in the same order as the input `keys`. Each element is `T | null`.
|
|
286
|
+
* `null` indicates the key did not exist.
|
|
287
|
+
*
|
|
288
|
+
* **Example:**
|
|
289
|
+
* ```ts
|
|
290
|
+
* const [a, b] = await cache.mget(['user:1', 'user:2']);
|
|
291
|
+
* // a === { name: 'alice' }, b === { name: 'bob' }
|
|
292
|
+
* ```
|
|
293
|
+
*
|
|
294
|
+
* **Parameters:**
|
|
295
|
+
* - `keys` - Cache keys to read. Will have the namespace prefix applied automatically
|
|
296
|
+
* if a namespace is configured.
|
|
297
|
+
* - `namespace` - Optional namespace prefix applied to every key. When provided,
|
|
298
|
+
* each key is internally transformed to `${namespace}:${key}`.
|
|
299
|
+
*
|
|
300
|
+
* @returns Values in input order; `null` for missing keys.
|
|
301
|
+
*/
|
|
299
302
|
mget<T = any>(keys: string[], namespace?: string): Promise<(T | null)[]>;
|
|
300
303
|
/**
|
|
301
304
|
* Stores multiple key/value entries in one call.
|
|
@@ -366,7 +369,7 @@ export declare class Cache {
|
|
|
366
369
|
*
|
|
367
370
|
* **Returns:**
|
|
368
371
|
* - `true` if the key existed and was deleted.
|
|
369
|
-
|
|
372
|
+
*
|
|
370
373
|
* **Example:**
|
|
371
374
|
* ```ts
|
|
372
375
|
* const removed = await cache.delete('user:1');
|
package/dist/cache.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import zlib from
|
|
2
|
-
import { promisify } from
|
|
3
|
-
import { defaultLogger } from
|
|
1
|
+
import zlib from "node:zlib";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { defaultLogger } from "./logger.js";
|
|
4
4
|
const gzip = promisify(zlib.gzip);
|
|
5
5
|
const gunzip = promisify(zlib.gunzip);
|
|
6
6
|
/**
|
|
@@ -23,6 +23,7 @@ export class Cache {
|
|
|
23
23
|
// private config: RedisConfig;
|
|
24
24
|
defaultTTL;
|
|
25
25
|
compressionThreshold;
|
|
26
|
+
namespace;
|
|
26
27
|
/**
|
|
27
28
|
* Creates a cache bound to a Redis client.
|
|
28
29
|
*
|
|
@@ -34,6 +35,7 @@ export class Cache {
|
|
|
34
35
|
* applied when no per-call TTL is specified.
|
|
35
36
|
* - `compressionThreshold` (number, optional, default: `1024`) - Byte threshold
|
|
36
37
|
* above which values are gzip-compressed transparently.
|
|
38
|
+
* - `namespace` (string, optional, default: `''`) - Namespace prefix for all keys.
|
|
37
39
|
* - `logger` - Optional pino-compatible logger. Supports `trace/debug/info/warn/error/fatal`
|
|
38
40
|
* levels and `child()` for namespace logging. Defaults to `console`.
|
|
39
41
|
*
|
|
@@ -42,15 +44,16 @@ export class Cache {
|
|
|
42
44
|
*
|
|
43
45
|
* **Example:**
|
|
44
46
|
* ```ts
|
|
45
|
-
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048 });
|
|
47
|
+
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048, namespace: 'myapp' });
|
|
46
48
|
* ```
|
|
47
49
|
*/
|
|
48
50
|
constructor(client, config, logger = defaultLogger) {
|
|
49
51
|
this.client = client;
|
|
50
|
-
this.logger = logger.child({ component:
|
|
52
|
+
this.logger = logger.child({ component: "Cache" });
|
|
51
53
|
// this.config = config;
|
|
52
54
|
this.defaultTTL = config.defaultTTL || 3600;
|
|
53
55
|
this.compressionThreshold = config.compressionThreshold || 1024;
|
|
56
|
+
this.namespace = config.namespace || "";
|
|
54
57
|
}
|
|
55
58
|
async serialize(value) {
|
|
56
59
|
// Convert to Buffer
|
|
@@ -58,10 +61,10 @@ export class Cache {
|
|
|
58
61
|
if (Buffer.isBuffer(value)) {
|
|
59
62
|
data = value;
|
|
60
63
|
}
|
|
61
|
-
else if (typeof value ===
|
|
64
|
+
else if (typeof value === "string") {
|
|
62
65
|
data = Buffer.from(value);
|
|
63
66
|
}
|
|
64
|
-
else if (typeof value ===
|
|
67
|
+
else if (typeof value === "number" || typeof value === "boolean") {
|
|
65
68
|
data = Buffer.from(String(value));
|
|
66
69
|
}
|
|
67
70
|
else {
|
|
@@ -75,7 +78,7 @@ export class Cache {
|
|
|
75
78
|
return { data: compressed, compressed: true };
|
|
76
79
|
}
|
|
77
80
|
catch (error) {
|
|
78
|
-
this.logger.warn(
|
|
81
|
+
this.logger.warn("Compression failed, storing uncompressed");
|
|
79
82
|
return { data, compressed: false };
|
|
80
83
|
}
|
|
81
84
|
}
|
|
@@ -88,14 +91,14 @@ export class Cache {
|
|
|
88
91
|
buffer = await gunzip(data);
|
|
89
92
|
}
|
|
90
93
|
catch (error) {
|
|
91
|
-
this.logger.warn(
|
|
94
|
+
this.logger.warn("Decompression failed, trying raw data");
|
|
92
95
|
// Attempt to use raw data if decompression fails
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
98
|
// Try to parse as JSON if it looks like JSON
|
|
96
99
|
const str = buffer.toString();
|
|
97
100
|
try {
|
|
98
|
-
if (str.startsWith(
|
|
101
|
+
if (str.startsWith("{") || str.startsWith("[")) {
|
|
99
102
|
return JSON.parse(str);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
@@ -104,8 +107,14 @@ export class Cache {
|
|
|
104
107
|
}
|
|
105
108
|
return str;
|
|
106
109
|
}
|
|
110
|
+
get getNamespace() {
|
|
111
|
+
return this.namespace?.trim() ? `${this.namespace}:` : "";
|
|
112
|
+
}
|
|
107
113
|
getKey(key, namespace) {
|
|
108
|
-
|
|
114
|
+
if (namespace?.trim()) {
|
|
115
|
+
return `${this.getNamespace}${namespace.trim()}:${key}`;
|
|
116
|
+
}
|
|
117
|
+
return `${this.getNamespace}${key}`;
|
|
109
118
|
}
|
|
110
119
|
/**
|
|
111
120
|
* Reads a cached value.
|
|
@@ -170,7 +179,7 @@ export class Cache {
|
|
|
170
179
|
// Check if stored with metadata
|
|
171
180
|
const parsed = JSON.parse(raw);
|
|
172
181
|
if (parsed._compressed && parsed._data) {
|
|
173
|
-
const data = Buffer.from(parsed._data,
|
|
182
|
+
const data = Buffer.from(parsed._data, "base64");
|
|
174
183
|
return this.deserialize(data, parsed._compressed);
|
|
175
184
|
}
|
|
176
185
|
// Legacy format - try to parse as JSON
|
|
@@ -212,7 +221,7 @@ export class Cache {
|
|
|
212
221
|
*
|
|
213
222
|
* **Type Parameters:**
|
|
214
223
|
* - `T` - The type of the value being stored. Can be any serializable JavaScript value.
|
|
215
|
-
|
|
224
|
+
*
|
|
216
225
|
* **Returns:**
|
|
217
226
|
* - `true` when the value was stored successfully (`result === 'OK'`).
|
|
218
227
|
*
|
|
@@ -250,7 +259,7 @@ export class Cache {
|
|
|
250
259
|
// Store with metadata
|
|
251
260
|
rawValue = JSON.stringify({
|
|
252
261
|
_compressed: true,
|
|
253
|
-
_data: data.toString(
|
|
262
|
+
_data: data.toString("base64"),
|
|
254
263
|
});
|
|
255
264
|
}
|
|
256
265
|
else {
|
|
@@ -258,7 +267,7 @@ export class Cache {
|
|
|
258
267
|
}
|
|
259
268
|
}
|
|
260
269
|
else {
|
|
261
|
-
if (typeof value ===
|
|
270
|
+
if (typeof value === "string") {
|
|
262
271
|
rawValue = value;
|
|
263
272
|
}
|
|
264
273
|
else if (Buffer.isBuffer(value)) {
|
|
@@ -269,11 +278,15 @@ export class Cache {
|
|
|
269
278
|
}
|
|
270
279
|
}
|
|
271
280
|
const result = await this.client.set(fullKey, rawValue, ttl);
|
|
272
|
-
this.logger.debug(
|
|
273
|
-
|
|
281
|
+
this.logger.debug("Cache set", {
|
|
282
|
+
key: fullKey,
|
|
283
|
+
ttl,
|
|
284
|
+
compressed: shouldCompress,
|
|
285
|
+
});
|
|
286
|
+
return result === "OK";
|
|
274
287
|
}
|
|
275
288
|
catch (error) {
|
|
276
|
-
this.logger.error(
|
|
289
|
+
this.logger.error("Cache set failed:", error);
|
|
277
290
|
return false;
|
|
278
291
|
}
|
|
279
292
|
}
|
|
@@ -324,12 +337,12 @@ export class Cache {
|
|
|
324
337
|
const fullKey = this.getKey(key, options.namespace);
|
|
325
338
|
const ttl = options.ttl || this.defaultTTL;
|
|
326
339
|
try {
|
|
327
|
-
const rawValue = typeof value ===
|
|
340
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
328
341
|
const result = await this.client.setnx(fullKey, rawValue, ttl);
|
|
329
342
|
return result === 1;
|
|
330
343
|
}
|
|
331
344
|
catch (error) {
|
|
332
|
-
this.logger.error(
|
|
345
|
+
this.logger.error("Cache setNX failed:", error);
|
|
333
346
|
return false;
|
|
334
347
|
}
|
|
335
348
|
}
|
|
@@ -384,12 +397,12 @@ export class Cache {
|
|
|
384
397
|
const fullKey = this.getKey(key, options.namespace);
|
|
385
398
|
const ttl = options.ttl || this.defaultTTL;
|
|
386
399
|
try {
|
|
387
|
-
const rawValue = typeof value ===
|
|
400
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
388
401
|
const result = await this.client.setexnx(fullKey, rawValue, ttl);
|
|
389
|
-
return result ===
|
|
402
|
+
return result === "OK";
|
|
390
403
|
}
|
|
391
404
|
catch (error) {
|
|
392
|
-
this.logger.error(
|
|
405
|
+
this.logger.error("Cache setEXNX failed:", error);
|
|
393
406
|
return false;
|
|
394
407
|
}
|
|
395
408
|
}
|
|
@@ -430,38 +443,38 @@ export class Cache {
|
|
|
430
443
|
* ```
|
|
431
444
|
*/
|
|
432
445
|
/**
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
*
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
446
|
+
* Reads multiple cache keys in one call.
|
|
447
|
+
*
|
|
448
|
+
* **Behavior:**
|
|
449
|
+
* - Cluster-safe: keys are grouped by hash slot under the hood, avoiding CROSS-SLOT errors.
|
|
450
|
+
* - Values are deserialized from JSON when stored as JSON. Strings/numbers/buffers
|
|
451
|
+
* are returned as-is.
|
|
452
|
+
* - Missing keys return `null` in the corresponding position.
|
|
453
|
+
*
|
|
454
|
+
* **Type Parameters:**
|
|
455
|
+
* - `T` - The expected type of each returned value. When the stored value is JSON,
|
|
456
|
+
* it will be parsed and coerced to `T`.
|
|
457
|
+
*
|
|
458
|
+
* **Returns:**
|
|
459
|
+
* - An array of values in the same order as the input `keys`. Each element is `T | null`.
|
|
460
|
+
* `null` indicates the key did not exist.
|
|
461
|
+
*
|
|
462
|
+
* **Example:**
|
|
463
|
+
* ```ts
|
|
464
|
+
* const [a, b] = await cache.mget(['user:1', 'user:2']);
|
|
465
|
+
* // a === { name: 'alice' }, b === { name: 'bob' }
|
|
466
|
+
* ```
|
|
467
|
+
*
|
|
468
|
+
* **Parameters:**
|
|
469
|
+
* - `keys` - Cache keys to read. Will have the namespace prefix applied automatically
|
|
470
|
+
* if a namespace is configured.
|
|
471
|
+
* - `namespace` - Optional namespace prefix applied to every key. When provided,
|
|
472
|
+
* each key is internally transformed to `${namespace}:${key}`.
|
|
473
|
+
*
|
|
474
|
+
* @returns Values in input order; `null` for missing keys.
|
|
475
|
+
*/
|
|
463
476
|
async mget(keys, namespace) {
|
|
464
|
-
const fullKeys = keys.map(k => this.getKey(k, namespace));
|
|
477
|
+
const fullKeys = keys.map((k) => this.getKey(k, namespace));
|
|
465
478
|
const raw = await this.client.mgetClusterAware(fullKeys);
|
|
466
479
|
return Promise.all(raw.map(async (item) => {
|
|
467
480
|
if (!item)
|
|
@@ -469,7 +482,7 @@ export class Cache {
|
|
|
469
482
|
try {
|
|
470
483
|
const parsed = JSON.parse(item);
|
|
471
484
|
if (parsed._compressed && parsed._data) {
|
|
472
|
-
const data = Buffer.from(parsed._data,
|
|
485
|
+
const data = Buffer.from(parsed._data, "base64");
|
|
473
486
|
return this.deserialize(data, parsed._compressed);
|
|
474
487
|
}
|
|
475
488
|
return parsed;
|
|
@@ -558,7 +571,7 @@ export class Cache {
|
|
|
558
571
|
const groups = new Map();
|
|
559
572
|
for (const [key, value] of Object.entries(entries)) {
|
|
560
573
|
const fullKey = this.getKey(key, namespace);
|
|
561
|
-
const rawValue = typeof value ===
|
|
574
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
562
575
|
const slot = this.client.calculateSlot(fullKey);
|
|
563
576
|
if (!groups.has(slot)) {
|
|
564
577
|
groups.set(slot, []);
|
|
@@ -568,17 +581,17 @@ export class Cache {
|
|
|
568
581
|
for (const group of groups.values()) {
|
|
569
582
|
const pipeline = this.client.pipeline();
|
|
570
583
|
for (const [fullKey, rawValue] of group) {
|
|
571
|
-
pipeline.set(fullKey, rawValue,
|
|
584
|
+
pipeline.set(fullKey, rawValue, "EX", ttl);
|
|
572
585
|
}
|
|
573
586
|
const results = await pipeline.exec();
|
|
574
|
-
if (!results?.every((result) => result[1] ===
|
|
587
|
+
if (!results?.every((result) => result[1] === "OK")) {
|
|
575
588
|
return false;
|
|
576
589
|
}
|
|
577
590
|
}
|
|
578
591
|
return true;
|
|
579
592
|
}
|
|
580
593
|
catch (error) {
|
|
581
|
-
this.logger.error(
|
|
594
|
+
this.logger.error("Cache mset failed:", error);
|
|
582
595
|
return false;
|
|
583
596
|
}
|
|
584
597
|
}
|
|
@@ -603,7 +616,7 @@ export class Cache {
|
|
|
603
616
|
*
|
|
604
617
|
* **Returns:**
|
|
605
618
|
* - `true` if the key existed and was deleted.
|
|
606
|
-
|
|
619
|
+
*
|
|
607
620
|
* **Example:**
|
|
608
621
|
* ```ts
|
|
609
622
|
* const removed = await cache.delete('user:1');
|
|
@@ -912,7 +925,7 @@ export class Cache {
|
|
|
912
925
|
*/
|
|
913
926
|
async hset(key, field, value, namespace) {
|
|
914
927
|
const fullKey = this.getKey(key, namespace);
|
|
915
|
-
const rawValue = typeof value ===
|
|
928
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
916
929
|
const result = await this.client.hset(fullKey, field, rawValue);
|
|
917
930
|
return result === 1;
|
|
918
931
|
}
|
|
@@ -1006,7 +1019,7 @@ export class Cache {
|
|
|
1006
1019
|
* @returns The number of deleted keys.
|
|
1007
1020
|
*/
|
|
1008
1021
|
async deletePattern(pattern, namespace) {
|
|
1009
|
-
const fullPattern =
|
|
1022
|
+
const fullPattern = this.getKey(pattern, namespace);
|
|
1010
1023
|
let deleted = 0;
|
|
1011
1024
|
for await (const key of this.client.scanIterator(fullPattern)) {
|
|
1012
1025
|
const result = await this.client.del(key);
|
|
@@ -1055,7 +1068,7 @@ export class Cache {
|
|
|
1055
1068
|
* @returns Matching keys.
|
|
1056
1069
|
*/
|
|
1057
1070
|
async keys(pattern, namespace) {
|
|
1058
|
-
const fullPattern =
|
|
1071
|
+
const fullPattern = this.getKey(pattern, namespace);
|
|
1059
1072
|
const keys = [];
|
|
1060
1073
|
for await (const key of this.client.scanIterator(fullPattern)) {
|
|
1061
1074
|
keys.push(key);
|
|
@@ -1097,6 +1110,6 @@ export class Cache {
|
|
|
1097
1110
|
* @returns The number of deleted keys.
|
|
1098
1111
|
*/
|
|
1099
1112
|
async clearNamespace(namespace) {
|
|
1100
|
-
return this.deletePattern(
|
|
1113
|
+
return this.deletePattern("*", namespace);
|
|
1101
1114
|
}
|
|
1102
1115
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Cluster, Redis as RedisClient } from "ioredis";
|
|
2
|
-
import { RateLimitOptionsInput, type ClusterInfo, type ClusterSlotRange, type ConnectionStatus, type RedisConfigInput, type RedisMode, DistributedLockInputOptions, CacheInputConfig } from "./types.js";
|
|
2
|
+
import { RateLimitOptionsInput, type ClusterInfo, type ClusterRedisConfig, type ClusterSlotRange, type ConnectionStatus, type RedisConfig, type RedisConfigInput, type RedisMode, type SentinelRedisConfig, type StandaloneRedisConfig, DistributedLockInputOptions, CacheInputConfig } from "./types.js";
|
|
3
3
|
import { type LoggerLike } from "./logger.js";
|
|
4
4
|
import { Cache } from "./cache.js";
|
|
5
5
|
import { PubSub } from "./pubsub.js";
|
|
@@ -48,6 +48,9 @@ type DeletePatternOptions = {
|
|
|
48
48
|
batchSize?: number;
|
|
49
49
|
scanCount?: number;
|
|
50
50
|
};
|
|
51
|
+
export declare function isClusterConfig(config: RedisConfig): config is ClusterRedisConfig;
|
|
52
|
+
export declare function isSentinelConfig(config: RedisConfig): config is SentinelRedisConfig;
|
|
53
|
+
export declare function isStandaloneConfig(config: RedisConfig): config is StandaloneRedisConfig;
|
|
51
54
|
/**
|
|
52
55
|
* Production-grade Redis client wrapper supporting:
|
|
53
56
|
*
|
package/dist/client.js
CHANGED
|
@@ -15,13 +15,13 @@ import { deepMerge } from "./utils/deepmerge.js";
|
|
|
15
15
|
// ============================================================================
|
|
16
16
|
// Type Guards
|
|
17
17
|
// ============================================================================
|
|
18
|
-
function isClusterConfig(config) {
|
|
18
|
+
export function isClusterConfig(config) {
|
|
19
19
|
return config.mode === "cluster";
|
|
20
20
|
}
|
|
21
|
-
function isSentinelConfig(config) {
|
|
21
|
+
export function isSentinelConfig(config) {
|
|
22
22
|
return config.mode === "sentinel";
|
|
23
23
|
}
|
|
24
|
-
function isStandaloneConfig(config) {
|
|
24
|
+
export function isStandaloneConfig(config) {
|
|
25
25
|
return config.mode === "standalone";
|
|
26
26
|
}
|
|
27
27
|
// ============================================================================
|
|
@@ -295,7 +295,6 @@ export class RedisClientWrapper {
|
|
|
295
295
|
revocationStore: options.revocationStore ?? this.revocationStore,
|
|
296
296
|
};
|
|
297
297
|
this._session = createSessionManager(params);
|
|
298
|
-
void this._session.init();
|
|
299
298
|
this.config.sessionOptions = params;
|
|
300
299
|
}
|
|
301
300
|
return this._session;
|
package/dist/lock.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RedisClientWrapper } from './client.js';
|
|
2
2
|
import { LoggerLike } from './logger.js';
|
|
3
|
+
import { DistributedLockOptions, LockInfo } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* Information about a distributed lock.
|
|
5
6
|
*
|
|
@@ -14,14 +15,6 @@ import { LoggerLike } from './logger.js';
|
|
|
14
15
|
* // { locked: true, ttl: 29, lockId: 'a1b2c3...' }
|
|
15
16
|
* ```
|
|
16
17
|
*/
|
|
17
|
-
export type LockInfo = {
|
|
18
|
-
/** Whether the lock is currently held. */
|
|
19
|
-
locked: boolean;
|
|
20
|
-
/** Remaining TTL in seconds (when held and TTL set). */
|
|
21
|
-
ttl?: number;
|
|
22
|
-
/** Unique owner id of the lock. */
|
|
23
|
-
lockId?: string;
|
|
24
|
-
};
|
|
25
18
|
/**
|
|
26
19
|
* Options for the distributed lock.
|
|
27
20
|
*
|
|
@@ -35,14 +28,6 @@ export type LockInfo = {
|
|
|
35
28
|
* const lock = new DistributedLock(client, { ttl: 10000, retryCount: 5 });
|
|
36
29
|
* ```
|
|
37
30
|
*/
|
|
38
|
-
export interface DistributedLockOptions {
|
|
39
|
-
/** Lock TTL in milliseconds. Default: `30000`. */
|
|
40
|
-
ttl?: number;
|
|
41
|
-
/** Number of acquisition attempts. Default: `3`. */
|
|
42
|
-
retryCount?: number;
|
|
43
|
-
/** Base delay between retries in ms (grows exponentially). Default: `200`. */
|
|
44
|
-
retryDelay?: number;
|
|
45
|
-
}
|
|
46
31
|
/**
|
|
47
32
|
* Distributed mutual-exclusion lock backed by Redis.
|
|
48
33
|
*
|
package/dist/lock.js
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
import { RedisError } from './errors.js';
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import { defaultLogger } from './logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Information about a distributed lock.
|
|
6
|
+
*
|
|
7
|
+
* **Fields:**
|
|
8
|
+
* - `locked`: Whether the lock is currently held.
|
|
9
|
+
* - `ttl`: Remaining TTL in seconds (when held and TTL set).
|
|
10
|
+
* - `lockId`: Unique owner id of the lock.
|
|
11
|
+
*
|
|
12
|
+
* **Example:**
|
|
13
|
+
* ```ts
|
|
14
|
+
* const info = await lock.getLockInfo('order:42');
|
|
15
|
+
* // { locked: true, ttl: 29, lockId: 'a1b2c3...' }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
// export type LockInfo = {
|
|
19
|
+
// /** Whether the lock is currently held. */
|
|
20
|
+
// locked: boolean;
|
|
21
|
+
// /** Remaining TTL in seconds (when held and TTL set). */
|
|
22
|
+
// ttl?: number;
|
|
23
|
+
// /** Unique owner id of the lock. */
|
|
24
|
+
// lockId?: string;
|
|
25
|
+
// };
|
|
26
|
+
/**
|
|
27
|
+
* Options for the distributed lock.
|
|
28
|
+
*
|
|
29
|
+
* **Fields:**
|
|
30
|
+
* - `ttl`: Lock TTL in milliseconds. Default: `30000`.
|
|
31
|
+
* - `retryCount`: Number of acquisition attempts. Default: `3`.
|
|
32
|
+
* - `retryDelay`: Base delay between retries in ms (grows exponentially). Default: `200`.
|
|
33
|
+
*
|
|
34
|
+
* **Example:**
|
|
35
|
+
* ```ts
|
|
36
|
+
* const lock = new DistributedLock(client, { ttl: 10000, retryCount: 5 });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
// export interface DistributedLockOptions {
|
|
40
|
+
// /** Lock TTL in milliseconds. Default: `30000`. */
|
|
41
|
+
// ttl?: number;
|
|
42
|
+
// /** Number of acquisition attempts. Default: `3`. */
|
|
43
|
+
// retryCount?: number;
|
|
44
|
+
// /** Base delay between retries in ms (grows exponentially). Default: `200`. */
|
|
45
|
+
// retryDelay?: number;
|
|
46
|
+
// }
|
|
4
47
|
/**
|
|
5
48
|
* Distributed mutual-exclusion lock backed by Redis.
|
|
6
49
|
*
|