ioredis-toolkit 0.0.4 → 0.0.6

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/dist/pubsub.js CHANGED
@@ -4,21 +4,50 @@ import { defaultLogger } from './logger.js';
4
4
  /**
5
5
  * Redis Pub/Sub with a dedicated publisher and subscriber connection.
6
6
  *
7
- * Messages are JSON-serialized on publish and auto-parsed on delivery.
8
- * Extends `EventEmitter` and emits `'error'` on subscriber failures.
7
+ * **Description:**
8
+ * Provides a higher-level interface over native Redis Pub/Sub with the following features:
9
+ * - Dedicated publisher connection (configured at construction)
10
+ * - Dedicated subscriber connection (opened via {@link connectSubscriber})
11
+ * - JSON serialization on publish, auto-parsing on delivery
12
+ * - Pattern subscription support (`psubscribe`, `punsubscribe`)
13
+ * - Extends Node.js `EventEmitter` for event-based handling
14
+ * - Emits `'error'` events on subscriber failures
9
15
  *
10
- * Works in all three modes (standalone, sentinel, cluster).
16
+ * **Type Parameters:**
17
+ * - `T` - The type of message payload. When publishing non-string values, they are
18
+ * JSON-serialized. When subscribing, messages are auto-parsed from JSON when possible.
11
19
  *
12
- * @example
20
+ * **Mode Support:**
21
+ * - Standalone, Sentinel, and Cluster modes are all supported.
22
+ * - The subscriber connection is created per the configured mode.
23
+ *
24
+ * **Example:**
13
25
  * ```ts
14
26
  * const pubsub = new PubSub(client);
15
- * await pubsub.connectSubscriber(redisConfig);
27
+ * await pubsub.connectSubscriber({ mode: 'standalone', host: 'localhost', port: 6379 });
16
28
  *
29
+ * // Subscribe to a channel
17
30
  * await pubsub.subscribe('orders:created', (message) => {
18
- * console.log(message); // { id: 1 }
31
+ * console.log(message); // { id: 1 } - JSON-parsed
32
+ * });
33
+ *
34
+ * // Publish a message
35
+ * const receivers = await pubsub.publish('orders:created', { id: 1 });
36
+ * // receivers === number of subscribers that received the message
37
+ *
38
+ * // Pattern subscription
39
+ * await pubsub.psubscribe('orders:*', ({ channel, message }) => {
40
+ * console.log(channel, message);
19
41
  * });
20
- * await pubsub.publish('orders:created', { id: 1 });
21
42
  * ```
43
+ *
44
+ * **Event Emissions:**
45
+ * - `message`: Emitted when a message is received on a subscribed channel.
46
+ * Payload: `{ channel: string, message: string }`
47
+ * - `pmessage`: Emitted when a pattern message is received.
48
+ * Payload: `{ pattern: string, channel: string, message: string }`
49
+ * - `error`: Emitted on subscriber errors.
50
+ * Payload: `Error`
22
51
  */
