@upstash/ratelimit 2.0.8 → 2.1.0

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.mjs CHANGED
@@ -156,7 +156,7 @@ var safeEval = async (ctx, script, keys, args) => {
156
156
  };
157
157
 
158
158
  // src/lua-scripts/single.ts
159
- var fixedWindowLimitScript = `
159
+ var fixedWindowLimitScript = `#!lua flags=allow-key-locking
160
160
  local key = KEYS[1]
161
161
  local dynamicLimitKey = KEYS[2] -- optional: key for dynamic limit in redis
162
162
  local tokens = tonumber(ARGV[1]) -- default limit
@@ -165,7 +165,7 @@ var fixedWindowLimitScript = `
165
165
 
166
166
  -- Check for dynamic limit
167
167
  local effectiveLimit = tokens
168
- if dynamicLimitKey ~= "" then
168
+ if dynamicLimitKey and dynamicLimitKey ~= "" then
169
169
  local dynamicLimit = redis.call("GET", dynamicLimitKey)
170
170
  if dynamicLimit then
171
171
  effectiveLimit = tonumber(dynamicLimit)
@@ -181,14 +181,14 @@ var fixedWindowLimitScript = `
181
181
 
182
182
  return {r, effectiveLimit}
183
183
  `;
184
- var fixedWindowRemainingTokensScript = `
184
+ var fixedWindowRemainingTokensScript = `#!lua flags=allow-key-locking
185
185
  local key = KEYS[1]
186
186
  local dynamicLimitKey = KEYS[2] -- optional: key for dynamic limit in redis
187
187
  local tokens = tonumber(ARGV[1]) -- default limit
188
188
 
189
189
  -- Check for dynamic limit
190
190
  local effectiveLimit = tokens
191
- if dynamicLimitKey ~= "" then
191
+ if dynamicLimitKey and dynamicLimitKey ~= "" then
192
192
  local dynamicLimit = redis.call("GET", dynamicLimitKey)
193
193
  if dynamicLimit then
194
194
  effectiveLimit = tonumber(dynamicLimit)
@@ -203,7 +203,7 @@ var fixedWindowRemainingTokensScript = `
203
203
 
204
204
  return {effectiveLimit - usedTokens, effectiveLimit}
205
205
  `;
206
- var slidingWindowLimitScript = `
206
+ var slidingWindowLimitScript = `#!lua flags=allow-key-locking
207
207
  local currentKey = KEYS[1] -- identifier including prefixes
208
208
  local previousKey = KEYS[2] -- key of the previous bucket
209
209
  local dynamicLimitKey = KEYS[3] -- optional: key for dynamic limit in redis
@@ -214,7 +214,7 @@ var slidingWindowLimitScript = `
214
214
 
215
215
  -- Check for dynamic limit
216
216
  local effectiveLimit = tokens
217
- if dynamicLimitKey ~= "" then
217
+ if dynamicLimitKey and dynamicLimitKey ~= "" then
218
218
  local dynamicLimit = redis.call("GET", dynamicLimitKey)
219
219
  if dynamicLimit then
220
220
  effectiveLimit = tonumber(dynamicLimit)
@@ -247,7 +247,7 @@ var slidingWindowLimitScript = `
247
247
  end
248
248
  return {effectiveLimit - ( newValue + requestsInPreviousWindow ), effectiveLimit}
249
249
  `;
250
- var slidingWindowRemainingTokensScript = `
250
+ var slidingWindowRemainingTokensScript = `#!lua flags=allow-key-locking
251
251
  local currentKey = KEYS[1] -- identifier including prefixes
252
252
  local previousKey = KEYS[2] -- key of the previous bucket
253
253
  local dynamicLimitKey = KEYS[3] -- optional: key for dynamic limit in redis
@@ -257,7 +257,7 @@ var slidingWindowRemainingTokensScript = `
257
257
 
258
258
  -- Check for dynamic limit
259
259
  local effectiveLimit = tokens
260
- if dynamicLimitKey ~= "" then
260
+ if dynamicLimitKey and dynamicLimitKey ~= "" then
261
261
  local dynamicLimit = redis.call("GET", dynamicLimitKey)
262
262
  if dynamicLimit then
263
263
  effectiveLimit = tonumber(dynamicLimit)
@@ -281,7 +281,7 @@ var slidingWindowRemainingTokensScript = `
281
281
  local usedTokens = requestsInPreviousWindow + requestsInCurrentWindow
