express-rate-limit 6.2.1 → 6.5.1
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/changelog.md +34 -0
- package/dist/index.cjs +27 -25
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +18 -12
- package/package.json +24 -24
- package/readme.md +23 -3
package/changelog.md
CHANGED
|
@@ -6,6 +6,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to
|
|
7
7
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [6.5.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.5.0)
|
|
10
|
+
|
|
11
|
+
## Changed
|
|
12
|
+
|
|
13
|
+
- The message option can now be a (sync/asynx) function that returns a value
|
|
14
|
+
(#311)
|
|
15
|
+
- Updated all dependencies
|
|
16
|
+
|
|
17
|
+
## [6.4.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.3.0)
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Adds Express 5 (`5.0.0-beta.1`) as a supported peer dependency (#304)
|
|
22
|
+
|
|
23
|
+
## Changed
|
|
24
|
+
|
|
25
|
+
- Tests are now run on Node 12, 14, 16 and 18 on CI (#305)
|
|
26
|
+
- Updated all development dependencies (#306)
|
|
27
|
+
|
|
28
|
+
## [6.3.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.3.0)
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- Changes the build target to es2019 so that ESBuild outputs code that can run
|
|
33
|
+
with Node 12.
|
|
34
|
+
- Changes the minimum required Node version to 12.9.0.
|
|
35
|
+
|
|
36
|
+
## [6.2.1](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.2.1)
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- Use the default value for an option when `undefined` is passed to the rate
|
|
41
|
+
limiter.
|
|
42
|
+
|
|
9
43
|
## [6.2.0](https://github.com/nfriedly/express-rate-limit/releases/tag/v6.2.0)
|
|
10
44
|
|
|
11
45
|
### Added
|
package/dist/index.cjs
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
|
|
6
6
|
var __export = (target, all) => {
|
|
7
7
|
for (var name in all)
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
|
-
var
|
|
11
|
-
if (
|
|
12
|
-
for (let key of __getOwnPropNames(
|
|
13
|
-
if (!__hasOwnProp.call(
|
|
14
|
-
__defProp(
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
15
|
}
|
|
16
|
-
return
|
|
16
|
+
return to;
|
|
17
17
|
};
|
|
18
|
-
var __toCommonJS =
|
|
19
|
-
return (module2, temp) => {
|
|
20
|
-
return cache && cache.get(module2) || (temp = __reExport(__markAsModule({}), module2, 1), cache && cache.set(module2, temp), temp);
|
|
21
|
-
};
|
|
22
|
-
})(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0);
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
23
19
|
|
|
24
20
|
// source/index.ts
|
|
25
21
|
var source_exports = {};
|
|
@@ -28,6 +24,7 @@ __export(source_exports, {
|
|
|
28
24
|
default: () => lib_default,
|
|
29
25
|
rateLimit: () => lib_default
|
|
30
26
|
});
|
|
27
|
+
module.exports = __toCommonJS(source_exports);
|
|
31
28
|
|
|
32
29
|
// source/memory-store.ts
|
|
33
30
|
var calculateNextResetTime = (windowMs) => {
|
|
@@ -48,7 +45,8 @@ var MemoryStore = class {
|
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
47
|
async increment(key) {
|
|
51
|
-
|
|
48
|
+
var _a;
|
|
49
|
+
const totalHits = ((_a = this.hits[key]) != null ? _a : 0) + 1;
|
|
52
50
|
this.hits[key] = totalHits;
|
|
53
51
|
return {
|
|
54
52
|
totalHits,
|
|
@@ -88,45 +86,50 @@ var promisifyStore = (passedStore) => {
|
|
|
88
86
|
});
|
|
89
87
|
}
|
|
90
88
|
async decrement(key) {
|
|
91
|
-
return
|
|
89
|
+
return legacyStore.decrement(key);
|
|
92
90
|
}
|
|
93
91
|
async resetKey(key) {
|
|
94
|
-
return
|
|
92
|
+
return legacyStore.resetKey(key);
|
|
95
93
|
}
|
|
96
94
|
async resetAll() {
|
|
97
95
|
if (typeof legacyStore.resetAll === "function")
|
|
98
|
-
return
|
|
96
|
+
return legacyStore.resetAll();
|
|
99
97
|
}
|
|
100
98
|
}
|
|
101
99
|
return new PromisifiedStore();
|
|
102
100
|
};
|
|
103
101
|
var parseOptions = (passedOptions) => {
|
|
102
|
+
var _a, _b, _c;
|
|
104
103
|
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
105
104
|
const config = {
|
|
106
105
|
windowMs: 60 * 1e3,
|
|
107
106
|
max: 5,
|
|
108
107
|
message: "Too many requests, please try again later.",
|
|
109
108
|
statusCode: 429,
|
|
110
|
-
legacyHeaders: passedOptions.headers
|
|
111
|
-
standardHeaders: passedOptions.draft_polli_ratelimit_headers
|
|
109
|
+
legacyHeaders: (_a = passedOptions.headers) != null ? _a : true,
|
|
110
|
+
standardHeaders: (_b = passedOptions.draft_polli_ratelimit_headers) != null ? _b : false,
|
|
112
111
|
requestPropertyName: "rateLimit",
|
|
113
112
|
skipFailedRequests: false,
|
|
114
113
|
skipSuccessfulRequests: false,
|
|
115
114
|
requestWasSuccessful: (_request, response) => response.statusCode < 400,
|
|
116
115
|
skip: (_request, _response) => false,
|
|
117
|
-
keyGenerator
|
|
116
|
+
keyGenerator(request, _response) {
|
|
118
117
|
if (!request.ip) {
|
|
119
118
|
console.error("WARN | `express-rate-limit` | `request.ip` is undefined. You can avoid this by providing a custom `keyGenerator` function, but it may be indicative of a larger issue.");
|
|
120
119
|
}
|
|
121
120
|
return request.ip;
|
|
122
121
|
},
|
|
123
|
-
handler
|
|
124
|
-
response.status(config.statusCode)
|
|
122
|
+
async handler(request, response, _next, _optionsUsed) {
|
|
123
|
+
response.status(config.statusCode);
|
|
124
|
+
const message = typeof config.message === "function" ? await config.message(request, response) : config.message;
|
|
125
|
+
if (!response.writableEnded) {
|
|
126
|
+
response.send(message != null ? message : "Too many requests, please try again later.");
|
|
127
|
+
}
|
|
125
128
|
},
|
|
126
|
-
onLimitReached
|
|
129
|
+
onLimitReached(_request, _response, _optionsUsed) {
|
|
127
130
|
},
|
|
128
131
|
...notUndefinedOptions,
|
|
129
|
-
store: promisifyStore(notUndefinedOptions.store
|
|
132
|
+
store: promisifyStore((_c = notUndefinedOptions.store) != null ? _c : new MemoryStore())
|
|
130
133
|
};
|
|
131
134
|
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || typeof config.store.resetAll !== "undefined" && typeof config.store.resetAll !== "function" || typeof config.store.init !== "undefined" && typeof config.store.init !== "function") {
|
|
132
135
|
throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");
|
|
@@ -141,7 +144,7 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
|
141
144
|
}
|
|
142
145
|
};
|
|
143
146
|
var rateLimit = (passedOptions) => {
|
|
144
|
-
const options = parseOptions(passedOptions
|
|
147
|
+
const options = parseOptions(passedOptions != null ? passedOptions : {});
|
|
145
148
|
if (typeof options.store.init === "function")
|
|
146
149
|
options.store.init(options);
|
|
147
150
|
const middleware = handleAsyncErrors(async (request, response, next) => {
|
|
@@ -231,5 +234,4 @@ var omitUndefinedOptions = (passedOptions) => {
|
|
|
231
234
|
return omittedOptions;
|
|
232
235
|
};
|
|
233
236
|
var lib_default = rateLimit;
|
|
234
|
-
module.exports = __toCommonJS(source_exports);
|
|
235
237
|
module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v6.
|
|
1
|
+
// Generated by dts-bundle-generator v6.12.0
|
|
2
2
|
|
|
3
3
|
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
@@ -152,7 +152,7 @@ export interface Options {
|
|
|
152
152
|
*
|
|
153
153
|
* Defaults to `'Too many requests, please try again later.'`
|
|
154
154
|
*/
|
|
155
|
-
readonly message: any
|
|
155
|
+
readonly message: any | ValueDeterminingMiddleware<any>;
|
|
156
156
|
/**
|
|
157
157
|
* The HTTP status code to send back when a client is rate limited.
|
|
158
158
|
*
|
|
@@ -276,7 +276,7 @@ export interface RateLimitInfo {
|
|
|
276
276
|
*
|
|
277
277
|
* @public
|
|
278
278
|
*/
|
|
279
|
-
export declare const rateLimit: (passedOptions?: Partial<Options>
|
|
279
|
+
export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
|
|
280
280
|
/**
|
|
281
281
|
* A `Store` that stores the hit count for each client in memory.
|
|
282
282
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -17,7 +17,8 @@ var MemoryStore = class {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
async increment(key) {
|
|
20
|
-
|
|
20
|
+
var _a;
|
|
21
|
+
const totalHits = ((_a = this.hits[key]) != null ? _a : 0) + 1;
|
|
21
22
|
this.hits[key] = totalHits;
|
|
22
23
|
return {
|
|
23
24
|
totalHits,
|
|
@@ -57,45 +58,50 @@ var promisifyStore = (passedStore) => {
|
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
async decrement(key) {
|
|
60
|
-
return
|
|
61
|
+
return legacyStore.decrement(key);
|
|
61
62
|
}
|
|
62
63
|
async resetKey(key) {
|
|
63
|
-
return
|
|
64
|
+
return legacyStore.resetKey(key);
|
|
64
65
|
}
|
|
65
66
|
async resetAll() {
|
|
66
67
|
if (typeof legacyStore.resetAll === "function")
|
|
67
|
-
return
|
|
68
|
+
return legacyStore.resetAll();
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
return new PromisifiedStore();
|
|
71
72
|
};
|
|
72
73
|
var parseOptions = (passedOptions) => {
|
|
74
|
+
var _a, _b, _c;
|
|
73
75
|
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
74
76
|
const config = {
|
|
75
77
|
windowMs: 60 * 1e3,
|
|
76
78
|
max: 5,
|
|
77
79
|
message: "Too many requests, please try again later.",
|
|
78
80
|
statusCode: 429,
|
|
79
|
-
legacyHeaders: passedOptions.headers
|
|
80
|
-
standardHeaders: passedOptions.draft_polli_ratelimit_headers
|
|
81
|
+
legacyHeaders: (_a = passedOptions.headers) != null ? _a : true,
|
|
82
|
+
standardHeaders: (_b = passedOptions.draft_polli_ratelimit_headers) != null ? _b : false,
|
|
81
83
|
requestPropertyName: "rateLimit",
|
|
82
84
|
skipFailedRequests: false,
|
|
83
85
|
skipSuccessfulRequests: false,
|
|
84
86
|
requestWasSuccessful: (_request, response) => response.statusCode < 400,
|
|
85
87
|
skip: (_request, _response) => false,
|
|
86
|
-
keyGenerator
|
|
88
|
+
keyGenerator(request, _response) {
|
|
87
89
|
if (!request.ip) {
|
|
88
90
|
console.error("WARN | `express-rate-limit` | `request.ip` is undefined. You can avoid this by providing a custom `keyGenerator` function, but it may be indicative of a larger issue.");
|
|
89
91
|
}
|
|
90
92
|
return request.ip;
|
|
91
93
|
},
|
|
92
|
-
handler
|
|
93
|
-
response.status(config.statusCode)
|
|
94
|
+
async handler(request, response, _next, _optionsUsed) {
|
|
95
|
+
response.status(config.statusCode);
|
|
96
|
+
const message = typeof config.message === "function" ? await config.message(request, response) : config.message;
|
|
97
|
+
if (!response.writableEnded) {
|
|
98
|
+
response.send(message != null ? message : "Too many requests, please try again later.");
|
|
99
|
+
}
|
|
94
100
|
},
|
|
95
|
-
onLimitReached
|
|
101
|
+
onLimitReached(_request, _response, _optionsUsed) {
|
|
96
102
|
},
|
|
97
103
|
...notUndefinedOptions,
|
|
98
|
-
store: promisifyStore(notUndefinedOptions.store
|
|
104
|
+
store: promisifyStore((_c = notUndefinedOptions.store) != null ? _c : new MemoryStore())
|
|
99
105
|
};
|
|
100
106
|
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || typeof config.store.resetAll !== "undefined" && typeof config.store.resetAll !== "function" || typeof config.store.init !== "undefined" && typeof config.store.init !== "function") {
|
|
101
107
|
throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");
|
|
@@ -110,7 +116,7 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
|
110
116
|
}
|
|
111
117
|
};
|
|
112
118
|
var rateLimit = (passedOptions) => {
|
|
113
|
-
const options = parseOptions(passedOptions
|
|
119
|
+
const options = parseOptions(passedOptions != null ? passedOptions : {});
|
|
114
120
|
if (typeof options.store.init === "function")
|
|
115
121
|
options.store.init(options);
|
|
116
122
|
const middleware = handleAsyncErrors(async (request, response, next) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.1",
|
|
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",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"changelog.md"
|
|
47
47
|
],
|
|
48
48
|
"engines": {
|
|
49
|
-
"node": ">=
|
|
49
|
+
"node": ">= 12.9.0"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
52
|
"clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
|
|
53
|
-
"build:cjs": "esbuild --bundle --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;\" source/index.ts",
|
|
54
|
-
"build:esm": "esbuild --bundle --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
53
|
+
"build:cjs": "esbuild --bundle --target=es2019 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;\" source/index.ts",
|
|
54
|
+
"build:esm": "esbuild --bundle --target=es2019 --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
55
55
|
"build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts",
|
|
56
56
|
"compile": "run-s clean build:*",
|
|
57
57
|
"lint:code": "xo --ignore test/external/",
|
|
@@ -67,28 +67,28 @@
|
|
|
67
67
|
"prepare": "run-s compile && husky install config/husky"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"express": "^4"
|
|
70
|
+
"express": "^4 || ^5"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@jest/globals": "
|
|
74
|
-
"@types/express": "
|
|
75
|
-
"@types/jest": "
|
|
76
|
-
"@types/node": "
|
|
77
|
-
"@types/supertest": "
|
|
78
|
-
"cross-env": "
|
|
79
|
-
"del-cli": "
|
|
80
|
-
"dts-bundle-generator": "
|
|
81
|
-
"esbuild": "
|
|
82
|
-
"express": "
|
|
83
|
-
"husky": "
|
|
84
|
-
"jest": "
|
|
85
|
-
"lint-staged": "
|
|
86
|
-
"npm-run-all": "
|
|
87
|
-
"supertest": "
|
|
88
|
-
"ts-jest": "
|
|
89
|
-
"ts-node": "
|
|
90
|
-
"typescript": "
|
|
91
|
-
"xo": "
|
|
73
|
+
"@jest/globals": "28.1.3",
|
|
74
|
+
"@types/express": "4.17.13",
|
|
75
|
+
"@types/jest": "28.1.6",
|
|
76
|
+
"@types/node": "18.0.6",
|
|
77
|
+
"@types/supertest": "2.0.12",
|
|
78
|
+
"cross-env": "7.0.3",
|
|
79
|
+
"del-cli": "4.0.1",
|
|
80
|
+
"dts-bundle-generator": "6.12.0",
|
|
81
|
+
"esbuild": "0.14.49",
|
|
82
|
+
"express": "4.18.1",
|
|
83
|
+
"husky": "8.0.1",
|
|
84
|
+
"jest": "28.1.3",
|
|
85
|
+
"lint-staged": "13.0.3",
|
|
86
|
+
"npm-run-all": "4.1.5",
|
|
87
|
+
"supertest": "6.2.4",
|
|
88
|
+
"ts-jest": "28.0.7",
|
|
89
|
+
"ts-node": "10.9.1",
|
|
90
|
+
"typescript": "4.7.4",
|
|
91
|
+
"xo": "0.49.0"
|
|
92
92
|
},
|
|
93
93
|
"xo": {
|
|
94
94
|
"prettier": true,
|
package/readme.md
CHANGED
|
@@ -239,11 +239,30 @@ const limiter = rateLimit({
|
|
|
239
239
|
The response body to send back when a client is rate limited.
|
|
240
240
|
|
|
241
241
|
May be a `string`, JSON object, or any other value that Express's
|
|
242
|
-
[response.send](https://expressjs.com/en/4x/api.html#
|
|
243
|
-
supports.
|
|
242
|
+
[`response.send`](https://expressjs.com/en/4x/api.html#res.send) method
|
|
243
|
+
supports. It can also be a (sync/async) function that accepts the Express
|
|
244
|
+
request and response objects and then returns a `string`, JSON object or any
|
|
245
|
+
other value the Express `response.send` function accepts.
|
|
244
246
|
|
|
245
247
|
Defaults to `'Too many requests, please try again later.'`
|
|
246
248
|
|
|
249
|
+
An example of using a function:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
const isPremium = async (user) => {
|
|
253
|
+
// ...
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const limiter = rateLimit({
|
|
257
|
+
// ...
|
|
258
|
+
message: async (request, response) => {
|
|
259
|
+
if (await isPremium(request.user))
|
|
260
|
+
return 'You can only make 10 requests every hour.'
|
|
261
|
+
else return 'You can only make 5 requests every hour.'
|
|
262
|
+
},
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
247
266
|
### `statusCode`
|
|
248
267
|
|
|
249
268
|
> `number`
|
|
@@ -348,7 +367,8 @@ const limiter = rateLimit({
|
|
|
348
367
|
Express request handler that sends back a response when a client is
|
|
349
368
|
rate-limited.
|
|
350
369
|
|
|
351
|
-
By default, sends back the `statusCode` and `message` set via the options
|
|
370
|
+
By default, sends back the `statusCode` and `message` set via the `options`,
|
|
371
|
+
similar to this:
|
|
352
372
|
|
|
353
373
|
```ts
|
|
354
374
|
const limiter = rateLimit({
|