23
52
  export class PubSub extends EventEmitter {
24
53
  publisher;
@@ -26,6 +55,18 @@ export class PubSub extends EventEmitter {
26
55
  logger;
27
56
  subscriptions = new Map();
28
57
  patternSubscriptions = new Map();
58
+ /**
59
+ * Creates a pub/sub instance. Publishing works immediately; subscribing
60
+ * requires calling {@link connectSubscriber} first.
61
+ *
62
+ * @param publisher - A {@link RedisClientWrapper} used for publishing.
63
+ * @param logger - Optional pino-compatible logger; defaults to `console`.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * const pubsub = new PubSub(client);
68
+ * ```
69
+ */
29
70
  /**
30
71
  * Creates a pub/sub instance. Publishing works immediately; subscribing
31
72
  * requires calling {@link connectSubscriber} first.
@@ -56,12 +97,46 @@ export class PubSub extends EventEmitter {
56
97
  * await pubsub.connectSubscriber({ mode: 'standalone', host: 'localhost', port: 6379 });
57
98
  * ```
58
99
  */
100
+ /**
101
+ * Opens a dedicated subscriber connection.
102
+ *
103
+ * **Behavior:**
104
+ * - Idempotent: a second call is a no-op while a subscriber is connected.
105
+ * - Subscriber errors are emitted as `'error'` events on the instance.
106
+ *
107
+ * **Parameters:**
108
+ * - `config` - Redis config for the subscriber connection (any mode: standalone, sentinel, or cluster).
109
+ *
110
+ * **Example:**
111
+ * ```ts
112
+ * // Connect with standalone configuration
113
+ * await pubsub.connectSubscriber({ mode: 'standalone', host: 'localhost', port: 6379 });
114
+ *
115
+ * // Connect with cluster configuration
116
+ * await pubsub.connectSubscriber({
117
+ * mode: 'cluster',
118
+ * clusterNodes: [{ host: 'redis1', port: 7000 }, { host: 'redis2', port: 7001 }],
119
+ * });
120
+ * ```
121
+ *
122
+ * @returns `Promise<void>` that resolves when the subscriber connection is established.
123
+ */
59
124
  async connectSubscriber(config) {
60
125
  if (this.subscriber)
61
126
  return;
62
127
  this.subscriber = new RedisClientWrapper(config, this.logger);
63
128
  this.setupSubscriber();
64
129
  }
130
+ /**
131
+ * Sets up event listeners on the subscriber connection.
132
+ *
133
+ * **Behavior:**
134
+ * - Listens for `message` events and dispatches to {@link handleMessage}.
135
+ * - Listens for `pmessage` events (pattern subscriptions) and dispatches to {@link handlePatternMessage}.
136
+ * - Listens for `error` events and emits them on the instance.
137
+ *
138
+ * @internal
139
+ */
65
140
  setupSubscriber() {
66
141
  if (!this.subscriber)
67
142
  return;
@@ -77,6 +152,20 @@ export class PubSub extends EventEmitter {
77
152
  this.emit('error', error);
78
153
  });
79
154
  }
155
+ /**
156
+ * Handles a standard message event from the subscriber.
157
+ *
158
+ * **Behavior:**
159
+ * - Parses the message payload as JSON when possible.
160
+ * - Invokes all registered handlers for the channel.
161
+ * - Catches and logs handler errors without crashing.
162
+ *
163
+ * **Parameters:**
164
+ * - `channel` - The Redis channel name.
165
+ * - `message` - The raw message string from Redis (JSON-encoded).
166
+ *
167
+ * @internal
168
+ */
80
169
  handleMessage(channel, message) {
81
170
  const handlers = this.subscriptions.get(channel);
82
171
  if (!handlers)
@@ -95,6 +184,22 @@ export class PubSub extends EventEmitter {
95
184
  }
96
185
  }
97
186
  }
