@upstash/ratelimit 2.0.5 → 2.0.7

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
@@ -92,9 +92,9 @@ var Cache = class {
92
92
  get(key) {
93
93
  return this.cache.get(key) || null;
94
94
  }
95
- incr(key) {
95
+ incr(key, incrementAmount = 1) {
96
96
  let value = this.cache.get(key) ?? 0;
97
- value += 1;
97
+ value += incrementAmount;
98
98
  this.cache.set(key, value);
99
99
  return value;
100
100
  }
@@ -145,13 +145,7 @@ var safeEval = async (ctx, script, keys, args) => {
145
145
  return await ctx.redis.evalsha(script.hash, keys, args);
146
146
  } catch (error) {
147
147
  if (`${error}`.includes("NOSCRIPT")) {
148
- const hash = await ctx.redis.scriptLoad(script.script);
149
- if (hash !== script.hash) {
150
- console.warn(
151
- "Upstash Ratelimit: Expected hash and the hash received from Redis are different. Ratelimit will work as usual but performance will be reduced."
152
- );
153
- }
154
- return await ctx.redis.evalsha(hash, keys, args);
148
+ return await ctx.redis.eval(script.script, keys, args);
155
149
  }
156
150
  throw error;
157
151
  }
@@ -188,7 +182,7 @@ var slidingWindowLimitScript = `
188
182
  local tokens = tonumber(ARGV[1]) -- tokens per window
189
183
  local now = ARGV[2] -- current timestamp in milliseconds
190
184
  local window = ARGV[3] -- interval in milliseconds
191
- local incrementBy = ARGV[4] -- increment rate per request at a given value, default is 1
185
+ local incrementBy = tonumber(ARGV[4]) -- increment rate per request at a given value, default is 1
192
186
 
193
187
  local requestsInCurrentWindow = redis.call("GET", currentKey)
194
188
  if requestsInCurrentWindow == false then
@@ -202,12 +196,14 @@ var slidingWindowLimitScript = `
202
196
  local percentageInCurrent = ( now % window ) / window
203
197
  -- weighted requests to consider from the previous window
204
198
  requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
205
- if requestsInPreviousWindow + requestsInCurrentWindow >= tokens then
199
+
200
+ -- Only check limit if not refunding (negative rate)
201
+ if incrementBy > 0 and requestsInPreviousWindow + requestsInCurrentWindow >= tokens then
206
202
  return -1
207
203
  end
208
204
 
209
205
  local newValue = redis.call("INCRBY", currentKey, incrementBy)
210
- if newValue == tonumber(incrementBy) then
206
+ if newValue == incrementBy then
211
207
  -- The first time this key is set, the value will be equal to incrementBy.
212
208
  -- So we only need the expire command once
213
209
  redis.call("PEXPIRE", currentKey, window * 2 + 1000) -- Enough time to overlap with a new window + 1 second
@@ -264,7 +260,8 @@ var tokenBucketLimitScript = `
264
260
  refilledAt = refilledAt + numRefills * interval
265
261
  end
266
262
 
267
- if tokens == 0 then
263
+ -- Only reject if tokens are 0 and we're consuming (not refunding)
264
+ if tokens == 0 and incrementBy > 0 then
268
265
  return {-1, refilledAt + interval}
269
266
  end
270
267
 
@@ -272,7 +269,10 @@ var tokenBucketLimitScript = `
272
269
  local expireAt = math.ceil(((maxTokens - remaining) / refillRate)) * interval
273
270
 
274
271
  redis.call("HSET", key, "refilledAt", refilledAt, "tokens", remaining)
275
- redis.call("PEXPIRE", key, expireAt)
272
+
273
+ if (expireAt > 0) then
274
+ redis.call("PEXPIRE", key, expireAt)
275
+ end
276
276
  return {remaining, refilledAt + interval}
277
277
  `;
278
278
  var tokenBucketIdentifierNotFound = -1;
@@ -360,7 +360,9 @@ var slidingWindowLimitScript2 = `
360
360
  end
361
361
 
362
362
  local percentageInCurrent = ( now % window) / window
363
- if requestsInPreviousWindow * (1 - percentageInCurrent ) + requestsInCurrentWindow >= tokens then
363
+
364
+ -- Only check limit if not refunding (negative rate)
365
+ if incrementBy > 0 and requestsInPreviousWindow * (1 - percentageInCurrent ) + requestsInCurrentWindow + incrementBy > tokens then
364
366
  return {currentFields, previousFields, false}
365
367
  end
366
368
 
@@ -438,7 +440,7 @@ var SCRIPTS = {
438
440
  slidingWindow: {
439
441
  limit: {
440
442
  script: slidingWindowLimitScript,
441
- hash: "e1391e429b699c780eb0480350cd5b7280fd9213"
443
+ hash: "9b7842963bd73721f1a3011650c23c0010848ee3"
442
444
  },
443
445
  getRemaining: {
444
446
  script: slidingWindowRemainingTokensScript,
@@ -448,7 +450,7 @@ var SCRIPTS = {
448
450
  tokenBucket: {
449
451
  limit: {
450
452
  script: tokenBucketLimitScript,
451
- hash: "5bece90aeef8189a8cfd28995b479529e270b3c6"
453
+ hash: "d1f857ebbdaeca90ccd2cd4eada61d7c8e5db1ca"
452
454
  },
453
455
  getRemaining: {
454
456
  script: tokenBucketRemainingTokensScript,
@@ -480,7 +482,7 @@ var SCRIPTS = {
480
482
  slidingWindow: {
481
483
  limit: {
482
484
  script: slidingWindowLimitScript2,
483
- hash: "cb4fdc2575056df7c6d422764df0de3a08d6753b"
485
+ hash: "1e7ca8dcd2d600a6d0124a67a57ea225ed62921b"
484
486
  },
485
487
  getRemaining: {
486
488
  script: slidingWindowRemainingTokensScript2,
@@ -956,7 +958,11 @@ var MultiRegionRatelimit = class extends Ratelimit {
956
958
  const windowDuration = ms(window);
957
959
  return () => ({
958
960
  async limit(ctx, identifier, rate) {
959
- if (ctx.cache) {
961
+ const requestId = randomId();
962
+ const bucket = Math.floor(Date.now() / windowDuration);
963
+ const key = [identifier, bucket].join(":");
964
+ const incrementBy = rate ?? 1;
965
+ if (ctx.cache && incrementBy > 0) {
960
966
  const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
961
967
  if (blocked) {
962
968
  return {
@@ -969,10 +975,6 @@ var MultiRegionRatelimit = class extends Ratelimit {
969
975
  };
970
976
  }
971
977
  }
972
- const requestId = randomId();
973
- const bucket = Math.floor(Date.now() / windowDuration);
974
- const key = [identifier, bucket].join(":");
975
- const incrementBy = rate ? Math.max(1, rate) : 1;
976
978
  const dbs = ctx.regionContexts.map((regionContext) => ({
977
979
  redis: regionContext.redis,
978
980
  request: safeEval(
@@ -983,24 +985,29 @@ var MultiRegionRatelimit = class extends Ratelimit {
983
985
  )
984
986
  }));
985
987
  const firstResponse = await Promise.any(dbs.map((s) => s.request));
986
- const usedTokens = firstResponse.reduce((accTokens, usedToken, index) => {
987
- let parsedToken = 0;
988
- if (index % 2) {
989
- parsedToken = Number.parseInt(usedToken);
990
- }
991
- return accTokens + parsedToken;
992
- }, 0);
988
+ const usedTokens = firstResponse.reduce(
989
+ (accTokens, usedToken, index) => {
990
+ let parsedToken = 0;
991
+ if (index % 2) {
992
+ parsedToken = Number.parseInt(usedToken);
993
+ }
994
+ return accTokens + parsedToken;
995
+ },
996
+ 0
997
+ );
993
998
  const remaining = tokens - usedTokens;
994
999
  async function sync() {
995
1000
  const individualIDs = await Promise.all(dbs.map((s) => s.request));
996
- const allIDs = [...new Set(
997
- individualIDs.flat().reduce((acc, curr, index) => {
998
- if (index % 2 === 0) {
999
- acc.push(curr);
1000
- }
1001
- return acc;
1002
- }, [])
1003
- ).values()];
1001
+ const allIDs = [
1002
+ ...new Set(
1003
+ individualIDs.flat().reduce((acc, curr, index) => {
1004
+ if (index % 2 === 0) {
1005
+ acc.push(curr);
1006
+ }
1007
+ return acc;
1008
+ }, [])
1009
+ ).values()
1010
+ ];
1004
1011
  for (const db of dbs) {
1005
1012
  const usedDbTokensRequest = await db.request;
1006
1013
  const usedDbTokens = usedDbTokensRequest.reduce(
@@ -1014,12 +1021,15 @@ var MultiRegionRatelimit = class extends Ratelimit {
1014
1021
  0
1015
1022
  );
1016
1023
  const dbIdsRequest = await db.request;
1017
- const dbIds = dbIdsRequest.reduce((ids, currentId, index) => {
1018
- if (index % 2 === 0) {
1019
- ids.push(currentId);
1020
- }
1021
- return ids;
1022
- }, []);
1024
+ const dbIds = dbIdsRequest.reduce(
1025
+ (ids, currentId, index) => {
1026
+ if (index % 2 === 0) {
1027
+ ids.push(currentId);
1028
+ }
1029
+ return ids;
1030
+ },
1031
+ []
1032
+ );
1023
1033
  if (usedDbTokens >= tokens) {
1024
1034
  continue;
1025
1035
  }
@@ -1032,10 +1042,14 @@ var MultiRegionRatelimit = class extends Ratelimit {
1032
1042
  }
1033
1043
  }
1034
1044
  }
1035
- const success = remaining > 0;
1045
+ const success = remaining >= 0;
1036
1046
  const reset = (bucket + 1) * windowDuration;
1037
- if (ctx.cache && !success) {
1038
- ctx.cache.blockUntil(identifier, reset);
1047
+ if (ctx.cache) {
1048
+ if (!success) {
1049
+ ctx.cache.blockUntil(identifier, reset);
1050
+ } else if (incrementBy < 0) {
1051
+ ctx.cache.pop(identifier);
1052
+ }
1039
1053
  }
1040
1054
  return {
1041
1055
  success,
@@ -1058,13 +1072,16 @@ var MultiRegionRatelimit = class extends Ratelimit {
1058
1072
  )
1059
1073
  }));
1060
1074
  const firstResponse = await Promise.any(dbs.map((s) => s.request));
1061
- const usedTokens = firstResponse.reduce((accTokens, usedToken, index) => {
1062
- let parsedToken = 0;
1063
- if (index % 2) {
1064
- parsedToken = Number.parseInt(usedToken);
1065
- }
1066
- return accTokens + parsedToken;
1067
- }, 0);
1075
+ const usedTokens = firstResponse.reduce(
1076
+ (accTokens, usedToken, index) => {
1077
+ let parsedToken = 0;
1078
+ if (index % 2) {
1079
+ parsedToken = Number.parseInt(usedToken);
1080
+ }
1081
+ return accTokens + parsedToken;
1082
+ },
1083
+ 0
1084
+ );
1068
1085
  return {
1069
1086
  remaining: Math.max(0, tokens - usedTokens),
1070
1087
  reset: (bucket + 1) * windowDuration
@@ -1075,14 +1092,11 @@ var MultiRegionRatelimit = class extends Ratelimit {
1075
1092
  if (ctx.cache) {
1076
1093
  ctx.cache.pop(identifier);
1077
1094
  }
1078
- await Promise.all(ctx.regionContexts.map((regionContext) => {
1079
- safeEval(
1080
- regionContext,
1081
- RESET_SCRIPT,
1082
- [pattern],
1083
- [null]
1084
- );
1085
- }));
1095
+ await Promise.all(
1096
+ ctx.regionContexts.map((regionContext) => {
1097
+ safeEval(regionContext, RESET_SCRIPT, [pattern], [null]);
1098
+ })
1099
+ );
1086
1100
  }
1087
1101
  });
1088
1102
  }
@@ -1107,7 +1121,14 @@ var MultiRegionRatelimit = class extends Ratelimit {
1107
1121
  const windowDuration = ms(window);
1108
1122
  return () => ({
1109
1123
  async limit(ctx, identifier, rate) {
1110
- if (ctx.cache) {
1124
+ const requestId = randomId();
1125
+ const now = Date.now();
1126
+ const currentWindow = Math.floor(now / windowSize);
1127
+ const currentKey = [identifier, currentWindow].join(":");
1128
+ const previousWindow = currentWindow - 1;
1129
+ const previousKey = [identifier, previousWindow].join(":");
1130
+ const incrementBy = rate ?? 1;
1131
+ if (ctx.cache && incrementBy > 0) {
1111
1132
  const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
1112
1133
  if (blocked) {
1113
1134
  return {
@@ -1120,13 +1141,6 @@ var MultiRegionRatelimit = class extends Ratelimit {
1120
1141
  };
1121
1142
  }
1122
1143
  }
1123
- const requestId = randomId();
1124
- const now = Date.now();
1125
- const currentWindow = Math.floor(now / windowSize);
1126
- const currentKey = [identifier, currentWindow].join(":");
1127
- const previousWindow = currentWindow - 1;
1128
- const previousKey = [identifier, previousWindow].join(":");
1129
- const incrementBy = rate ? Math.max(1, rate) : 1;
1130
1144
  const dbs = ctx.regionContexts.map((regionContext) => ({
1131
1145
  redis: regionContext.redis,
1132
1146
  request: safeEval(
@@ -1138,37 +1152,49 @@ var MultiRegionRatelimit = class extends Ratelimit {
1138
1152
  )
1139
1153
  }));
1140
1154
  const percentageInCurrent = now % windowDuration / windowDuration;
1141
- const [current, previous, success] = await Promise.any(dbs.map((s) => s.request));
1155
+ const [current, previous, success] = await Promise.any(
1156
+ dbs.map((s) => s.request)
1157
+ );
1142
1158
  if (success) {
1143
1159
  current.push(requestId, incrementBy.toString());
1144
1160
  }
1145
- const previousUsedTokens = previous.reduce((accTokens, usedToken, index) => {
1146
- let parsedToken = 0;
1147
- if (index % 2) {
1148
- parsedToken = Number.parseInt(usedToken);
1149
- }
1150
- return accTokens + parsedToken;
1151
- }, 0);
1152
- const currentUsedTokens = current.reduce((accTokens, usedToken, index) => {
1153
- let parsedToken = 0;
1154
- if (index % 2) {
1155
- parsedToken = Number.parseInt(usedToken);
1156
- }
1157
- return accTokens + parsedToken;
1158
- }, 0);
1159
- const previousPartialUsed = Math.ceil(previousUsedTokens * (1 - percentageInCurrent));
1161
+ const previousUsedTokens = previous.reduce(
1162
+ (accTokens, usedToken, index) => {
1163
+ let parsedToken = 0;
1164
+ if (index % 2) {
1165
+ parsedToken = Number.parseInt(usedToken);
1166
+ }
1167
+ return accTokens + parsedToken;
1168
+ },
1169
+ 0
1170
+ );
1171
+ const currentUsedTokens = current.reduce(
1172
+ (accTokens, usedToken, index) => {
1173
+ let parsedToken = 0;
1174
+ if (index % 2) {
1175
+ parsedToken = Number.parseInt(usedToken);
1176
+ }
1177
+ return accTokens + parsedToken;
1178
+ },
1179
+ 0
1180
+ );
1181
+ const previousPartialUsed = Math.ceil(
1182
+ previousUsedTokens * (1 - percentageInCurrent)
1183
+ );
1160
1184
  const usedTokens = previousPartialUsed + currentUsedTokens;
1161
1185
  const remaining = tokens - usedTokens;
1162
1186
  async function sync() {
1163
1187
  const res = await Promise.all(dbs.map((s) => s.request));
1164
- const allCurrentIds = [...new Set(
1165
- res.flatMap(([current2]) => current2).reduce((acc, curr, index) => {
1166
- if (index % 2 === 0) {
1167
- acc.push(curr);
1168
- }
1169
- return acc;
1170
- }, [])
1171
- ).values()];
1188
+ const allCurrentIds = [
1189
+ ...new Set(
1190
+ res.flatMap(([current2]) => current2).reduce((acc, curr, index) => {
1191
+ if (index % 2 === 0) {
1192
+ acc.push(curr);
1193
+ }
1194
+ return acc;
1195
+ }, [])
1196
+ ).values()
1197
+ ];
1172
1198
  for (const db of dbs) {
1173
1199
  const [current2, _previous, _success] = await db.request;
1174
1200
  const dbIds = current2.reduce((ids, currentId, index) => {
@@ -1177,13 +1203,16 @@ var MultiRegionRatelimit = class extends Ratelimit {
1177
1203
  }
1178
1204
  return ids;
1179
1205
  }, []);
1180
- const usedDbTokens = current2.reduce((accTokens, usedToken, index) => {
1181
- let parsedToken = 0;
1182
- if (index % 2) {
1183
- parsedToken = Number.parseInt(usedToken);
1184
- }
1185
- return accTokens + parsedToken;
1186
- }, 0);
1206
+ const usedDbTokens = current2.reduce(
1207
+ (accTokens, usedToken, index) => {
1208
+ let parsedToken = 0;
1209
+ if (index % 2) {
1210
+ parsedToken = Number.parseInt(usedToken);
1211
+ }
1212
+ return accTokens + parsedToken;
1213
+ },
1214
+ 0
1215
+ );
1187
1216
  if (usedDbTokens >= tokens) {
1188
1217
  continue;
1189
1218
  }
@@ -1197,8 +1226,12 @@ var MultiRegionRatelimit = class extends Ratelimit {
1197
1226
  }
1198
1227
  }
1199
1228
  const reset = (currentWindow + 1) * windowDuration;
1200
- if (ctx.cache && !success) {
1201
- ctx.cache.blockUntil(identifier, reset);
1229
+ if (ctx.cache) {
1230
+ if (!success) {
1231
+ ctx.cache.blockUntil(identifier, reset);
1232
+ } else if (incrementBy < 0) {
1233
+ ctx.cache.pop(identifier);
1234
+ }
1202
1235
  }
1203
1236
  return {
1204
1237
  success: Boolean(success),
@@ -1235,14 +1268,11 @@ var MultiRegionRatelimit = class extends Ratelimit {
1235
1268
  if (ctx.cache) {
1236
1269
  ctx.cache.pop(identifier);
1237
1270
  }
1238
- await Promise.all(ctx.regionContexts.map((regionContext) => {
1239
- safeEval(
1240
- regionContext,
1241
- RESET_SCRIPT,
1242
- [pattern],
1243
- [null]
1244
- );
1245
- }));
1271
+ await Promise.all(
1272
+ ctx.regionContexts.map((regionContext) => {
1273
+ safeEval(regionContext, RESET_SCRIPT, [pattern], [null]);
1274
+ })
1275
+ );
1246
1276
  }
1247
1277
  });
1248
1278
  }
@@ -1291,7 +1321,8 @@ var RegionRatelimit = class extends Ratelimit {
1291
1321
  async limit(ctx, identifier, rate) {
1292
1322
  const bucket = Math.floor(Date.now() / windowDuration);
1293
1323
  const key = [identifier, bucket].join(":");
1294
- if (ctx.cache) {
1324
+ const incrementBy = rate ?? 1;
1325
+ if (ctx.cache && incrementBy > 0) {
1295
1326
  const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
1296
1327
  if (blocked) {
1297
1328
  return {
@@ -1304,7 +1335,6 @@ var RegionRatelimit = class extends Ratelimit {
1304
1335
  };
1305
1336
  }
1306
1337
  }
1307
- const incrementBy = rate ? Math.max(1, rate) : 1;
1308
1338
  const usedTokensAfterUpdate = await safeEval(
1309
1339
  ctx,
1310
1340
  SCRIPTS.singleRegion.fixedWindow.limit,
@@ -1314,8 +1344,12 @@ var RegionRatelimit = class extends Ratelimit {
1314
1344
  const success = usedTokensAfterUpdate <= tokens;
1315
1345
  const remainingTokens = Math.max(0, tokens - usedTokensAfterUpdate);
1316
1346
  const reset = (bucket + 1) * windowDuration;
1317
- if (ctx.cache && !success) {
1318
- ctx.cache.blockUntil(identifier, reset);
1347
+ if (ctx.cache) {
1348
+ if (!success) {
1349
+ ctx.cache.blockUntil(identifier, reset);
1350
+ } else if (incrementBy < 0) {
1351
+ ctx.cache.pop(identifier);
1352
+ }
1319
1353
  }
1320
1354
  return {
1321
1355
  success,
@@ -1378,7 +1412,8 @@ var RegionRatelimit = class extends Ratelimit {
1378
1412
  const currentKey = [identifier, currentWindow].join(":");
1379
1413
  const previousWindow = currentWindow - 1;
1380
1414
  const previousKey = [identifier, previousWindow].join(":");
1381
- if (ctx.cache) {
1415
+ const incrementBy = rate ?? 1;
1416
+ if (ctx.cache && incrementBy > 0) {
1382
1417
  const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
1383
1418
  if (blocked) {
1384
1419
  return {
@@ -1391,7 +1426,6 @@ var RegionRatelimit = class extends Ratelimit {
1391
1426
  };
1392
1427
  }
1393
1428
  }
1394
- const incrementBy = rate ? Math.max(1, rate) : 1;
1395
1429
  const remainingTokens = await safeEval(
1396
1430
  ctx,
1397
1431
  SCRIPTS.singleRegion.slidingWindow.limit,
@@ -1400,8 +1434,12 @@ var RegionRatelimit = class extends Ratelimit {
1400
1434
  );
1401
1435
  const success = remainingTokens >= 0;
1402
1436
  const reset = (currentWindow + 1) * windowSize;
1403
- if (ctx.cache && !success) {
1404
- ctx.cache.blockUntil(identifier, reset);
1437
+ if (ctx.cache) {
1438
+ if (!success) {
1439
+ ctx.cache.blockUntil(identifier, reset);
1440
+ } else if (incrementBy < 0) {
1441
+ ctx.cache.pop(identifier);
1442
+ }
1405
1443
  }
1406
1444
  return {
1407
1445
  success,
@@ -1459,7 +1497,9 @@ var RegionRatelimit = class extends Ratelimit {
1459
1497
  const intervalDuration = ms(interval);
1460
1498
  return () => ({
1461
1499
  async limit(ctx, identifier, rate) {
1462
- if (ctx.cache) {
1500
+ const now = Date.now();
1501
+ const incrementBy = rate ?? 1;
1502
+ if (ctx.cache && incrementBy > 0) {
1463
1503
  const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
1464
1504
  if (blocked) {
1465
1505
  return {
@@ -1472,8 +1512,6 @@ var RegionRatelimit = class extends Ratelimit {
1472
1512
  };
1473
1513
  }
1474
1514
  }
1475
- const now = Date.now();
1476
- const incrementBy = rate ? Math.max(1, rate) : 1;
1477
1515
  const [remaining, reset] = await safeEval(
1478
1516
  ctx,
1479
1517
  SCRIPTS.singleRegion.tokenBucket.limit,
@@ -1481,8 +1519,12 @@ var RegionRatelimit = class extends Ratelimit {
1481
1519
  [maxTokens, intervalDuration, refillRate, now, incrementBy]
1482
1520
  );
1483
1521
  const success = remaining >= 0;
1484
- if (ctx.cache && !success) {
1485
- ctx.cache.blockUntil(identifier, reset);
1522
+ if (ctx.cache) {
1523
+ if (!success) {
1524
+ ctx.cache.blockUntil(identifier, reset);
1525
+ } else if (incrementBy < 0) {
1526
+ ctx.cache.pop(identifier);
1527
+ }
1486
1528
  }
1487
1529
  return {
1488
1530
  success,
@@ -1554,10 +1596,10 @@ var RegionRatelimit = class extends Ratelimit {
1554
1596
  const bucket = Math.floor(Date.now() / windowDuration);
1555
1597
  const key = [identifier, bucket].join(":");
1556
1598
  const reset = (bucket + 1) * windowDuration;
1557
- const incrementBy = rate ? Math.max(1, rate) : 1;
1599
+ const incrementBy = rate ?? 1;
1558
1600
  const hit = typeof ctx.cache.get(key) === "number";
1559
1601
  if (hit) {
1560
- const cachedTokensAfterUpdate = ctx.cache.incr(key);
1602
+ const cachedTokensAfterUpdate = ctx.cache.incr(key, incrementBy);
1561
1603
  const success = cachedTokensAfterUpdate < tokens;
1562
1604
  const pending = success ? safeEval(
1563
1605
  ctx,