282
282
  return {effectiveLimit - usedTokens, effectiveLimit}
283
283
  `;
284
- var tokenBucketLimitScript = `
284
+ var tokenBucketLimitScript = `#!lua flags=allow-key-locking
285
285
  local key = KEYS[1] -- identifier including prefixes
286
286
  local dynamicLimitKey = KEYS[2] -- optional: key for dynamic limit in redis
287
287
  local maxTokens = tonumber(ARGV[1]) -- default maximum number of tokens
@@ -292,7 +292,7 @@ var tokenBucketLimitScript = `
292
292
 
293
293
  -- Check for dynamic limit
294
294
  local effectiveLimit = maxTokens
295
- if dynamicLimitKey ~= "" then
295
+ if dynamicLimitKey and dynamicLimitKey ~= "" then
296
296
  local dynamicLimit = redis.call("GET", dynamicLimitKey)
297
297
  if dynamicLimit then
298
298
  effectiveLimit = tonumber(dynamicLimit)
@@ -335,14 +335,14 @@ var tokenBucketLimitScript = `
335
335
  return {remaining, refilledAt + interval, effectiveLimit}
336
336
  `;
337
337
  var tokenBucketIdentifierNotFound = -1;
338
- var tokenBucketRemainingTokensScript = `
338
+ var tokenBucketRemainingTokensScript = `#!lua flags=allow-key-locking
339
339
  local key = KEYS[1]
340
340
  local dynamicLimitKey = KEYS[2] -- optional: key for dynamic limit in redis
341
341
  local maxTokens = tonumber(ARGV[1]) -- default maximum number of tokens
342
342
 
343
343
  -- Check for dynamic limit
344
344
  local effectiveLimit = maxTokens
345
- if dynamicLimitKey ~= "" then
345
+ if dynamicLimitKey and dynamicLimitKey ~= "" then
346
346
  local dynamicLimit = redis.call("GET", dynamicLimitKey)
347
347
  if dynamicLimit then
348
348
  effectiveLimit = tonumber(dynamicLimit)
@@ -357,7 +357,7 @@ var tokenBucketRemainingTokensScript = `
357
357
 
358
358
  return {tonumber(bucket[2]), tonumber(bucket[1]), effectiveLimit}
359
359
  `;
360
- var cachedFixedWindowLimitScript = `
360
+ var cachedFixedWindowLimitScript = `#!lua flags=allow-key-locking
361
361
  local key = KEYS[1]
362
362
  local window = ARGV[1]
363
363
  local incrementBy = ARGV[2] -- increment rate per request at a given value, default is 1
@@ -371,7 +371,7 @@ var cachedFixedWindowLimitScript = `
371
371
 
372
372
  return r
373
373
  `;
374
- var cachedFixedWindowRemainingTokenScript = `
374
+ var cachedFixedWindowRemainingTokenScript = `#!lua flags=allow-key-locking
375
375
  local key = KEYS[1]
376
376
  local tokens = 0
377
377
 
@@ -383,7 +383,7 @@ var cachedFixedWindowRemainingTokenScript = `
383
383
  `;
384
384
 
385
385
  // src/lua-scripts/multi.ts
386
- var fixedWindowLimitScript2 = `
386
+ var fixedWindowLimitScript2 = `#!lua flags=allow-key-locking
387
387
  local key = KEYS[1]
388
388
  local id = ARGV[1]
389
389
  local window = ARGV[2]
@@ -399,7 +399,7 @@ var fixedWindowLimitScript2 = `
399
399
 
400
400
  return fields
401
401
  `;
402
- var fixedWindowRemainingTokensScript2 = `
402
+ var fixedWindowRemainingTokensScript2 = `#!lua flags=allow-key-locking
403
403
  local key = KEYS[1]
404
404
  local tokens = 0
405
405
 