187
+ /**
188
+ * Handles a pattern message event from the subscriber.
189
+ *
190
+ * **Behavior:**
191
+ * - Parses the message payload as JSON when possible.
192
+ * - Invokes all registered handlers for the pattern.
193
+ * - Each handler receives an object with `channel` and `message` properties.
194
+ * - Catches and logs handler errors without crashing.
195
+ *
196
+ * **Parameters:**
197
+ * - `pattern` - The pattern that matched.
198
+ * - `channel` - The specific channel that was matched.
199
+ * - `message` - The raw message string from Redis (JSON-encoded).
200
+ *
201
+ * @internal
202
+ */
98
203
  handlePatternMessage(pattern, channel, message) {
99
204
  const handlers = this.patternSubscriptions.get(pattern);
100
205
  if (!handlers)
@@ -127,6 +232,32 @@ export class PubSub extends EventEmitter {
127
232
  * const receivers = await pubsub.publish('orders:created', { id: 1, total: 99 });
128
233
  * ```
129
234
  */
235
+ /**
236
+ * Publishes a message to a channel.
237
+ *
238
+ * **Behavior:**
239
+ * - Non-string values are JSON-serialized automatically.
240
+ * - The raw message is published to the Redis channel.
241
+ * - Returns the number of subscribers that received the message.
242
+ *
243
+ * **Type Parameters:**
244
+ * - `T` - The type of the message payload. Non-string values are JSON-serialized.
245
+ *
246
+ * **Returns:**
247
+ * - The number of subscribers that received the message.
248
+ *
249
+ * **Example:**
250
+ * ```ts
251
+ * const receivers = await pubsub.publish('orders:created', { id: 1, total: 99 });
252
+ * // receivers === number of subscribed clients
253
+ * ```
254
+ *
255
+ * **Parameters:**
256
+ * - `channel` - The channel name.
257
+ * - `message` - The message payload (string or any JSON-serializable value).
258
+ *
259
+ * @returns The number of subscribers that received the message.
260
+ */
130
261
  async publish(channel, message) {
131
262
  const raw = typeof message === 'string' ? message : JSON.stringify(message);
132
263
  return this.publisher.raw.publish(channel, raw);
@@ -148,6 +279,34 @@ export class PubSub extends EventEmitter {
148
279
  * });
149
280
  * ```
150
281
  */
282
+ /**
283
+ * Subscribes a handler to a channel.
284
+ *
285
+ * **Behavior:**
286
+ * - Multiple handlers per channel are supported; the channel is subscribed on
287
+ * Redis only once.
288
+ * - Delivered payloads are JSON-parsed when possible.
289
+ * - Throws an error if the subscriber connection is not open.
290
+ *
291
+ * **Type Parameters:**
292
+ * - `T` - The type of the message data received in the handler.
293
+ *
294
+ * **Returns:**
295
+ * - `Promise<void>` that resolves when the subscription is established.
296
+ *
297
+ * **Example:**
298
+ * ```ts
299
+ * await pubsub.subscribe('orders:created', (order) => {
300
+ * console.log(order.id);
301
+ * });
302
+ * ```
303
+ *
304
+ * **Parameters:**
305
+ * - `channel` - The channel name.
306
+ * - `handler` - Callback receiving the (parsed) message.
307
+ *
308
+ * @throws `Error` if the subscriber connection is not open.
309
+ */
151
310
  async subscribe(channel, handler) {
152
311
  if (!this.subscriber) {
153
312
  throw new Error('Subscriber not connected');
@@ -175,6 +334,28 @@ export class PubSub extends EventEmitter {
175
334
  * await pubsub.unsubscribe('orders:created'); // remove everything
176
335
  * ```
177
336
  */
337
+ /**
338
+ * Removes a handler (or all handlers) from a channel.
339
+ *
340
+ * **Behavior:**
341
+ * - The Redis subscription is dropped once the last handler for the channel is
342
+ * removed.
343
+ * - Without a handler, the whole channel is unsubscribed.
344
+ *
345
+ * **Returns:**
346
+ * - `Promise<void>` that resolves when the unsubscription is complete.
347
+ *
348
+ * **Example:**
349
+ * ```ts
350
+ * await pubsub.unsubscribe('orders:created', myHandler);
351
+ * await pubsub.unsubscribe('orders:created'); // remove everything
352
+ * ```
353
+ *
354
+ * **Parameters:**
355
+ * - `channel` - The channel name.
356
+ * - `handler` - Optional specific handler to remove; when omitted all
357
+ * handlers for the channel are removed.
358
+ */
178
359
  async unsubscribe(channel, handler) {
179
360
  if (!this.subscriber)
180
361
  return;
@@ -207,6 +388,32 @@ export class PubSub extends EventEmitter {
207
388
  * });
208
389
  * ```
209
390
  */
391
+ /**
392
+ * Subscribes a handler to all channels matching a glob pattern.
393
+ *
394
+ * **Behavior:**
395
+ * - Pattern handlers receive `{ channel, message }` (message JSON-parsed).
396
+ * - The Redis subscription is set up once per pattern.
397
+ *
398
+ * **Type Parameters:**
399
+ * - `T` - The type of the message data received in the handler.
400
+ *
401
+ * **Returns:**
402
+ * - `Promise<void>` that resolves when the pattern subscription is established.
403
+ *
404
+ * **Example:**
405
+ * ```ts
406
+ * await pubsub.psubscribe('orders:*', ({ channel, message }) => {
407
+ * console.log(channel, message);
408
+ * });
409
+ * ```
410
+ *
411
+ * **Parameters:**
412
+ * - `pattern` - Glob pattern, e.g. `'orders:*'`.
413
+ * - `handler` - Callback receiving `{ channel, message }`.
414
+ *
415
+ * @throws `Error` if the subscriber connection is not open.
416
+ */
210
417
  async psubscribe(pattern, handler) {
211
418
  if (!this.subscriber) {
212
419
  throw new Error('Subscriber not connected');
@@ -230,6 +437,23 @@ export class PubSub extends EventEmitter {
230
437
  * await pubsub.punsubscribe('orders:*');
231
438
  * ```
232
439
  */
440
+ /**
441
+ * Removes a handler (or all handlers) from a pattern subscription.
442
+ *
443
+ * **Returns:**
444
+ * - `Promise<void>` that resolves when the punsubscription is complete.
445
+ *
446
+ * **Example:**
447
+ * ```ts
448
+ * await pubsub.punsubscribe('orders:*', myHandler);
449
+ * await pubsub.punsubscribe('orders:*');
450
+ * ```
451
+ *
452
+ * **Parameters:**
453
+ * - `pattern` - The glob pattern.
454
+ * - `handler` - Optional specific handler to remove; when omitted all
455
+ * handlers for the pattern are removed.
456
+ */
233
457
  async punsubscribe(pattern, handler) {
234
458
  if (!this.subscriber)
235
459
  return;
@@ -256,6 +480,20 @@ export class PubSub extends EventEmitter {
256
480
  * await pubsub.close();
257
481
  * ```
258
482
  */
483
+ /**
484
+ * Closes the subscriber connection and clears all subscriptions.
485
+ *
486
+ * **Behavior:**
487
+ * - The publisher client is not closed (it is owned by the caller).
488
+ * - All subscriptions are cleared from memory.
489
+ *
490
+ * **Example:**
491
+ * ```ts
492
+ * await pubsub.close();
493
+ * ```
494
+ *
495
+ * @returns `Promise<void>` that resolves when the subscriber is closed.
496
+ */
259
497
  async close() {
260
498
  if (this.subscriber) {
261
499
  await this.subscriber.close();
@@ -275,6 +513,20 @@ export class PubSub extends EventEmitter {
275
513
  * // { subscriptions: 2, patternSubscriptions: 1, connected: true }
276
514
  * ```
277
515
  */
516
+ /**
517
+ * Returns subscription statistics.
518
+ *
519
+ * **Returns:**
520
+ * - A {@link PubSubStats} object with the current subscription state.
521
+ *
522
+ * **Example:**
523
+ * ```ts
524
+ * const stats = pubsub.getStats();
525
+ * // { subscriptions: 2, patternSubscriptions: 1, connected: true }
526
+ * ```
527
+ *
528
+ * @returns `{ subscriptions, patternSubscriptions, connected }`.
529
+ */
278
530
  getStats() {
279
531
  return {
280
532
  subscriptions: this.subscriptions.size,
@@ -6,10 +6,32 @@ import { RateLimitOptionsInput } from './types.js';
6
6
  * - `fixed` - fixed window via `INCR`/`EXPIRE` (simple, cheapest)
7
7
  * - `sliding` - sliding window via an atomic Lua script over a sorted set (smoothest)
8
8
  */
9
+ /**
10
+ * Window algorithm used by the rate limiter.
11
+ *
12
+ * - `fixed` - Fixed window via `INCR`/`EXPIRE` (simple, cheapest).
13
+ * - `sliding` - Sliding window via an atomic Lua script over a sorted set (smoothest,
14
+ * precise rolling window).
15
+ */
9
16
  export type RateLimitAlgorithm = 'fixed' | 'sliding';
10
17
  /**
11
18
  * Options for a rate limiter instance or an individual call.
12
19
  */
20
+ /**
21
+ * Options for a rate limiter instance or an individual call.
22
+ *
23
+ * **Algorithm Details:**
24
+ * - `sliding` (default): Uses a sorted set with a Lua script for a precise rolling window.
25
+ * New entries are added with the current timestamp, and old entries outside the window
26
+ * are purged before counting. This provides the smoothest rate limiting experience.
27
+ * - `fixed`: Uses a simple counter with `INCR`/`EXPIRE`. The window resets at fixed
28
+ * boundaries (e.g., every 60 seconds from the start). This is the cheapest algorithm
29
+ * but has slightly less precise rate limiting.
30
+ *
31
+ * **Key Naming:**
32
+ * Keys are namespaced as `ratelimit:{namespace}:{resource}:{identifier}` so each
33
+ * resource + identifier pair is tracked independently.
34
+ */
13
35
  export interface RateLimitOptions {
14
36
  /** Maximum allowed requests within `duration`. Default: `100`. */
15
37
  limit?: number;
@@ -23,6 +45,29 @@ export interface RateLimitOptions {
23
45
  /**
24
46
  * Result of a rate limit `consume`/`check` call.
25
47
  */
48
+ /**
49
+ * Result of a rate limit `consume`/`check` call.
50
+ *
51
+ * **Fields:**
52
+ * - `allowed`: `true` when the request is within the rate limit and may proceed.
53
+ * - `limit`: The configured maximum number of requests within the window.
54
+ * - `used`: The number of requests already counted in the current window.
55
+ * - `remaining`: The number of requests still available (`limit - used`, floored at `0`).
56
+ * - `resetAt`: Epoch milliseconds when the current window resets. `0` when the request
57
+ * is allowed (indicating the window is still open).
58
+ * - `retryAfter`: Seconds to wait before retrying the request. `0` when the request
59
+ * is allowed.
60
+ *
61
+ * **Example:**
62
+ * ```ts
63
+ * const result = await limiter.consume('/api/login', 'ip-10.0.0.1');
64
+ * if (!result.allowed) {
65
+ * // result.retryAfter tells you how many seconds to wait before retrying
66
+ * console.log(`Retry after ${result.retryAfter}s`);
67
+ * console.log(`Window resets at ${new Date(result.resetAt)}`);
68
+ * }
69
+ * ```
70
+ */
26
71
  export interface RateLimitResult {
27
72
  /** `true` when the request is within the limit. */
28
73
  allowed: boolean;
@@ -32,7 +77,7 @@ export interface RateLimitResult {
32
77
  used: number;
33
78
  /** Requests still available (`limit - used`, floored at `0`). */
34
79
  remaining: number;
35
- /** Epoch milliseconds when the window resets (0 retry hint when allowed). */
80
+ /** Epoch milliseconds when the window resets. */
36
81
  resetAt: number;
37
82
  /** Seconds to wait before retrying; `0` when allowed. */
38
83
  retryAfter: number;
@@ -78,6 +123,31 @@ export declare class RateLimiter {
78
123
  * ```
79
124
  */
80
125
  constructor(client: RedisClientWrapper, options?: RateLimitOptionsInput, logger?: LoggerLike);
126
+ /**
127
+ * Creates a rate limiter bound to a Redis client.
128
+ *
129
+ * **Default Configuration:**
130
+ * - `limit`: `100` requests per window
131
+ * - `duration`: `60` seconds per window
132
+ * - `algorithm`: `'sliding'` (precise rolling window)
133
+ * - `namespace`: `'ratelimit'` key prefix
134
+ *
135
+ * **Example:**
136
+ * ```ts
137
+ * // Rate limit per route, per IP, 100 requests per 60 seconds (sliding window)
138
+ * const limiter = new RateLimiter(client, { limit: 100, duration: 60 });
139
+ *
140
+ * // Fixed window: 10 requests per 1 second
141
+ * const fixed = new RateLimiter(client, { limit: 10, duration: 1, algorithm: 'fixed' });
142
+ * ```
143
+ *
144
+ * **Parameters:**
145
+ * - `client` - The underlying {@link RedisClientWrapper}. All rate limit operations
146
+ * delegate to this client.
147
+ * - `options` - Default rate limit settings. Overridden per-call via the `consume`
148
+ * and `check` methods.
149
+ * - `logger` - Optional pino-compatible logger. Defaults to `console`.
150
+ */
81
151
  /**
82
152
  * Builds the Redis key for a resource + identifier combination.
83
153
  *
@@ -93,6 +163,27 @@ export declare class RateLimiter {
93
163
  * // 'ratelimit:/api/login:ip-10.0.0.1'
94
164
  * ```
95
165
  */
166
+ /**
167
+ * Builds the Redis key for a resource + identifier combination.
168
+ *
169
+ * **Key Format:**
170
+ * The generated key follows the pattern: `${namespace}:${resource}:${identifier}`
171
+ * For example: `ratelimit:/api/login:ip-10.0.0.1`
172
+ *
173
+ * **Example:**
174
+ * ```ts
175
+ * const key = limiter.makeKey('/api/login', 'ip-10.0.0.1');
176
+ * // key === 'ratelimit:/api/login:ip-10.0.0.1'
177
+ * ```
178
+ *
179
+ * **Parameters:**
180
+ * - `resource` - The rate-limited resource, e.g. a route `'/api/login'` or
181
+ * a resource name `'email:send'`.
182
+ * - `identifier` - The caller identity, e.g. an IP, user id or API key.
183
+ * - `namespace` - Key prefix. Defaults to the limiter's configured namespace.
184
+ *
185
+ * @returns The full key, e.g. `'ratelimit:/api/login:ip-10.0.0.1'`.
186
+ */
96
187
  makeKey(resource: string, identifier: string, namespace?: string): string;
97
188
  /**
98
189
  * Consumes one unit of capacity for a resource + identifier and returns the
@@ -119,6 +210,50 @@ export declare class RateLimiter {
119
210
  * }
120
211
  * ```
121
212
  */
213
+ /**
214
+ * Consumes one unit of capacity for a resource + identifier and returns the
215
+ * resulting limit state.
216
+ *
217
+ * **Behavior:**
218
+ * - When the limit is reached, the request is not recorded and `allowed` is `false`
219
+ * with `retryAfter` (seconds) and `resetAt` (epoch ms) hints.
220
+ * - Fails open (allows the request) if Redis errors occur, so an outage cannot take
221
+ * down the whole app.
222
+ * - Two algorithm modes are available: `sliding` (default, precise rolling window)
223
+ * and `fixed` (simple counter-based).
224
+ *
225
+ * **Type Parameters:**
226
+ * - The return type is {@link RateLimitResult}.
227
+ *
228
+ * **Returns:**
229
+ * - A {@link RateLimitResult} object containing:
230
+ * - `allowed`: whether the request may proceed
231
+ * - `limit`: the configured max
232
+ * - `used`: requests in current window
233
+ * - `remaining`: left in the window
234
+ * - `resetAt`: epoch ms when window resets
235
+ * - `retryAfter`: seconds to wait (0 when allowed)
236
+ *
237
+ * **Example:**
238
+ * ```ts
239
+ * const result = await limiter.consume('/api/login', 'ip-10.0.0.1');
240
+ * if (!result.allowed) {
241
+ * // HTTP 429, set Retry-After: result.retryAfter
242
+ * res.setHeader('Retry-After', String(result.retryAfter));
243
+ * return res.status(429).json({ error: 'Too many requests' });
244
+ * }
245
+ * // allowed === true, request may proceed
246
+ * ```
247
+ *
248
+ * **Parameters:**
249
+ * - `resource` - The rate-limited resource, e.g. a route `'/api/login'` or
250
+ * a resource name `'email:send'`.
251
+ * - `identifier` - The caller identity, e.g. an IP, user id or API key.
252
+ * - `options` - Per-call overrides for `limit`, `duration`, `algorithm`, and `namespace`.
253
+ *
254
+ * @returns The limit state: `allowed`, `limit`, `used`, `remaining`,
255
+ * `resetAt` (epoch ms), `retryAfter` (seconds).
256
+ */
122
257
  consume(resource: string, identifier: string, options?: RateLimitOptions): Promise<RateLimitResult>;
123
258
  /**
124
259
  * Peeks at the current limit state without consuming capacity.
@@ -140,6 +275,37 @@ export declare class RateLimiter {
140
275
  * }
141
276
  * ```
142
277
  */
278
+ /**
279
+ * Peeks at the current limit state without consuming capacity.
280
+ *
281
+ * **Behavior:**
282
+ * - Useful for pre-flight checks (e.g. showing "limit reached" in a UI before
283
+ * the actual request).
284
+ * - Does not increment the counter; only reads the current state.
285
+ * - Fails open (allows the request) if Redis errors occur.
286
+ *
287
+ * **Type Parameters:**
288
+ * - The return type is {@link RateLimitResult}.
289
+ *
290
+ * **Returns:**
291
+ * - A {@link RateLimitResult} object representing the current state.
292
+ * `used` is not incremented.
293
+ *
294
+ * **Example:**
295
+ * ```ts
296
+ * const state = await limiter.check('/api/search', 'user-1');
297
+ * if (state.remaining === 0) {
298
+ * // disable the search button
299
+ * }
300
+ * ```
301
+ *
302
+ * **Parameters:**
303
+ * - `resource` - The rate-limited resource.
304
+ * - `identifier` - The caller identity.
305
+ * - `options` - Per-call overrides for `limit`, `duration`, `algorithm`, and `namespace`.
306
+ *
307
+ * @returns The current limit state; `used` is not incremented.
308
+ */
143
309
  check(resource: string, identifier: string, options?: RateLimitOptions): Promise<RateLimitResult>;
144
310
  /**
145
311
  * Resets the counter for a resource + identifier, granting full capacity again.
@@ -155,9 +321,121 @@ export declare class RateLimiter {
155
321
  * await limiter.reset('/api/export', 'user-7');
156
322
  * ```
157
323
  */
324
+ /**
325
+ * Resets the counter for a resource + identifier, granting full capacity again.
326
+ *
327
+ * **Behavior:**
328
+ * - Deletes the rate limit key from Redis, resetting the counter to zero.
329
+ * - After reset, the next request will be allowed (full capacity available).
330
+ *
331
+ * **Returns:**
332
+ * - `true` if a counter existed and was removed.
333
+ * - `false` if no counter existed (key already deleted).
334
+ *
335
+ * **Example:**
336
+ * ```ts
337
+ * // User upgraded to a premium plan, lift their limits
338
+ * await limiter.reset('/api/export', 'user-7');
339
+ * ```
340
+ *
341
+ * **Parameters:**
342
+ * - `resource` - The rate-limited resource.
343
+ * - `identifier` - The caller identity.
344
+ * - `namespace` - Key prefix. Defaults to the limiter's configured namespace.
345
+ *
346
+ * @returns `true` if a counter existed and was removed.
347
+ */
158
348
  reset(resource: string, identifier: string, namespace?: string): Promise<boolean>;
349
+ /**
350
+ * Consumes one unit of capacity using the fixed-window algorithm.
351
+ *
352
+ * **Behavior:**
353
+ * - Uses Redis `INCR` to increment a counter key.
354
+ * - If the counter was `1` (first request in the window), sets a TTL via `EXPIRE`.
355
+ * - The window resets at fixed boundaries determined by the TTL.
356
+ * - Returns `allowed: true` as long as `count <= limit`.
357
+ *
358
+ * **Returns:**
359
+ * - A {@link RateLimitResult} with the current window state.
360
+ *
361
+ * **Parameters:**
362
+ * - `key` - The Redis key for this resource + identifier combination.
363
+ * - `limit` - The maximum allowed requests within the window.
364
+ * - `duration` - The TTL in seconds for the key (also the window length).
365
+ *
366
+ * @internal
367
+ */
159
368
  private consumeFixed;
369
+ /**
370
+ * Consumes one unit of capacity using the sliding-window algorithm.
371
+ *
372
+ * **Behavior:**
373
+ * - Uses an atomic Lua script over a sorted set for a precise rolling window.
374
+ * - Old entries outside the window are purged before counting.
375
+ * - A unique member (timestamp + UUID) is added for each request.
376
+ * - The `PEXPIRE` command ensures the key expires after the window duration.
377
+ * - Returns `allowed: true` as long as the count of entries within the window is < limit.
378
+ *
379
+ * **The Lua script** (see {@link CONSUME_SCRIPT}) performs these operations atomically:
380
+ * 1. Remove entries with scores older than `now - window`
381
+ * 2. Count remaining entries (`ZCARD`)
382
+ * 3. If count >= limit, return `allowed: false` with `retryAfter`
383
+ * 4. Otherwise, add the new entry (`ZADD`) and return `allowed: true`
384
+ *
385
+ * **Returns:**
386
+ * - A {@link RateLimitResult} with the current window state.
387
+ *
388
+ * **Parameters:**
389
+ * - `key` - The Redis key for this resource + identifier combination.
390
+ * - `limit` - The maximum allowed requests within the window.
391
+ * - `duration` - The window length in seconds.
392
+ *
393
+ * @internal
394
+ */
160
395
  private consumeSliding;
396
+ /**
397
+ * Peeks at the current limit using the fixed-window algorithm.
398
+ *
399
+ * **Behavior:**
400
+ * - Reads the current counter value from Redis via `GET`.
401
+ * - If the key does not exist, `used` is `0`.
402
+ * - Returns `allowed: true` when `used < limit`.
403
+ *
404
+ * **Returns:**
405
+ * - A {@link RateLimitResult} with the current window state.
406
+ *
407
+ * **Parameters:**
408
+ * - `key` - The Redis key for this resource + identifier combination.
409
+ * - `limit` - The maximum allowed requests within the window.
410
+ * - `duration` - The TTL/window length in seconds.
411
+ *
412
+ * @internal
413
+ */
161
414
  private checkFixed;
415
+ /**
416
+ * Peeks at the current limit using the sliding-window algorithm.
417
+ *
418
+ * **Behavior:**
419
+ * - Uses an atomic Lua script (see {@link PEEK_SCRIPT}) to count entries within
420
+ * the rolling window without consuming capacity.
421
+ * - Old entries outside the window are purged before counting.
422
+ * - Returns `allowed: true` when the count of entries within the window is < limit.
423
+ *
424
+ * **The Lua script** (see {@link PEEK_SCRIPT}) performs:
425
+ * 1. Remove entries with scores older than `now - window`
426
+ * 2. Count remaining entries (`ZCARD`)
427
+ * 3. Return the count and optional `retryAfter`
428
+ *
429
+ * **Returns:**
430
+ * - A {@link RateLimitResult} with the current window state.
431
+ * `used` is the count of entries in the window; not incremented.
432
+ *
433
+ * **Parameters:**
434
+ * - `key` - The Redis key for this resource + identifier combination.
435
+ * - `limit` - The maximum allowed requests within the window.
436
+ * - `duration` - The window length in seconds.
437
+ *
438
+ * @internal
439
+ */
162
440
  private checkSliding;
163
441
  }