@upstash/ratelimit 1.2.1 → 2.0.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/README.md +2 -1
- package/dist/index.d.mts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +43 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@upstash/ratelimit)
|
|
4
4
|
[](https://github.com/upstash/ratelimit/actions/workflows/tests.yaml)
|
|
5
5
|
|
|
6
|
-
> [!NOTE]
|
|
6
|
+
> [!NOTE]
|
|
7
|
+
> **This project is in GA Stage.**
|
|
7
8
|
> The Upstash Professional Support fully covers this project. It receives regular updates, and bug fixes. The Upstash team is committed to maintaining and improving its functionality.
|
|
8
9
|
|
|
9
10
|
It is the only connectionless (HTTP based) rate limiting library and designed
|
package/dist/index.d.mts
CHANGED
|
@@ -90,7 +90,10 @@ type Algorithm<TContext> = () => {
|
|
|
90
90
|
limit: (ctx: TContext, identifier: string, rate?: number, opts?: {
|
|
91
91
|
cache?: EphemeralCache;
|
|
92
92
|
}) => Promise<RatelimitResponse>;
|
|
93
|
-
getRemaining: (ctx: TContext, identifier: string) => Promise<
|
|
93
|
+
getRemaining: (ctx: TContext, identifier: string) => Promise<{
|
|
94
|
+
remaining: number;
|
|
95
|
+
reset: number;
|
|
96
|
+
}>;
|
|
94
97
|
resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
|
|
95
98
|
};
|
|
96
99
|
type IsDenied = 0 | 1;
|
|
@@ -328,7 +331,18 @@ declare abstract class Ratelimit<TContext extends Context> {
|
|
|
328
331
|
*/
|
|
329
332
|
blockUntilReady: (identifier: string, timeout: number) => Promise<RatelimitResponse>;
|
|
330
333
|
resetUsedTokens: (identifier: string) => Promise<void>;
|
|
331
|
-
|
|
334
|
+
/**
|
|
335
|
+
* Returns the remaining token count together with a reset timestamps
|
|
336
|
+
*
|
|
337
|
+
* @param identifier identifir to check
|
|
338
|
+
* @returns object with `remaining` and reset fields. `remaining` denotes
|
|
339
|
+
* the remaining tokens and reset denotes the timestamp when the
|
|
340
|
+
* tokens reset.
|
|
341
|
+
*/
|
|
342
|
+
getRemaining: (identifier: string) => Promise<{
|
|
343
|
+
remaining: number;
|
|
344
|
+
reset: number;
|
|
345
|
+
}>;
|
|
332
346
|
/**
|
|
333
347
|
* Checks if the identifier or the values in req are in the deny list cache.
|
|
334
348
|
* If so, returns the default denied response.
|
package/dist/index.d.ts
CHANGED
|
@@ -90,7 +90,10 @@ type Algorithm<TContext> = () => {
|
|
|
90
90
|
limit: (ctx: TContext, identifier: string, rate?: number, opts?: {
|
|
91
91
|
cache?: EphemeralCache;
|
|
92
92
|
}) => Promise<RatelimitResponse>;
|
|
93
|
-
getRemaining: (ctx: TContext, identifier: string) => Promise<
|
|
93
|
+
getRemaining: (ctx: TContext, identifier: string) => Promise<{
|
|
94
|
+
remaining: number;
|
|
95
|
+
reset: number;
|
|
96
|
+
}>;
|
|
94
97
|
resetTokens: (ctx: TContext, identifier: string) => Promise<void>;
|
|
95
98
|
};
|
|
96
99
|
type IsDenied = 0 | 1;
|
|
@@ -328,7 +331,18 @@ declare abstract class Ratelimit<TContext extends Context> {
|
|
|
328
331
|
*/
|
|
329
332
|
blockUntilReady: (identifier: string, timeout: number) => Promise<RatelimitResponse>;
|
|
330
333
|
resetUsedTokens: (identifier: string) => Promise<void>;
|
|
331
|
-
|
|
334
|
+
/**
|
|
335
|
+
* Returns the remaining token count together with a reset timestamps
|
|
336
|
+
*
|
|
337
|
+
* @param identifier identifir to check
|
|
338
|
+
* @returns object with `remaining` and reset fields. `remaining` denotes
|
|
339
|
+
* the remaining tokens and reset denotes the timestamp when the
|
|
340
|
+
* tokens reset.
|
|
341
|
+
*/
|
|
342
|
+
getRemaining: (identifier: string) => Promise<{
|
|
343
|
+
remaining: number;
|
|
344
|
+
reset: number;
|
|
345
|
+
}>;
|
|
332
346
|
/**
|
|
333
347
|
* Checks if the identifier or the values in req are in the deny list cache.
|
|
334
348
|
* If so, returns the default denied response.
|
package/dist/index.js
CHANGED
|
@@ -583,6 +583,14 @@ var Ratelimit = class {
|
|
|
583
583
|
const pattern = [this.prefix, identifier].join(":");
|
|
584
584
|
await this.limiter().resetTokens(this.ctx, pattern);
|
|
585
585
|
};
|
|
586
|
+
/**
|
|
587
|
+
* Returns the remaining token count together with a reset timestamps
|
|
588
|
+
*
|
|
589
|
+
* @param identifier identifir to check
|
|
590
|
+
* @returns object with `remaining` and reset fields. `remaining` denotes
|
|
591
|
+
* the remaining tokens and reset denotes the timestamp when the
|
|
592
|
+
* tokens reset.
|
|
593
|
+
*/
|
|
586
594
|
getRemaining = async (identifier) => {
|
|
587
595
|
const pattern = [this.prefix, identifier].join(":");
|
|
588
596
|
return await this.limiter().getRemaining(this.ctx, pattern);
|
|
@@ -867,7 +875,10 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
867
875
|
}
|
|
868
876
|
return accTokens + parsedToken;
|
|
869
877
|
}, 0);
|
|
870
|
-
return
|
|
878
|
+
return {
|
|
879
|
+
remaining: Math.max(0, tokens - usedTokens),
|
|
880
|
+
reset: (bucket + 1) * windowDuration
|
|
881
|
+
};
|
|
871
882
|
},
|
|
872
883
|
async resetTokens(ctx, identifier) {
|
|
873
884
|
const pattern = [identifier, "*"].join(":");
|
|
@@ -1029,7 +1040,10 @@ var MultiRegionRatelimit = class extends Ratelimit {
|
|
|
1029
1040
|
)
|
|
1030
1041
|
}));
|
|
1031
1042
|
const usedTokens = await Promise.any(dbs.map((s) => s.request));
|
|
1032
|
-
return
|
|
1043
|
+
return {
|
|
1044
|
+
remaining: Math.max(0, tokens - usedTokens),
|
|
1045
|
+
reset: (currentWindow + 1) * windowSize
|
|
1046
|
+
};
|
|
1033
1047
|
},
|
|
1034
1048
|
async resetTokens(ctx, identifier) {
|
|
1035
1049
|
const pattern = [identifier, "*"].join(":");
|
|
@@ -1168,17 +1182,18 @@ var tokenBucketLimitScript = `
|
|
|
1168
1182
|
redis.call("PEXPIRE", key, expireAt)
|
|
1169
1183
|
return {remaining, refilledAt + interval}
|
|
1170
1184
|
`;
|
|
1185
|
+
var tokenBucketIdentifierNotFound = -1;
|
|
1171
1186
|
var tokenBucketRemainingTokensScript = `
|
|
1172
1187
|
local key = KEYS[1]
|
|
1173
1188
|
local maxTokens = tonumber(ARGV[1])
|
|
1174
1189
|
|
|
1175
|
-
local bucket = redis.call("HMGET", key, "tokens")
|
|
1190
|
+
local bucket = redis.call("HMGET", key, "refilledAt", "tokens")
|
|
1176
1191
|
|
|
1177
1192
|
if bucket[1] == false then
|
|
1178
|
-
return maxTokens
|
|
1193
|
+
return {maxTokens, ${tokenBucketIdentifierNotFound}}
|
|
1179
1194
|
end
|
|
1180
1195
|
|
|
1181
|
-
return tonumber(bucket[1])
|
|
1196
|
+
return {tonumber(bucket[2]), tonumber(bucket[1])}
|
|
1182
1197
|
`;
|
|
1183
1198
|
var cachedFixedWindowLimitScript = `
|
|
1184
1199
|
local key = KEYS[1]
|
|
@@ -1295,7 +1310,10 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1295
1310
|
[key],
|
|
1296
1311
|
[null]
|
|
1297
1312
|
);
|
|
1298
|
-
return
|
|
1313
|
+
return {
|
|
1314
|
+
remaining: Math.max(0, tokens - usedTokens),
|
|
1315
|
+
reset: (bucket + 1) * windowDuration
|
|
1316
|
+
};
|
|
1299
1317
|
},
|
|
1300
1318
|
async resetTokens(ctx, identifier) {
|
|
1301
1319
|
const pattern = [identifier, "*"].join(":");
|
|
@@ -1384,7 +1402,10 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1384
1402
|
[currentKey, previousKey],
|
|
1385
1403
|
[now, windowSize]
|
|
1386
1404
|
);
|
|
1387
|
-
return
|
|
1405
|
+
return {
|
|
1406
|
+
remaining: Math.max(0, tokens - usedTokens),
|
|
1407
|
+
reset: (currentWindow + 1) * windowSize
|
|
1408
|
+
};
|
|
1388
1409
|
},
|
|
1389
1410
|
async resetTokens(ctx, identifier) {
|
|
1390
1411
|
const pattern = [identifier, "*"].join(":");
|
|
@@ -1453,14 +1474,19 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1453
1474
|
};
|
|
1454
1475
|
},
|
|
1455
1476
|
async getRemaining(ctx, identifier) {
|
|
1456
|
-
const remainingTokens = await safeEval(
|
|
1477
|
+
const [remainingTokens, refilledAt] = await safeEval(
|
|
1457
1478
|
ctx,
|
|
1458
1479
|
tokenBucketRemainingTokensScript,
|
|
1459
1480
|
"getRemainingHash",
|
|
1460
1481
|
[identifier],
|
|
1461
1482
|
[maxTokens]
|
|
1462
1483
|
);
|
|
1463
|
-
|
|
1484
|
+
const freshRefillAt = Date.now() + intervalDuration;
|
|
1485
|
+
const identifierRefillsAt = refilledAt + intervalDuration;
|
|
1486
|
+
return {
|
|
1487
|
+
remaining: remainingTokens,
|
|
1488
|
+
reset: refilledAt === tokenBucketIdentifierNotFound ? freshRefillAt : identifierRefillsAt
|
|
1489
|
+
};
|
|
1464
1490
|
},
|
|
1465
1491
|
async resetTokens(ctx, identifier) {
|
|
1466
1492
|
const pattern = identifier;
|
|
@@ -1557,7 +1583,10 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1557
1583
|
const hit = typeof ctx.cache.get(key) === "number";
|
|
1558
1584
|
if (hit) {
|
|
1559
1585
|
const cachedUsedTokens = ctx.cache.get(key) ?? 0;
|
|
1560
|
-
return
|
|
1586
|
+
return {
|
|
1587
|
+
remaining: Math.max(0, tokens - cachedUsedTokens),
|
|
1588
|
+
reset: (bucket + 1) * windowDuration
|
|
1589
|
+
};
|
|
1561
1590
|
}
|
|
1562
1591
|
const usedTokens = await safeEval(
|
|
1563
1592
|
ctx,
|
|
@@ -1566,7 +1595,10 @@ var RegionRatelimit = class extends Ratelimit {
|
|
|
1566
1595
|
[key],
|
|
1567
1596
|
[null]
|
|
1568
1597
|
);
|
|
1569
|
-
return
|
|
1598
|
+
return {
|
|
1599
|
+
remaining: Math.max(0, tokens - usedTokens),
|
|
1600
|
+
reset: (bucket + 1) * windowDuration
|
|
1601
|
+
};
|
|
1570
1602
|
},
|
|
1571
1603
|
async resetTokens(ctx, identifier) {
|
|
1572
1604
|
if (!ctx.cache) {
|