express-rate-limit 7.1.5 → 7.3.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/dist/index.cjs +30 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.mjs +30 -0
- package/package.json +2 -2
- package/readme.md +49 -12
package/dist/index.cjs
CHANGED
|
@@ -99,6 +99,7 @@ var ValidationError = class extends Error {
|
|
|
99
99
|
};
|
|
100
100
|
var ChangeWarning = class extends ValidationError {
|
|
101
101
|
};
|
|
102
|
+
var usedStores = /* @__PURE__ */ new Set();
|
|
102
103
|
var singleCountKeys = /* @__PURE__ */ new WeakMap();
|
|
103
104
|
var validations = {
|
|
104
105
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -182,6 +183,19 @@ var validations = {
|
|
|
182
183
|
);
|
|
183
184
|
}
|
|
184
185
|
},
|
|
186
|
+
/**
|
|
187
|
+
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
188
|
+
*/
|
|
189
|
+
unsharedStore(store) {
|
|
190
|
+
if (usedStores.has(store)) {
|
|
191
|
+
const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";
|
|
192
|
+
throw new ValidationError(
|
|
193
|
+
"ERR_ERL_STORE_REUSE",
|
|
194
|
+
`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
usedStores.add(store);
|
|
198
|
+
},
|
|
185
199
|
/**
|
|
186
200
|
* Ensures a given key is incremented only once per request.
|
|
187
201
|
*
|
|
@@ -296,6 +310,20 @@ var validations = {
|
|
|
296
310
|
);
|
|
297
311
|
}
|
|
298
312
|
}
|
|
313
|
+
},
|
|
314
|
+
/**
|
|
315
|
+
* Checks to see if the instance was created inside of a request handler, which would prevent it from working correctly.
|
|
316
|
+
*/
|
|
317
|
+
creationStack() {
|
|
318
|
+
const { stack } = new Error(
|
|
319
|
+
"express-rate-limit validation check (set options.validate.creationStack=false to disable)"
|
|
320
|
+
);
|
|
321
|
+
if (stack?.includes("Layer.handle [as handle_request]")) {
|
|
322
|
+
throw new ValidationError(
|
|
323
|
+
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
324
|
+
`express-rate-limit instance should be created at app initialization, not when responding to a request.`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
299
327
|
}
|
|
300
328
|
};
|
|
301
329
|
var getValidations = (_enabled) => {
|
|
@@ -616,6 +644,8 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
|
616
644
|
var rateLimit = (passedOptions) => {
|
|
617
645
|
const config = parseOptions(passedOptions ?? {});
|
|
618
646
|
const options = getOptionsFromConfig(config);
|
|
647
|
+
config.validations.creationStack();
|
|
648
|
+
config.validations.unsharedStore(config.store);
|
|
619
649
|
if (typeof config.store.init === "function")
|
|
620
650
|
config.store.init(options);
|
|
621
651
|
const middleware = handleAsyncErrors(
|
package/dist/index.d.cts
CHANGED
|
@@ -45,6 +45,10 @@ declare const validations: {
|
|
|
45
45
|
* @param hits {any} - The `totalHits` returned by the store.
|
|
46
46
|
*/
|
|
47
47
|
positiveHits(hits: any): void;
|
|
48
|
+
/**
|
|
49
|
+
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
50
|
+
*/
|
|
51
|
+
unsharedStore(store: Store): void;
|
|
48
52
|
/**
|
|
49
53
|
* Ensures a given key is incremented only once per request.
|
|
50
54
|
*
|
|
@@ -97,6 +101,10 @@ declare const validations: {
|
|
|
97
101
|
* If any unrecognized values are found, an error is logged that includes the list of supported vaidations.
|
|
98
102
|
*/
|
|
99
103
|
validationsConfig(): void;
|
|
104
|
+
/**
|
|
105
|
+
* Checks to see if the instance was created inside of a request handler, which would prevent it from working correctly.
|
|
106
|
+
*/
|
|
107
|
+
creationStack(): void;
|
|
100
108
|
};
|
|
101
109
|
export type Validations = typeof validations;
|
|
102
110
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -45,6 +45,10 @@ declare const validations: {
|
|
|
45
45
|
* @param hits {any} - The `totalHits` returned by the store.
|
|
46
46
|
*/
|
|
47
47
|
positiveHits(hits: any): void;
|
|
48
|
+
/**
|
|
49
|
+
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
50
|
+
*/
|
|
51
|
+
unsharedStore(store: Store): void;
|
|
48
52
|
/**
|
|
49
53
|
* Ensures a given key is incremented only once per request.
|
|
50
54
|
*
|
|
@@ -97,6 +101,10 @@ declare const validations: {
|
|
|
97
101
|
* If any unrecognized values are found, an error is logged that includes the list of supported vaidations.
|
|
98
102
|
*/
|
|
99
103
|
validationsConfig(): void;
|
|
104
|
+
/**
|
|
105
|
+
* Checks to see if the instance was created inside of a request handler, which would prevent it from working correctly.
|
|
106
|
+
*/
|
|
107
|
+
creationStack(): void;
|
|
100
108
|
};
|
|
101
109
|
export type Validations = typeof validations;
|
|
102
110
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -45,6 +45,10 @@ declare const validations: {
|
|
|
45
45
|
* @param hits {any} - The `totalHits` returned by the store.
|
|
46
46
|
*/
|
|
47
47
|
positiveHits(hits: any): void;
|
|
48
|
+
/**
|
|
49
|
+
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
50
|
+
*/
|
|
51
|
+
unsharedStore(store: Store): void;
|
|
48
52
|
/**
|
|
49
53
|
* Ensures a given key is incremented only once per request.
|
|
50
54
|
*
|
|
@@ -97,6 +101,10 @@ declare const validations: {
|
|
|
97
101
|
* If any unrecognized values are found, an error is logged that includes the list of supported vaidations.
|
|
98
102
|
*/
|
|
99
103
|
validationsConfig(): void;
|
|
104
|
+
/**
|
|
105
|
+
* Checks to see if the instance was created inside of a request handler, which would prevent it from working correctly.
|
|
106
|
+
*/
|
|
107
|
+
creationStack(): void;
|
|
100
108
|
};
|
|
101
109
|
export type Validations = typeof validations;
|
|
102
110
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -71,6 +71,7 @@ var ValidationError = class extends Error {
|
|
|
71
71
|
};
|
|
72
72
|
var ChangeWarning = class extends ValidationError {
|
|
73
73
|
};
|
|
74
|
+
var usedStores = /* @__PURE__ */ new Set();
|
|
74
75
|
var singleCountKeys = /* @__PURE__ */ new WeakMap();
|
|
75
76
|
var validations = {
|
|
76
77
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -154,6 +155,19 @@ var validations = {
|
|
|
154
155
|
);
|
|
155
156
|
}
|
|
156
157
|
},
|
|
158
|
+
/**
|
|
159
|
+
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
160
|
+
*/
|
|
161
|
+
unsharedStore(store) {
|
|
162
|
+
if (usedStores.has(store)) {
|
|
163
|
+
const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";
|
|
164
|
+
throw new ValidationError(
|
|
165
|
+
"ERR_ERL_STORE_REUSE",
|
|
166
|
+
`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
usedStores.add(store);
|
|
170
|
+
},
|
|
157
171
|
/**
|
|
158
172
|
* Ensures a given key is incremented only once per request.
|
|
159
173
|
*
|
|
@@ -268,6 +282,20 @@ var validations = {
|
|
|
268
282
|
);
|
|
269
283
|
}
|
|
270
284
|
}
|
|
285
|
+
},
|
|
286
|
+
/**
|
|
287
|
+
* Checks to see if the instance was created inside of a request handler, which would prevent it from working correctly.
|
|
288
|
+
*/
|
|
289
|
+
creationStack() {
|
|
290
|
+
const { stack } = new Error(
|
|
291
|
+
"express-rate-limit validation check (set options.validate.creationStack=false to disable)"
|
|
292
|
+
);
|
|
293
|
+
if (stack?.includes("Layer.handle [as handle_request]")) {
|
|
294
|
+
throw new ValidationError(
|
|
295
|
+
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
296
|
+
`express-rate-limit instance should be created at app initialization, not when responding to a request.`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
271
299
|
}
|
|
272
300
|
};
|
|
273
301
|
var getValidations = (_enabled) => {
|
|
@@ -588,6 +616,8 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
|
588
616
|
var rateLimit = (passedOptions) => {
|
|
589
617
|
const config = parseOptions(passedOptions ?? {});
|
|
590
618
|
const options = getOptionsFromConfig(config);
|
|
619
|
+
config.validations.creationStack();
|
|
620
|
+
config.validations.unsharedStore(config.store);
|
|
591
621
|
if (typeof config.store.init === "function")
|
|
592
622
|
config.store.init(options);
|
|
593
623
|
const middleware = handleAsyncErrors(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0",
|
|
4
4
|
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nathan Friedly",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"del-cli": "5.1.0",
|
|
88
88
|
"dts-bundle-generator": "8.0.1",
|
|
89
89
|
"esbuild": "0.19.5",
|
|
90
|
-
"express": "4.
|
|
90
|
+
"express": "4.19.2",
|
|
91
91
|
"husky": "8.0.3",
|
|
92
92
|
"jest": "29.7.0",
|
|
93
93
|
"lint-staged": "15.0.2",
|
package/readme.md
CHANGED
|
@@ -9,16 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
Sponsored by [Zuplo](https://zuplo.link/express-rate-limit) a fully-managed API
|
|
15
|
-
Gateway for developers. Add
|
|
16
|
-
[dynamic rate-limiting](https://zuplo.link/dynamic-rate-limiting),
|
|
17
|
-
authentication and more to any API in minutes. Learn more at
|
|
18
|
-
[zuplo.com](https://zuplo.link/express-rate-limit)
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
12
|
Basic rate-limiting middleware for [Express](http://expressjs.com/). Use to
|
|
23
13
|
limit repeated requests to public APIs and/or endpoints such as password reset.
|
|
24
14
|
Plays nice with
|
|
@@ -38,13 +28,58 @@ const limiter = rateLimit({
|
|
|
38
28
|
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
|
|
39
29
|
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
|
|
40
30
|
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
|
|
41
|
-
// store: ... , //
|
|
31
|
+
// store: ... , // Redis, Memcached, etc. See below.
|
|
42
32
|
})
|
|
43
33
|
|
|
44
34
|
// Apply the rate limiting middleware to all requests.
|
|
45
35
|
app.use(limiter)
|
|
46
36
|
```
|
|
47
37
|
|
|
38
|
+
### Data Stores
|
|
39
|
+
|
|
40
|
+
The rate limiter comes with a built-in memory store, and supports a variety of
|
|
41
|
+
[external data stores](https://express-rate-limit.mintlify.app/reference/stores).
|
|
42
|
+
|
|
43
|
+
### Configuration
|
|
44
|
+
|
|
45
|
+
All function options may be async. Click the name for additional info and
|
|
46
|
+
default values.
|
|
47
|
+
|
|
48
|
+
| Option | Type | Remarks |
|
|
49
|
+
| ------------------------------------------------------------------------------------------------------------------ | -------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
50
|
+
| [`windowMs`](https://express-rate-limit.mintlify.app/reference/configuration#windowms) | `number` | How long to remember requests for, in milliseconds. |
|
|
51
|
+
| [`limit`](https://express-rate-limit.mintlify.app/reference/configuration#limit) | `number` \| `function` | How many requests to allow. |
|
|
52
|
+
| [`message`](https://express-rate-limit.mintlify.app/reference/configuration#message) | `string` \| `json` \| `function` | Response to return after limit is reached. |
|
|
53
|
+
| [`statusCode`](https://express-rate-limit.mintlify.app/reference/configuration#statuscode) | `number` | HTTP status code after limit is reached (default is 429). |
|
|
54
|
+
| [`legacyHeaders`](https://express-rate-limit.mintlify.app/reference/configuration#legacyheaders) | `boolean` | Enable the `X-Rate-Limit` header. |
|
|
55
|
+
| [`standardHeaders`](https://express-rate-limit.mintlify.app/reference/configuration#standardheaders) | `'draft-6'` \| `'draft-7'` | Enable the `Ratelimit` header. |
|
|
56
|
+
| [`requestPropertyName`](https://express-rate-limit.mintlify.app/reference/configuration#requestpropertyname) | `string` | Add rate limit info to the `req` object. |
|
|
57
|
+
| [`skipFailedRequests`](https://express-rate-limit.mintlify.app/reference/configuration#skipfailedrequests) | `boolean` | Uncount 4xx/5xx responses. |
|
|
58
|
+
| [`skipSuccessfulRequests`](https://express-rate-limit.mintlify.app/reference/configuration#skipsuccessfulrequests) | `boolean` | Uncount 1xx/2xx/3xx responses. |
|
|
59
|
+
| [`keyGenerator`](https://express-rate-limit.mintlify.app/reference/configuration#keygenerator) | `function` | Identify users (defaults to IP address). |
|
|
60
|
+
| [`handler`](https://express-rate-limit.mintlify.app/reference/configuration#handler) | `function` | Function to run after limit is reached (overrides `message` and `statusCode` settings, if set). |
|
|
61
|
+
| [`skip`](https://express-rate-limit.mintlify.app/reference/configuration#skip) | `function` | Return `true` to bypass the limiter for the given request. |
|
|
62
|
+
| [`requestWasSuccessful`](https://express-rate-limit.mintlify.app/reference/configuration#requestwassuccessful) | `function` | Used by `skipFailedRequests` and `skipSuccessfulRequests`. |
|
|
63
|
+
| [`validate`](https://express-rate-limit.mintlify.app/reference/configuration#validate) | `boolean` \| `object` | Enable or disable built-in validation checks. |
|
|
64
|
+
| [`store`](https://express-rate-limit.mintlify.app/reference/configuration#store) | `Store` | Use a custom store to share hit counts across multiple nodes. |
|
|
65
|
+
|
|
66
|
+
## Thank You
|
|
67
|
+
|
|
68
|
+
Sponsored by [Zuplo](https://zuplo.link/express-rate-limit) a fully-managed API
|
|
69
|
+
Gateway for developers. Add
|
|
70
|
+
[dynamic rate-limiting](https://zuplo.link/dynamic-rate-limiting),
|
|
71
|
+
authentication and more to any API in minutes. Learn more at
|
|
72
|
+
[zuplo.com](https://zuplo.link/express-rate-limit)
|
|
73
|
+
|
|
74
|
+
<p align="center">
|
|
75
|
+
<a href="https://zuplo.link/express-rate-limit">
|
|
76
|
+
<picture width="322">
|
|
77
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/express-rate-limit/express-rate-limit/assets/114976/cd2f6fa7-eae1-4fbb-be7d-b17df4c6f383">
|
|
78
|
+
<img alt="zuplo-logo" src="https://github.com/express-rate-limit/express-rate-limit/assets/114976/66fd75fa-b39e-4a8c-8d7a-52369bf244dc" width="322">
|
|
79
|
+
</picture>
|
|
80
|
+
</a>
|
|
81
|
+
</p>
|
|
82
|
+
|
|
48
83
|
---
|
|
49
84
|
|
|
50
85
|
Thanks to Mintlify for hosting the documentation at
|
|
@@ -58,11 +93,13 @@ Thanks to Mintlify for hosting the documentation at
|
|
|
58
93
|
|
|
59
94
|
---
|
|
60
95
|
|
|
96
|
+
Finally, thank you to everyone who's contributed to this project in any way! 🫶
|
|
97
|
+
|
|
61
98
|
## Issues and Contributing
|
|
62
99
|
|
|
63
100
|
If you encounter a bug or want to see something added/changed, please go ahead
|
|
64
101
|
and
|
|
65
|
-
[open an issue](https://github.com/
|
|
102
|
+
[open an issue](https://github.com/express-rate-limit/express-rate-limit/issues/new)!
|
|
66
103
|
If you need help with something, feel free to
|
|
67
104
|
[start a discussion](https://github.com/express-rate-limit/express-rate-limit/discussions/new)!
|
|
68
105
|
|