ioredis-toolkit 0.0.1

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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +645 -0
  3. package/dist/cache.d.ts +298 -0
  4. package/dist/cache.js +606 -0
  5. package/dist/client.d.ts +177 -0
  6. package/dist/client.js +958 -0
  7. package/dist/cluster-slot.d.ts +4 -0
  8. package/dist/cluster-slot.js +31 -0
  9. package/dist/cluster.d.ts +79 -0
  10. package/dist/cluster.js +156 -0
  11. package/dist/errors.d.ts +30 -0
  12. package/dist/errors.js +63 -0
  13. package/dist/health.d.ts +39 -0
  14. package/dist/health.js +106 -0
  15. package/dist/index.d.ts +51 -0
  16. package/dist/index.js +44 -0
  17. package/dist/lock.d.ts +215 -0
  18. package/dist/lock.js +385 -0
  19. package/dist/logger.d.ts +12 -0
  20. package/dist/logger.js +40 -0
  21. package/dist/pubsub.d.ts +171 -0
  22. package/dist/pubsub.js +285 -0
  23. package/dist/ratelimiter.d.ts +162 -0
  24. package/dist/ratelimiter.js +289 -0
  25. package/dist/session/index.d.ts +23 -0
  26. package/dist/session/index.js +16 -0
  27. package/dist/session/revocation-store.d.ts +171 -0
  28. package/dist/session/revocation-store.js +310 -0
  29. package/dist/session/scripts/cleanup-index.lua +21 -0
  30. package/dist/session/scripts/conditional-update-encrypted.lua +60 -0
  31. package/dist/session/scripts/conditional-update.lua +63 -0
  32. package/dist/session/scripts/create.lua +68 -0
  33. package/dist/session/scripts/delete-by-user.lua +29 -0
  34. package/dist/session/scripts/delete.lua +15 -0
  35. package/dist/session/scripts/enforce-limit.lua +38 -0
  36. package/dist/session/scripts/revoke.lua +61 -0
  37. package/dist/session/scripts/rotate-encrypted.lua +107 -0
  38. package/dist/session/scripts/rotate.lua +119 -0
  39. package/dist/session/scripts/touch-encrypted.lua +89 -0
  40. package/dist/session/scripts/touch.lua +72 -0
  41. package/dist/session/scripts/validate.lua +90 -0
  42. package/dist/session/session-circuit-breaker.d.ts +42 -0
  43. package/dist/session/session-circuit-breaker.js +129 -0
  44. package/dist/session/session-config.d.ts +335 -0
  45. package/dist/session/session-config.js +162 -0
  46. package/dist/session/session-cookie.d.ts +72 -0
  47. package/dist/session/session-cookie.js +101 -0
  48. package/dist/session/session-encryption.d.ts +87 -0
  49. package/dist/session/session-encryption.js +139 -0
  50. package/dist/session/session-errors.d.ts +85 -0
  51. package/dist/session/session-errors.js +145 -0
  52. package/dist/session/session-health.d.ts +38 -0
  53. package/dist/session/session-health.js +60 -0
  54. package/dist/session/session-keys.d.ts +51 -0
  55. package/dist/session/session-keys.js +113 -0
  56. package/dist/session/session-manager.d.ts +59 -0
  57. package/dist/session/session-manager.js +94 -0
  58. package/dist/session/session-metrics.d.ts +33 -0
  59. package/dist/session/session-metrics.js +112 -0
  60. package/dist/session/session-repository.d.ts +161 -0
  61. package/dist/session/session-repository.js +683 -0
  62. package/dist/session/session-scripts.d.ts +36 -0
  63. package/dist/session/session-scripts.js +130 -0
  64. package/dist/session/session-serializer.d.ts +42 -0
  65. package/dist/session/session-serializer.js +248 -0
  66. package/dist/session/session-service.d.ts +104 -0
  67. package/dist/session/session-service.js +611 -0
  68. package/dist/session/session-token.d.ts +38 -0
  69. package/dist/session/session-token.js +86 -0
  70. package/dist/session/session-types.d.ts +253 -0
  71. package/dist/session/session-types.js +16 -0
  72. package/dist/types.d.ts +782 -0
  73. package/dist/types.js +140 -0
  74. package/package.json +97 -0
