@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.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +168 -126
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +168 -126
- 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
|
}
|
|
@@ -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
|
-
|
|
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]
|
|
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
|
-
|
|
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 ==
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
-
|
|
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(
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
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 = [
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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(
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
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
|
|
1045
|
+
const success = remaining >= 0;
|
|
1036
1046
|
const reset = (bucket + 1) * windowDuration;
|
|
1037
|
-
if (ctx.cache
|
|
1038
|
-
|
|
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(
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
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(
|
|
1079
|
-
|
|
1080
|
-
regionContext,
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
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 = [
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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(
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
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
|
|
1201
|
-
|
|
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(
|
|
1239
|
-
|
|
1240
|
-
regionContext,
|
|
1241
|
-
|
|
1242
|
-
|
|
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
|
-
|
|
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
|
|
1318
|
-
|
|
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
|
-
|
|
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
|
|
1404
|
-
|
|
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
|
-
|
|
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
|
|
1485
|
-
|
|
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
|
|
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,
|