@@ -407,7 +407,7 @@ var fixedWindowRemainingTokensScript2 = `
407
407
 
408
408
  return fields
409
409
  `;
410
- var slidingWindowLimitScript2 = `
410
+ var slidingWindowLimitScript2 = `#!lua flags=allow-key-locking
411
411
  local currentKey = KEYS[1] -- identifier including prefixes
412
412
  local previousKey = KEYS[2] -- key of the previous bucket
413
413
  local tokens = tonumber(ARGV[1]) -- tokens per window
@@ -444,7 +444,7 @@ var slidingWindowLimitScript2 = `
444
444
  end
445
445
  return {currentFields, previousFields, true}
446
446
  `;
447
- var slidingWindowRemainingTokensScript2 = `
447
+ var slidingWindowRemainingTokensScript2 = `#!lua flags=allow-key-locking
448
448
  local currentKey = KEYS[1] -- identifier including prefixes
449
449
  local previousKey = KEYS[2] -- key of the previous bucket
450
450
  local now = ARGV[1] -- current timestamp in milliseconds
@@ -499,41 +499,41 @@ var SCRIPTS = {
499
499
  fixedWindow: {
500
500
  limit: {
501
501
  script: fixedWindowLimitScript,
502
- hash: "472e55443b62f60d0991028456c57815a387066d"
502
+ hash: "b2efc02798c8e9b471a10f9b1d5745db89923ff3"
503
503
  },
504
504
  getRemaining: {
505
505
  script: fixedWindowRemainingTokensScript,
506
- hash: "40515c9dd0a08f8584f5f9b593935f6a87c1c1c3"
506
+ hash: "823cb2a5a902d69681244f5e28e5ebb8f2a89edd"
507
507
  }
508
508
  },
509
509
  slidingWindow: {
510
510
  limit: {
511
511
  script: slidingWindowLimitScript,
512
- hash: "977fb636fb5ceb7e98a96d1b3a1272ba018efdae"
512
+ hash: "74740bb3d7792848093cdef586b6b6037c455fd6"
513
513
  },
514
514
  getRemaining: {
515
515
  script: slidingWindowRemainingTokensScript,
516
- hash: "ee3a3265fad822f83acad23f8a1e2f5c0b156b03"
516
+ hash: "7ca36169e0fc9caedd81b4eef8e99854c157f9b5"
517
517
  }
518
518
  },
519
519
  tokenBucket: {
520
520
  limit: {
521
521
  script: tokenBucketLimitScript,
522
- hash: "b35c5bc0b7fdae7dd0573d4529911cabaf9d1d89"
522
+ hash: "a9873a8026b509e99a49a0e5a2049756c693c384"
523
523
  },
524
524
  getRemaining: {
525
525
  script: tokenBucketRemainingTokensScript,
526
- hash: "deb03663e8af5a968deee895dd081be553d2611b"
526
+ hash: "40daf4a441df736f5c99f8b77bbe176c909e5de4"
527
527
  }
528
528
  },
529
529
  cachedFixedWindow: {
530
530
  limit: {
531
531
  script: cachedFixedWindowLimitScript,
532
- hash: "c26b12703dd137939b9a69a3a9b18e906a2d940f"
532
+ hash: "1861a700bafe96c833a483af6b1c28a8897cfdb0"
533
533
  },
534
534
  getRemaining: {
535
535
  script: cachedFixedWindowRemainingTokenScript,
536
- hash: "8e8f222ccae68b595ee6e3f3bf2199629a62b91a"
536
+ hash: "eb82f0e853d2fc9a236fa9bfbe704fda6ac2fc36"
537
537
  }
538
538
  }
539
539
  },
