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
|
@@ -7,7 +7,7 @@ import type { SessionKeyStrategy } from './session-keys.js';
|
|
|
7
7
|
import { SessionMetrics } from './session-metrics.js';
|
|
8
8
|
import { SessionRepository } from './session-repository.js';
|
|
9
9
|
import type { SessionTokenManager } from './session-token.js';
|
|
10
|
-
import type { CreatedSession, ListOptions, RotateOptions, RotatedSession, SessionCreateInput, SessionRecord, SessionUpdatePatch, SessionValidationResult, TouchOptions, TouchOutcome, UpdateOptions, ValidateOptions } from './session-types.js';
|
|
10
|
+
import type { CreatedSession, ListOptions, ReconcileUserResult, RotateOptions, RotatedSession, SessionCreateInput, SessionRecord, SessionUpdatePatch, SessionValidationResult, TouchOptions, TouchOutcome, UpdateOptions, ValidateOptions } from './session-types.js';
|
|
11
11
|
export interface SessionServiceDeps {
|
|
12
12
|
config: SessionConfig;
|
|
13
13
|
client: RedisClientWrapper;
|
|
@@ -85,6 +85,25 @@ export declare class SessionService {
|
|
|
85
85
|
*/
|
|
86
86
|
setSecurityVersion(userId: string, version?: number): Promise<number>;
|
|
87
87
|
getSecurityVersion(userId: string): Promise<number | null>;
|
|
88
|
+
/**
|
|
89
|
+
* Bounded administrative repair pass for one user: prunes stale
|
|
90
|
+
* user-index entries and, when the global jti index is enabled, rewrites
|
|
91
|
+
* any missing/stale jti-index entry for that user's live active sessions.
|
|
92
|
+
*
|
|
93
|
+
* This is NOT required for authentication correctness - every read path
|
|
94
|
+
* (validate/touch/rotate) already treats the session record as
|
|
95
|
+
* authoritative and self-heals stale index entries lazily. This exists
|
|
96
|
+
* purely to shrink the window during which JTI-only lookup (`find(jti)`
|
|
97
|
+
* without a known userId) can miss a live session after a partial write
|
|
98
|
+
* (ยง67), and to give operators a way to proactively repair known drift
|
|
99
|
+
* (e.g. after a Redis incident) instead of waiting for it to be hit
|
|
100
|
+
* randomly. Safe to call repeatedly; every effect is idempotent.
|
|
101
|
+
*
|
|
102
|
+
* Bounded by config.limits.maxSessionsPerUserHardCap, same as
|
|
103
|
+
* revokeAll/deleteByUser - never scans the cluster and is not called
|
|
104
|
+
* from a hot auth path.
|
|
105
|
+
*/
|
|
106
|
+
reconcileUser(userId: string): Promise<ReconcileUserResult>;
|
|
88
107
|
/** Dependency health (PING latency + recent error rate). */
|
|
89
108
|
health(): Promise<ReturnType<SessionHealthChecker['check']>>;
|
|
90
109
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mapWithConcurrency } from '../cluster.js';
|
|
2
|
-
import { SessionConfigurationError, SessionConcurrencyError, SessionError, SessionExpiredError, SessionInvalidError, SessionNotFoundError, SessionRevokedError, SessionRotationError, SessionSerializationError, SessionStorageError, } from './session-errors.js';
|
|
2
|
+
import { SessionConfigurationError, SessionConcurrencyError, SessionError, SessionExpiredError, SessionInvalidError, SessionNotFoundError, SessionReplayError, SessionRevokedError, SessionRotationError, SessionSerializationError, SessionStorageError, } from './session-errors.js';
|
|
3
3
|
import { SessionMetrics } from './session-metrics.js';
|
|
4
4
|
import { assertHeaderMatches, deserializeSession } from './session-serializer.js';
|
|
5
5
|
const IDEMPOTENCY_MIN_LENGTH = 8;
|
|
@@ -150,6 +150,10 @@ export class SessionService {
|
|
|
150
150
|
rotatedTo: null,
|
|
151
151
|
consumedAt: null,
|
|
152
152
|
rotationNonceHash: null,
|
|
153
|
+
// First generation of a lineage: familyId equals its own jti (the
|
|
154
|
+
// convention rotate.lua's self-heal also falls back to for legacy
|
|
155
|
+
// records missing the field - see session-types.ts).
|
|
156
|
+
familyId: jti,
|
|
153
157
|
};
|
|
154
158
|
const ttl = Math.max(1, record.absoluteExpiresAt - now);
|
|
155
159
|
const result = await this.repository.create(record, ttl);
|
|
@@ -315,6 +319,14 @@ export class SessionService {
|
|
|
315
319
|
rotatedTo: null,
|
|
316
320
|
consumedAt: null,
|
|
317
321
|
rotationNonceHash: null,
|
|
322
|
+
// Placeholder only: familyId is an identity field decided
|
|
323
|
+
// authoritatively from the OLD session, not the app. The plain-path
|
|
324
|
+
// script (rotate.lua) always overwrites this before writing; the
|
|
325
|
+
// encrypted path resolves the real value from the just-decrypted
|
|
326
|
+
// predecessor in SessionRepository.rotate() (Lua can't rewrite
|
|
327
|
+
// ciphertext, so that's the only place it can be fixed up). Any
|
|
328
|
+
// syntactically valid jti-shaped string is fine here.
|
|
329
|
+
familyId: oldJti,
|
|
318
330
|
};
|
|
319
331
|
const result = await this.repository.rotate({
|
|
320
332
|
userId,
|
|
@@ -325,6 +337,7 @@ export class SessionService {
|
|
|
325
337
|
: {}),
|
|
326
338
|
...(rotationNonceHash !== undefined ? { rotationNonceHash } : {}),
|
|
327
339
|
retainTombstone: this.config.retainConsumedTombstones,
|
|
340
|
+
revokeFamilyOnReplay: this.config.revokeFamilyOnReplay,
|
|
328
341
|
});
|
|
329
342
|
if (result.code === 1 || result.code === 2) {
|
|
330
343
|
const replayed = result.code === 2;
|
|
@@ -346,6 +359,23 @@ export class SessionService {
|
|
|
346
359
|
// and re-authenticate rather than reusing the old token.
|
|
347
360
|
return replayed ? { session, replayed } : { token: successorToken, session, replayed };
|
|
348
361
|
}
|
|
362
|
+
if (result.code === -6) {
|
|
363
|
+
// Genuine reuse of an already-rotated-away token: the entire
|
|
364
|
+
// lineage's currently active generation was atomically revoked
|
|
365
|
+
// (this is a strong security signal, never an infra/storage
|
|
366
|
+
// failure - see SessionReplayError, not SessionStorageError, so it
|
|
367
|
+
// never trips the circuit breaker per guard()'s classification).
|
|
368
|
+
// The old jti-index entry (if any) no longer points anywhere
|
|
369
|
+
// useful; best-effort clean it up.
|
|
370
|
+
await this.repository.deleteJtiIndex(oldJti);
|
|
371
|
+
throw new SessionReplayError({
|
|
372
|
+
reason: 'family_revoked',
|
|
373
|
+
...(result.familyId !== undefined ? { familyId: result.familyId } : {}),
|
|
374
|
+
...(result.headJtiRevoked !== undefined
|
|
375
|
+
? { headJtiRevoked: result.headJtiRevoked }
|
|
376
|
+
: {}),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
349
379
|
throw rotationError(result.code, result.status);
|
|
350
380
|
});
|
|
351
381
|
}
|
|
@@ -477,6 +507,35 @@ export class SessionService {
|
|
|
477
507
|
});
|
|
478
508
|
}
|
|
479
509
|
/* ------------------------------------------------------------------------ */
|
|
510
|
+
/* Reconciliation (ยง25 / ยง67 / ยง68) */
|
|
511
|
+
/* ------------------------------------------------------------------------ */
|
|
512
|
+
/**
|
|
513
|
+
* Bounded administrative repair pass for one user: prunes stale
|
|
514
|
+
* user-index entries and, when the global jti index is enabled, rewrites
|
|
515
|
+
* any missing/stale jti-index entry for that user's live active sessions.
|
|
516
|
+
*
|
|
517
|
+
* This is NOT required for authentication correctness - every read path
|
|
518
|
+
* (validate/touch/rotate) already treats the session record as
|
|
519
|
+
* authoritative and self-heals stale index entries lazily. This exists
|
|
520
|
+
* purely to shrink the window during which JTI-only lookup (`find(jti)`
|
|
521
|
+
* without a known userId) can miss a live session after a partial write
|
|
522
|
+
* (ยง67), and to give operators a way to proactively repair known drift
|
|
523
|
+
* (e.g. after a Redis incident) instead of waiting for it to be hit
|
|
524
|
+
* randomly. Safe to call repeatedly; every effect is idempotent.
|
|
525
|
+
*
|
|
526
|
+
* Bounded by config.limits.maxSessionsPerUserHardCap, same as
|
|
527
|
+
* revokeAll/deleteByUser - never scans the cluster and is not called
|
|
528
|
+
* from a hot auth path.
|
|
529
|
+
*/
|
|
530
|
+
reconcileUser(userId) {
|
|
531
|
+
return this.guard('reconcile_user', async () => {
|
|
532
|
+
validateUserId(userId);
|
|
533
|
+
const result = await this.repository.reconcileUser(userId, this.config.limits.maxSessionsPerUserHardCap, this.now());
|
|
534
|
+
this.metrics.reconcileUser(result.jtiIndexRepaired, result.staleIndexRemoved);
|
|
535
|
+
return { userId, ...result };
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
/* ------------------------------------------------------------------------ */
|
|
480
539
|
/* Health */
|
|
481
540
|
/* ------------------------------------------------------------------------ */
|
|
482
541
|
/** Dependency health (PING latency + recent error rate). */
|
|
@@ -42,6 +42,15 @@ export type SessionRecord = {
|
|
|
42
42
|
metadata: Record<string, unknown> | null;
|
|
43
43
|
/** JTI this session was rotated from (rotation chains, reuse detection). */
|
|
44
44
|
rotatedFrom: string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Stable identifier for this session's rotation lineage ("family"),
|
|
47
|
+
* unchanged across every generation produced by rotate(). Equal to the
|
|
48
|
+
* first generation's own jti (unguessable, 256-bit) - no separate
|
|
49
|
+
* randomness is generated for it. Used only to detect and respond to
|
|
50
|
+
* stolen-refresh-token reuse (see rotate.lua); it is never consulted by
|
|
51
|
+
* validate() and can never by itself grant authentication (I7).
|
|
52
|
+
*/
|
|
53
|
+
familyId: string;
|
|
45
54
|
/** JTI this session was rotated to (enables retry-safe rotation). */
|
|
46
55
|
rotatedTo: string | null;
|
|
47
56
|
/** When the session was consumed by a rotation (Unix seconds), or null. */
|
|
@@ -189,6 +198,20 @@ export type BindingMismatch = {
|
|
|
189
198
|
userAgent: boolean;
|
|
190
199
|
deviceId: boolean;
|
|
191
200
|
};
|
|
201
|
+
/**
|
|
202
|
+
* Result of a bounded {@link SessionService.reconcileUser} repair pass.
|
|
203
|
+
* `checked` is the number of live session records inspected (bounded by
|
|
204
|
+
* maxSessionsPerUserHardCap); `staleIndexRemoved` counts user-index entries
|
|
205
|
+
* removed because their session record no longer exists or was corrupt;
|
|
206
|
+
* `jtiIndexRepaired` counts global jti-index entries that were missing or
|
|
207
|
+
* stale for a live active session and were rewritten.
|
|
208
|
+
*/
|
|
209
|
+
export type ReconcileUserResult = {
|
|
210
|
+
userId: string;
|
|
211
|
+
checked: number;
|
|
212
|
+
staleIndexRemoved: number;
|
|
213
|
+
jtiIndexRepaired: number;
|
|
214
|
+
};
|
|
192
215
|
/** Schema version of the persisted envelope. */
|
|
193
216
|
export type SerializedSchemaVersion = 1 | 2;
|
|
194
217
|
/**
|
|
@@ -233,6 +256,11 @@ export type EncryptedSessionEnvelope = {
|
|
|
233
256
|
rn: string | null;
|
|
234
257
|
/** Plaintext rotated-to JTI mirror (script access). */
|
|
235
258
|
rj: string | null;
|
|
259
|
+
/**
|
|
260
|
+
* Plaintext rotation-family-id mirror (script access - see rotate.lua).
|
|
261
|
+
* Immutable identity field, unchanged across rotations of one lineage.
|
|
262
|
+
*/
|
|
263
|
+
fam: string;
|
|
236
264
|
};
|
|
237
265
|
export type SessionEnvelope = PlainSessionEnvelope | EncryptedSessionEnvelope;
|
|
238
266
|
export type RevocationRecord = {
|
package/dist/types.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ export type CacheOptions = {
|
|
|
50
50
|
export declare const CacheOptionsSchema: z.ZodObject<{
|
|
51
51
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
52
52
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
53
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
53
54
|
}, z.core.$strip>;
|
|
54
55
|
export type CacheInputConfig = z.input<typeof CacheOptionsSchema>;
|
|
55
56
|
export type CacheStats = {
|
|
@@ -135,6 +136,7 @@ export declare const BaseRedisConfigSchema: z.ZodObject<{
|
|
|
135
136
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
136
137
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
137
138
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
139
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
138
140
|
}, z.core.$strip>>;
|
|
139
141
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
140
142
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -179,6 +181,7 @@ export declare const StandaloneRedisConfigSchema: z.ZodObject<{
|
|
|
179
181
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
180
182
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
181
183
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
184
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
182
185
|
}, z.core.$strip>>;
|
|
183
186
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
184
187
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -224,6 +227,7 @@ export declare const SentinelRedisConfigSchema: z.ZodObject<{
|
|
|
224
227
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
225
228
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
226
229
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
230
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
227
231
|
}, z.core.$strip>>;
|
|
228
232
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
229
233
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -271,6 +275,7 @@ export declare const ClusterRedisConfigSchema: z.ZodObject<{
|
|
|
271
275
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
272
276
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
273
277
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
278
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
274
279
|
}, z.core.$strip>>;
|
|
275
280
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
276
281
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -322,6 +327,7 @@ export declare const RedisConfigInputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
322
327
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
323
328
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
324
329
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
330
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
325
331
|
}, z.core.$strip>>;
|
|
326
332
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
327
333
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -362,6 +368,7 @@ export declare const RedisConfigInputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
362
368
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
363
369
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
364
370
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
371
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
365
372
|
}, z.core.$strip>>;
|
|
366
373
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
367
374
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -403,6 +410,7 @@ export declare const RedisConfigInputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
403
410
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
404
411
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
405
412
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
413
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
406
414
|
}, z.core.$strip>>;
|
|
407
415
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
408
416
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -462,6 +470,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
462
470
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
463
471
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
464
472
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
473
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
465
474
|
}, z.core.$strip>>;
|
|
466
475
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
467
476
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -502,6 +511,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
502
511
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
503
512
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
504
513
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
514
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
505
515
|
}, z.core.$strip>>;
|
|
506
516
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
507
517
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -543,6 +553,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
543
553
|
cacheOptions: z.ZodOptional<z.ZodObject<{
|
|
544
554
|
defaultTTL: z.ZodDefault<z.ZodNumber>;
|
|
545
555
|
compressionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
556
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
546
557
|
}, z.core.$strip>>;
|
|
547
558
|
slowCommandThreshold: z.ZodDefault<z.ZodNumber>;
|
|
548
559
|
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
@@ -592,6 +603,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
592
603
|
cacheOptions?: {
|
|
593
604
|
defaultTTL: number;
|
|
594
605
|
compressionThreshold: number;
|
|
606
|
+
namespace: string;
|
|
595
607
|
} | undefined;
|
|
596
608
|
rateLimit?: {
|
|
597
609
|
limit: number;
|
|
@@ -630,6 +642,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
630
642
|
cacheOptions?: {
|
|
631
643
|
defaultTTL: number;
|
|
632
644
|
compressionThreshold: number;
|
|
645
|
+
namespace: string;
|
|
633
646
|
} | undefined;
|
|
634
647
|
rateLimit?: {
|
|
635
648
|
limit: number;
|
|
@@ -666,6 +679,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
666
679
|
cacheOptions?: {
|
|
667
680
|
defaultTTL: number;
|
|
668
681
|
compressionThreshold: number;
|
|
682
|
+
namespace: string;
|
|
669
683
|
} | undefined;
|
|
670
684
|
rateLimit?: {
|
|
671
685
|
limit: number;
|
|
@@ -702,6 +716,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
702
716
|
cacheOptions?: {
|
|
703
717
|
defaultTTL: number;
|
|
704
718
|
compressionThreshold: number;
|
|
719
|
+
namespace: string;
|
|
705
720
|
} | undefined;
|
|
706
721
|
rateLimit?: {
|
|
707
722
|
limit: number;
|
|
@@ -743,6 +758,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
743
758
|
cacheOptions?: {
|
|
744
759
|
defaultTTL: number;
|
|
745
760
|
compressionThreshold: number;
|
|
761
|
+
namespace: string;
|
|
746
762
|
} | undefined;
|
|
747
763
|
rateLimit?: {
|
|
748
764
|
limit: number;
|
|
@@ -781,6 +797,7 @@ export declare const RedisConfigSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodObje
|
|
|
781
797
|
cacheOptions?: {
|
|
782
798
|
defaultTTL: number;
|
|
783
799
|
compressionThreshold: number;
|
|
800
|
+
namespace: string;
|
|
784
801
|
} | undefined;
|
|
785
802
|
rateLimit?: {
|
|
786
803
|
limit: number;
|
package/dist/types.js
CHANGED
|
@@ -7,6 +7,7 @@ export const DistributedLockOptionsSchema = z.object({
|
|
|
7
7
|
export const CacheOptionsSchema = z.object({
|
|
8
8
|
defaultTTL: z.number().int().min(0).default(3_600),
|
|
9
9
|
compressionThreshold: z.number().int().min(1).default(1_024),
|
|
10
|
+
namespace: z.string().default('')
|
|
10
11
|
});
|
|
11
12
|
// ============================================================================
|
|
12
13
|
// Rate Limiting
|
package/package.json
CHANGED