@@ -0,0 +1,171 @@
1
+ import { RedisClientWrapper } from './client.js';
2
+ import { EventEmitter } from 'node:events';
3
+ import { RedisConfig } from './types.js';
4
+ import { LoggerLike } from './logger.js';
5
+ /**
6
+ * Redis Pub/Sub with a dedicated publisher and subscriber connection.
7
+ *
8
+ * Messages are JSON-serialized on publish and auto-parsed on delivery.
9
+ * Extends `EventEmitter` and emits `'error'` on subscriber failures.
10
+ *
11
+ * Works in all three modes (standalone, sentinel, cluster).
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const pubsub = new PubSub(client);
16
+ * await pubsub.connectSubscriber(redisConfig);
17
+ *
18
+ * await pubsub.subscribe('orders:created', (message) => {
19
+ * console.log(message); // { id: 1 }
20
+ * });
21
+ * await pubsub.publish('orders:created', { id: 1 });
22
+ * ```
23
+ */
24
+ export declare class PubSub extends EventEmitter {
25
+ private publisher;
26
+ private subscriber;
27
+ private logger;
28
+ private subscriptions;
29
+ private patternSubscriptions;
30
+ /**
31
+ * Creates a pub/sub instance. Publishing works immediately; subscribing
32
+ * requires calling {@link connectSubscriber} first.
33
+ *
34
+ * @param publisher - A {@link RedisClientWrapper} used for publishing.
35
+ * @param logger - Optional pino-compatible logger; defaults to `console`.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const pubsub = new PubSub(client);
40
+ * ```
41
+ */
42
+ constructor(publisher: RedisClientWrapper, logger?: LoggerLike);
43
+ /**
44
+ * Opens a dedicated subscriber connection.
45
+ *
46
+ * Idempotent: a second call is a no-op while a subscriber is connected.
47
+ * Subscriber errors are emitted as `'error'` events on the instance.
48
+ *
49
+ * @param config - Redis config for the subscriber connection (any mode).
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * await pubsub.connectSubscriber({ mode: 'standalone', host: 'localhost', port: 6379 });
54
+ * ```
55
+ */
56
+ connectSubscriber(config: RedisConfig): Promise<void>;
57
+ private setupSubscriber;
58
+ private handleMessage;
59
+ private handlePatternMessage;
60
+ /**
61
+ * Publishes a message to a channel.
62
+ *
63
+ * Non-string values are JSON-serialized.
64
+ *
65
+ * @param channel - The channel name.
66
+ * @param message - The message payload (string or any JSON-serializable value).
67
+ * @returns The number of subscribers that received the message.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const receivers = await pubsub.publish('orders:created', { id: 1, total: 99 });
72
+ * ```
73
+ */
74
+ publish<T = any>(channel: string, message: T): Promise<number>;
75
+ /**
76
+ * Subscribes a handler to a channel.
77
+ *
78
+ * Multiple handlers per channel are supported; the channel is subscribed on
79
+ * Redis only once. Delivered payloads are JSON-parsed when possible.
80
+ *
81
+ * @param channel - The channel name.
82
+ * @param handler - Callback receiving the (parsed) message.
83
+ * @throws `Error` if the subscriber connection is not open.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * await pubsub.subscribe('orders:created', (order) => {
88
+ * console.log(order.id);
89
+ * });
90
+ * ```
91
+ */
92
+ subscribe<T = any>(channel: string, handler: (data: T) => void): Promise<void>;
93
+ /**
94
+ * Removes a handler (or all handlers) from a channel.
95
+ *
96
+ * The Redis subscription is dropped once the last handler for the channel is
97
+ * removed. Without a handler, the whole channel is unsubscribed.
98
+ *
99
+ * @param channel - The channel name.
100
+ * @param handler - Optional specific handler to remove; when omitted all
101
+ * handlers for the channel are removed.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * await pubsub.unsubscribe('orders:created', myHandler);
106
+ * await pubsub.unsubscribe('orders:created'); // remove everything
107
+ * ```
108
+ */
109
+ unsubscribe<T = any>(channel: string, handler?: (data: T) => void): Promise<void>;
110
+ /**
111
+ * Subscribes a handler to all channels matching a glob pattern.
112
+ *
113
+ * Pattern handlers receive `{ channel, message }` (message JSON-parsed).
114
+ *
115
+ * @param pattern - Glob pattern, e.g. `'orders:*'`.
116
+ * @param handler - Callback receiving `{ channel, message }`.
117
+ * @throws `Error` if the subscriber connection is not open.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * await pubsub.psubscribe('orders:*', ({ channel, message }) => {
122
+ * console.log(channel, message);
123
+ * });
124
+ * ```
125
+ */
126
+ psubscribe<T = any>(pattern: string, handler: (data: {
127
+ channel: string;
128
+ message: T;
129
+ }) => void): Promise<void>;
130
+ /**
131
+ * Removes a handler (or all handlers) from a pattern subscription.
132
+ *
133
+ * @param pattern - The glob pattern.
134
+ * @param handler - Optional specific handler to remove; when omitted all
135
+ * handlers for the pattern are removed.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * await pubsub.punsubscribe('orders:*', myHandler);
140
+ * await pubsub.punsubscribe('orders:*');
141
+ * ```
142
+ */
143
+ punsubscribe(pattern: string, handler?: (data: any) => void): Promise<void>;
144
+ /**
145
+ * Closes the subscriber connection and clears all subscriptions.
146
+ *
147
+ * The publisher client is not closed (it is owned by the caller).
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * await pubsub.close();
152
+ * ```
153
+ */
154
+ close(): Promise<void>;
155
+ /**
156
+ * Returns subscription statistics.
157
+ *
158
+ * @returns `{ subscriptions, patternSubscriptions, connected }`.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const stats = pubsub.getStats();
163
+ * // { subscriptions: 2, patternSubscriptions: 1, connected: true }
164
+ * ```
165
+ */
166
+ getStats(): {
167
+ subscriptions: number;
168
+ patternSubscriptions: number;
169
+ connected: boolean;
170
+ };
171
+ }
package/dist/pubsub.js ADDED
@@ -0,0 +1,285 @@
1
+ import { RedisClientWrapper } from './client.js';
2
+ import { EventEmitter } from 'node:events';
3
+ import { defaultLogger } from './logger.js';
4
+ /**
5
+ * Redis Pub/Sub with a dedicated publisher and subscriber connection.
6
+ *
7
+ * Messages are JSON-serialized on publish and auto-parsed on delivery.
8
+ * Extends `EventEmitter` and emits `'error'` on subscriber failures.
9
+ *
10
+ * Works in all three modes (standalone, sentinel, cluster).
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const pubsub = new PubSub(client);
15
+ * await pubsub.connectSubscriber(redisConfig);
16
+ *
17
+ * await pubsub.subscribe('orders:created', (message) => {
18
+ * console.log(message); // { id: 1 }
19
+ * });
20
+ * await pubsub.publish('orders:created', { id: 1 });
21
+ * ```
22
+ */
23
+ export class PubSub extends EventEmitter {
24
+ publisher;
25
+ subscriber = null;
26
+ logger;
27
+ subscriptions = new Map();
28
+ patternSubscriptions = new Map();
29
+ /**
30
+ * Creates a pub/sub instance. Publishing works immediately; subscribing
31
+ * requires calling {@link connectSubscriber} first.
32
+ *
33
+ * @param publisher - A {@link RedisClientWrapper} used for publishing.
34
+ * @param logger - Optional pino-compatible logger; defaults to `console`.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const pubsub = new PubSub(client);
39
+ * ```
40
+ */
41
+ constructor(publisher, logger = defaultLogger) {
42
+ super();
43
+ this.publisher = publisher;
44
+ this.logger = logger.child({ component: 'PubSub' });
45
+ }
46
+ /**
47
+ * Opens a dedicated subscriber connection.
48
+ *
49
+ * Idempotent: a second call is a no-op while a subscriber is connected.
50
+ * Subscriber errors are emitted as `'error'` events on the instance.
51
+ *
52
+ * @param config - Redis config for the subscriber connection (any mode).
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * await pubsub.connectSubscriber({ mode: 'standalone', host: 'localhost', port: 6379 });
57
+ * ```
58
+ */
59
+ async connectSubscriber(config) {
60
+ if (this.subscriber)
61
+ return;
62
+ this.subscriber = new RedisClientWrapper(config, this.logger);
63
+ this.setupSubscriber();
64
+ }
65
+ setupSubscriber() {
66
+ if (!this.subscriber)
67
+ return;
68
+ const raw = this.subscriber.raw;
69
+ raw.on('message', (channel, message) => {
70
+ this.handleMessage(channel, message);
71
+ });
72
+ raw.on('pmessage', (pattern, channel, message) => {
73
+ this.handlePatternMessage(pattern, channel, message);
74
+ });
75
+ raw.on('error', (error) => {
76
+ this.logger.error('Subscriber error:', error);
77
+ this.emit('error', error);
78
+ });
79
+ }
80
+ handleMessage(channel, message) {
81
+ const handlers = this.subscriptions.get(channel);
82
+ if (!handlers)
83
+ return;
84
+ let parsed = message;
85
+ try {
86
+ parsed = JSON.parse(message);
87
+ }
88
+ catch { }
89
+ for (const handler of handlers) {
90
+ try {
91
+ handler(parsed);
92
+ }
93
+ catch (error) {
94
+ this.logger.error('Handler error:', error);
95
+ }
96
+ }
97
+ }
98
+ handlePatternMessage(pattern, channel, message) {
99
+ const handlers = this.patternSubscriptions.get(pattern);
100
+ if (!handlers)
101
+ return;
102
+ let parsed = message;
103
+ try {
104
+ parsed = JSON.parse(message);
105
+ }
106
+ catch { }
107
+ for (const handler of handlers) {
108
+ try {
109
+ handler({ channel, message: parsed });
110
+ }
111
+ catch (error) {
112
+ this.logger.error('Handler error:', error);
113
+ }
114
+ }
115
+ }
116
+ /**
117
+ * Publishes a message to a channel.
118
+ *
119
+ * Non-string values are JSON-serialized.
120
+ *
121
+ * @param channel - The channel name.
122
+ * @param message - The message payload (string or any JSON-serializable value).
123
+ * @returns The number of subscribers that received the message.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * const receivers = await pubsub.publish('orders:created', { id: 1, total: 99 });
128
+ * ```
129
+ */
130
+ async publish(channel, message) {
131
+ const raw = typeof message === 'string' ? message : JSON.stringify(message);
132
+ return this.publisher.raw.publish(channel, raw);
133
+ }
134
+ /**
135
+ * Subscribes a handler to a channel.
136
+ *
137
+ * Multiple handlers per channel are supported; the channel is subscribed on
138
+ * Redis only once. Delivered payloads are JSON-parsed when possible.
139
+ *
140
+ * @param channel - The channel name.
141
+ * @param handler - Callback receiving the (parsed) message.
142
+ * @throws `Error` if the subscriber connection is not open.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * await pubsub.subscribe('orders:created', (order) => {
147
+ * console.log(order.id);
148
+ * });
149
+ * ```
150
+ */
151
+ async subscribe(channel, handler) {
152
+ if (!this.subscriber) {
153
+ throw new Error('Subscriber not connected');
154
+ }
155
+ if (!this.subscriptions.has(channel)) {
156
+ this.subscriptions.set(channel, new Set());
157
+ await this.subscriber.raw.subscribe(channel);
158
+ }
159
+ this.subscriptions.get(channel).add(handler);
160
+ this.logger.debug('Subscribed to channel', { channel });
161
+ }
162
+ /**
163
+ * Removes a handler (or all handlers) from a channel.
164
+ *
165
+ * The Redis subscription is dropped once the last handler for the channel is
166
+ * removed. Without a handler, the whole channel is unsubscribed.
167
+ *
168
+ * @param channel - The channel name.
169
+ * @param handler - Optional specific handler to remove; when omitted all
170
+ * handlers for the channel are removed.
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * await pubsub.unsubscribe('orders:created', myHandler);
175
+ * await pubsub.unsubscribe('orders:created'); // remove everything
176
+ * ```
177
+ */
178
+ async unsubscribe(channel, handler) {
179
+ if (!this.subscriber)
180
+ return;
181
+ if (handler && this.subscriptions.has(channel)) {
182
+ const handlers = this.subscriptions.get(channel);
183
+ handlers.delete(handler);
184
+ if (handlers.size === 0) {
185
+ this.subscriptions.delete(channel);
186
+ await this.subscriber.raw.unsubscribe(channel);
187
+ }
188
+ }
189
+ else {
190
+ this.subscriptions.delete(channel);
191
+ await this.subscriber.raw.unsubscribe(channel);
192
+ }
193
+ }
194
+ /**
195
+ * Subscribes a handler to all channels matching a glob pattern.
196
+ *
197
+ * Pattern handlers receive `{ channel, message }` (message JSON-parsed).
198
+ *
199
+ * @param pattern - Glob pattern, e.g. `'orders:*'`.
200
+ * @param handler - Callback receiving `{ channel, message }`.
201
+ * @throws `Error` if the subscriber connection is not open.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * await pubsub.psubscribe('orders:*', ({ channel, message }) => {
206
+ * console.log(channel, message);
207
+ * });
208
+ * ```
209
+ */
210
+ async psubscribe(pattern, handler) {
211
+ if (!this.subscriber) {
212
+ throw new Error('Subscriber not connected');
213
+ }
214
+ if (!this.patternSubscriptions.has(pattern)) {
215
+ this.patternSubscriptions.set(pattern, new Set());
216
+ await this.subscriber.raw.psubscribe(pattern);
217
+ }
218
+ this.patternSubscriptions.get(pattern).add(handler);
219
+ }
220
+ /**
221
+ * Removes a handler (or all handlers) from a pattern subscription.
222
+ *
223
+ * @param pattern - The glob pattern.
224
+ * @param handler - Optional specific handler to remove; when omitted all
225
+ * handlers for the pattern are removed.
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * await pubsub.punsubscribe('orders:*', myHandler);
230
+ * await pubsub.punsubscribe('orders:*');
231
+ * ```
232
+ */
233
+ async punsubscribe(pattern, handler) {
234
+ if (!this.subscriber)
235
+ return;
236
+ if (handler && this.patternSubscriptions.has(pattern)) {
237
+ const handlers = this.patternSubscriptions.get(pattern);
238
+ handlers.delete(handler);
239
+ if (handlers.size === 0) {
240
+ this.patternSubscriptions.delete(pattern);
241
+ await this.subscriber.raw.punsubscribe(pattern);
242
+ }
243
+ }
244
+ else {
245
+ this.patternSubscriptions.delete(pattern);
246
+ await this.subscriber.raw.punsubscribe(pattern);
247
+ }
248
+ }
249
+ /**
250
+ * Closes the subscriber connection and clears all subscriptions.
251
+ *
252
+ * The publisher client is not closed (it is owned by the caller).
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * await pubsub.close();
257
+ * ```
258
+ */
259
+ async close() {
260
+ if (this.subscriber) {
261
+ await this.subscriber.close();
262
+ this.subscriber = null;
263
+ }
264
+ this.subscriptions.clear();
265
+ this.patternSubscriptions.clear();
266
+ }
267
+ /**
268
+ * Returns subscription statistics.
269
+ *
270
+ * @returns `{ subscriptions, patternSubscriptions, connected }`.
271
+ *
272
+ * @example
273
+ * ```ts
274
+ * const stats = pubsub.getStats();
275
+ * // { subscriptions: 2, patternSubscriptions: 1, connected: true }
276
+ * ```
277
+ */
278
+ getStats() {
279
+ return {
280
+ subscriptions: this.subscriptions.size,
281
+ patternSubscriptions: this.patternSubscriptions.size,
282
+ connected: this.subscriber !== null,
283
+ };
284
+ }
285
+ }
@@ -0,0 +1,162 @@
1
+ import { RedisClientWrapper } from './client.js';
2
+ import { LoggerLike } from './logger.js';
3
+ /**
4
+ * Window algorithm used by the rate limiter.
5
+ * - `fixed` - fixed window via `INCR`/`EXPIRE` (simple, cheapest)
6
+ * - `sliding` - sliding window via an atomic Lua script over a sorted set (smoothest)
7
+ */
8
+ export type RateLimitAlgorithm = 'fixed' | 'sliding';
9
+ /**
10
+ * Options for a rate limiter instance or an individual call.
11
+ */
12
+ export interface RateLimitOptions {
13
+ /** Maximum allowed requests within `duration`. Default: `100`. */
14
+ limit?: number;
15
+ /** Window length in seconds. Default: `60`. */
16
+ duration?: number;
17
+ /** Window algorithm. Default: `'sliding'`. */
18
+ algorithm?: RateLimitAlgorithm;
19
+ /** Redis key prefix. Default: `'ratelimit'`. */
20
+ namespace?: string;
21
+ }
22
+ /**
23
+ * Result of a rate limit `consume`/`check` call.
24
+ */
25
+ export interface RateLimitResult {
26
+ /** `true` when the request is within the limit. */
27
+ allowed: boolean;
28
+ /** The configured maximum within the window. */
29
+ limit: number;
30
+ /** Requests already counted in the current window. */
31
+ used: number;
32
+ /** Requests still available (`limit - used`, floored at `0`). */
33
+ remaining: number;
34
+ /** Epoch milliseconds when the window resets (0 retry hint when allowed). */
35
+ resetAt: number;
36
+ /** Seconds to wait before retrying; `0` when allowed. */
37
+ retryAfter: number;
38
+ }
39
+ /**
40
+ * Generic Redis-backed rate limiter that works for any resource: routes, API
41
+ * endpoints, users, IPs, databases, email sending, etc.
42
+ *
43
+ * Keys are namespaced as `ratelimit:{namespace}:{resource}:{identifier}` so each
44
+ * resource + identifier combination is tracked independently. Supports fixed-window
45
+ * (`INCR`/`EXPIRE`) and sliding-window (atomic Lua over a sorted set) algorithms.
46
+ * Fails open when Redis is unavailable.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const limiter = new RateLimiter(client, { limit: 100, duration: 60 });
51
+ *
52
+ * const result = await limiter.consume('/api/login', 'ip-10.0.0.1');
53
+ * if (!result.allowed) {
54
+ * throw new Error(`Slow down, retry in ${result.retryAfter}s`);
55
+ * }
56
+ * ```
57
+ */
58
+ export declare class RateLimiter {
59
+ private client;
60
+ private logger;
61
+ private defaultLimit;
62
+ private defaultDuration;
63
+ private defaultAlgorithm;
64
+ private defaultNamespace;
65
+ /**
66
+ * Creates a rate limiter bound to a Redis client.
67
+ *
68
+ * @param client - The underlying {@link RedisClientWrapper}.
69
+ * @param options - Defaults applied when a call does not override them:
70
+ * `limit` (default `100`), `duration` in seconds (default `60`),
71
+ * `algorithm` (default `'sliding'`), `namespace` (default `'ratelimit'`).
72
+ * @param logger - Optional pino-compatible logger; defaults to `console`.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const limiter = new RateLimiter(client, { limit: 10, duration: 1, algorithm: 'fixed' });
77
+ * ```
78
+ */
79
+ constructor(client: RedisClientWrapper, options?: RateLimitOptions, logger?: LoggerLike);
80
+ /**
81
+ * Builds the Redis key for a resource + identifier combination.
82
+ *
83
+ * @param resource - The rate-limited resource, e.g. a route `'/api/login'` or
84
+ * a resource name `'email:send'`.
85
+ * @param identifier - The caller identity, e.g. an IP, user id or API key.
86
+ * @param namespace - Key prefix (defaults to the limiter's namespace).
87
+ * @returns The full key, e.g. `'ratelimit:/api/login:ip-10.0.0.1'`.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * limiter.makeKey('/api/login', 'ip-10.0.0.1');
92
+ * // 'ratelimit:/api/login:ip-10.0.0.1'
93
+ * ```
94
+ */
95
+ makeKey(resource: string, identifier: string, namespace?: string): string;
96
+ /**
97
+ * Consumes one unit of capacity for a resource + identifier and returns the
98
+ * resulting limit state.
99
+ *
100
+ * When the limit is reached the request is not recorded and `allowed` is
101
+ * `false` with `retryAfter` (seconds) and `resetAt` (epoch ms) hints.
102
+ * Fails open (allows the request) if Redis errors.
103
+ *
104
+ * @param resource - The rate-limited resource, e.g. a route `'/api/login'` or
105
+ * a resource name `'db:write'`.
106
+ * @param identifier - The caller identity, e.g. an IP, user id or API key.
107
+ * @param options - Per-call overrides for `limit`, `duration`, `algorithm`,
108
+ * and `namespace`.
109
+ * @returns The limit state: `allowed`, `limit`, `used`, `remaining`,
110
+ * `resetAt` (epoch ms), `retryAfter` (seconds).
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const result = await limiter.consume('/api/orders', 'user-7', { limit: 5, duration: 60 });
115
+ * if (!result.allowed) {
116
+ * res.setHeader('Retry-After', String(result.retryAfter));
117
+ * return res.status(429).json({ error: 'Too many requests' });
118
+ * }
119
+ * ```
120
+ */
121
+ consume(resource: string, identifier: string, options?: RateLimitOptions): Promise<RateLimitResult>;
122
+ /**
123
+ * Peeks at the current limit state without consuming capacity.
124
+ *
125
+ * Useful for pre-flight checks (e.g. showing "limit reached" in a UI before
126
+ * the actual request). Also fails open on Redis errors.
127
+ *
128
+ * @param resource - The rate-limited resource.
129
+ * @param identifier - The caller identity.
130
+ * @param options - Per-call overrides for `limit`, `duration`, `algorithm`,
131
+ * and `namespace`.
132
+ * @returns The current limit state; `used` is not incremented.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * const state = await limiter.check('/api/search', 'user-1');
137
+ * if (state.remaining === 0) {
138
+ * // disable the search button
139
+ * }
140
+ * ```
141
+ */
142
+ check(resource: string, identifier: string, options?: RateLimitOptions): Promise<RateLimitResult>;
143
+ /**
144
+ * Resets the counter for a resource + identifier, granting full capacity again.
145
+ *
146
+ * @param resource - The rate-limited resource.
147
+ * @param identifier - The caller identity.
148
+ * @param namespace - Key prefix (defaults to the limiter's namespace).
149
+ * @returns `true` if a counter existed and was removed.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * // user upgraded to a premium plan, lift their limits
154
+ * await limiter.reset('/api/export', 'user-7');
155
+ * ```
156
+ */
157
+ reset(resource: string, identifier: string, namespace?: string): Promise<boolean>;
158
+ private consumeFixed;
159
+ private consumeSliding;
160
+ private checkFixed;
161
+ private checkSliding;
162
+ }