@depup/express-slow-down 3.1.0-depup.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 +31 -0
- package/changelog.md +84 -0
- package/changes.json +10 -0
- package/dist/index.cjs +127 -0
- package/dist/index.d.cts +112 -0
- package/dist/index.d.mts +112 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.mjs +99 -0
- package/license.md +20 -0
- package/package.json +162 -0
- package/readme.md +283 -0
- package/tsconfig.json +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @depup/express-slow-down
|
|
2
|
+
|
|
3
|
+
> Dependency-bumped version of [express-slow-down](https://www.npmjs.com/package/express-slow-down)
|
|
4
|
+
|
|
5
|
+
Generated by [DepUp](https://github.com/depup/npm) -- all production
|
|
6
|
+
dependencies bumped to latest versions.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @depup/express-slow-down
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Field | Value |
|
|
15
|
+
|-------|-------|
|
|
16
|
+
| Original | [express-slow-down](https://www.npmjs.com/package/express-slow-down) @ 3.1.0 |
|
|
17
|
+
| Processed | 2026-03-19 |
|
|
18
|
+
| Smoke test | passed |
|
|
19
|
+
| Deps updated | 1 |
|
|
20
|
+
|
|
21
|
+
## Dependency Changes
|
|
22
|
+
|
|
23
|
+
| Dependency | From | To |
|
|
24
|
+
|------------|------|-----|
|
|
25
|
+
| express-rate-limit | 8 | ^8.3.1 |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Source: https://github.com/depup/npm | Original: https://www.npmjs.com/package/express-slow-down
|
|
30
|
+
|
|
31
|
+
License inherited from the original package.
|
package/changelog.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# express-slow-down changelog
|
|
2
|
+
|
|
3
|
+
## v3.1.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `legacyHeaders` and `standardHeaders` options can now be passed through to
|
|
8
|
+
express-rate-limit
|
|
9
|
+
|
|
10
|
+
## v3.0.1
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Only pass supported options to express-rate-limit to avoid triggering warning
|
|
15
|
+
added for unexpected options in ERL v8.2.0
|
|
16
|
+
|
|
17
|
+
## v3.0.0
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Added support for grouping IPv6 addresses by subnet (defaults to /56) via
|
|
22
|
+
upgrading express-rate-limit dependency to 8.x
|
|
23
|
+
|
|
24
|
+
## v2.1.0
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Changed distributed JS to no longer bundle in `express-rate-limit`, instead
|
|
29
|
+
using the version installed via npm. This enables several new
|
|
30
|
+
express-rate-limit features that have been released since v7.0.1.
|
|
31
|
+
|
|
32
|
+
## v2.0.3
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Fixed `peerDependencies` compatibility with express 5 beta.
|
|
37
|
+
|
|
38
|
+
## v2.0.2
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Allowed `express-slow-down` to be used with `express` v5.
|
|
43
|
+
|
|
44
|
+
## v2.0.1
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- Fixed an incorrect `WRN_ERL_MAX_ZERO` warning when supplying a custom
|
|
49
|
+
validation object in the config.
|
|
50
|
+
|
|
51
|
+
## v2.0.0
|
|
52
|
+
|
|
53
|
+
express-slow-down v2 is built on top of express-rate-limit v7.
|
|
54
|
+
|
|
55
|
+
### Breaking
|
|
56
|
+
|
|
57
|
+
- Changed behavior of `delayMs` when set to a number
|
|
58
|
+
- Previous behavior multiplied `delayMs` value by the number of slowed
|
|
59
|
+
requests to determine the delay amount
|
|
60
|
+
- New behavior treats a numeric value as a fixed delay that is applied to each
|
|
61
|
+
slowed request without multiplication
|
|
62
|
+
- Set to `function(used) { return (used - this.delayAfter) * 1000; }` to
|
|
63
|
+
restore old behavior. (Change `1000` to match old value if necessary.)
|
|
64
|
+
- Changed arguments passed to `delayMs` when set to a function
|
|
65
|
+
- Previous signature was `function(req, res): number`
|
|
66
|
+
- New signature is `function(used, req, res): number | Promise<number>` where
|
|
67
|
+
`used` is the number of hits from this user during the current window
|
|
68
|
+
- Dropped support for `onLimitReached` method
|
|
69
|
+
- Dropped support for `headers` option
|
|
70
|
+
- Renamed `req.slowDown.current` to `req.slowDown.used`
|
|
71
|
+
- `current` is now a hidden getter that will return the `used` value, but will
|
|
72
|
+
not be included when iteration over keys or running through
|
|
73
|
+
`JSON.stringify()`
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- `delayAfter`, `delayMs`, and `maxDelayMs` may now be async functions that
|
|
78
|
+
return a number or a promise that resolves to a number
|
|
79
|
+
- The MemoryStore now uses precise, per-user reset times rather than a global
|
|
80
|
+
window that resets all users at once.
|
|
81
|
+
- Now using express-rate-limit's validator to detect and warn about common
|
|
82
|
+
misconfigurations. See
|
|
83
|
+
https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes for
|
|
84
|
+
more info.
|
package/changes.json
ADDED
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
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
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// source/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => slow_down_default,
|
|
24
|
+
slowDown: () => slowDown
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// source/slow-down.ts
|
|
29
|
+
var import_express_rate_limit = require("express-rate-limit");
|
|
30
|
+
var filterUndefinedOptions = (passedOptions) => {
|
|
31
|
+
const filteredOptions = {};
|
|
32
|
+
for (const k of Object.keys(passedOptions)) {
|
|
33
|
+
const key = k;
|
|
34
|
+
if (passedOptions[key] !== void 0) {
|
|
35
|
+
filteredOptions[key] = passedOptions[key];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return filteredOptions;
|
|
39
|
+
};
|
|
40
|
+
var ExpressSlowDownWarning = class extends Error {
|
|
41
|
+
constructor(code, message) {
|
|
42
|
+
const url = `https://express-rate-limit.github.io/${code}/`;
|
|
43
|
+
super(`${message} See ${url} for more information.`);
|
|
44
|
+
this.name = this.constructor.name;
|
|
45
|
+
this.code = code;
|
|
46
|
+
this.help = url;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var slowDown = (passedOptions = {}) => {
|
|
50
|
+
const notUndefinedOptions = filterUndefinedOptions(passedOptions);
|
|
51
|
+
if (notUndefinedOptions.max !== void 0 || notUndefinedOptions.limit !== void 0)
|
|
52
|
+
throw new Error(
|
|
53
|
+
"The limit/max option is not supported by express-slow-down, please use delayAfter instead."
|
|
54
|
+
);
|
|
55
|
+
const validate = typeof notUndefinedOptions.validate === "boolean" ? { default: notUndefinedOptions.validate } : notUndefinedOptions.validate ?? { default: true };
|
|
56
|
+
if (typeof notUndefinedOptions.delayMs === "number" && // Make sure the validation check is not disabled.
|
|
57
|
+
(validate.delayMs === true || validate.delayMs === void 0 && validate.default)) {
|
|
58
|
+
const message = `The behaviour of the 'delayMs' option was changed in express-slow-down v2:
|
|
59
|
+
- For the old behavior, change the delayMs option to:
|
|
60
|
+
|
|
61
|
+
delayMs: (used, req) => {
|
|
62
|
+
const delayAfter = req.${notUndefinedOptions.requestPropertyName ?? "slowDown"}.limit;
|
|
63
|
+
return (used - delayAfter) * ${notUndefinedOptions.delayMs};
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
- For the new behavior, change the delayMs option to:
|
|
67
|
+
|
|
68
|
+
delayMs: () => ${notUndefinedOptions.delayMs},
|
|
69
|
+
|
|
70
|
+
Or set 'options.validate: {delayMs: false}' to disable this message.`.replace(
|
|
71
|
+
/^(\t){3}/gm,
|
|
72
|
+
""
|
|
73
|
+
);
|
|
74
|
+
console.warn(new ExpressSlowDownWarning("WRN_ESD_DELAYMS", message));
|
|
75
|
+
}
|
|
76
|
+
delete validate?.delayMs;
|
|
77
|
+
const options = {
|
|
78
|
+
// The following settings are defaults that may be overridden by the user's options.
|
|
79
|
+
delayAfter: 1,
|
|
80
|
+
delayMs(used, request, response) {
|
|
81
|
+
const delayAfter2 = request[options.requestPropertyName].limit;
|
|
82
|
+
return (used - delayAfter2) * 1e3;
|
|
83
|
+
},
|
|
84
|
+
maxDelayMs: Number.POSITIVE_INFINITY,
|
|
85
|
+
requestPropertyName: "slowDown",
|
|
86
|
+
// Disable the headers by default, but allow users to override
|
|
87
|
+
legacyHeaders: false,
|
|
88
|
+
standardHeaders: false,
|
|
89
|
+
// Next the user's options are pulled in, overriding defaults from above
|
|
90
|
+
...notUndefinedOptions,
|
|
91
|
+
// This is a combination of the user's validate settings and our own overrides
|
|
92
|
+
validate: {
|
|
93
|
+
...validate,
|
|
94
|
+
limit: false
|
|
95
|
+
// We know the behavior of limit=0 changed - we depend on the new behavior!
|
|
96
|
+
},
|
|
97
|
+
// These settings cannot be overridden.
|
|
98
|
+
limit: 0,
|
|
99
|
+
// We want the handler to run on every request.
|
|
100
|
+
// The handler contains the slow-down logic, so don't allow it to be overridden.
|
|
101
|
+
async handler(_request, response, next) {
|
|
102
|
+
const delayAfter2 = typeof options.delayAfter === "function" ? await options.delayAfter(_request, response) : options.delayAfter;
|
|
103
|
+
const request = _request;
|
|
104
|
+
const info = request[options.requestPropertyName];
|
|
105
|
+
info.limit = delayAfter2;
|
|
106
|
+
info.remaining = Math.max(0, delayAfter2 - info.used);
|
|
107
|
+
let delay = 0;
|
|
108
|
+
if (info.used > delayAfter2) {
|
|
109
|
+
const unboundedDelay = typeof options.delayMs === "function" ? await options.delayMs(info.used, request, response) : options.delayMs;
|
|
110
|
+
const maxDelayMs2 = typeof options.maxDelayMs === "function" ? await options.maxDelayMs(request, response) : options.maxDelayMs;
|
|
111
|
+
delay = Math.max(0, Math.min(unboundedDelay, maxDelayMs2));
|
|
112
|
+
}
|
|
113
|
+
request[options.requestPropertyName].delay = delay;
|
|
114
|
+
if (delay <= 0) return next();
|
|
115
|
+
const timerId = setTimeout(() => next(), delay);
|
|
116
|
+
response.on("close", () => clearTimeout(timerId));
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const { delayAfter, delayMs, maxDelayMs, ...erlOptions } = options;
|
|
120
|
+
return (0, import_express_rate_limit.rateLimit)(erlOptions);
|
|
121
|
+
};
|
|
122
|
+
var slow_down_default = slowDown;
|
|
123
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
124
|
+
0 && (module.exports = {
|
|
125
|
+
slowDown
|
|
126
|
+
});
|
|
127
|
+
module.exports = slowDown; module.exports.default = slowDown; module.exports.slowDown = slowDown;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v8.0.1
|
|
2
|
+
|
|
3
|
+
import { Response } from 'express';
|
|
4
|
+
import { AugmentedRequest as RateLimitedRequest, EnabledValidations, Options as RateLimitOptions, RateLimitInfo, RateLimitRequestHandler, ValueDeterminingMiddleware } from 'express-rate-limit';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A modified Express request handler with the rate limit and slow down methods.
|
|
8
|
+
*/
|
|
9
|
+
export type SlowDownRequestHandler = RateLimitRequestHandler;
|
|
10
|
+
/**
|
|
11
|
+
* Method to generate the delay to apply to the incoming request.
|
|
12
|
+
*
|
|
13
|
+
* @param used {number} - The number of requests made by the client so far.
|
|
14
|
+
* @param request {Request} - The Express request object.
|
|
15
|
+
* @param response {Response} - The Express response object.
|
|
16
|
+
*
|
|
17
|
+
* @returns {number} - The delay to apply.
|
|
18
|
+
*/
|
|
19
|
+
export type DelayFn = (used: number, request: AugmentedRequest, response: Response) => number | Promise<number>;
|
|
20
|
+
/**
|
|
21
|
+
* Extra validation checks provided by `express-slow-down`.
|
|
22
|
+
*/
|
|
23
|
+
export type ExtendedValidations = EnabledValidations & {
|
|
24
|
+
delayMs?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Options present in `express-rate-limit` that this package overrides.
|
|
28
|
+
*/
|
|
29
|
+
export type OverriddenOptions = {
|
|
30
|
+
/**
|
|
31
|
+
* The `limit` option is set from the handler using `delayAfter`.
|
|
32
|
+
*/
|
|
33
|
+
limit: never;
|
|
34
|
+
max: never;
|
|
35
|
+
/**
|
|
36
|
+
* The `handler` option is overridden by the library.
|
|
37
|
+
*/
|
|
38
|
+
handler: never;
|
|
39
|
+
};
|
|
40
|
+
export type OverridenOptions = OverriddenOptions;
|
|
41
|
+
/**
|
|
42
|
+
* All the `express-slow-down` specific options.
|
|
43
|
+
*/
|
|
44
|
+
export type SlowDownOptions = {
|
|
45
|
+
/**
|
|
46
|
+
* The max number of requests allowed during windowMs before the middleware
|
|
47
|
+
* starts delaying responses.
|
|
48
|
+
*
|
|
49
|
+
* Can be the limit itself as a number or a (sync/async) function that accepts
|
|
50
|
+
* the Express req and res objects and then returns a number.
|
|
51
|
+
*
|
|
52
|
+
* Defaults to 1.
|
|
53
|
+
*/
|
|
54
|
+
delayAfter: number | ValueDeterminingMiddleware<number>;
|
|
55
|
+
/**
|
|
56
|
+
* The delay to apply to each request once the limit is reached.
|
|
57
|
+
*
|
|
58
|
+
* Can be the limit itself as a number or a (sync/async) function that accepts
|
|
59
|
+
* the Express req and res objects and then returns a number.
|
|
60
|
+
*
|
|
61
|
+
* By default, it increases the delay by 1 second for every request over the limit.
|
|
62
|
+
*/
|
|
63
|
+
delayMs: number | DelayFn;
|
|
64
|
+
/**
|
|
65
|
+
* The absolute maximum value for delayMs. After many consecutive attempts,
|
|
66
|
+
* the delay will always be this value. This option should be used especially
|
|
67
|
+
* when your application is running behind a load balancer or reverse proxy
|
|
68
|
+
* that has a request timeout.
|
|
69
|
+
*
|
|
70
|
+
* Defaults to infinity.
|
|
71
|
+
*/
|
|
72
|
+
maxDelayMs: number | ValueDeterminingMiddleware<number>;
|
|
73
|
+
/**
|
|
74
|
+
* Allows the developer to turn off the validation check for `delayMs` being a
|
|
75
|
+
* function.
|
|
76
|
+
*/
|
|
77
|
+
validate: boolean | ExtendedValidations;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The configuration options for the middleware.
|
|
81
|
+
*/
|
|
82
|
+
export type Options = RateLimitOptions & OverriddenOptions & SlowDownOptions;
|
|
83
|
+
/**
|
|
84
|
+
* The extended request object that includes information about the client's
|
|
85
|
+
* rate limit and delay.
|
|
86
|
+
*/
|
|
87
|
+
export type AugmentedRequest = RateLimitedRequest & {
|
|
88
|
+
[key: string]: SlowDownInfo;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The rate limit and delay related information for each client included in the
|
|
92
|
+
* Express request object.
|
|
93
|
+
*/
|
|
94
|
+
export type SlowDownInfo = RateLimitInfo & {
|
|
95
|
+
delay: number;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Create an instance of middleware that slows down responses to Express requests.
|
|
99
|
+
*
|
|
100
|
+
* @param passedOptions {Options} - Options to configure the speed limiter.
|
|
101
|
+
*
|
|
102
|
+
* @returns {SlowDownRequestHandle} - The middleware that speed-limits clients based on your configuration.
|
|
103
|
+
*
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
export declare const slowDown: (passedOptions?: Partial<Options>) => SlowDownRequestHandler;
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
slowDown as default,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export {};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v8.0.1
|
|
2
|
+
|
|
3
|
+
import { Response } from 'express';
|
|
4
|
+
import { AugmentedRequest as RateLimitedRequest, EnabledValidations, Options as RateLimitOptions, RateLimitInfo, RateLimitRequestHandler, ValueDeterminingMiddleware } from 'express-rate-limit';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A modified Express request handler with the rate limit and slow down methods.
|
|
8
|
+
*/
|
|
9
|
+
export type SlowDownRequestHandler = RateLimitRequestHandler;
|
|
10
|
+
/**
|
|
11
|
+
* Method to generate the delay to apply to the incoming request.
|
|
12
|
+
*
|
|
13
|
+
* @param used {number} - The number of requests made by the client so far.
|
|
14
|
+
* @param request {Request} - The Express request object.
|
|
15
|
+
* @param response {Response} - The Express response object.
|
|
16
|
+
*
|
|
17
|
+
* @returns {number} - The delay to apply.
|
|
18
|
+
*/
|
|
19
|
+
export type DelayFn = (used: number, request: AugmentedRequest, response: Response) => number | Promise<number>;
|
|
20
|
+
/**
|
|
21
|
+
* Extra validation checks provided by `express-slow-down`.
|
|
22
|
+
*/
|
|
23
|
+
export type ExtendedValidations = EnabledValidations & {
|
|
24
|
+
delayMs?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Options present in `express-rate-limit` that this package overrides.
|
|
28
|
+
*/
|
|
29
|
+
export type OverriddenOptions = {
|
|
30
|
+
/**
|
|
31
|
+
* The `limit` option is set from the handler using `delayAfter`.
|
|
32
|
+
*/
|
|
33
|
+
limit: never;
|
|
34
|
+
max: never;
|
|
35
|
+
/**
|
|
36
|
+
* The `handler` option is overridden by the library.
|
|
37
|
+
*/
|
|
38
|
+
handler: never;
|
|
39
|
+
};
|
|
40
|
+
export type OverridenOptions = OverriddenOptions;
|
|
41
|
+
/**
|
|
42
|
+
* All the `express-slow-down` specific options.
|
|
43
|
+
*/
|
|
44
|
+
export type SlowDownOptions = {
|
|
45
|
+
/**
|
|
46
|
+
* The max number of requests allowed during windowMs before the middleware
|
|
47
|
+
* starts delaying responses.
|
|
48
|
+
*
|
|
49
|
+
* Can be the limit itself as a number or a (sync/async) function that accepts
|
|
50
|
+
* the Express req and res objects and then returns a number.
|
|
51
|
+
*
|
|
52
|
+
* Defaults to 1.
|
|
53
|
+
*/
|
|
54
|
+
delayAfter: number | ValueDeterminingMiddleware<number>;
|
|
55
|
+
/**
|
|
56
|
+
* The delay to apply to each request once the limit is reached.
|
|
57
|
+
*
|
|
58
|
+
* Can be the limit itself as a number or a (sync/async) function that accepts
|
|
59
|
+
* the Express req and res objects and then returns a number.
|
|
60
|
+
*
|
|
61
|
+
* By default, it increases the delay by 1 second for every request over the limit.
|
|
62
|
+
*/
|
|
63
|
+
delayMs: number | DelayFn;
|
|
64
|
+
/**
|
|
65
|
+
* The absolute maximum value for delayMs. After many consecutive attempts,
|
|
66
|
+
* the delay will always be this value. This option should be used especially
|
|
67
|
+
* when your application is running behind a load balancer or reverse proxy
|
|
68
|
+
* that has a request timeout.
|
|
69
|
+
*
|
|
70
|
+
* Defaults to infinity.
|
|
71
|
+
*/
|
|
72
|
+
maxDelayMs: number | ValueDeterminingMiddleware<number>;
|
|
73
|
+
/**
|
|
74
|
+
* Allows the developer to turn off the validation check for `delayMs` being a
|
|
75
|
+
* function.
|
|
76
|
+
*/
|
|
77
|
+
validate: boolean | ExtendedValidations;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The configuration options for the middleware.
|
|
81
|
+
*/
|
|
82
|
+
export type Options = RateLimitOptions & OverriddenOptions & SlowDownOptions;
|
|
83
|
+
/**
|
|
84
|
+
* The extended request object that includes information about the client's
|
|
85
|
+
* rate limit and delay.
|
|
86
|
+
*/
|
|
87
|
+
export type AugmentedRequest = RateLimitedRequest & {
|
|
88
|
+
[key: string]: SlowDownInfo;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The rate limit and delay related information for each client included in the
|
|
92
|
+
* Express request object.
|
|
93
|
+
*/
|
|
94
|
+
export type SlowDownInfo = RateLimitInfo & {
|
|
95
|
+
delay: number;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Create an instance of middleware that slows down responses to Express requests.
|
|
99
|
+
*
|
|
100
|
+
* @param passedOptions {Options} - Options to configure the speed limiter.
|
|
101
|
+
*
|
|
102
|
+
* @returns {SlowDownRequestHandle} - The middleware that speed-limits clients based on your configuration.
|
|
103
|
+
*
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
export declare const slowDown: (passedOptions?: Partial<Options>) => SlowDownRequestHandler;
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
slowDown as default,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v8.0.1
|
|
2
|
+
|
|
3
|
+
import { Response } from 'express';
|
|
4
|
+
import { AugmentedRequest as RateLimitedRequest, EnabledValidations, Options as RateLimitOptions, RateLimitInfo, RateLimitRequestHandler, ValueDeterminingMiddleware } from 'express-rate-limit';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A modified Express request handler with the rate limit and slow down methods.
|
|
8
|
+
*/
|
|
9
|
+
export type SlowDownRequestHandler = RateLimitRequestHandler;
|
|
10
|
+
/**
|
|
11
|
+
* Method to generate the delay to apply to the incoming request.
|
|
12
|
+
*
|
|
13
|
+
* @param used {number} - The number of requests made by the client so far.
|
|
14
|
+
* @param request {Request} - The Express request object.
|
|
15
|
+
* @param response {Response} - The Express response object.
|
|
16
|
+
*
|
|
17
|
+
* @returns {number} - The delay to apply.
|
|
18
|
+
*/
|
|
19
|
+
export type DelayFn = (used: number, request: AugmentedRequest, response: Response) => number | Promise<number>;
|
|
20
|
+
/**
|
|
21
|
+
* Extra validation checks provided by `express-slow-down`.
|
|
22
|
+
*/
|
|
23
|
+
export type ExtendedValidations = EnabledValidations & {
|
|
24
|
+
delayMs?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Options present in `express-rate-limit` that this package overrides.
|
|
28
|
+
*/
|
|
29
|
+
export type OverriddenOptions = {
|
|
30
|
+
/**
|
|
31
|
+
* The `limit` option is set from the handler using `delayAfter`.
|
|
32
|
+
*/
|
|
33
|
+
limit: never;
|
|
34
|
+
max: never;
|
|
35
|
+
/**
|
|
36
|
+
* The `handler` option is overridden by the library.
|
|
37
|
+
*/
|
|
38
|
+
handler: never;
|
|
39
|
+
};
|
|
40
|
+
export type OverridenOptions = OverriddenOptions;
|
|
41
|
+
/**
|
|
42
|
+
* All the `express-slow-down` specific options.
|
|
43
|
+
*/
|
|
44
|
+
export type SlowDownOptions = {
|
|
45
|
+
/**
|
|
46
|
+
* The max number of requests allowed during windowMs before the middleware
|
|
47
|
+
* starts delaying responses.
|
|
48
|
+
*
|
|
49
|
+
* Can be the limit itself as a number or a (sync/async) function that accepts
|
|
50
|
+
* the Express req and res objects and then returns a number.
|
|
51
|
+
*
|
|
52
|
+
* Defaults to 1.
|
|
53
|
+
*/
|
|
54
|
+
delayAfter: number | ValueDeterminingMiddleware<number>;
|
|
55
|
+
/**
|
|
56
|
+
* The delay to apply to each request once the limit is reached.
|
|
57
|
+
*
|
|
58
|
+
* Can be the limit itself as a number or a (sync/async) function that accepts
|
|
59
|
+
* the Express req and res objects and then returns a number.
|
|
60
|
+
*
|
|
61
|
+
* By default, it increases the delay by 1 second for every request over the limit.
|
|
62
|
+
*/
|
|
63
|
+
delayMs: number | DelayFn;
|
|
64
|
+
/**
|
|
65
|
+
* The absolute maximum value for delayMs. After many consecutive attempts,
|
|
66
|
+
* the delay will always be this value. This option should be used especially
|
|
67
|
+
* when your application is running behind a load balancer or reverse proxy
|
|
68
|
+
* that has a request timeout.
|
|
69
|
+
*
|
|
70
|
+
* Defaults to infinity.
|
|
71
|
+
*/
|
|
72
|
+
maxDelayMs: number | ValueDeterminingMiddleware<number>;
|
|
73
|
+
/**
|
|
74
|
+
* Allows the developer to turn off the validation check for `delayMs` being a
|
|
75
|
+
* function.
|
|
76
|
+
*/
|
|
77
|
+
validate: boolean | ExtendedValidations;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The configuration options for the middleware.
|
|
81
|
+
*/
|
|
82
|
+
export type Options = RateLimitOptions & OverriddenOptions & SlowDownOptions;
|
|
83
|
+
/**
|
|
84
|
+
* The extended request object that includes information about the client's
|
|
85
|
+
* rate limit and delay.
|
|
86
|
+
*/
|
|
87
|
+
export type AugmentedRequest = RateLimitedRequest & {
|
|
88
|
+
[key: string]: SlowDownInfo;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The rate limit and delay related information for each client included in the
|
|
92
|
+
* Express request object.
|
|
93
|
+
*/
|
|
94
|
+
export type SlowDownInfo = RateLimitInfo & {
|
|
95
|
+
delay: number;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Create an instance of middleware that slows down responses to Express requests.
|
|
99
|
+
*
|
|
100
|
+
* @param passedOptions {Options} - Options to configure the speed limiter.
|
|
101
|
+
*
|
|
102
|
+
* @returns {SlowDownRequestHandle} - The middleware that speed-limits clients based on your configuration.
|
|
103
|
+
*
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
export declare const slowDown: (passedOptions?: Partial<Options>) => SlowDownRequestHandler;
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
slowDown as default,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export {};
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// source/slow-down.ts
|
|
2
|
+
import { rateLimit } from "express-rate-limit";
|
|
3
|
+
var filterUndefinedOptions = (passedOptions) => {
|
|
4
|
+
const filteredOptions = {};
|
|
5
|
+
for (const k of Object.keys(passedOptions)) {
|
|
6
|
+
const key = k;
|
|
7
|
+
if (passedOptions[key] !== void 0) {
|
|
8
|
+
filteredOptions[key] = passedOptions[key];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return filteredOptions;
|
|
12
|
+
};
|
|
13
|
+
var ExpressSlowDownWarning = class extends Error {
|
|
14
|
+
constructor(code, message) {
|
|
15
|
+
const url = `https://express-rate-limit.github.io/${code}/`;
|
|
16
|
+
super(`${message} See ${url} for more information.`);
|
|
17
|
+
this.name = this.constructor.name;
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.help = url;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var slowDown = (passedOptions = {}) => {
|
|
23
|
+
const notUndefinedOptions = filterUndefinedOptions(passedOptions);
|
|
24
|
+
if (notUndefinedOptions.max !== void 0 || notUndefinedOptions.limit !== void 0)
|
|
25
|
+
throw new Error(
|
|
26
|
+
"The limit/max option is not supported by express-slow-down, please use delayAfter instead."
|
|
27
|
+
);
|
|
28
|
+
const validate = typeof notUndefinedOptions.validate === "boolean" ? { default: notUndefinedOptions.validate } : notUndefinedOptions.validate ?? { default: true };
|
|
29
|
+
if (typeof notUndefinedOptions.delayMs === "number" && // Make sure the validation check is not disabled.
|
|
30
|
+
(validate.delayMs === true || validate.delayMs === void 0 && validate.default)) {
|
|
31
|
+
const message = `The behaviour of the 'delayMs' option was changed in express-slow-down v2:
|
|
32
|
+
- For the old behavior, change the delayMs option to:
|
|
33
|
+
|
|
34
|
+
delayMs: (used, req) => {
|
|
35
|
+
const delayAfter = req.${notUndefinedOptions.requestPropertyName ?? "slowDown"}.limit;
|
|
36
|
+
return (used - delayAfter) * ${notUndefinedOptions.delayMs};
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
- For the new behavior, change the delayMs option to:
|
|
40
|
+
|
|
41
|
+
delayMs: () => ${notUndefinedOptions.delayMs},
|
|
42
|
+
|
|
43
|
+
Or set 'options.validate: {delayMs: false}' to disable this message.`.replace(
|
|
44
|
+
/^(\t){3}/gm,
|
|
45
|
+
""
|
|
46
|
+
);
|
|
47
|
+
console.warn(new ExpressSlowDownWarning("WRN_ESD_DELAYMS", message));
|
|
48
|
+
}
|
|
49
|
+
delete validate?.delayMs;
|
|
50
|
+
const options = {
|
|
51
|
+
// The following settings are defaults that may be overridden by the user's options.
|
|
52
|
+
delayAfter: 1,
|
|
53
|
+
delayMs(used, request, response) {
|
|
54
|
+
const delayAfter2 = request[options.requestPropertyName].limit;
|
|
55
|
+
return (used - delayAfter2) * 1e3;
|
|
56
|
+
},
|
|
57
|
+
maxDelayMs: Number.POSITIVE_INFINITY,
|
|
58
|
+
requestPropertyName: "slowDown",
|
|
59
|
+
// Disable the headers by default, but allow users to override
|
|
60
|
+
legacyHeaders: false,
|
|
61
|
+
standardHeaders: false,
|
|
62
|
+
// Next the user's options are pulled in, overriding defaults from above
|
|
63
|
+
...notUndefinedOptions,
|
|
64
|
+
// This is a combination of the user's validate settings and our own overrides
|
|
65
|
+
validate: {
|
|
66
|
+
...validate,
|
|
67
|
+
limit: false
|
|
68
|
+
// We know the behavior of limit=0 changed - we depend on the new behavior!
|
|
69
|
+
},
|
|
70
|
+
// These settings cannot be overridden.
|
|
71
|
+
limit: 0,
|
|
72
|
+
// We want the handler to run on every request.
|
|
73
|
+
// The handler contains the slow-down logic, so don't allow it to be overridden.
|
|
74
|
+
async handler(_request, response, next) {
|
|
75
|
+
const delayAfter2 = typeof options.delayAfter === "function" ? await options.delayAfter(_request, response) : options.delayAfter;
|
|
76
|
+
const request = _request;
|
|
77
|
+
const info = request[options.requestPropertyName];
|
|
78
|
+
info.limit = delayAfter2;
|
|
79
|
+
info.remaining = Math.max(0, delayAfter2 - info.used);
|
|
80
|
+
let delay = 0;
|
|
81
|
+
if (info.used > delayAfter2) {
|
|
82
|
+
const unboundedDelay = typeof options.delayMs === "function" ? await options.delayMs(info.used, request, response) : options.delayMs;
|
|
83
|
+
const maxDelayMs2 = typeof options.maxDelayMs === "function" ? await options.maxDelayMs(request, response) : options.maxDelayMs;
|
|
84
|
+
delay = Math.max(0, Math.min(unboundedDelay, maxDelayMs2));
|
|
85
|
+
}
|
|
86
|
+
request[options.requestPropertyName].delay = delay;
|
|
87
|
+
if (delay <= 0) return next();
|
|
88
|
+
const timerId = setTimeout(() => next(), delay);
|
|
89
|
+
response.on("close", () => clearTimeout(timerId));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const { delayAfter, delayMs, maxDelayMs, ...erlOptions } = options;
|
|
93
|
+
return rateLimit(erlOptions);
|
|
94
|
+
};
|
|
95
|
+
var slow_down_default = slowDown;
|
|
96
|
+
export {
|
|
97
|
+
slow_down_default as default,
|
|
98
|
+
slowDown
|
|
99
|
+
};
|
package/license.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2023 Nathan Friedly, Vedant K
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@depup/express-slow-down",
|
|
3
|
+
"version": "3.1.0-depup.0",
|
|
4
|
+
"description": "Basic IP rate-limiting middleware for Express that slows down responses rather than blocking the user. (with updated dependencies)",
|
|
5
|
+
"homepage": "https://github.com/express-rate-limit/express-slow-down",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Nathan Friedly",
|
|
8
|
+
"url": "http://nfriedly.com/"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/express-rate-limit/express-slow-down.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"express-slow-down",
|
|
17
|
+
"depup",
|
|
18
|
+
"updated-dependencies",
|
|
19
|
+
"security",
|
|
20
|
+
"latest",
|
|
21
|
+
"patched",
|
|
22
|
+
"express-rate-limit",
|
|
23
|
+
"express",
|
|
24
|
+
"rate",
|
|
25
|
+
"limit",
|
|
26
|
+
"ratelimit",
|
|
27
|
+
"rate-limit",
|
|
28
|
+
"middleware",
|
|
29
|
+
"ip",
|
|
30
|
+
"auth",
|
|
31
|
+
"authorization",
|
|
32
|
+
"brute",
|
|
33
|
+
"force",
|
|
34
|
+
"bruteforce",
|
|
35
|
+
"brute-force",
|
|
36
|
+
"attack"
|
|
37
|
+
],
|
|
38
|
+
"type": "module",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"import": {
|
|
42
|
+
"types": "./dist/index.d.mts",
|
|
43
|
+
"default": "./dist/index.mjs"
|
|
44
|
+
},
|
|
45
|
+
"require": {
|
|
46
|
+
"types": "./dist/index.d.cts",
|
|
47
|
+
"default": "./dist/index.cjs"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"main": "dist/index.cjs",
|
|
52
|
+
"module": "./dist/index.mjs",
|
|
53
|
+
"types": "./dist/index.d.ts",
|
|
54
|
+
"files": [
|
|
55
|
+
"dist/",
|
|
56
|
+
"tsconfig.json",
|
|
57
|
+
"readme.md",
|
|
58
|
+
"license.md",
|
|
59
|
+
"changelog.md",
|
|
60
|
+
"changes.json",
|
|
61
|
+
"README.md"
|
|
62
|
+
],
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">= 16"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
|
|
68
|
+
"build:cjs": "esbuild --platform=node --bundle --target=es2022 --packages=external --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = slowDown; module.exports.default = slowDown; module.exports.slowDown = slowDown; \" source/index.ts",
|
|
69
|
+
"build:esm": "esbuild --platform=node --bundle --target=es2022 --packages=external --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
70
|
+
"build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts && cp dist/index.d.ts dist/index.d.cts && cp dist/index.d.ts dist/index.d.mts",
|
|
71
|
+
"compile": "run-s clean build:*",
|
|
72
|
+
"lint:code": "xo",
|
|
73
|
+
"lint:rest": "prettier --check .",
|
|
74
|
+
"lint": "run-s lint:*",
|
|
75
|
+
"format:code": "xo --fix",
|
|
76
|
+
"format:rest": "prettier --write .",
|
|
77
|
+
"format": "run-s format:*",
|
|
78
|
+
"test:lib": "jest test/library/",
|
|
79
|
+
"test:int": "jest test/integration/",
|
|
80
|
+
"test": "run-s lint test:*",
|
|
81
|
+
"pre-commit": "lint-staged"
|
|
82
|
+
},
|
|
83
|
+
"peerDependencies": {
|
|
84
|
+
"express": "4 || 5 || ^5.0.0-beta.1"
|
|
85
|
+
},
|
|
86
|
+
"dependencies": {
|
|
87
|
+
"express-rate-limit": "^8.3.1"
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"@express-rate-limit/prettier": "1.1.1",
|
|
91
|
+
"@express-rate-limit/tsconfig": "1.0.2",
|
|
92
|
+
"@jest/globals": "29.7.0",
|
|
93
|
+
"@types/express": "4.17.18",
|
|
94
|
+
"@types/jest": "29.5.5",
|
|
95
|
+
"@types/supertest": "2.0.12",
|
|
96
|
+
"body-parser": "1.20.3",
|
|
97
|
+
"del-cli": "5.1.0",
|
|
98
|
+
"dts-bundle-generator": "8.0.1",
|
|
99
|
+
"esbuild": "0.25.0",
|
|
100
|
+
"express": "4.21.2",
|
|
101
|
+
"husky": "8.0.3",
|
|
102
|
+
"jest": "29.7.0",
|
|
103
|
+
"jest-expect-message": "1.1.3",
|
|
104
|
+
"lint-staged": "16.1.2",
|
|
105
|
+
"npm-run-all": "4.1.5",
|
|
106
|
+
"prettier": "3.0.3",
|
|
107
|
+
"supertest": "6.3.3",
|
|
108
|
+
"ts-jest": "29.1.1",
|
|
109
|
+
"typescript": "5.2.2",
|
|
110
|
+
"xo": "0.56.0"
|
|
111
|
+
},
|
|
112
|
+
"xo": {
|
|
113
|
+
"prettier": true,
|
|
114
|
+
"rules": {
|
|
115
|
+
"@typescript-eslint/prefer-nullish-coalescing": [
|
|
116
|
+
"error",
|
|
117
|
+
{
|
|
118
|
+
"ignoreConditionalTests": true
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"@typescript-eslint/no-confusing-void-expression": 0,
|
|
122
|
+
"@typescript-eslint/consistent-indexed-object-style": [
|
|
123
|
+
"error",
|
|
124
|
+
"index-signature"
|
|
125
|
+
],
|
|
126
|
+
"unicorn/prefer-string-replace-all": 0
|
|
127
|
+
},
|
|
128
|
+
"overrides": [
|
|
129
|
+
{
|
|
130
|
+
"files": "test/**/*.ts",
|
|
131
|
+
"rules": {
|
|
132
|
+
"@typescript-eslint/no-unsafe-argument": 0,
|
|
133
|
+
"@typescript-eslint/no-unsafe-assignment": 0,
|
|
134
|
+
"@typescript-eslint/no-unsafe-call": 0,
|
|
135
|
+
"@typescript-eslint/no-unsafe-return": 0,
|
|
136
|
+
"@typescript-eslint/no-empty-function": 0,
|
|
137
|
+
"import/no-named-as-default": 0,
|
|
138
|
+
"unicorn/prefer-event-target": 0,
|
|
139
|
+
"unicorn/prevent-abbreviations": 0
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
"prettier": "@express-rate-limit/prettier",
|
|
145
|
+
"lint-staged": {
|
|
146
|
+
"{source,test}/**/*.ts": "xo --fix",
|
|
147
|
+
"**/*.{json,yaml,md}": "prettier --write"
|
|
148
|
+
},
|
|
149
|
+
"depup": {
|
|
150
|
+
"changes": {
|
|
151
|
+
"express-rate-limit": {
|
|
152
|
+
"from": "8",
|
|
153
|
+
"to": "^8.3.1"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"depsUpdated": 1,
|
|
157
|
+
"originalPackage": "express-slow-down",
|
|
158
|
+
"originalVersion": "3.1.0",
|
|
159
|
+
"processedAt": "2026-03-19T03:04:02.971Z",
|
|
160
|
+
"smokeTest": "passed"
|
|
161
|
+
}
|
|
162
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# <div align="center"> Express Slow Down </div>
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[](https://github.com/express-rate-limit/express-slow-down/actions/workflows/ci.yaml)
|
|
6
|
+
[](https://npmjs.org/package/express-slow-down 'View this project on NPM')
|
|
7
|
+
[](https://www.npmjs.com/package/express-slow-down)
|
|
8
|
+
|
|
9
|
+
Basic rate-limiting middleware for Express that slows down responses rather than
|
|
10
|
+
blocking them outright. Use to slow repeated requests to public APIs and/or
|
|
11
|
+
endpoints such as password reset.
|
|
12
|
+
|
|
13
|
+
Plays nice with (and built on top of)
|
|
14
|
+
[Express Rate Limit](https://npmjs.org/package/express-rate-limit)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
### Stores
|
|
19
|
+
|
|
20
|
+
The default memory store does not share state with any other processes or
|
|
21
|
+
servers. It's sufficient for basic abuse prevention, but an external store will
|
|
22
|
+
provide more consistency.
|
|
23
|
+
|
|
24
|
+
express-slow-down uses
|
|
25
|
+
[express-rate-limit's stores](https://express-rate-limit.mintlify.app/reference/stores)
|
|
26
|
+
|
|
27
|
+
> **Note**: when using express-slow-down and express-rate-limit with an external
|
|
28
|
+
> store, you'll need to create two instances of the store and provide different
|
|
29
|
+
> prefixes so that they don't double-count requests.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
From the npm registry:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
# Using npm
|
|
37
|
+
> npm install express-slow-down
|
|
38
|
+
# Using yarn or pnpm
|
|
39
|
+
> yarn/pnpm add express-slow-down
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
From Github Releases:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
# Using npm
|
|
46
|
+
> npm install https://github.com/express-rate-limit/express-slow-down/releases/download/v{version}/express-slow-down.tgz
|
|
47
|
+
# Using yarn or pnpm
|
|
48
|
+
> yarn/pnpm add https://github.com/express-rate-limit/express-slow-down/releases/download/v{version}/express-slow-down.tgz
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Replace `{version}` with the version of the package that you want to your, e.g.:
|
|
52
|
+
`2.0.0`.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Importing
|
|
57
|
+
|
|
58
|
+
This library is provided in ESM as well as CJS forms, and works with both
|
|
59
|
+
Javascript and Typescript projects.
|
|
60
|
+
|
|
61
|
+
**This package requires you to use Node 16 or above.**
|
|
62
|
+
|
|
63
|
+
Import it in a CommonJS project (`type: commonjs` or no `type` field in
|
|
64
|
+
`package.json`) as follows:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const { slowDown } = require('express-slow-down')
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Import it in a ESM project (`type: module` in `package.json`) as follows:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { slowDown } from 'express-slow-down'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Examples
|
|
77
|
+
|
|
78
|
+
To use it in an API-only server where the speed-limiter should be applied to all
|
|
79
|
+
requests:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { slowDown } from 'express-slow-down'
|
|
83
|
+
|
|
84
|
+
const limiter = slowDown({
|
|
85
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
86
|
+
delayAfter: 5, // Allow 5 requests per 15 minutes.
|
|
87
|
+
delayMs: (hits) => hits * 100, // Add 100 ms of delay to every request after the 5th one.
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* So:
|
|
91
|
+
*
|
|
92
|
+
* - requests 1-5 are not delayed.
|
|
93
|
+
* - request 6 is delayed by 600ms
|
|
94
|
+
* - request 7 is delayed by 700ms
|
|
95
|
+
* - request 8 is delayed by 800ms
|
|
96
|
+
*
|
|
97
|
+
* and so on. After 15 minutes, the delay is reset to 0.
|
|
98
|
+
*/
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Apply the delay middleware to all requests.
|
|
102
|
+
app.use(limiter)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
To use it in a 'regular' web server (e.g. anything that uses
|
|
106
|
+
`express.static()`), where the rate-limiter should only apply to certain
|
|
107
|
+
requests:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { slowDown } from 'express-slow-down'
|
|
111
|
+
|
|
112
|
+
const apiLimiter = slowDown({
|
|
113
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
114
|
+
delayAfter: 1, // Allow only one request to go at full-speed.
|
|
115
|
+
delayMs: (hits) => hits * hits * 1000, // 2nd request has a 4 second delay, 3rd is 9 seconds, 4th is 16, etc.
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Apply the delay middleware to API calls only.
|
|
119
|
+
app.use('/api', apiLimiter)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
To use a custom store:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { slowDown } from 'express-slow-down'
|
|
126
|
+
import { MemcachedStore } from 'rate-limit-memcached'
|
|
127
|
+
|
|
128
|
+
const speedLimiter = slowDown({
|
|
129
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
130
|
+
delayAfter: 1, // Allow only one request to go at full-speed.
|
|
131
|
+
delayMs: (hits) => hits * hits * 1000, // Add exponential delay after 1 request.
|
|
132
|
+
store: new MemcachedStore({
|
|
133
|
+
/* ... */
|
|
134
|
+
}), // Use the external store
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// Apply the rate limiting middleware to all requests.
|
|
138
|
+
app.use(speedLimiter)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> **Note:** most stores will require additional configuration, such as custom
|
|
142
|
+
> prefixes, when using multiple instances. The default built-in memory store is
|
|
143
|
+
> an exception to this rule.
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
### [`windowMs`](https://express-rate-limit.mintlify.app/reference/configuration#windowms)
|
|
148
|
+
|
|
149
|
+
> `number`
|
|
150
|
+
|
|
151
|
+
Time frame for which requests are checked/remembered.
|
|
152
|
+
|
|
153
|
+
Note that some stores have to be passed the value manually, while others infer
|
|
154
|
+
it from the options passed to this middleware.
|
|
155
|
+
|
|
156
|
+
Defaults to `60000` ms (= 1 minute).
|
|
157
|
+
|
|
158
|
+
### `delayAfter`
|
|
159
|
+
|
|
160
|
+
> `number` | `function`
|
|
161
|
+
|
|
162
|
+
The max number of requests allowed during `windowMs` before the middleware
|
|
163
|
+
starts delaying responses. Can be the limit itself as a number or a (sync/async)
|
|
164
|
+
function that accepts the Express `req` and `res` objects and then returns a
|
|
165
|
+
number.
|
|
166
|
+
|
|
167
|
+
Defaults to `1`.
|
|
168
|
+
|
|
169
|
+
An example of using a function:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const isPremium = async (user) => {
|
|
173
|
+
// ...
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const limiter = slowDown({
|
|
177
|
+
// ...
|
|
178
|
+
delayAfter: async (req, res) => {
|
|
179
|
+
if (await isPremium(req.user)) return 10
|
|
180
|
+
else return 1
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `delayMs`
|
|
186
|
+
|
|
187
|
+
> `number | function`
|
|
188
|
+
|
|
189
|
+
The delay to apply to each request once the limit is reached. Can be the delay
|
|
190
|
+
itself (in milliseconds) as a number or a (sync/async) function that accepts a
|
|
191
|
+
number (number of requests in the current window), the Express `req` and `res`
|
|
192
|
+
objects and then returns a number.
|
|
193
|
+
|
|
194
|
+
By default, it increases the delay by 1 second for every request over the limit:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
const limiter = slowDown({
|
|
198
|
+
// ...
|
|
199
|
+
delayMs: (used) => (used - delayAfter) * 1000,
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### `maxDelayMs`
|
|
204
|
+
|
|
205
|
+
> `number | function`
|
|
206
|
+
|
|
207
|
+
The absolute maximum value for `delayMs`. After many consecutive attempts, the
|
|
208
|
+
delay will always be this value. This option should be used especially when your
|
|
209
|
+
application is running behind a load balancer or reverse proxy that has a
|
|
210
|
+
request timeout. Can be the number itself (in milliseconds) or a (sync/async)
|
|
211
|
+
function that accepts the Express `req` and `res` objects and then returns a
|
|
212
|
+
number.
|
|
213
|
+
|
|
214
|
+
Defaults to `Infinity`.
|
|
215
|
+
|
|
216
|
+
For example, for the following configuration:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
const limiter = slowDown({
|
|
220
|
+
// ...
|
|
221
|
+
delayAfter: 1,
|
|
222
|
+
delayMs: (hits) => hits * 1000,
|
|
223
|
+
maxDelayMs: 4000,
|
|
224
|
+
})
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The first request will have no delay. The second will have a 2 second delay, the
|
|
228
|
+
3rd will have a 3 second delay, but the fourth, fifth, sixth, seventh and so on
|
|
229
|
+
requests will all have a 4 second delay.
|
|
230
|
+
|
|
231
|
+
### Options from [`express-rate-limit`](https://github.com/express-rate-limit/express-rate-limit)
|
|
232
|
+
|
|
233
|
+
Because
|
|
234
|
+
[`express-rate-limit`](https://github.com/express-rate-limit/express-rate-limit)
|
|
235
|
+
is used internally, additional options that it supports may be passed in. Some
|
|
236
|
+
of them are listed below; see `express-rate-limit`'s
|
|
237
|
+
[documentation](https://express-rate-limit.mintlify.app/reference/configuration)
|
|
238
|
+
for the complete list.
|
|
239
|
+
|
|
240
|
+
> **Note**: The `limit` (`max`) option is not supported (use `delayAfter`
|
|
241
|
+
> instead), nor is the `handler` option.
|
|
242
|
+
|
|
243
|
+
- [`standardHeaders`](https://express-rate-limit.mintlify.app/reference/configuration#standardheaders)
|
|
244
|
+
- [`legacyHeaders`](https://express-rate-limit.mintlify.app/reference/configuration#legacyheaders)
|
|
245
|
+
- [`requestPropertyName`](https://express-rate-limit.mintlify.app/reference/configuration#requestpropertyname)
|
|
246
|
+
- [`skipFailedRequests`](https://express-rate-limit.mintlify.app/reference/configuration#skipfailedrequests)
|
|
247
|
+
- [`skipSuccessfulRequests`](https://express-rate-limit.mintlify.app/reference/configuration#skipsuccessfulrequests)
|
|
248
|
+
- [`keyGenerator`](https://express-rate-limit.mintlify.app/reference/configuration#keygenerator)
|
|
249
|
+
- [`ipv6Subnet`](https://express-rate-limit.mintlify.app/reference/configuration#ipv6subnet)
|
|
250
|
+
- [`skip`](https://express-rate-limit.mintlify.app/reference/configuration#skip)
|
|
251
|
+
- [`requestWasSuccessful`](https://express-rate-limit.mintlify.app/reference/configuration#requestwassuccessful)
|
|
252
|
+
- [`validate`](https://express-rate-limit.mintlify.app/reference/configuration#validate)
|
|
253
|
+
- [`store`](https://express-rate-limit.mintlify.app/reference/configuration#store)
|
|
254
|
+
|
|
255
|
+
## Request API
|
|
256
|
+
|
|
257
|
+
A `req.slowDown` property is added to all requests with the `limit`, `used`, and
|
|
258
|
+
`remaining` number of requests and, if the store provides it, a `resetTime` Date
|
|
259
|
+
object. It also has the `delay` property, which is the amount of delay imposed
|
|
260
|
+
on current request (milliseconds). These may be used in your application code to
|
|
261
|
+
take additional actions or inform the user of their status.
|
|
262
|
+
|
|
263
|
+
Note that `used` includes the current request, so it should always be > 0.
|
|
264
|
+
|
|
265
|
+
The property name can be configured with the configuration option
|
|
266
|
+
`requestPropertyName`.
|
|
267
|
+
|
|
268
|
+
## Issues and Contributing
|
|
269
|
+
|
|
270
|
+
If you encounter a bug or want to see something added/changed, please go ahead
|
|
271
|
+
and
|
|
272
|
+
[open an issue](https://github.com/express-rate-limit/express-slow-down/issues/new)!
|
|
273
|
+
If you need help with something, feel free to
|
|
274
|
+
[start a discussion](https://github.com/express-rate-limit/express-slow-down/discussions/new)!
|
|
275
|
+
|
|
276
|
+
If you wish to contribute to the library, thanks! First, please read
|
|
277
|
+
[the contributing guide](contributing.md). Then you can pick up any issue and
|
|
278
|
+
fix/implement it!
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT © [Nathan Friedly](http://nfriedly.com/),
|
|
283
|
+
[Vedant K](https://github.com/gamemaker1)
|