@upstash/ratelimit 2.0.3 → 2.0.4
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 +12 -15
- package/dist/index.d.ts +12 -15
- package/dist/index.js +45 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -46
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Aggregate } from '@upstash/core-analytics';
|
|
2
|
-
import {
|
|
2
|
+
import { Redis as Redis$1 } from '@upstash/redis';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* EphemeralCache is used to block certain identifiers right away in case they have already exceeded the ratelimit.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
type EphemeralCache = {
|
|
8
8
|
isBlocked: (identifier: string) => {
|
|
9
9
|
blocked: boolean;
|
|
10
10
|
reset: number;
|
|
@@ -16,7 +16,7 @@ interface EphemeralCache {
|
|
|
16
16
|
pop: (key: string) => void;
|
|
17
17
|
empty: () => void;
|
|
18
18
|
size: () => number;
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
type RegionContext = {
|
|
21
21
|
redis: Redis;
|
|
22
22
|
cache?: EphemeralCache;
|
|
@@ -90,7 +90,6 @@ type Algorithm<TContext> = () => {
|
|
|
90
90
|
}>;
|
|
91
91
|
resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
|
|
92
92
|
};
|
|
93
|
-
type IsDenied = 0 | 1;
|
|
94
93
|
type DeniedValue = string | undefined;
|
|
95
94
|
type LimitOptions = {
|
|
96
95
|
geo?: Geo;
|
|
@@ -102,17 +101,15 @@ type LimitOptions = {
|
|
|
102
101
|
/**
|
|
103
102
|
* This is all we need from the redis sdk.
|
|
104
103
|
*/
|
|
105
|
-
|
|
106
|
-
sadd:
|
|
107
|
-
hset:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
multi: () => Pipeline;
|
|
115
|
-
}
|
|
104
|
+
type Redis = {
|
|
105
|
+
sadd: Redis$1["sadd"];
|
|
106
|
+
hset: Redis$1["hset"];
|
|
107
|
+
eval: Redis$1["eval"];
|
|
108
|
+
evalsha: Redis$1["evalsha"];
|
|
109
|
+
scriptLoad: Redis$1["scriptLoad"];
|
|
110
|
+
smismember: Redis$1["smismember"];
|
|
111
|
+
multi: Redis$1["multi"];
|
|
112
|
+
};
|
|
116
113
|
|
|
117
114
|
type Geo = {
|
|
118
115
|
country?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Aggregate } from '@upstash/core-analytics';
|
|
2
|
-
import {
|
|
2
|
+
import { Redis as Redis$1 } from '@upstash/redis';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* EphemeralCache is used to block certain identifiers right away in case they have already exceeded the ratelimit.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
type EphemeralCache = {
|
|
8
8
|
isBlocked: (identifier: string) => {
|
|
9
9
|
blocked: boolean;
|
|
10
10
|
reset: number;
|
|
@@ -16,7 +16,7 @@ interface EphemeralCache {
|
|
|
16
16
|
pop: (key: string) => void;
|
|
17
17
|
empty: () => void;
|
|
18
18
|
size: () => number;
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
type RegionContext = {
|
|
21
21
|
redis: Redis;
|
|
22
22
|
cache?: EphemeralCache;
|
|
@@ -90,7 +90,6 @@ type Algorithm<TContext> = () => {
|
|
|
90
90
|
}>;
|
|
91
91
|
resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
|
|
92
92
|
};
|
|
93
|
-
type IsDenied = 0 | 1;
|
|
94
93
|
type DeniedValue = string | undefined;
|
|
95
94
|
type LimitOptions = {
|
|
96
95
|
geo?: Geo;
|
|
@@ -102,17 +101,15 @@ type LimitOptions = {
|
|
|
102
101
|
/**
|
|
103
102
|
* This is all we need from the redis sdk.
|
|
104
103
|
*/
|
|
105
|
-
|
|
106
|
-
sadd:
|
|
107
|
-
hset:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
multi: () => Pipeline;
|
|
115
|
-
}
|
|
104
|
+
type Redis = {
|
|
105
|
+
sadd: Redis$1["sadd"];
|
|
106
|
+
hset: Redis$1["hset"];
|
|
107
|
+
eval: Redis$1["eval"];
|
|
108
|
+
evalsha: Redis$1["evalsha"];
|
|
109
|
+
scriptLoad: Redis$1["scriptLoad"];
|
|
110
|
+
smismember: Redis$1["smismember"];
|
|
111
|
+
multi: Redis$1["multi"];
|
|
112
|
+
};
|
|
116
113
|
|
|
117
114
|
type Geo = {
|
|
118
115
|
country?: string;
|
package/dist/index.js
CHANGED
|
@@ -49,10 +49,10 @@ var Analytics = class {
|
|
|
49
49
|
* @returns
|
|
50
50
|
*/
|
|
51
51
|
extractGeo(req) {
|
|
52
|
-
if (
|
|
52
|
+
if (req.geo !== void 0) {
|
|
53
53
|
return req.geo;
|
|
54
54
|
}
|
|
55
|
-
if (
|
|
55
|
+
if (req.cf !== void 0) {
|
|
56
56
|
return req.cf;
|
|
57
57
|
}
|
|
58
58
|
return {};
|
|
@@ -141,18 +141,24 @@ function ms(d) {
|
|
|
141
141
|
const time = Number.parseInt(match[1]);
|
|
142
142
|
const unit = match[2];
|
|
143
143
|
switch (unit) {
|
|
144
|
-
case "ms":
|
|
144
|
+
case "ms": {
|
|
145
145
|
return time;
|
|
146
|
-
|
|
146
|
+
}
|
|
147
|
+
case "s": {
|
|
147
148
|
return time * 1e3;
|
|
148
|
-
|
|
149
|
+
}
|
|
150
|
+
case "m": {
|
|
149
151
|
return time * 1e3 * 60;
|
|
150
|
-
|
|
152
|
+
}
|
|
153
|
+
case "h": {
|
|
151
154
|
return time * 1e3 * 60 * 60;
|
|
152
|
-
|
|
155
|
+
}
|
|
156
|
+
case "d": {
|
|
153
157
|
return time * 1e3 * 60 * 60 * 24;
|
|
154
|
-
|
|
158
|
+
}
|
|
159
|
+
default: {
|
|
155
160
|
throw new Error(`Unable to parse window size: ${d}`);
|
|
161
|
+
}
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
|
|
@@ -594,7 +600,7 @@ var updateIpDenyList = async (redis, prefix, threshold, ttl) => {
|
|
|
594
600
|
const transaction = redis.multi();
|
|
595
601
|
transaction.sdiffstore(allDenyLists, allDenyLists, ipDenyList);
|
|
596
602
|
transaction.del(ipDenyList);
|
|
597
|
-
transaction.sadd(ipDenyList, ...allIps);
|
|
603
|
+
transaction.sadd(ipDenyList, allIps.at(0), ...allIps.slice(1));
|
|
598
604
|
transaction.sdiffstore(ipDenyList, ipDenyList, allDenyLists);
|
|
599
605
|
transaction.sunionstore(allDenyLists, allDenyLists, ipDenyList);
|
|
600
606
|
transaction.set(statusKey, "valid", { px: ttl ?? getIpListTTL() });
|
|
@@ -696,7 +702,7 @@ var Ratelimit = class {
|
|
|
696
702
|
}) : void 0;
|
|
697
703
|
if (config.ephemeralCache instanceof Map) {
|
|
698
704
|
this.ctx.cache = new Cache(config.ephemeralCache);
|
|
699
|
-
} else if (
|
|
705
|
+
} else if (config.ephemeralCache === void 0) {
|
|
700
706
|
this.ctx.cache = new Cache(/* @__PURE__ */ new Map());
|
|
701
707
|
}
|
|
702
708
|
}
|
|
@@ -827,15 +833,10 @@ var Ratelimit = class {
|
|
|
827
833
|
const key = this.getKey(identifier);
|
|
828
834
|
const definedMembers = this.getDefinedMembers(identifier, req);
|
|
829
835
|
const deniedValue = checkDenyListCache(definedMembers);
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
result = await Promise.all([
|
|
835
|
-
this.limiter().limit(this.ctx, key, req?.rate),
|
|
836
|
-
this.enableProtection ? checkDenyList(this.primaryRedis, this.prefix, definedMembers) : { deniedValue: void 0, invalidIpDenyList: false }
|
|
837
|
-
]);
|
|
838
|
-
}
|
|
836
|
+
const result = deniedValue ? [defaultDeniedResponse(deniedValue), { deniedValue, invalidIpDenyList: false }] : await Promise.all([
|
|
837
|
+
this.limiter().limit(this.ctx, key, req?.rate),
|
|
838
|
+
this.enableProtection ? checkDenyList(this.primaryRedis, this.prefix, definedMembers) : { deniedValue: void 0, invalidIpDenyList: false }
|
|
839
|
+
]);
|
|
839
840
|
return resolveLimitPayload(this.primaryRedis, this.prefix, result, this.denyListThreshold);
|
|
840
841
|
};
|
|
841
842
|
/**
|
|
@@ -885,9 +886,9 @@ var Ratelimit = class {
|
|
|
885
886
|
time: Date.now(),
|
|
886
887
|
success: ratelimitResponse.reason === "denyList" ? "denied" : ratelimitResponse.success,
|
|
887
888
|
...geo
|
|
888
|
-
}).catch((
|
|
889
|
+
}).catch((error) => {
|
|
889
890
|
let errorMessage = "Failed to record analytics";
|
|
890
|
-
if (`${
|
|
891
|
+
if (`${error}`.includes("WRONGTYPE")) {
|
|
891
892
|
errorMessage = `
|
|
892
893
|
Failed to record analytics. See the information below:
|
|
893
894
|
|
|
@@ -900,11 +901,11 @@ var Ratelimit = class {
|
|
|
900
901
|
|
|
901
902
|
`;
|
|
902
903
|
}
|
|
903
|
-
console.warn(errorMessage,
|
|
904
|
+
console.warn(errorMessage, error);
|
|
904
905
|
});
|
|
905
906
|
ratelimitResponse.pending = Promise.all([ratelimitResponse.pending, analyticsP]);
|
|
906
|
-
} catch (
|
|
907
|
-
console.warn("Failed to record analytics",
|
|
907
|
+
} catch (error) {
|
|
908
|
+
console.warn("Failed to record analytics", error);
|
|
908
909
|
}
|
|
909
910
|
;
|
|
910
911
|
}
|
|
@@ -1015,18 +1016,17 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1015
1016
|
const remaining = tokens - usedTokens;
|
|
1016
1017
|
async function sync() {
|
|
1017
1018
|
const individualIDs = await Promise.all(dbs.map((s) => s.request));
|
|
1018
|
-
const allIDs =
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
).values()
|
|
1027
|
-
);
|
|
1019
|
+
const allIDs = [...new Set(
|
|
1020
|
+
individualIDs.flat().reduce((acc, curr, index) => {
|
|
1021
|
+
if (index % 2 === 0) {
|
|
1022
|
+
acc.push(curr);
|
|
1023
|
+
}
|
|
1024
|
+
return acc;
|
|
1025
|
+
}, [])
|
|
1026
|
+
).values()];
|
|
1028
1027
|
for (const db of dbs) {
|
|
1029
|
-
const
|
|
1028
|
+
const usedDbTokensRequest = await db.request;
|
|
1029
|
+
const usedDbTokens = usedDbTokensRequest.reduce(
|
|
1030
1030
|
(accTokens, usedToken, index) => {
|
|
1031
1031
|
let parsedToken = 0;
|
|
1032
1032
|
if (index % 2) {
|
|
@@ -1036,7 +1036,8 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1036
1036
|
},
|
|
1037
1037
|
0
|
|
1038
1038
|
);
|
|
1039
|
-
const
|
|
1039
|
+
const dbIdsRequest = await db.request;
|
|
1040
|
+
const dbIds = dbIdsRequest.reduce((ids, currentId, index) => {
|
|
1040
1041
|
if (index % 2 === 0) {
|
|
1041
1042
|
ids.push(currentId);
|
|
1042
1043
|
}
|
|
@@ -1183,16 +1184,14 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1183
1184
|
const remaining = tokens - usedTokens;
|
|
1184
1185
|
async function sync() {
|
|
1185
1186
|
const res = await Promise.all(dbs.map((s) => s.request));
|
|
1186
|
-
const allCurrentIds =
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
).values()
|
|
1195
|
-
);
|
|
1187
|
+
const allCurrentIds = [...new Set(
|
|
1188
|
+
res.flatMap(([current2]) => current2).reduce((acc, curr, index) => {
|
|
1189
|
+
if (index % 2 === 0) {
|
|
1190
|
+
acc.push(curr);
|
|
1191
|
+
}
|
|
1192
|
+
return acc;
|
|
1193
|
+
}, [])
|
|
1194
|
+
).values()];
|
|
1196
1195
|
for (const db of dbs) {
|
|
1197
1196
|
const [current2, _previous, _success] = await db.request;
|
|
1198
1197
|
const dbIds = current2.reduce((ids, currentId, index) => {
|