@@ -541,21 +541,21 @@ var SCRIPTS = {
541
541
  fixedWindow: {
542
542
  limit: {
543
543
  script: fixedWindowLimitScript2,
544
- hash: "a8c14f3835aa87bd70e5e2116081b81664abcf5c"
544
+ hash: "e04b753a75909b7f99aae4c04cf8869a8de02a9e"
545
545
  },
546
546
  getRemaining: {
547
547
  script: fixedWindowRemainingTokensScript2,
548
- hash: "8ab8322d0ed5fe5ac8eb08f0c2e4557f1b4816fd"
548
+ hash: "e066c8ce3eaca142b0894ad1541e0a58c9924819"
549
549
  }
550
550
  },
551
551
  slidingWindow: {
552
552
  limit: {
553
553
  script: slidingWindowLimitScript2,
554
- hash: "1e7ca8dcd2d600a6d0124a67a57ea225ed62921b"
554
+ hash: "aeb4d8f381e8dafc8e69041baae1324d254ded44"
555
555
  },
556
556
  getRemaining: {
557
557
  script: slidingWindowRemainingTokensScript2,
558
- hash: "558c9306b7ec54abb50747fe0b17e5d44bd24868"
558
+ hash: "87828c1088f6a8d1f512a434ea330189982a1c0a"
559
559
  }
560
560
  }
561
561
  }
@@ -1036,6 +1036,27 @@ var Ratelimit = class {
1036
1036
  };
1037
1037
  };
1038
1038
 
1039
+ // src/version.ts
1040
+ var VERSION = "2.1.0";
1041
+
1042
+ // src/telemetry.ts
1043
+ var taggedClients = /* @__PURE__ */ new WeakSet();
1044
+ var getSafeEnv = () => typeof process === "object" && process && typeof process.env === "object" ? process.env : {};
1045
+ var addTelemetry = (redis, enableTelemetry = true) => {
1046
+ if (!enableTelemetry || getSafeEnv().UPSTASH_DISABLE_TELEMETRY)
1047
+ return;
1048
+ if (!redis || typeof redis !== "object")
1049
+ return;
1050
+ if (taggedClients.has(redis))
1051
+ return;
1052
+ taggedClients.add(redis);
1053
+ try {
1054
+ const client = redis;
1055
+ client.addTelemetry?.({ sdk: `@upstash/ratelimit@${VERSION}` });
1056
+ } catch {
1057
+ }
1058
+ };
1059
+
1039
1060
  // src/multi.ts
1040
1061
  function randomId() {
1041
1062
  let result = "";
@@ -1065,6 +1086,9 @@ var MultiRegionRatelimit = class extends Ratelimit {
1065
1086
  cache: config.ephemeralCache ? new Cache(config.ephemeralCache) : void 0
1066
1087
  }
1067
1088
  });
1089
+ for (const redis of config.redis) {
1090
+ addTelemetry(redis, config.enableTelemetry);
1091
+ }
1068
1092
  if (config.dynamicLimits) {
1069
1093
  console.warn(
1070
1094
  "Warning: Dynamic limits are not yet supported for multi-region rate limiters. The dynamicLimits option will be ignored."
@@ -1435,6 +1459,7 @@ var RegionRatelimit = class extends Ratelimit {
1435
1459
  denyListThreshold: config.denyListThreshold,
1436
1460
  dynamicLimits: config.dynamicLimits
1437
1461
  });
1462
+ addTelemetry(config.redis, config.enableTelemetry);
1438
1463
  }
