rate-limiter-flexible 10.0.0 → 11.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/CONTEXT.md +13 -5
- package/README.md +3 -2
- package/index.js +2 -0
- package/lib/RLWrapperBlackAndWhite.js +44 -64
- package/lib/RLWrapperTimeouts.js +8 -4
- package/lib/RateLimiterAbstract.js +1 -1
- package/lib/RateLimiterCompatibleAbstract.js +49 -0
- package/lib/RateLimiterInsuredAbstract.js +4 -3
- package/lib/RateLimiterStoreAbstract.js +21 -2
- package/lib/RateLimiterUnion.js +3 -3
- package/lib/helper/isRateLimiterCompatible.js +6 -0
- package/package.json +1 -1
- package/types.d.ts +66 -10
package/CONTEXT.md
CHANGED
|
@@ -30,7 +30,7 @@ const { RateLimiterMemory } = require("rate-limiter-flexible");
|
|
|
30
30
|
## Core Concepts
|
|
31
31
|
|
|
32
32
|
- **Atomic increments** — all operations use atomic increments to prevent race conditions.
|
|
33
|
-
- **
|
|
33
|
+
- **Enhanced fixed window** algorithm — starts counting from the moment a request is received, diversifying rate limit reset times across clients. Much faster than rolling window. See [comparative benchmarks](https://github.com/animir/node-rate-limiter-flexible/wiki/Comparative-benchmarks).
|
|
34
34
|
- **Zero production dependencies.**
|
|
35
35
|
- **Deno compatible** — see [example gist](https://gist.github.com/animir/d06ca92931677f330d3f2d4c6c3108e4).
|
|
36
36
|
- **Browser compatible** — `RateLimiterMemory` works in the browser.
|
|
@@ -45,8 +45,8 @@ Options can be changed at runtime: `rateLimiter.points = 50`, `rateLimiter.durat
|
|
|
45
45
|
|
|
46
46
|
| Option | Default | Description |
|
|
47
47
|
|--------|---------|-------------|
|
|
48
|
-
| `points` |
|
|
49
|
-
| `duration` |
|
|
48
|
+
| `points` | **Required** | Max points consumable over `duration`. Must be a number. |
|
|
49
|
+
| `duration` | **Required** | Seconds before points reset (from first consume). Must be >= 0. `0` = never expire. |
|
|
50
50
|
| `keyPrefix` | `'rlflx'` | Unique prefix per limiter to avoid key collisions. For some stores, used as table/collection name. |
|
|
51
51
|
| `blockDuration` | `0` | If >0, block key for this many seconds once points exhausted. |
|
|
52
52
|
| `storeClient` | — | Required for store limiters. Accepts `@valkey/valkey-glide`, `iovalkey`, `redis`, `ioredis`, `memcached`, `mongodb`, `pg`, `mysql2`, `mysql`, Sequelize, TypeORM, Knex, or any related pool/connection. |
|
|
@@ -156,9 +156,9 @@ const headers = {
|
|
|
156
156
|
| Limiter | Description |
|
|
157
157
|
|---------|-------------|
|
|
158
158
|
| `BurstyRateLimiter` | Combines two limiters: primary + burst allowance. |
|
|
159
|
-
| `RateLimiterUnion` | Consume from multiple limiters simultaneously. Only `consume` method. |
|
|
159
|
+
| `RateLimiterUnion` | Consume from multiple limiters simultaneously. Only `consume` method. Accepts any `RateLimiterAbstract` or `RateLimiterCompatibleAbstract` instance. |
|
|
160
160
|
| `RateLimiterQueue` | Queue actions and execute at controlled rate (FIFO). |
|
|
161
|
-
| `RLWrapperBlackAndWhite` | Wrap any limiter with black/white IP lists. |
|
|
161
|
+
| `RLWrapperBlackAndWhite` | Wrap any limiter with black/white IP lists. Can be used as `insuranceLimiter`, in `RLWrapperTimeouts`, or `RateLimiterUnion`. |
|
|
162
162
|
| `RLWrapperTimeouts` | Wrap any limiter with custom timeout behavior. |
|
|
163
163
|
|
|
164
164
|
## Common Patterns
|
|
@@ -469,3 +469,11 @@ Any new limiter with storage must extend `RateLimiterStoreAbstract` and implemen
|
|
|
469
469
|
- `_upsert` — atomic or non-atomic increment. Must support `forceExpire` mode. If non-atomic, suffix class with `NonAtomic` (e.g. `RateLimiterRedisNonAtomic`).
|
|
470
470
|
- `_get` — return raw data by key or `null`.
|
|
471
471
|
- `_delete` — delete key data, return `true`/`false`.
|
|
472
|
+
|
|
473
|
+
## Creating Custom Wrappers
|
|
474
|
+
|
|
475
|
+
For wrapper classes that don't need full `RateLimiterAbstract` functionality (like `points`, `duration`, etc.), extend `RateLimiterCompatibleAbstract` instead. This lightweight abstract class requires implementing:
|
|
476
|
+
- `consume`, `penalty`, `reward`, `get`, `set`, `block`, `delete` methods
|
|
477
|
+
- `blockDuration` and `execEvenly` getters/setters (if not used, empty no-op implementations can be provided)
|
|
478
|
+
|
|
479
|
+
Classes extending `RateLimiterCompatibleAbstract` can be used anywhere `RateLimiterAbstract` is accepted: as `insuranceLimiter`, in `RLWrapperTimeouts`, `RateLimiterUnion`, etc. See `RLWrapperBlackAndWhite` for an example implementation.
|
package/README.md
CHANGED
|
@@ -32,8 +32,7 @@ Memory limiter also works in the browser.
|
|
|
32
32
|
|
|
33
33
|
**Deno compatible** See [this example](https://gist.github.com/animir/d06ca92931677f330d3f2d4c6c3108e4)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
[See comparative benchmarks with other libraries here](https://github.com/animir/node-rate-limiter-flexible/wiki/Comparative-benchmarks)
|
|
35
|
+
The enhanced fixed window algorithm starts counting from the moment a request is received, diversifying rate limit reset times across clients.
|
|
37
36
|
|
|
38
37
|
## Installation
|
|
39
38
|
|
|
@@ -227,4 +226,6 @@ It has to implement 4 methods:
|
|
|
227
226
|
|
|
228
227
|
All other methods depend on the store. See `RateLimiterRedis` or `RateLimiterPostgres` for examples.
|
|
229
228
|
|
|
229
|
+
For wrapper classes that don't need full `RateLimiterAbstract` functionality, extend `RateLimiterCompatibleAbstract` instead. It requires implementing `consume`, `penalty`, `reward`, `get`, `set`, `block`, `delete` methods and `blockDuration`/`execEvenly` getters/setters. If the wrapper doesn't use `blockDuration` or `execEvenly`, empty no-op implementations can be provided. See `RLWrapperBlackAndWhite` for an example.
|
|
230
|
+
|
|
230
231
|
Note: all changes should be covered by tests.
|
package/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const RateLimiterUnion = require('./lib/RateLimiterUnion');
|
|
|
12
12
|
const RateLimiterQueue = require('./lib/RateLimiterQueue');
|
|
13
13
|
const BurstyRateLimiter = require('./lib/BurstyRateLimiter');
|
|
14
14
|
const RateLimiterRes = require('./lib/RateLimiterRes');
|
|
15
|
+
const RateLimiterCompatibleAbstract = require('./lib/RateLimiterCompatibleAbstract');
|
|
15
16
|
const RateLimiterDynamo = require('./lib/RateLimiterDynamo');
|
|
16
17
|
const RateLimiterPrisma = require('./lib/RateLimiterPrisma');
|
|
17
18
|
const RateLimiterDrizzle = require('./lib/RateLimiterDrizzle');
|
|
@@ -41,6 +42,7 @@ module.exports = {
|
|
|
41
42
|
RateLimiterQueue,
|
|
42
43
|
BurstyRateLimiter,
|
|
43
44
|
RateLimiterRes,
|
|
45
|
+
RateLimiterCompatibleAbstract,
|
|
44
46
|
RateLimiterDynamo,
|
|
45
47
|
RateLimiterPrisma,
|
|
46
48
|
RateLimiterValkey,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const RateLimiterRes = require('./RateLimiterRes');
|
|
2
|
+
const RateLimiterCompatibleAbstract = require('./RateLimiterCompatibleAbstract');
|
|
2
3
|
|
|
3
|
-
module.exports = class RLWrapperBlackAndWhite {
|
|
4
|
+
module.exports = class RLWrapperBlackAndWhite extends RateLimiterCompatibleAbstract {
|
|
4
5
|
constructor(opts = {}) {
|
|
6
|
+
super();
|
|
5
7
|
this.limiter = opts.limiter;
|
|
6
8
|
this.blackList = opts.blackList;
|
|
7
9
|
this.whiteList = opts.whiteList;
|
|
@@ -22,6 +24,26 @@ module.exports = class RLWrapperBlackAndWhite {
|
|
|
22
24
|
this._limiter = value;
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
get keyPrefix() {
|
|
28
|
+
return this.limiter.keyPrefix;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get blockDuration() {
|
|
32
|
+
return this.limiter.blockDuration;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
set blockDuration(value) {
|
|
36
|
+
this.limiter.blockDuration = value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get execEvenly() {
|
|
40
|
+
return this.limiter.execEvenly;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
set execEvenly(value) {
|
|
44
|
+
this.limiter.execEvenly = value;
|
|
45
|
+
}
|
|
46
|
+
|
|
25
47
|
get runActionAnyway() {
|
|
26
48
|
return this._runActionAnyway;
|
|
27
49
|
}
|
|
@@ -102,94 +124,52 @@ module.exports = class RLWrapperBlackAndWhite {
|
|
|
102
124
|
return Promise.resolve(this.getWhiteRes());
|
|
103
125
|
}
|
|
104
126
|
|
|
105
|
-
|
|
127
|
+
_execAction(action, key, args, rejectOnBlack = false) {
|
|
106
128
|
let res;
|
|
107
129
|
if (this.isWhiteListedSomewhere(key)) {
|
|
108
130
|
res = this.resolveWhite();
|
|
109
131
|
} else if (this.isBlackListedSomewhere(key)) {
|
|
110
|
-
res = this.rejectBlack();
|
|
132
|
+
res = rejectOnBlack ? this.rejectBlack() : this.resolveBlack();
|
|
111
133
|
}
|
|
112
134
|
|
|
113
135
|
if (typeof res === 'undefined') {
|
|
114
|
-
return this.limiter
|
|
136
|
+
return this.limiter[action](key, ...args);
|
|
115
137
|
}
|
|
116
138
|
|
|
117
139
|
if (this.runActionAnyway) {
|
|
118
|
-
this.limiter
|
|
140
|
+
this.limiter[action](key, ...args).catch(() => {});
|
|
119
141
|
}
|
|
120
142
|
return res;
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (this.isWhiteListedSomewhere(key)) {
|
|
126
|
-
res = this.resolveWhite();
|
|
127
|
-
} else if (this.isBlackListedSomewhere(key)) {
|
|
128
|
-
res = this.resolveBlack();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (typeof res === 'undefined') {
|
|
132
|
-
return this.limiter.block(key, secDuration);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (this.runActionAnyway) {
|
|
136
|
-
this.limiter.block(key, secDuration).catch(() => {});
|
|
137
|
-
}
|
|
138
|
-
return res;
|
|
145
|
+
consume(key, pointsToConsume = 1, options = {}) {
|
|
146
|
+
return this._execAction('consume', key, [pointsToConsume, options], true);
|
|
139
147
|
}
|
|
140
148
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (this.isWhiteListedSomewhere(key)) {
|
|
144
|
-
res = this.resolveWhite();
|
|
145
|
-
} else if (this.isBlackListedSomewhere(key)) {
|
|
146
|
-
res = this.resolveBlack();
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (typeof res === 'undefined') {
|
|
150
|
-
return this.limiter.penalty(key, points);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (this.runActionAnyway) {
|
|
154
|
-
this.limiter.penalty(key, points).catch(() => {});
|
|
155
|
-
}
|
|
156
|
-
return res;
|
|
149
|
+
block(key, secDuration, options = {}) {
|
|
150
|
+
return this._execAction('block', key, [secDuration, options]);
|
|
157
151
|
}
|
|
158
152
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
res = this.resolveWhite();
|
|
163
|
-
} else if (this.isBlackListedSomewhere(key)) {
|
|
164
|
-
res = this.resolveBlack();
|
|
165
|
-
}
|
|
153
|
+
penalty(key, points = 1, options = {}) {
|
|
154
|
+
return this._execAction('penalty', key, [points, options]);
|
|
155
|
+
}
|
|
166
156
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
157
|
+
reward(key, points = 1, options = {}) {
|
|
158
|
+
return this._execAction('reward', key, [points, options]);
|
|
159
|
+
}
|
|
170
160
|
|
|
161
|
+
get(key, options = {}) {
|
|
171
162
|
if (this.runActionAnyway) {
|
|
172
|
-
this.limiter.
|
|
163
|
+
return this.limiter.get(key, options);
|
|
173
164
|
}
|
|
174
|
-
return
|
|
165
|
+
return this._execAction('get', key, [options]);
|
|
175
166
|
}
|
|
176
167
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (this.isWhiteListedSomewhere(key)) {
|
|
180
|
-
res = this.resolveWhite();
|
|
181
|
-
} else if (this.isBlackListedSomewhere(key)) {
|
|
182
|
-
res = this.resolveBlack();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (typeof res === 'undefined' || this.runActionAnyway) {
|
|
186
|
-
return this.limiter.get(key);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return res;
|
|
168
|
+
set(key, points, secDuration, options = {}) {
|
|
169
|
+
return this._execAction('set', key, [points, secDuration, options]);
|
|
190
170
|
}
|
|
191
171
|
|
|
192
|
-
delete(key) {
|
|
193
|
-
return this.limiter.delete(key);
|
|
172
|
+
delete(key, options = {}) {
|
|
173
|
+
return this.limiter.delete(key, options);
|
|
194
174
|
}
|
|
195
175
|
};
|
package/lib/RLWrapperTimeouts.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
const RateLimiterAbstract = require('./RateLimiterAbstract');
|
|
2
1
|
const RateLimiterInsuredAbstract = require('./RateLimiterInsuredAbstract');
|
|
2
|
+
const isRateLimiterCompatible = require('./helper/isRateLimiterCompatible');
|
|
3
3
|
|
|
4
4
|
module.exports = class RLWrapperTimeouts extends RateLimiterInsuredAbstract {
|
|
5
5
|
constructor(opts= {}) {
|
|
6
|
-
super(
|
|
6
|
+
super({
|
|
7
|
+
...opts,
|
|
8
|
+
points: 0,
|
|
9
|
+
duration: 0,
|
|
10
|
+
});
|
|
7
11
|
this.limiter = opts.limiter;
|
|
8
12
|
this.timeoutMs = opts.timeoutMs || 0;
|
|
9
13
|
}
|
|
@@ -13,8 +17,8 @@ module.exports = class RLWrapperTimeouts extends RateLimiterInsuredAbstract {
|
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
set limiter(limiter) {
|
|
16
|
-
if (!(limiter
|
|
17
|
-
throw new TypeError('limiter must be an instance of RateLimiterAbstract');
|
|
20
|
+
if (!isRateLimiterCompatible(limiter)) {
|
|
21
|
+
throw new TypeError('limiter must be an instance of RateLimiterAbstract or RateLimiterCompatibleAbstract');
|
|
18
22
|
}
|
|
19
23
|
this._limiter = limiter;
|
|
20
24
|
if (!this.insuranceLimiter && limiter instanceof RateLimiterInsuredAbstract) {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module.exports = class RateLimiterCompatibleAbstract {
|
|
2
|
+
get keyPrefix() {
|
|
3
|
+
throw new Error("You have to implement the getter 'keyPrefix'!");
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
get blockDuration() {
|
|
7
|
+
throw new Error("You have to implement the getter 'blockDuration'!");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
set blockDuration(value) {
|
|
11
|
+
throw new Error("You have to implement the setter 'blockDuration'!");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get execEvenly() {
|
|
15
|
+
throw new Error("You have to implement the getter 'execEvenly'!");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
set execEvenly(value) {
|
|
19
|
+
throw new Error("You have to implement the setter 'execEvenly'!");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
consume() {
|
|
23
|
+
throw new Error("You have to implement the method 'consume'!");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
penalty() {
|
|
27
|
+
throw new Error("You have to implement the method 'penalty'!");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
reward() {
|
|
31
|
+
throw new Error("You have to implement the method 'reward'!");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get() {
|
|
35
|
+
throw new Error("You have to implement the method 'get'!");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set() {
|
|
39
|
+
throw new Error("You have to implement the method 'set'!");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
block() {
|
|
43
|
+
throw new Error("You have to implement the method 'block'!");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
delete() {
|
|
47
|
+
throw new Error("You have to implement the method 'delete'!");
|
|
48
|
+
}
|
|
49
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const RateLimiterAbstract = require('./RateLimiterAbstract');
|
|
2
2
|
const RateLimiterRes = require('./RateLimiterRes');
|
|
3
|
+
const isRateLimiterCompatible = require('./helper/isRateLimiterCompatible');
|
|
3
4
|
|
|
4
5
|
module.exports = class RateLimiterInsuredAbstract extends RateLimiterAbstract {
|
|
5
6
|
constructor(opts = {}) {
|
|
@@ -12,8 +13,8 @@ module.exports = class RateLimiterInsuredAbstract extends RateLimiterAbstract {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
set insuranceLimiter(value) {
|
|
15
|
-
if (typeof value !== 'undefined' && !(value
|
|
16
|
-
throw new Error('insuranceLimiter must be instance of RateLimiterAbstract');
|
|
16
|
+
if (typeof value !== 'undefined' && !isRateLimiterCompatible(value)) {
|
|
17
|
+
throw new Error('insuranceLimiter must be instance of RateLimiterAbstract or RateLimiterCompatibleAbstract');
|
|
17
18
|
}
|
|
18
19
|
this._insuranceLimiter = value;
|
|
19
20
|
if (this._insuranceLimiter) {
|
|
@@ -25,7 +26,7 @@ module.exports = class RateLimiterInsuredAbstract extends RateLimiterAbstract {
|
|
|
25
26
|
_handleError(err, funcName, resolve, reject, params) {
|
|
26
27
|
if (err instanceof RateLimiterRes) {
|
|
27
28
|
reject(err);
|
|
28
|
-
} else if (!(this.insuranceLimiter
|
|
29
|
+
} else if (!isRateLimiterCompatible(this.insuranceLimiter)) {
|
|
29
30
|
reject(err);
|
|
30
31
|
} else {
|
|
31
32
|
this.insuranceLimiter[funcName](...params)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const RateLimiterAbstract = require('./RateLimiterAbstract');
|
|
2
1
|
const BlockedKeys = require('./component/BlockedKeys');
|
|
3
2
|
const RateLimiterRes = require('./RateLimiterRes');
|
|
4
3
|
const RateLimiterInsuredAbstract = require('./RateLimiterInsuredAbstract');
|
|
@@ -152,7 +151,7 @@ module.exports = class RateLimiterStoreAbstract extends RateLimiterInsuredAbstra
|
|
|
152
151
|
*/
|
|
153
152
|
set(key, points, secDuration, options = {}) {
|
|
154
153
|
const msDuration = (secDuration >= 0 ? secDuration : this.duration) * 1000;
|
|
155
|
-
return this.
|
|
154
|
+
return this._set(this.getKey(key), points, msDuration, options);
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
/**
|
|
@@ -300,6 +299,26 @@ module.exports = class RateLimiterStoreAbstract extends RateLimiterInsuredAbstra
|
|
|
300
299
|
});
|
|
301
300
|
}
|
|
302
301
|
|
|
302
|
+
/**
|
|
303
|
+
* @param rlKey
|
|
304
|
+
* @param points
|
|
305
|
+
* @param msDuration
|
|
306
|
+
* @param {Object} options
|
|
307
|
+
*
|
|
308
|
+
* @return Promise<any>
|
|
309
|
+
*/
|
|
310
|
+
_set(rlKey, points, msDuration, options = {}) {
|
|
311
|
+
return new Promise((resolve, reject) => {
|
|
312
|
+
this._upsert(rlKey, points, msDuration, true, options)
|
|
313
|
+
.then(() => {
|
|
314
|
+
resolve(new RateLimiterRes(0, msDuration > 0 ? msDuration : -1, points));
|
|
315
|
+
})
|
|
316
|
+
.catch((err) => {
|
|
317
|
+
this._handleError(err, 'set', resolve, reject, [this.parseKey(rlKey), points, msDuration / 1000, options]);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
303
322
|
/**
|
|
304
323
|
* Have to be implemented in every limiter
|
|
305
324
|
* Resolve with raw result from Store OR null if rlKey is not set
|
package/lib/RateLimiterUnion.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const isRateLimiterCompatible = require('./helper/isRateLimiterCompatible');
|
|
2
2
|
|
|
3
3
|
module.exports = class RateLimiterUnion {
|
|
4
4
|
constructor(...limiters) {
|
|
@@ -6,8 +6,8 @@ module.exports = class RateLimiterUnion {
|
|
|
6
6
|
throw new Error('RateLimiterUnion: at least one limiter have to be passed');
|
|
7
7
|
}
|
|
8
8
|
limiters.forEach((limiter) => {
|
|
9
|
-
if (!(limiter
|
|
10
|
-
throw new Error('RateLimiterUnion: all limiters have to be instance of RateLimiterAbstract');
|
|
9
|
+
if (!isRateLimiterCompatible(limiter)) {
|
|
10
|
+
throw new Error('RateLimiterUnion: all limiters have to be instance of RateLimiterAbstract or RateLimiterCompatibleAbstract');
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const RateLimiterAbstract = require('../RateLimiterAbstract');
|
|
2
|
+
const RateLimiterCompatibleAbstract = require('../RateLimiterCompatibleAbstract');
|
|
3
|
+
|
|
4
|
+
module.exports = function isRateLimiterCompatible(obj) {
|
|
5
|
+
return obj instanceof RateLimiterAbstract || obj instanceof RateLimiterCompatibleAbstract;
|
|
6
|
+
};
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -27,6 +27,55 @@ export class RateLimiterRes {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export class RateLimiterCompatibleAbstract {
|
|
31
|
+
readonly keyPrefix: string;
|
|
32
|
+
blockDuration: number;
|
|
33
|
+
execEvenly: boolean;
|
|
34
|
+
|
|
35
|
+
consume(
|
|
36
|
+
key: string | number,
|
|
37
|
+
pointsToConsume?: number,
|
|
38
|
+
options?: { [key: string]: any }
|
|
39
|
+
): Promise<RateLimiterRes>;
|
|
40
|
+
|
|
41
|
+
penalty(
|
|
42
|
+
key: string | number,
|
|
43
|
+
points?: number,
|
|
44
|
+
options?: { [key: string]: any }
|
|
45
|
+
): Promise<RateLimiterRes>;
|
|
46
|
+
|
|
47
|
+
reward(
|
|
48
|
+
key: string | number,
|
|
49
|
+
points?: number,
|
|
50
|
+
options?: { [key: string]: any }
|
|
51
|
+
): Promise<RateLimiterRes>;
|
|
52
|
+
|
|
53
|
+
get(
|
|
54
|
+
key: string | number,
|
|
55
|
+
options?: { [key: string]: any }
|
|
56
|
+
): Promise<RateLimiterRes | null>;
|
|
57
|
+
|
|
58
|
+
set(
|
|
59
|
+
key: string | number,
|
|
60
|
+
points: number,
|
|
61
|
+
secDuration: number,
|
|
62
|
+
options?: { [key: string]: any }
|
|
63
|
+
): Promise<RateLimiterRes>;
|
|
64
|
+
|
|
65
|
+
block(
|
|
66
|
+
key: string | number,
|
|
67
|
+
secDuration: number,
|
|
68
|
+
options?: { [key: string]: any }
|
|
69
|
+
): Promise<RateLimiterRes>;
|
|
70
|
+
|
|
71
|
+
delete(
|
|
72
|
+
key: string | number,
|
|
73
|
+
options?: { [key: string]: any }
|
|
74
|
+
): Promise<boolean>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type RateLimiterLike = RateLimiterAbstract | RateLimiterCompatibleAbstract;
|
|
78
|
+
|
|
30
79
|
export class RateLimiterAbstract {
|
|
31
80
|
constructor(opts: IRateLimiterOptions);
|
|
32
81
|
|
|
@@ -224,7 +273,7 @@ interface IRateLimiterOptions {
|
|
|
224
273
|
execEvenly?: boolean;
|
|
225
274
|
execEvenlyMinDelayMs?: number;
|
|
226
275
|
blockDuration?: number;
|
|
227
|
-
insuranceLimiter?:
|
|
276
|
+
insuranceLimiter?: RateLimiterLike;
|
|
228
277
|
}
|
|
229
278
|
|
|
230
279
|
interface IRateLimiterClusterOptions extends IRateLimiterOptions {
|
|
@@ -236,7 +285,7 @@ interface IRateLimiterStoreOptions extends IRateLimiterOptions {
|
|
|
236
285
|
storeType?: string;
|
|
237
286
|
inMemoryBlockOnConsumed?: number;
|
|
238
287
|
inMemoryBlockDuration?: number;
|
|
239
|
-
insuranceLimiter?:
|
|
288
|
+
insuranceLimiter?: RateLimiterLike;
|
|
240
289
|
dbName?: string;
|
|
241
290
|
tableName?: string;
|
|
242
291
|
tableCreated?: boolean;
|
|
@@ -277,7 +326,7 @@ interface ICallbackReady {
|
|
|
277
326
|
}
|
|
278
327
|
|
|
279
328
|
interface IRLWrapperBlackAndWhiteOptions {
|
|
280
|
-
limiter:
|
|
329
|
+
limiter: RateLimiterLike;
|
|
281
330
|
blackList?: string[] | number[];
|
|
282
331
|
whiteList?: string[] | number[];
|
|
283
332
|
isBlackListed?(key: any): boolean;
|
|
@@ -393,17 +442,24 @@ export class RateLimiterDrizzleNonAtomic extends RateLimiterStoreAbstract {
|
|
|
393
442
|
export class RateLimiterMemcache extends RateLimiterStoreAbstract { }
|
|
394
443
|
|
|
395
444
|
export class RateLimiterUnion {
|
|
396
|
-
constructor(...limiters:
|
|
445
|
+
constructor(...limiters: RateLimiterLike[]);
|
|
397
446
|
|
|
398
447
|
consume(key: string | number, points?: number): Promise<Record<string, RateLimiterRes>>;
|
|
399
448
|
}
|
|
400
449
|
|
|
401
|
-
export class RLWrapperBlackAndWhite extends
|
|
450
|
+
export class RLWrapperBlackAndWhite extends RateLimiterCompatibleAbstract {
|
|
402
451
|
constructor(opts: IRLWrapperBlackAndWhiteOptions);
|
|
452
|
+
|
|
453
|
+
limiter: RateLimiterLike;
|
|
454
|
+
blackList: string[] | number[];
|
|
455
|
+
whiteList: string[] | number[];
|
|
456
|
+
isBlackListed: (key: any) => boolean;
|
|
457
|
+
isWhiteListed: (key: any) => boolean;
|
|
458
|
+
runActionAnyway: boolean;
|
|
403
459
|
}
|
|
404
460
|
|
|
405
|
-
interface IRLWrapperTimeoutsOptions extends IRateLimiterOptions {
|
|
406
|
-
limiter:
|
|
461
|
+
interface IRLWrapperTimeoutsOptions extends Omit<IRateLimiterOptions, 'points' | 'duration'> {
|
|
462
|
+
limiter: RateLimiterLike;
|
|
407
463
|
timeoutMs?: number;
|
|
408
464
|
}
|
|
409
465
|
|
|
@@ -417,7 +473,7 @@ interface IRateLimiterQueueOpts {
|
|
|
417
473
|
|
|
418
474
|
export class RateLimiterQueue {
|
|
419
475
|
constructor(
|
|
420
|
-
limiterFlexible:
|
|
476
|
+
limiterFlexible: RateLimiterLike | BurstyRateLimiter,
|
|
421
477
|
opts?: IRateLimiterQueueOpts
|
|
422
478
|
);
|
|
423
479
|
|
|
@@ -428,8 +484,8 @@ export class RateLimiterQueue {
|
|
|
428
484
|
|
|
429
485
|
export class BurstyRateLimiter {
|
|
430
486
|
constructor(
|
|
431
|
-
rateLimiter:
|
|
432
|
-
burstLimiter:
|
|
487
|
+
rateLimiter: RateLimiterLike,
|
|
488
|
+
burstLimiter: RateLimiterLike
|
|
433
489
|
);
|
|
434
490
|
|
|
435
491
|
consume(
|