ioredis-toolkit 0.0.9 → 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 +38 -37
- package/dist/cache.js +74 -79
- 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.js +1 -1
- 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.
|
|
@@ -32,7 +32,7 @@ export declare class Cache {
|
|
|
32
32
|
* applied when no per-call TTL is specified.
|
|
33
33
|
* - `compressionThreshold` (number, optional, default: `1024`) - Byte threshold
|
|
34
34
|
* above which values are gzip-compressed transparently.
|
|
35
|
-
* - `namespace` (string, optional, default: `
|
|
35
|
+
* - `namespace` (string, optional, default: `''`) - Namespace prefix for all keys.
|
|
36
36
|
* - `logger` - Optional pino-compatible logger. Supports `trace/debug/info/warn/error/fatal`
|
|
37
37
|
* levels and `child()` for namespace logging. Defaults to `console`.
|
|
38
38
|
*
|
|
@@ -41,12 +41,13 @@ export declare class Cache {
|
|
|
41
41
|
*
|
|
42
42
|
* **Example:**
|
|
43
43
|
* ```ts
|
|
44
|
-
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048, namespace:
|
|
44
|
+
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048, namespace: 'myapp' });
|
|
45
45
|
* ```
|
|
46
46
|
*/
|
|
47
47
|
constructor(client: RedisClientWrapper, config: CacheInputConfig, logger?: LoggerLike);
|
|
48
48
|
private serialize;
|
|
49
49
|
private deserialize;
|
|
50
|
+
private get getNamespace();
|
|
50
51
|
private getKey;
|
|
51
52
|
/**
|
|
52
53
|
* Reads a cached value.
|
|
@@ -134,7 +135,7 @@ export declare class Cache {
|
|
|
134
135
|
*
|
|
135
136
|
* **Type Parameters:**
|
|
136
137
|
* - `T` - The type of the value being stored. Can be any serializable JavaScript value.
|
|
137
|
-
|
|
138
|
+
*
|
|
138
139
|
* **Returns:**
|
|
139
140
|
* - `true` when the value was stored successfully (`result === 'OK'`).
|
|
140
141
|
*
|
|
@@ -268,36 +269,36 @@ export declare class Cache {
|
|
|
268
269
|
* ```
|
|
269
270
|
*/
|
|
270
271
|
/**
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
+
*/
|
|
301
302
|
mget<T = any>(keys: string[], namespace?: string): Promise<(T | null)[]>;
|
|
302
303
|
/**
|
|
303
304
|
* Stores multiple key/value entries in one call.
|
|
@@ -368,7 +369,7 @@ export declare class Cache {
|
|
|
368
369
|
*
|
|
369
370
|
* **Returns:**
|
|
370
371
|
* - `true` if the key existed and was deleted.
|
|
371
|
-
|
|
372
|
+
*
|
|
372
373
|
* **Example:**
|
|
373
374
|
* ```ts
|
|
374
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
|
/**
|
|
@@ -35,7 +35,7 @@ export class Cache {
|
|
|
35
35
|
* applied when no per-call TTL is specified.
|
|
36
36
|
* - `compressionThreshold` (number, optional, default: `1024`) - Byte threshold
|
|
37
37
|
* above which values are gzip-compressed transparently.
|
|
38
|
-
* - `namespace` (string, optional, default: `
|
|
38
|
+
* - `namespace` (string, optional, default: `''`) - Namespace prefix for all keys.
|
|
39
39
|
* - `logger` - Optional pino-compatible logger. Supports `trace/debug/info/warn/error/fatal`
|
|
40
40
|
* levels and `child()` for namespace logging. Defaults to `console`.
|
|
41
41
|
*
|
|
@@ -44,16 +44,16 @@ export class Cache {
|
|
|
44
44
|
*
|
|
45
45
|
* **Example:**
|
|
46
46
|
* ```ts
|
|
47
|
-
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048, namespace:
|
|
47
|
+
* const cache = new Cache(client, { defaultTTL: 600, compressionThreshold: 2048, namespace: 'myapp' });
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
50
|
constructor(client, config, logger = defaultLogger) {
|
|
51
51
|
this.client = client;
|
|
52
|
-
this.logger = logger.child({ component:
|
|
52
|
+
this.logger = logger.child({ component: "Cache" });
|
|
53
53
|
// this.config = config;
|
|
54
54
|
this.defaultTTL = config.defaultTTL || 3600;
|
|
55
55
|
this.compressionThreshold = config.compressionThreshold || 1024;
|
|
56
|
-
this.namespace = config.namespace
|
|
56
|
+
this.namespace = config.namespace || "";
|
|
57
57
|
}
|
|
58
58
|
async serialize(value) {
|
|
59
59
|
// Convert to Buffer
|
|
@@ -61,10 +61,10 @@ export class Cache {
|
|
|
61
61
|
if (Buffer.isBuffer(value)) {
|
|
62
62
|
data = value;
|
|
63
63
|
}
|
|
64
|
-
else if (typeof value ===
|
|
64
|
+
else if (typeof value === "string") {
|
|
65
65
|
data = Buffer.from(value);
|
|
66
66
|
}
|
|
67
|
-
else if (typeof value ===
|
|
67
|
+
else if (typeof value === "number" || typeof value === "boolean") {
|
|
68
68
|
data = Buffer.from(String(value));
|
|
69
69
|
}
|
|
70
70
|
else {
|
|
@@ -78,7 +78,7 @@ export class Cache {
|
|
|
78
78
|
return { data: compressed, compressed: true };
|
|
79
79
|
}
|
|
80
80
|
catch (error) {
|
|
81
|
-
this.logger.warn(
|
|
81
|
+
this.logger.warn("Compression failed, storing uncompressed");
|
|
82
82
|
return { data, compressed: false };
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -91,14 +91,14 @@ export class Cache {
|
|
|
91
91
|
buffer = await gunzip(data);
|
|
92
92
|
}
|
|
93
93
|
catch (error) {
|
|
94
|
-
this.logger.warn(
|
|
94
|
+
this.logger.warn("Decompression failed, trying raw data");
|
|
95
95
|
// Attempt to use raw data if decompression fails
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
// Try to parse as JSON if it looks like JSON
|
|
99
99
|
const str = buffer.toString();
|
|
100
100
|
try {
|
|
101
|
-
if (str.startsWith(
|
|
101
|
+
if (str.startsWith("{") || str.startsWith("[")) {
|
|
102
102
|
return JSON.parse(str);
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -107,11 +107,14 @@ export class Cache {
|
|
|
107
107
|
}
|
|
108
108
|
return str;
|
|
109
109
|
}
|
|
110
|
+
get getNamespace() {
|
|
111
|
+
return this.namespace?.trim() ? `${this.namespace}:` : "";
|
|
112
|
+
}
|
|
110
113
|
getKey(key, namespace) {
|
|
111
114
|
if (namespace?.trim()) {
|
|
112
|
-
return `${this.
|
|
115
|
+
return `${this.getNamespace}${namespace.trim()}:${key}`;
|
|
113
116
|
}
|
|
114
|
-
return `${this.
|
|
117
|
+
return `${this.getNamespace}${key}`;
|
|
115
118
|
}
|
|
116
119
|
/**
|
|
117
120
|
* Reads a cached value.
|
|
@@ -176,7 +179,7 @@ export class Cache {
|
|
|
176
179
|
// Check if stored with metadata
|
|
177
180
|
const parsed = JSON.parse(raw);
|
|
178
181
|
if (parsed._compressed && parsed._data) {
|
|
179
|
-
const data = Buffer.from(parsed._data,
|
|
182
|
+
const data = Buffer.from(parsed._data, "base64");
|
|
180
183
|
return this.deserialize(data, parsed._compressed);
|
|
181
184
|
}
|
|
182
185
|
// Legacy format - try to parse as JSON
|
|
@@ -218,7 +221,7 @@ export class Cache {
|
|
|
218
221
|
*
|
|
219
222
|
* **Type Parameters:**
|
|
220
223
|
* - `T` - The type of the value being stored. Can be any serializable JavaScript value.
|
|
221
|
-
|
|
224
|
+
*
|
|
222
225
|
* **Returns:**
|
|
223
226
|
* - `true` when the value was stored successfully (`result === 'OK'`).
|
|
224
227
|
*
|
|
@@ -256,7 +259,7 @@ export class Cache {
|
|
|
256
259
|
// Store with metadata
|
|
257
260
|
rawValue = JSON.stringify({
|
|
258
261
|
_compressed: true,
|
|
259
|
-
_data: data.toString(
|
|
262
|
+
_data: data.toString("base64"),
|
|
260
263
|
});
|
|
261
264
|
}
|
|
262
265
|
else {
|
|
@@ -264,7 +267,7 @@ export class Cache {
|
|
|
264
267
|
}
|
|
265
268
|
}
|
|
266
269
|
else {
|
|
267
|
-
if (typeof value ===
|
|
270
|
+
if (typeof value === "string") {
|
|
268
271
|
rawValue = value;
|
|
269
272
|
}
|
|
270
273
|
else if (Buffer.isBuffer(value)) {
|
|
@@ -275,11 +278,15 @@ export class Cache {
|
|
|
275
278
|
}
|
|
276
279
|
}
|
|
277
280
|
const result = await this.client.set(fullKey, rawValue, ttl);
|
|
278
|
-
this.logger.debug(
|
|
279
|
-
|
|
281
|
+
this.logger.debug("Cache set", {
|
|
282
|
+
key: fullKey,
|
|
283
|
+
ttl,
|
|
284
|
+
compressed: shouldCompress,
|
|
285
|
+
});
|
|
286
|
+
return result === "OK";
|
|
280
287
|
}
|
|
281
288
|
catch (error) {
|
|
282
|
-
this.logger.error(
|
|
289
|
+
this.logger.error("Cache set failed:", error);
|
|
283
290
|
return false;
|
|
284
291
|
}
|
|
285
292
|
}
|
|
@@ -330,12 +337,12 @@ export class Cache {
|
|
|
330
337
|
const fullKey = this.getKey(key, options.namespace);
|
|
331
338
|
const ttl = options.ttl || this.defaultTTL;
|
|
332
339
|
try {
|
|
333
|
-
const rawValue = typeof value ===
|
|
340
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
334
341
|
const result = await this.client.setnx(fullKey, rawValue, ttl);
|
|
335
342
|
return result === 1;
|
|
336
343
|
}
|
|
337
344
|
catch (error) {
|
|
338
|
-
this.logger.error(
|
|
345
|
+
this.logger.error("Cache setNX failed:", error);
|
|
339
346
|
return false;
|
|
340
347
|
}
|
|
341
348
|
}
|
|
@@ -390,12 +397,12 @@ export class Cache {
|
|
|
390
397
|
const fullKey = this.getKey(key, options.namespace);
|
|
391
398
|
const ttl = options.ttl || this.defaultTTL;
|
|
392
399
|
try {
|
|
393
|
-
const rawValue = typeof value ===
|
|
400
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
394
401
|
const result = await this.client.setexnx(fullKey, rawValue, ttl);
|
|
395
|
-
return result ===
|
|
402
|
+
return result === "OK";
|
|
396
403
|
}
|
|
397
404
|
catch (error) {
|
|
398
|
-
this.logger.error(
|
|
405
|
+
this.logger.error("Cache setEXNX failed:", error);
|
|
399
406
|
return false;
|
|
400
407
|
}
|
|
401
408
|
}
|
|
@@ -436,38 +443,38 @@ export class Cache {
|
|
|
436
443
|
* ```
|
|
437
444
|
*/
|
|
438
445
|
/**
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
*
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
+
*/
|
|
469
476
|
async mget(keys, namespace) {
|
|
470
|
-
const fullKeys = keys.map(k => this.getKey(k, namespace));
|
|
477
|
+
const fullKeys = keys.map((k) => this.getKey(k, namespace));
|
|
471
478
|
const raw = await this.client.mgetClusterAware(fullKeys);
|
|
472
479
|
return Promise.all(raw.map(async (item) => {
|
|
473
480
|
if (!item)
|
|
@@ -475,7 +482,7 @@ export class Cache {
|
|
|
475
482
|
try {
|
|
476
483
|
const parsed = JSON.parse(item);
|
|
477
484
|
if (parsed._compressed && parsed._data) {
|
|
478
|
-
const data = Buffer.from(parsed._data,
|
|
485
|
+
const data = Buffer.from(parsed._data, "base64");
|
|
479
486
|
return this.deserialize(data, parsed._compressed);
|
|
480
487
|
}
|
|
481
488
|
return parsed;
|
|
@@ -564,7 +571,7 @@ export class Cache {
|
|
|
564
571
|
const groups = new Map();
|
|
565
572
|
for (const [key, value] of Object.entries(entries)) {
|
|
566
573
|
const fullKey = this.getKey(key, namespace);
|
|
567
|
-
const rawValue = typeof value ===
|
|
574
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
568
575
|
const slot = this.client.calculateSlot(fullKey);
|
|
569
576
|
if (!groups.has(slot)) {
|
|
570
577
|
groups.set(slot, []);
|
|
@@ -574,17 +581,17 @@ export class Cache {
|
|
|
574
581
|
for (const group of groups.values()) {
|
|
575
582
|
const pipeline = this.client.pipeline();
|
|
576
583
|
for (const [fullKey, rawValue] of group) {
|
|
577
|
-
pipeline.set(fullKey, rawValue,
|
|
584
|
+
pipeline.set(fullKey, rawValue, "EX", ttl);
|
|
578
585
|
}
|
|
579
586
|
const results = await pipeline.exec();
|
|
580
|
-
if (!results?.every((result) => result[1] ===
|
|
587
|
+
if (!results?.every((result) => result[1] === "OK")) {
|
|
581
588
|
return false;
|
|
582
589
|
}
|
|
583
590
|
}
|
|
584
591
|
return true;
|
|
585
592
|
}
|
|
586
593
|
catch (error) {
|
|
587
|
-
this.logger.error(
|
|
594
|
+
this.logger.error("Cache mset failed:", error);
|
|
588
595
|
return false;
|
|
589
596
|
}
|
|
590
597
|
}
|
|
@@ -609,7 +616,7 @@ export class Cache {
|
|
|
609
616
|
*
|
|
610
617
|
* **Returns:**
|
|
611
618
|
* - `true` if the key existed and was deleted.
|
|
612
|
-
|
|
619
|
+
*
|
|
613
620
|
* **Example:**
|
|
614
621
|
* ```ts
|
|
615
622
|
* const removed = await cache.delete('user:1');
|
|
@@ -918,7 +925,7 @@ export class Cache {
|
|
|
918
925
|
*/
|
|
919
926
|
async hset(key, field, value, namespace) {
|
|
920
927
|
const fullKey = this.getKey(key, namespace);
|
|
921
|
-
const rawValue = typeof value ===
|
|
928
|
+
const rawValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
922
929
|
const result = await this.client.hset(fullKey, field, rawValue);
|
|
923
930
|
return result === 1;
|
|
924
931
|
}
|
|
@@ -1012,13 +1019,7 @@ export class Cache {
|
|
|
1012
1019
|
* @returns The number of deleted keys.
|
|
1013
1020
|
*/
|
|
1014
1021
|
async deletePattern(pattern, namespace) {
|
|
1015
|
-
|
|
1016
|
-
if (namespace?.trim()) {
|
|
1017
|
-
fullPattern = `${this.namespace}:${namespace.trim()}:${pattern}`;
|
|
1018
|
-
}
|
|
1019
|
-
else {
|
|
1020
|
-
fullPattern = `${this.namespace}:${pattern}`;
|
|
1021
|
-
}
|
|
1022
|
+
const fullPattern = this.getKey(pattern, namespace);
|
|
1022
1023
|
let deleted = 0;
|
|
1023
1024
|
for await (const key of this.client.scanIterator(fullPattern)) {
|
|
1024
1025
|
const result = await this.client.del(key);
|
|
@@ -1067,13 +1068,7 @@ export class Cache {
|
|
|
1067
1068
|
* @returns Matching keys.
|
|
1068
1069
|
*/
|
|
1069
1070
|
async keys(pattern, namespace) {
|
|
1070
|
-
|
|
1071
|
-
if (namespace?.trim()) {
|
|
1072
|
-
fullPattern = `${this.namespace}:${namespace.trim()}:${pattern}`;
|
|
1073
|
-
}
|
|
1074
|
-
else {
|
|
1075
|
-
fullPattern = `${this.namespace}:${pattern}`;
|
|
1076
|
-
}
|
|
1071
|
+
const fullPattern = this.getKey(pattern, namespace);
|
|
1077
1072
|
const keys = [];
|
|
1078
1073
|
for await (const key of this.client.scanIterator(fullPattern)) {
|
|
1079
1074
|
keys.push(key);
|
|
@@ -1115,6 +1110,6 @@ export class Cache {
|
|
|
1115
1110
|
* @returns The number of deleted keys.
|
|
1116
1111
|
*/
|
|
1117
1112
|
async clearNamespace(namespace) {
|
|
1118
|
-
return this.deletePattern(
|
|
1113
|
+
return this.deletePattern("*", namespace);
|
|
1119
1114
|
}
|
|
1120
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
|
*
|