@upstash/ratelimit 2.0.6 → 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.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +167 -119
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +167 -119
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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 +=
|
|
97
|
+
value += incrementAmount;
|
|
98
98
|
this.cache.set(key, value);
|
|
99
99
|
return value;
|
|
100
100
|
}
|
|
@@ -182,7 +182,7 @@ var slidingWindowLimitScript = `
|
|
|
182
182
|
local tokens = tonumber(ARGV[1]) -- tokens per window
|
|
183
183
|
local now = ARGV[2] -- current timestamp in milliseconds
|
|
184
184
|
local window = ARGV[3] -- interval in milliseconds
|
|
185
|
-
local incrementBy = ARGV[4]
|
|
185
|
+
local incrementBy = tonumber(ARGV[4]) -- increment rate per request at a given value, default is 1
|
|
186
186
|
|
|
187
187
|
local requestsInCurrentWindow = redis.call("GET", currentKey)
|
|
188
188
|
if requestsInCurrentWindow == false then
|
|
@@ -196,12 +196,14 @@ var slidingWindowLimitScript = `
|
|
|
196
196
|
local percentageInCurrent = ( now % window ) / window
|
|
197
197
|
-- weighted requests to consider from the previous window
|
|
198
198
|
requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
|
|
199
|
-
|
|
199
|
+
|
|
200
|
+
-- Only check limit if not refunding (negative rate)
|
|
201
|
+
if incrementBy > 0 and requestsInPreviousWindow + requestsInCurrentWindow >= tokens then
|
|
200
202
|
return -1
|
|
201
203
|
end
|
|
202
204
|
|
|
203
205
|
local newValue = redis.call("INCRBY", currentKey, incrementBy)
|
|
204
|
-
if newValue ==
|
|
206
|
+
if newValue == incrementBy then
|
|
205
207
|
-- The first time this key is set, the value will be equal to incrementBy.
|
|
206
208
|
-- So we only need the expire command once
|
|
207
209
|
redis.call("PEXPIRE", currentKey, window * 2 + 1000) -- Enough time to overlap with a new window + 1 second
|
|
@@ -258,7 +260,8 @@ var tokenBucketLimitScript = `
|
|
|
258
260
|
refilledAt = refilledAt + numRefills * interval
|
|
259
261
|
end
|
|
260
262
|
|
|
261
|
-
if tokens
|
|
263
|
+
-- Only reject if tokens are 0 and we're consuming (not refunding)
|
|
264
|
+
if tokens == 0 and incrementBy > 0 then
|
|
262
265
|
return {-1, refilledAt + interval}
|
|
263
266
|
end
|
|
264
267
|
|
|
@@ -266,7 +269,10 @@ var tokenBucketLimitScript = `
|
|
|
266
269
|
local expireAt = math.ceil(((maxTokens - remaining) / refillRate)) * interval
|
|
267
270
|
|
|
268
271
|
redis.call("HSET", key, "refilledAt", refilledAt, "tokens", remaining)
|
|
269
|
-
|
|
272
|
+
|
|
273
|
+
if (expireAt > 0) then
|
|
274
|
+
redis.call("PEXPIRE", key, expireAt)
|
|
275
|
+
end
|
|
270
276
|
return {remaining, refilledAt + interval}
|
|
271
277
|
`;
|
|
272
278
|
var tokenBucketIdentifierNotFound = -1;
|
|
@@ -354,7 +360,9 @@ var slidingWindowLimitScript2 = `
|
|
|
354
360
|
end
|
|
355
361
|
|
|
356
362
|
local percentageInCurrent = ( now % window) / window
|
|
357
|
-
|
|
363
|
+
|
|
364
|
+
-- Only check limit if not refunding (negative rate)
|
|
365
|
+
if incrementBy > 0 and requestsInPreviousWindow * (1 - percentageInCurrent ) + requestsInCurrentWindow + incrementBy > tokens then
|
|
358
366
|
return {currentFields, previousFields, false}
|
|
359
367
|
end
|
|
360
368
|
|
|
@@ -432,7 +440,7 @@ var SCRIPTS = {
|
|
|
432
440
|
slidingWindow: {
|
|
433
441
|
limit: {
|
|
434
442
|
script: slidingWindowLimitScript,
|
|
435
|
-
hash: "
|
|
443
|
+
hash: "9b7842963bd73721f1a3011650c23c0010848ee3"
|
|
436
444
|
},
|
|
437
445
|
getRemaining: {
|
|
438
446
|
script: slidingWindowRemainingTokensScript,
|
|
@@ -442,7 +450,7 @@ var SCRIPTS = {
|
|
|
442
450
|
tokenBucket: {
|
|
443
451
|
limit: {
|
|
444
452
|
script: tokenBucketLimitScript,
|
|
445
|
-
hash: "
|
|
453
|
+
hash: "d1f857ebbdaeca90ccd2cd4eada61d7c8e5db1ca"
|
|
446
454
|
},
|
|
447
455
|
getRemaining: {
|
|
448
456
|
script: tokenBucketRemainingTokensScript,
|
|
@@ -474,7 +482,7 @@ var SCRIPTS = {
|
|
|
474
482
|
slidingWindow: {
|
|
475
483
|
limit: {
|
|
476
484
|
script: slidingWindowLimitScript2,
|
|
477
|
-
hash: "
|
|
485
|
+
hash: "1e7ca8dcd2d600a6d0124a67a57ea225ed62921b"
|
|
478
486
|
},
|
|
479
487
|
getRemaining: {
|
|
480
488
|
script: slidingWindowRemainingTokensScript2,
|
|
@@ -950,7 +958,11 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
950
958
|
const windowDuration = ms(window);
|
|
951
959
|
return () => ({
|
|
952
960
|
async limit(ctx, identifier, rate) {
|
|
953
|
-
|
|
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) {
|
|
954
966
|
const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
|
|
955
967
|
if (blocked) {
|
|
956
968
|
return {
|
|
@@ -963,10 +975,6 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
963
975
|
};
|
|
964
976
|
}
|
|
965
977
|
}
|
|
966
|
-
const requestId = randomId();
|
|
967
|
-
const bucket = Math.floor(Date.now() / windowDuration);
|
|
968
|
-
const key = [identifier, bucket].join(":");
|
|
969
|
-
const incrementBy = rate ? Math.max(1, rate) : 1;
|
|
970
978
|
const dbs = ctx.regionContexts.map((regionContext) => ({
|
|
971
979
|
redis: regionContext.redis,
|
|
972
980
|
request: safeEval(
|
|
@@ -977,24 +985,29 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
977
985
|
)
|
|
978
986
|
}));
|
|
979
987
|
const firstResponse = await Promise.any(dbs.map((s) => s.request));
|
|
980
|
-
const usedTokens = firstResponse.reduce(
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
+
);
|
|
987
998
|
const remaining = tokens - usedTokens;
|
|
988
999
|
async function sync() {
|
|
989
1000
|
const individualIDs = await Promise.all(dbs.map((s) => s.request));
|
|
990
|
-
const allIDs = [
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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
|
+
];
|
|
998
1011
|
for (const db of dbs) {
|
|
999
1012
|
const usedDbTokensRequest = await db.request;
|
|
1000
1013
|
const usedDbTokens = usedDbTokensRequest.reduce(
|
|
@@ -1008,12 +1021,15 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1008
1021
|
0
|
|
1009
1022
|
);
|
|
1010
1023
|
const dbIdsRequest = await db.request;
|
|
1011
|
-
const dbIds = dbIdsRequest.reduce(
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
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
|
+
);
|
|
1017
1033
|
if (usedDbTokens >= tokens) {
|
|
1018
1034
|
continue;
|
|
1019
1035
|
}
|
|
@@ -1026,10 +1042,14 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1026
1042
|
}
|
|
1027
1043
|
}
|
|
1028
1044
|
}
|
|
1029
|
-
const success = remaining
|
|
1045
|
+
const success = remaining >= 0;
|
|
1030
1046
|
const reset = (bucket + 1) * windowDuration;
|
|
1031
|
-
if (ctx.cache
|
|
1032
|
-
|
|
1047
|
+
if (ctx.cache) {
|
|
1048
|
+
if (!success) {
|
|
1049
|
+
ctx.cache.blockUntil(identifier, reset);
|
|
1050
|
+
} else if (incrementBy < 0) {
|
|
1051
|
+
ctx.cache.pop(identifier);
|
|
1052
|
+
}
|
|
1033
1053
|
}
|
|
1034
1054
|
return {
|
|
1035
1055
|
success,
|
|
@@ -1052,13 +1072,16 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1052
1072
|
)
|
|
1053
1073
|
}));
|
|
1054
1074
|
const firstResponse = await Promise.any(dbs.map((s) => s.request));
|
|
1055
|
-
const usedTokens = firstResponse.reduce(
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
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
|
+
);
|
|
1062
1085
|
return {
|
|
1063
1086
|
remaining: Math.max(0, tokens - usedTokens),
|
|
1064
1087
|
reset: (bucket + 1) * windowDuration
|
|
@@ -1069,14 +1092,11 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1069
1092
|
if (ctx.cache) {
|
|
1070
1093
|
ctx.cache.pop(identifier);
|
|
1071
1094
|
}
|
|
1072
|
-
await Promise.all(
|
|
1073
|
-
|
|
1074
|
-
regionContext,
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
[null]
|
|
1078
|
-
);
|
|
1079
|
-
}));
|
|
1095
|
+
await Promise.all(
|
|
1096
|
+
ctx.regionContexts.map((regionContext) => {
|
|
1097
|
+
safeEval(regionContext, RESET_SCRIPT, [pattern], [null]);
|
|
1098
|
+
})
|
|
1099
|
+
);
|
|
1080
1100
|
}
|
|
1081
1101
|
});
|
|
1082
1102
|
}
|
|
@@ -1101,7 +1121,14 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1101
1121
|
const windowDuration = ms(window);
|
|
1102
1122
|
return () => ({
|
|
1103
1123
|
async limit(ctx, identifier, rate) {
|
|
1104
|
-
|
|
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) {
|
|
1105
1132
|
const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
|
|
1106
1133
|
if (blocked) {
|
|
1107
1134
|
return {
|
|
@@ -1114,13 +1141,6 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1114
1141
|
};
|
|
1115
1142
|
}
|
|
1116
1143
|
}
|
|
1117
|
-
const requestId = randomId();
|
|
1118
|
-
const now = Date.now();
|
|
1119
|
-
const currentWindow = Math.floor(now / windowSize);
|
|
1120
|
-
const currentKey = [identifier, currentWindow].join(":");
|
|
1121
|
-
const previousWindow = currentWindow - 1;
|
|
1122
|
-
const previousKey = [identifier, previousWindow].join(":");
|
|
1123
|
-
const incrementBy = rate ? Math.max(1, rate) : 1;
|
|
1124
1144
|
const dbs = ctx.regionContexts.map((regionContext) => ({
|
|
1125
1145
|
redis: regionContext.redis,
|
|
1126
1146
|
request: safeEval(
|
|
@@ -1132,37 +1152,49 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1132
1152
|
)
|
|
1133
1153
|
}));
|
|
1134
1154
|
const percentageInCurrent = now % windowDuration / windowDuration;
|
|
1135
|
-
const [current, previous, success] = await Promise.any(
|
|
1155
|
+
const [current, previous, success] = await Promise.any(
|
|
1156
|
+
dbs.map((s) => s.request)
|
|
1157
|
+
);
|
|
1136
1158
|
if (success) {
|
|
1137
1159
|
current.push(requestId, incrementBy.toString());
|
|
1138
1160
|
}
|
|
1139
|
-
const previousUsedTokens = previous.reduce(
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
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
|
+
);
|
|
1154
1184
|
const usedTokens = previousPartialUsed + currentUsedTokens;
|
|
1155
1185
|
const remaining = tokens - usedTokens;
|
|
1156
1186
|
async function sync() {
|
|
1157
1187
|
const res = await Promise.all(dbs.map((s) => s.request));
|
|
1158
|
-
const allCurrentIds = [
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
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
|
+
];
|
|
1166
1198
|
for (const db of dbs) {
|
|
1167
1199
|
const [current2, _previous, _success] = await db.request;
|
|
1168
1200
|
const dbIds = current2.reduce((ids, currentId, index) => {
|
|
@@ -1171,13 +1203,16 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1171
1203
|
}
|
|
1172
1204
|
return ids;
|
|
1173
1205
|
}, []);
|
|
1174
|
-
const usedDbTokens = current2.reduce(
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
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
|
+
);
|
|
1181
1216
|
if (usedDbTokens >= tokens) {
|
|
1182
1217
|
continue;
|
|
1183
1218
|
}
|
|
@@ -1191,8 +1226,12 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1191
1226
|
}
|
|
1192
1227
|
}
|
|
1193
1228
|
const reset = (currentWindow + 1) * windowDuration;
|
|
1194
|
-
if (ctx.cache
|
|
1195
|
-
|
|
1229
|
+
if (ctx.cache) {
|
|
1230
|
+
if (!success) {
|
|
1231
|
+
ctx.cache.blockUntil(identifier, reset);
|
|
1232
|
+
} else if (incrementBy < 0) {
|
|
1233
|
+
ctx.cache.pop(identifier);
|
|
1234
|
+
}
|
|
1196
1235
|
}
|
|
1197
1236
|
return {
|
|
1198
1237
|
success: Boolean(success),
|
|
@@ -1229,14 +1268,11 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1229
1268
|
if (ctx.cache) {
|
|
1230
1269
|
ctx.cache.pop(identifier);
|
|
1231
1270
|
}
|
|
1232
|
-
await Promise.all(
|
|
1233
|
-
|
|
1234
|
-
regionContext,
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
[null]
|
|
1238
|
-
);
|
|
1239
|
-
}));
|
|
1271
|
+
await Promise.all(
|
|
1272
|
+
ctx.regionContexts.map((regionContext) => {
|
|
1273
|
+
safeEval(regionContext, RESET_SCRIPT, [pattern], [null]);
|
|
1274
|
+
})
|
|
1275
|
+
);
|
|
1240
1276
|
}
|
|
1241
1277
|
});
|
|
1242
1278
|
}
|
|
@@ -1285,7 +1321,8 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1285
1321
|
async limit(ctx, identifier, rate) {
|
|
1286
1322
|
const bucket = Math.floor(Date.now() / windowDuration);
|
|
1287
1323
|
const key = [identifier, bucket].join(":");
|
|
1288
|
-
|
|
1324
|
+
const incrementBy = rate ?? 1;
|
|
1325
|
+
if (ctx.cache && incrementBy > 0) {
|
|
1289
1326
|
const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
|
|
1290
1327
|
if (blocked) {
|
|
1291
1328
|
return {
|
|
@@ -1298,7 +1335,6 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1298
1335
|
};
|
|
1299
1336
|
}
|
|
1300
1337
|
}
|
|
1301
|
-
const incrementBy = rate ? Math.max(1, rate) : 1;
|
|
1302
1338
|
const usedTokensAfterUpdate = await safeEval(
|
|
1303
1339
|
ctx,
|
|
1304
1340
|
SCRIPTS.singleRegion.fixedWindow.limit,
|
|
@@ -1308,8 +1344,12 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1308
1344
|
const success = usedTokensAfterUpdate <= tokens;
|
|
1309
1345
|
const remainingTokens = Math.max(0, tokens - usedTokensAfterUpdate);
|
|
1310
1346
|
const reset = (bucket + 1) * windowDuration;
|
|
1311
|
-
if (ctx.cache
|
|
1312
|
-
|
|
1347
|
+
if (ctx.cache) {
|
|
1348
|
+
if (!success) {
|
|
1349
|
+
ctx.cache.blockUntil(identifier, reset);
|
|
1350
|
+
} else if (incrementBy < 0) {
|
|
1351
|
+
ctx.cache.pop(identifier);
|
|
1352
|
+
}
|
|
1313
1353
|
}
|
|
1314
1354
|
return {
|
|
1315
1355
|
success,
|
|
@@ -1372,7 +1412,8 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1372
1412
|
const currentKey = [identifier, currentWindow].join(":");
|
|
1373
1413
|
const previousWindow = currentWindow - 1;
|
|
1374
1414
|
const previousKey = [identifier, previousWindow].join(":");
|
|
1375
|
-
|
|
1415
|
+
const incrementBy = rate ?? 1;
|
|
1416
|
+
if (ctx.cache && incrementBy > 0) {
|
|
1376
1417
|
const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
|
|
1377
1418
|
if (blocked) {
|
|
1378
1419
|
return {
|
|
@@ -1385,7 +1426,6 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1385
1426
|
};
|
|
1386
1427
|
}
|
|
1387
1428
|
}
|
|
1388
|
-
const incrementBy = rate ? Math.max(1, rate) : 1;
|
|
1389
1429
|
const remainingTokens = await safeEval(
|
|
1390
1430
|
ctx,
|
|
1391
1431
|
SCRIPTS.singleRegion.slidingWindow.limit,
|
|
@@ -1394,8 +1434,12 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1394
1434
|
);
|
|
1395
1435
|
const success = remainingTokens >= 0;
|
|
1396
1436
|
const reset = (currentWindow + 1) * windowSize;
|
|
1397
|
-
if (ctx.cache
|
|
1398
|
-
|
|
1437
|
+
if (ctx.cache) {
|
|
1438
|
+
if (!success) {
|
|
1439
|
+
ctx.cache.blockUntil(identifier, reset);
|
|
1440
|
+
} else if (incrementBy < 0) {
|
|
1441
|
+
ctx.cache.pop(identifier);
|
|
1442
|
+
}
|
|
1399
1443
|
}
|
|
1400
1444
|
return {
|
|
1401
1445
|
success,
|
|
@@ -1453,7 +1497,9 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1453
1497
|
const intervalDuration = ms(interval);
|
|
1454
1498
|
return () => ({
|
|
1455
1499
|
async limit(ctx, identifier, rate) {
|
|
1456
|
-
|
|
1500
|
+
const now = Date.now();
|
|
1501
|
+
const incrementBy = rate ?? 1;
|
|
1502
|
+
if (ctx.cache && incrementBy > 0) {
|
|
1457
1503
|
const { blocked, reset: reset2 } = ctx.cache.isBlocked(identifier);
|
|
1458
1504
|
if (blocked) {
|
|
1459
1505
|
return {
|
|
@@ -1466,8 +1512,6 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1466
1512
|
};
|
|
1467
1513
|
}
|
|
1468
1514
|
}
|
|
1469
|
-
const now = Date.now();
|
|
1470
|
-
const incrementBy = rate ? Math.max(1, rate) : 1;
|
|
1471
1515
|
const [remaining, reset] = await safeEval(
|
|
1472
1516
|
ctx,
|
|
1473
1517
|
SCRIPTS.singleRegion.tokenBucket.limit,
|
|
@@ -1475,8 +1519,12 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1475
1519
|
[maxTokens, intervalDuration, refillRate, now, incrementBy]
|
|
1476
1520
|
);
|
|
1477
1521
|
const success = remaining >= 0;
|
|
1478
|
-
if (ctx.cache
|
|
1479
|
-
|
|
1522
|
+
if (ctx.cache) {
|
|
1523
|
+
if (!success) {
|
|
1524
|
+
ctx.cache.blockUntil(identifier, reset);
|
|
1525
|
+
} else if (incrementBy < 0) {
|
|
1526
|
+
ctx.cache.pop(identifier);
|
|
1527
|
+
}
|
|
1480
1528
|
}
|
|
1481
1529
|
return {
|
|
1482
1530
|
success,
|
|
@@ -1548,10 +1596,10 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1548
1596
|
const bucket = Math.floor(Date.now() / windowDuration);
|
|
1549
1597
|
const key = [identifier, bucket].join(":");
|
|
1550
1598
|
const reset = (bucket + 1) * windowDuration;
|
|
1551
|
-
const incrementBy = rate
|
|
1599
|
+
const incrementBy = rate ?? 1;
|
|
1552
1600
|
const hit = typeof ctx.cache.get(key) === "number";
|
|
1553
1601
|
if (hit) {
|
|
1554
|
-
const cachedTokensAfterUpdate = ctx.cache.incr(key);
|
|
1602
|
+
const cachedTokensAfterUpdate = ctx.cache.incr(key, incrementBy);
|
|
1555
1603
|
const success = cachedTokensAfterUpdate < tokens;
|
|
1556
1604
|
const pending = success ? safeEval(
|
|
1557
1605
|
ctx,
|