1439
1464
  /**
1440
1465
  * Each request inside a fixed time increases a counter.
@@ -1474,11 +1499,11 @@ var RegionRatelimit = class extends Ratelimit {
1474
1499
  };
1475
1500
  }
1476
1501
  }
1477
- const dynamicLimitKey = ctx.dynamicLimits ? `${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}` : "";
1502
+ const dynamicLimitKeys = ctx.dynamicLimits ? [`${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}`] : [];
1478
1503
  const [usedTokensAfterUpdate, effectiveLimit] = await safeEval(
1479
1504
  ctx,
1480
1505
  SCRIPTS.singleRegion.fixedWindow.limit,
1481
- [key, dynamicLimitKey],
1506
+ [key, ...dynamicLimitKeys],
1482
1507
  [tokens, windowDuration, incrementBy]
1483
1508
  );
1484
1509
  const success = usedTokensAfterUpdate <= effectiveLimit;
@@ -1502,11 +1527,11 @@ var RegionRatelimit = class extends Ratelimit {
1502
1527
  async getRemaining(ctx, identifier) {
1503
1528
  const bucket = Math.floor(Date.now() / windowDuration);
1504
1529
  const key = [identifier, bucket].join(":");
1505
- const dynamicLimitKey = ctx.dynamicLimits ? `${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}` : "";
1530
+ const dynamicLimitKeys = ctx.dynamicLimits ? [`${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}`] : [];
1506
1531
  const [remaining, effectiveLimit] = await safeEval(
1507
1532
  ctx,
1508
1533
  SCRIPTS.singleRegion.fixedWindow.getRemaining,
1509
- [key, dynamicLimitKey],
1534
+ [key, ...dynamicLimitKeys],
1510
1535
  [tokens]
1511
1536
  );
1512
1537
  return {
@@ -1568,11 +1593,11 @@ var RegionRatelimit = class extends Ratelimit {
1568
1593
  };
1569
1594
  }
1570
1595
  }
1571
- const dynamicLimitKey = ctx.dynamicLimits ? `${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}` : "";
1596
+ const dynamicLimitKeys = ctx.dynamicLimits ? [`${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}`] : [];
1572
1597
  const [remainingTokens, effectiveLimit] = await safeEval(
1573
1598
  ctx,
1574
1599
  SCRIPTS.singleRegion.slidingWindow.limit,
1575
- [currentKey, previousKey, dynamicLimitKey],
1600
+ [currentKey, previousKey, ...dynamicLimitKeys],
1576
1601
  [tokens, now, windowSize, incrementBy]
1577
1602
  );
1578
1603
  const success = remainingTokens >= 0;
@@ -1598,11 +1623,11 @@ var RegionRatelimit = class extends Ratelimit {
1598
1623
  const currentKey = [identifier, currentWindow].join(":");
1599
1624
  const previousWindow = currentWindow - 1;
1600
1625
  const previousKey = [identifier, previousWindow].join(":");
1601
- const dynamicLimitKey = ctx.dynamicLimits ? `${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}` : "";
1626
+ const dynamicLimitKeys = ctx.dynamicLimits ? [`${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}`] : [];
1602
1627
  const [remaining, effectiveLimit] = await safeEval(
1603
1628
  ctx,
1604
1629
  SCRIPTS.singleRegion.slidingWindow.getRemaining,
1605
- [currentKey, previousKey, dynamicLimitKey],
1630
+ [currentKey, previousKey, ...dynamicLimitKeys],
1606
1631
  [tokens, now, windowSize]
1607
1632
  );
1608
1633
  return {
@@ -1657,11 +1682,11 @@ var RegionRatelimit = class extends Ratelimit {
1657
1682
  };
1658
1683
  }
1659
1684
  }
1660
- const dynamicLimitKey = ctx.dynamicLimits ? `${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}` : "";
1685
+ const dynamicLimitKeys = ctx.dynamicLimits ? [`${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}`] : [];
1661
1686
  const [remaining, reset, effectiveLimit] = await safeEval(
1662
1687
  ctx,
1663
1688
  SCRIPTS.singleRegion.tokenBucket.limit,
1664
- [identifier, dynamicLimitKey],
1689
+ [identifier, ...dynamicLimitKeys],
1665
1690
  [maxTokens, intervalDuration, refillRate, now, incrementBy]
1666
1691
  );
1667
1692
  const success = remaining >= 0;
@@ -1681,11 +1706,11 @@ var RegionRatelimit = class extends Ratelimit {
1681
1706
  };
1682
1707
  },
1683
1708
  async getRemaining(ctx, identifier) {
1684
- const dynamicLimitKey = ctx.dynamicLimits ? `${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}` : "";
1709
+ const dynamicLimitKeys = ctx.dynamicLimits ? [`${ctx.prefix}${DYNAMIC_LIMIT_KEY_SUFFIX}`] : [];
1685
1710
  const [remainingTokens, refilledAt, effectiveLimit] = await safeEval(
1686
1711
  ctx,
1687
1712
  SCRIPTS.singleRegion.tokenBucket.getRemaining,
1688
- [identifier, dynamicLimitKey],
1713
+ [identifier, ...dynamicLimitKeys],
1689
1714
  [maxTokens]
1690
1715
  );
1691
1716
  const freshRefillAt = Date.now() + intervalDuration;