@upstash/ratelimit 2.0.6 → 2.0.8

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/index.d.mts CHANGED
@@ -12,7 +12,7 @@ type EphemeralCache = {
12
12
  blockUntil: (identifier: string, reset: number) => void;
13
13
  set: (key: string, value: number) => void;
14
14
  get: (key: string) => number | null;
15
- incr: (key: string) => number;
15
+ incr: (key: string, incrementAmount?: number) => number;
16
16
  pop: (key: string) => void;
17
17
  empty: () => void;
18
18
  size: () => number;
@@ -20,6 +20,15 @@ type EphemeralCache = {
20
20
  type RegionContext = {
21
21
  redis: Redis$1;
22
22
  cache?: EphemeralCache;
23
+ /**
24
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
25
+ * using MGET before applying the regular limit.
26
+ */
27
+ dynamicLimits?: boolean;
28
+ /**
29
+ * The prefix used for Redis keys
30
+ */
31
+ prefix: string;
23
32
  };
24
33
  type MultiRegionContext = {
25
34
  regionContexts: Omit<RegionContext[], "cache">;
@@ -87,6 +96,7 @@ type Algorithm<TContext> = () => {
87
96
  getRemaining: (ctx: TContext, identifier: string) => Promise<{
88
97
  remaining: number;
89
98
  reset: number;
99
+ limit: number;
90
100
  }>;
91
101
  resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
92
102
  };
@@ -183,6 +193,16 @@ type RatelimitConfig<TContext> = {
183
193
  * @default `@upstash/ratelimit`
184
194
  */
185
195
  prefix?: string;
196
+ /**
197
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
198
+ * before applying the regular limit. This allows you to change the rate
199
+ * limit at runtime using setDynamicLimit().
200
+ *
201
+ * When enabled, adds +1 Redis command (GET) to every limit check.
202
+ *
203
+ * @default false
204
+ */
205
+ dynamicLimits?: boolean;
186
206
  /**
187
207
  * If enabled, the ratelimiter will keep a global cache of identifiers, that have
188
208
  * exhausted their ratelimit. In serverless environments this is only possible if
@@ -249,6 +269,7 @@ declare abstract class Ratelimit<TContext extends Context> {
249
269
  protected readonly analytics?: Analytics;
250
270
  protected readonly enableProtection: boolean;
251
271
  protected readonly denyListThreshold: number;
272
+ protected readonly dynamicLimits: boolean;
252
273
  constructor(config: RatelimitConfig<TContext>);
253
274
  /**
254
275
  * Determine if a request should pass or be rejected based on the identifier and previously chosen ratelimit.
@@ -315,13 +336,14 @@ declare abstract class Ratelimit<TContext extends Context> {
315
336
  * Returns the remaining token count together with a reset timestamps
316
337
  *
317
338
  * @param identifier identifir to check
318
- * @returns object with `remaining` and reset fields. `remaining` denotes
319
- * the remaining tokens and reset denotes the timestamp when the
320
- * tokens reset.
339
+ * @returns object with `remaining`, `reset`, and `limit` fields. `remaining` denotes
340
+ * the remaining tokens, `limit` is the effective limit (considering dynamic
341
+ * limits if enabled), and `reset` denotes the timestamp when the tokens reset.
321
342
  */
322
343
  getRemaining: (identifier: string) => Promise<{
323
344
  remaining: number;
324
345
  reset: number;
346
+ limit: number;
325
347
  }>;
326
348
  /**
327
349
  * Checks if the identifier or the values in req are in the deny list cache.
@@ -363,6 +385,46 @@ declare abstract class Ratelimit<TContext extends Context> {
363
385
  * @returns list of defined values
364
386
  */
365
387
  private getDefinedMembers;
388
+ /**
389
+ * Set a dynamic rate limit globally.
390
+ *
391
+ * When dynamicLimits is enabled, this limit will override the default limit
392
+ * set in the constructor for all requests.
393
+ *
394
+ * @example
395
+ * ```ts
396
+ * const ratelimit = new Ratelimit({
397
+ * redis: Redis.fromEnv(),
398
+ * limiter: Ratelimit.slidingWindow(10, "10 s"),
399
+ * dynamicLimits: true
400
+ * });
401
+ *
402
+ * // Set global dynamic limit to 120 requests
403
+ * await ratelimit.setDynamicLimit({ limit: 120 });
404
+ *
405
+ * // Disable dynamic limit (falls back to default)
406
+ * await ratelimit.setDynamicLimit({ limit: false });
407
+ * ```
408
+ *
409
+ * @param options.limit - The new rate limit to apply globally, or false to disable
410
+ */
411
+ setDynamicLimit: (options: {
412
+ limit: number | false;
413
+ }) => Promise<void>;
414
+ /**
415
+ * Get the current global dynamic rate limit.
416
+ *
417
+ * @example
418
+ * ```ts
419
+ * const { dynamicLimit } = await ratelimit.getDynamicLimit();
420
+ * console.log(dynamicLimit); // 120 or null if not set
421
+ * ```
422
+ *
423
+ * @returns Object containing the current global dynamic limit, or null if not set
424
+ */
425
+ getDynamicLimit: () => Promise<{
426
+ dynamicLimit: number | null;
427
+ }>;
366
428
  }
367
429
 
368
430
  type MultiRegionRatelimitConfig = {
@@ -422,6 +484,17 @@ type MultiRegionRatelimitConfig = {
422
484
  * @default true
423
485
  */
424
486
  cacheScripts?: boolean;
487
+ /**
488
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
489
+ * before applying the regular limit. This allows you to change the rate
490
+ * limit at runtime using setDynamicLimit().
491
+ *
492
+ * Note: Dynamic limits are not yet supported for multi-region rate limiters.
493
+ * This option will be ignored for MultiRegionRatelimit.
494
+ *
495
+ * @default false
496
+ */
497
+ dynamicLimits?: boolean;
425
498
  };
426
499
  /**
427
500
  * Ratelimiter using serverless redis from https://upstash.com/
@@ -497,7 +570,7 @@ declare class MultiRegionRatelimit extends Ratelimit<MultiRegionContext> {
497
570
  window: Duration): Algorithm<MultiRegionContext>;
498
571
  }
499
572
 
500
- type Redis = Pick<Redis$1, "get" | "set">;
573
+ type Redis = Pick<Redis$1, "evalsha" | "get" | "set">;
501
574
  type RegionRatelimitConfig = {
502
575
  /**
503
576
  * Instance of `@upstash/redis`
@@ -569,6 +642,16 @@ type RegionRatelimitConfig = {
569
642
  * @default 6
570
643
  */
571
644
  denyListThreshold?: number;
645
+ /**
646
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
647
+ * before applying the regular limit. This allows you to change the rate
648
+ * limit at runtime using setDynamicLimit().
649
+ *
650
+ * When enabled, adds +1 Redis command (GET) to every limit check.
651
+ *
652
+ * @default false
653
+ */
654
+ dynamicLimits?: boolean;
572
655
  };
573
656
  /**
574
657
  * Ratelimiter using serverless redis from https://upstash.com/
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ type EphemeralCache = {
12
12
  blockUntil: (identifier: string, reset: number) => void;
13
13
  set: (key: string, value: number) => void;
14
14
  get: (key: string) => number | null;
15
- incr: (key: string) => number;
15
+ incr: (key: string, incrementAmount?: number) => number;
16
16
  pop: (key: string) => void;
17
17
  empty: () => void;
18
18
  size: () => number;
@@ -20,6 +20,15 @@ type EphemeralCache = {
20
20
  type RegionContext = {
21
21
  redis: Redis$1;
22
22
  cache?: EphemeralCache;
23
+ /**
24
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
25
+ * using MGET before applying the regular limit.
26
+ */
27
+ dynamicLimits?: boolean;
28
+ /**
29
+ * The prefix used for Redis keys
30
+ */
31
+ prefix: string;
23
32
  };
24
33
  type MultiRegionContext = {
25
34
  regionContexts: Omit<RegionContext[], "cache">;
@@ -87,6 +96,7 @@ type Algorithm<TContext> = () => {
87
96
  getRemaining: (ctx: TContext, identifier: string) => Promise<{
88
97
  remaining: number;
89
98
  reset: number;
99
+ limit: number;
90
100
  }>;
91
101
  resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
92
102
  };
@@ -183,6 +193,16 @@ type RatelimitConfig<TContext> = {
183
193
  * @default `@upstash/ratelimit`
184
194
  */
185
195
  prefix?: string;
196
+ /**
197
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
198
+ * before applying the regular limit. This allows you to change the rate
199
+ * limit at runtime using setDynamicLimit().
200
+ *
201
+ * When enabled, adds +1 Redis command (GET) to every limit check.
202
+ *
203
+ * @default false
204
+ */
205
+ dynamicLimits?: boolean;
186
206
  /**
187
207
  * If enabled, the ratelimiter will keep a global cache of identifiers, that have
188
208
  * exhausted their ratelimit. In serverless environments this is only possible if
@@ -249,6 +269,7 @@ declare abstract class Ratelimit<TContext extends Context> {
249
269
  protected readonly analytics?: Analytics;
250
270
  protected readonly enableProtection: boolean;
251
271
  protected readonly denyListThreshold: number;
272
+ protected readonly dynamicLimits: boolean;
252
273
  constructor(config: RatelimitConfig<TContext>);
253
274
  /**
254
275
  * Determine if a request should pass or be rejected based on the identifier and previously chosen ratelimit.
@@ -315,13 +336,14 @@ declare abstract class Ratelimit<TContext extends Context> {
315
336
  * Returns the remaining token count together with a reset timestamps
316
337
  *
317
338
  * @param identifier identifir to check
318
- * @returns object with `remaining` and reset fields. `remaining` denotes
319
- * the remaining tokens and reset denotes the timestamp when the
320
- * tokens reset.
339
+ * @returns object with `remaining`, `reset`, and `limit` fields. `remaining` denotes
340
+ * the remaining tokens, `limit` is the effective limit (considering dynamic
341
+ * limits if enabled), and `reset` denotes the timestamp when the tokens reset.
321
342
  */
322
343
  getRemaining: (identifier: string) => Promise<{
323
344
  remaining: number;
324
345
  reset: number;
346
+ limit: number;
325
347
  }>;
326
348
  /**
327
349
  * Checks if the identifier or the values in req are in the deny list cache.
@@ -363,6 +385,46 @@ declare abstract class Ratelimit<TContext extends Context> {
363
385
  * @returns list of defined values
364
386
  */
365
387
  private getDefinedMembers;
388
+ /**
389
+ * Set a dynamic rate limit globally.
390
+ *
391
+ * When dynamicLimits is enabled, this limit will override the default limit
392
+ * set in the constructor for all requests.
393
+ *
394
+ * @example
395
+ * ```ts
396
+ * const ratelimit = new Ratelimit({
397
+ * redis: Redis.fromEnv(),
398
+ * limiter: Ratelimit.slidingWindow(10, "10 s"),
399
+ * dynamicLimits: true
400
+ * });
401
+ *
402
+ * // Set global dynamic limit to 120 requests
403
+ * await ratelimit.setDynamicLimit({ limit: 120 });
404
+ *
405
+ * // Disable dynamic limit (falls back to default)
406
+ * await ratelimit.setDynamicLimit({ limit: false });
407
+ * ```
408
+ *
409
+ * @param options.limit - The new rate limit to apply globally, or false to disable
410
+ */
411
+ setDynamicLimit: (options: {
412
+ limit: number | false;
413
+ }) => Promise<void>;
414
+ /**
415
+ * Get the current global dynamic rate limit.
416
+ *
417
+ * @example
418
+ * ```ts
419
+ * const { dynamicLimit } = await ratelimit.getDynamicLimit();
420
+ * console.log(dynamicLimit); // 120 or null if not set
421
+ * ```
422
+ *
423
+ * @returns Object containing the current global dynamic limit, or null if not set
424
+ */
425
+ getDynamicLimit: () => Promise<{
426
+ dynamicLimit: number | null;
427
+ }>;
366
428
  }
367
429
 
368
430
  type MultiRegionRatelimitConfig = {
@@ -422,6 +484,17 @@ type MultiRegionRatelimitConfig = {
422
484
  * @default true
423
485
  */
424
486
  cacheScripts?: boolean;
487
+ /**
488
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
489
+ * before applying the regular limit. This allows you to change the rate
490
+ * limit at runtime using setDynamicLimit().
491
+ *
492
+ * Note: Dynamic limits are not yet supported for multi-region rate limiters.
493
+ * This option will be ignored for MultiRegionRatelimit.
494
+ *
495
+ * @default false
496
+ */
497
+ dynamicLimits?: boolean;
425
498
  };
426
499
  /**
427
500
  * Ratelimiter using serverless redis from https://upstash.com/
@@ -497,7 +570,7 @@ declare class MultiRegionRatelimit extends Ratelimit<MultiRegionContext> {
497
570
  window: Duration): Algorithm<MultiRegionContext>;
498
571
  }
499
572
 
500
- type Redis = Pick<Redis$1, "get" | "set">;
573
+ type Redis = Pick<Redis$1, "evalsha" | "get" | "set">;
501
574
  type RegionRatelimitConfig = {
502
575
  /**
503
576
  * Instance of `@upstash/redis`
@@ -569,6 +642,16 @@ type RegionRatelimitConfig = {
569
642
  * @default 6
570
643
  */
571
644
  denyListThreshold?: number;
645
+ /**
646
+ * If enabled, the ratelimiter will check for dynamic limits in Redis
647
+ * before applying the regular limit. This allows you to change the rate
648
+ * limit at runtime using setDynamicLimit().
649
+ *
650
+ * When enabled, adds +1 Redis command (GET) to every limit check.
651
+ *
652
+ * @default false
653
+ */
654
+ dynamicLimits?: boolean;
572
655
  };
573
656
  /**
574
657
  * Ratelimiter using serverless redis from https://upstash.com/