express-rate-limit 8.2.1 → 8.5.2
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 +82 -25
- package/dist/index.d.cts +31 -1
- package/dist/index.d.mts +31 -1
- package/dist/index.d.ts +31 -1
- package/dist/index.mjs +82 -25
- package/package.json +19 -18
- package/readme.md +5 -17
package/dist/index.cjs
CHANGED
|
@@ -31,8 +31,13 @@ module.exports = __toCommonJS(index_exports);
|
|
|
31
31
|
var import_node_net = require("node:net");
|
|
32
32
|
var import_ip_address = require("ip-address");
|
|
33
33
|
function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
34
|
-
if (
|
|
35
|
-
|
|
34
|
+
if ((0, import_node_net.isIPv6)(ip)) {
|
|
35
|
+
const address = new import_ip_address.Address6(ip);
|
|
36
|
+
if (address.is4()) return address.to4().correctForm();
|
|
37
|
+
if (ipv6Subnet) {
|
|
38
|
+
const subnet = new import_ip_address.Address6(`${ip}/${ipv6Subnet}`);
|
|
39
|
+
return subnet.networkForm();
|
|
40
|
+
}
|
|
36
41
|
}
|
|
37
42
|
return ip;
|
|
38
43
|
}
|
|
@@ -196,6 +201,16 @@ var MemoryStore = class {
|
|
|
196
201
|
// source/rate-limit.ts
|
|
197
202
|
var import_node_net3 = require("node:net");
|
|
198
203
|
|
|
204
|
+
// source/console-logger.ts
|
|
205
|
+
var ConsoleLogger = {
|
|
206
|
+
warn(...args) {
|
|
207
|
+
console.warn(...args.reverse());
|
|
208
|
+
},
|
|
209
|
+
error(...args) {
|
|
210
|
+
console.error(...args.reverse());
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
199
214
|
// source/headers.ts
|
|
200
215
|
var import_node_buffer = require("node:buffer");
|
|
201
216
|
var import_node_crypto = require("node:crypto");
|
|
@@ -543,7 +558,8 @@ var validations = {
|
|
|
543
558
|
validate: true,
|
|
544
559
|
headers: true,
|
|
545
560
|
max: true,
|
|
546
|
-
passOnStoreError: true
|
|
561
|
+
passOnStoreError: true,
|
|
562
|
+
logger: true
|
|
547
563
|
};
|
|
548
564
|
const validOptions = Object.keys(optionsMap).concat(
|
|
549
565
|
"draft_polli_ratelimit_headers",
|
|
@@ -656,7 +672,15 @@ var validations = {
|
|
|
656
672
|
}
|
|
657
673
|
}
|
|
658
674
|
};
|
|
659
|
-
|
|
675
|
+
function validateLogger(logger) {
|
|
676
|
+
if (typeof logger !== "object" || typeof logger.error !== "function" || typeof logger.warn !== "function") {
|
|
677
|
+
throw new TypeError(
|
|
678
|
+
"Provided logger does not implement the Logger interface"
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
var getValidations = (_enabled, logger) => {
|
|
683
|
+
validateLogger(logger);
|
|
660
684
|
let enabled;
|
|
661
685
|
if (typeof _enabled === "boolean") {
|
|
662
686
|
enabled = {
|
|
@@ -682,8 +706,8 @@ var getValidations = (_enabled) => {
|
|
|
682
706
|
args
|
|
683
707
|
);
|
|
684
708
|
} catch (error) {
|
|
685
|
-
if (error instanceof ChangeWarning)
|
|
686
|
-
else
|
|
709
|
+
if (error instanceof ChangeWarning) logger.warn(error);
|
|
710
|
+
else logger.error(error);
|
|
687
711
|
}
|
|
688
712
|
};
|
|
689
713
|
}
|
|
@@ -736,7 +760,11 @@ var getOptionsFromConfig = (config) => {
|
|
|
736
760
|
};
|
|
737
761
|
var parseOptions = (passedOptions) => {
|
|
738
762
|
const notUndefinedOptions = omitUndefinedProperties(passedOptions);
|
|
739
|
-
const
|
|
763
|
+
const logger = passedOptions.logger ?? ConsoleLogger;
|
|
764
|
+
const validations2 = getValidations(
|
|
765
|
+
notUndefinedOptions?.validate ?? true,
|
|
766
|
+
logger
|
|
767
|
+
);
|
|
740
768
|
validations2.validationsConfig();
|
|
741
769
|
validations2.knownOptions(passedOptions);
|
|
742
770
|
validations2.draftPolliHeaders(
|
|
@@ -811,7 +839,8 @@ var parseOptions = (passedOptions) => {
|
|
|
811
839
|
notUndefinedOptions.store ?? new MemoryStore(validations2)
|
|
812
840
|
),
|
|
813
841
|
// Print an error to the console if a few known misconfigurations are detected.
|
|
814
|
-
validations: validations2
|
|
842
|
+
validations: validations2,
|
|
843
|
+
logger
|
|
815
844
|
};
|
|
816
845
|
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {
|
|
817
846
|
throw new TypeError(
|
|
@@ -832,9 +861,29 @@ var rateLimit = (passedOptions) => {
|
|
|
832
861
|
const options = getOptionsFromConfig(config);
|
|
833
862
|
config.validations.creationStack(config.store);
|
|
834
863
|
config.validations.unsharedStore(config.store);
|
|
835
|
-
if (typeof config.store.init === "function")
|
|
864
|
+
if (typeof config.store.init === "function") {
|
|
865
|
+
try {
|
|
866
|
+
const storeInit = config.store.init(options);
|
|
867
|
+
if (storeInit instanceof Promise) {
|
|
868
|
+
storeInit.catch(
|
|
869
|
+
(error) => config.logger.error(
|
|
870
|
+
error,
|
|
871
|
+
"express-rate-limit: async error during store initialization."
|
|
872
|
+
)
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
} catch (error) {
|
|
876
|
+
config.logger.error(
|
|
877
|
+
error,
|
|
878
|
+
"express-rate-limit: error during store initialization."
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
836
882
|
const middleware = handleAsyncErrors(
|
|
837
883
|
async (request, response, next) => {
|
|
884
|
+
const closePromise = config.skipFailedRequests && new Promise((resolve) => response.once("close", resolve));
|
|
885
|
+
const finishPromise = (config.skipFailedRequests || config.skipSuccessfulRequests) && new Promise((resolve) => response.once("finish", resolve));
|
|
886
|
+
const errorPromise = config.skipFailedRequests && new Promise((resolve) => response.once("error", resolve));
|
|
838
887
|
const skip = await config.skip(request, response);
|
|
839
888
|
if (skip) {
|
|
840
889
|
next();
|
|
@@ -850,9 +899,9 @@ var rateLimit = (passedOptions) => {
|
|
|
850
899
|
resetTime = incrementResult.resetTime;
|
|
851
900
|
} catch (error) {
|
|
852
901
|
if (config.passOnStoreError) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
error
|
|
902
|
+
config.logger.error(
|
|
903
|
+
error,
|
|
904
|
+
"express-rate-limit: error from store, allowing request without rate-limiting."
|
|
856
905
|
);
|
|
857
906
|
next();
|
|
858
907
|
return;
|
|
@@ -913,22 +962,30 @@ var rateLimit = (passedOptions) => {
|
|
|
913
962
|
}
|
|
914
963
|
};
|
|
915
964
|
if (config.skipFailedRequests) {
|
|
916
|
-
|
|
917
|
-
|
|
965
|
+
if (finishPromise) {
|
|
966
|
+
void finishPromise.then(async () => {
|
|
967
|
+
if (!await config.requestWasSuccessful(request, response))
|
|
968
|
+
await decrementKey();
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
if (closePromise) {
|
|
972
|
+
void closePromise.then(async () => {
|
|
973
|
+
if (!response.writableEnded) await decrementKey();
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
if (errorPromise) {
|
|
977
|
+
void errorPromise.then(async () => {
|
|
918
978
|
await decrementKey();
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
if (!response.writableEnded) await decrementKey();
|
|
922
|
-
});
|
|
923
|
-
response.on("error", async () => {
|
|
924
|
-
await decrementKey();
|
|
925
|
-
});
|
|
979
|
+
});
|
|
980
|
+
}
|
|
926
981
|
}
|
|
927
982
|
if (config.skipSuccessfulRequests) {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
await
|
|
931
|
-
|
|
983
|
+
if (finishPromise) {
|
|
984
|
+
void finishPromise.then(async () => {
|
|
985
|
+
if (await config.requestWasSuccessful(request, response))
|
|
986
|
+
await decrementKey();
|
|
987
|
+
});
|
|
988
|
+
}
|
|
932
989
|
}
|
|
933
990
|
}
|
|
934
991
|
config.validations.disable();
|
package/dist/index.d.cts
CHANGED
|
@@ -159,6 +159,26 @@ declare const validations: {
|
|
|
159
159
|
windowMs(windowMs: number): void;
|
|
160
160
|
};
|
|
161
161
|
export type Validations = typeof validations;
|
|
162
|
+
/**
|
|
163
|
+
* Basic logging function
|
|
164
|
+
*
|
|
165
|
+
* @param error {unknown} - The error to log
|
|
166
|
+
* @param message {string | undefined} - Additional details about the error
|
|
167
|
+
*/
|
|
168
|
+
export type LoggerFn = (error: unknown, message?: string) => void;
|
|
169
|
+
/**
|
|
170
|
+
* Minimal interface for logging warnings and errors
|
|
171
|
+
*/
|
|
172
|
+
export type Logger = {
|
|
173
|
+
/**
|
|
174
|
+
* Function to log an error
|
|
175
|
+
*/
|
|
176
|
+
error: LoggerFn;
|
|
177
|
+
/**
|
|
178
|
+
* Function to log a warning
|
|
179
|
+
*/
|
|
180
|
+
warn: LoggerFn;
|
|
181
|
+
};
|
|
162
182
|
/**
|
|
163
183
|
* Callback that fires when a client's hit counter is incremented.
|
|
164
184
|
*
|
|
@@ -265,9 +285,15 @@ export type Store = {
|
|
|
265
285
|
* Method that initializes the store, and has access to the options passed to
|
|
266
286
|
* the middleware too.
|
|
267
287
|
*
|
|
288
|
+
* Called once during initialization.
|
|
289
|
+
*
|
|
290
|
+
* Errors / promise rejections will be caught and logged.
|
|
291
|
+
*
|
|
292
|
+
* Note that the result is not awaited - other store methods (such as increment) may be called before init returns and/or after it throws/rejects.
|
|
293
|
+
*
|
|
268
294
|
* @param options {Options} - The options used to setup the middleware.
|
|
269
295
|
*/
|
|
270
|
-
init?: (options: Options) => void
|
|
296
|
+
init?: (options: Options) => void | Promise<void>;
|
|
271
297
|
/**
|
|
272
298
|
* Method to fetch a client's hit count and reset time.
|
|
273
299
|
*
|
|
@@ -477,6 +503,10 @@ export type Options = {
|
|
|
477
503
|
* If the Store generates an error, allow the request to pass.
|
|
478
504
|
*/
|
|
479
505
|
passOnStoreError: boolean;
|
|
506
|
+
/**
|
|
507
|
+
* The logger to use to log errors. If absent, logs to the console.
|
|
508
|
+
*/
|
|
509
|
+
logger: Logger;
|
|
480
510
|
};
|
|
481
511
|
/**
|
|
482
512
|
* The extended request object that includes information about the client's
|
package/dist/index.d.mts
CHANGED
|
@@ -159,6 +159,26 @@ declare const validations: {
|
|
|
159
159
|
windowMs(windowMs: number): void;
|
|
160
160
|
};
|
|
161
161
|
export type Validations = typeof validations;
|
|
162
|
+
/**
|
|
163
|
+
* Basic logging function
|
|
164
|
+
*
|
|
165
|
+
* @param error {unknown} - The error to log
|
|
166
|
+
* @param message {string | undefined} - Additional details about the error
|
|
167
|
+
*/
|
|
168
|
+
export type LoggerFn = (error: unknown, message?: string) => void;
|
|
169
|
+
/**
|
|
170
|
+
* Minimal interface for logging warnings and errors
|
|
171
|
+
*/
|
|
172
|
+
export type Logger = {
|
|
173
|
+
/**
|
|
174
|
+
* Function to log an error
|
|
175
|
+
*/
|
|
176
|
+
error: LoggerFn;
|
|
177
|
+
/**
|
|
178
|
+
* Function to log a warning
|
|
179
|
+
*/
|
|
180
|
+
warn: LoggerFn;
|
|
181
|
+
};
|
|
162
182
|
/**
|
|
163
183
|
* Callback that fires when a client's hit counter is incremented.
|
|
164
184
|
*
|
|
@@ -265,9 +285,15 @@ export type Store = {
|
|
|
265
285
|
* Method that initializes the store, and has access to the options passed to
|
|
266
286
|
* the middleware too.
|
|
267
287
|
*
|
|
288
|
+
* Called once during initialization.
|
|
289
|
+
*
|
|
290
|
+
* Errors / promise rejections will be caught and logged.
|
|
291
|
+
*
|
|
292
|
+
* Note that the result is not awaited - other store methods (such as increment) may be called before init returns and/or after it throws/rejects.
|
|
293
|
+
*
|
|
268
294
|
* @param options {Options} - The options used to setup the middleware.
|
|
269
295
|
*/
|
|
270
|
-
init?: (options: Options) => void
|
|
296
|
+
init?: (options: Options) => void | Promise<void>;
|
|
271
297
|
/**
|
|
272
298
|
* Method to fetch a client's hit count and reset time.
|
|
273
299
|
*
|
|
@@ -477,6 +503,10 @@ export type Options = {
|
|
|
477
503
|
* If the Store generates an error, allow the request to pass.
|
|
478
504
|
*/
|
|
479
505
|
passOnStoreError: boolean;
|
|
506
|
+
/**
|
|
507
|
+
* The logger to use to log errors. If absent, logs to the console.
|
|
508
|
+
*/
|
|
509
|
+
logger: Logger;
|
|
480
510
|
};
|
|
481
511
|
/**
|
|
482
512
|
* The extended request object that includes information about the client's
|
package/dist/index.d.ts
CHANGED
|
@@ -159,6 +159,26 @@ declare const validations: {
|
|
|
159
159
|
windowMs(windowMs: number): void;
|
|
160
160
|
};
|
|
161
161
|
export type Validations = typeof validations;
|
|
162
|
+
/**
|
|
163
|
+
* Basic logging function
|
|
164
|
+
*
|
|
165
|
+
* @param error {unknown} - The error to log
|
|
166
|
+
* @param message {string | undefined} - Additional details about the error
|
|
167
|
+
*/
|
|
168
|
+
export type LoggerFn = (error: unknown, message?: string) => void;
|
|
169
|
+
/**
|
|
170
|
+
* Minimal interface for logging warnings and errors
|
|
171
|
+
*/
|
|
172
|
+
export type Logger = {
|
|
173
|
+
/**
|
|
174
|
+
* Function to log an error
|
|
175
|
+
*/
|
|
176
|
+
error: LoggerFn;
|
|
177
|
+
/**
|
|
178
|
+
* Function to log a warning
|
|
179
|
+
*/
|
|
180
|
+
warn: LoggerFn;
|
|
181
|
+
};
|
|
162
182
|
/**
|
|
163
183
|
* Callback that fires when a client's hit counter is incremented.
|
|
164
184
|
*
|
|
@@ -265,9 +285,15 @@ export type Store = {
|
|
|
265
285
|
* Method that initializes the store, and has access to the options passed to
|
|
266
286
|
* the middleware too.
|
|
267
287
|
*
|
|
288
|
+
* Called once during initialization.
|
|
289
|
+
*
|
|
290
|
+
* Errors / promise rejections will be caught and logged.
|
|
291
|
+
*
|
|
292
|
+
* Note that the result is not awaited - other store methods (such as increment) may be called before init returns and/or after it throws/rejects.
|
|
293
|
+
*
|
|
268
294
|
* @param options {Options} - The options used to setup the middleware.
|
|
269
295
|
*/
|
|
270
|
-
init?: (options: Options) => void
|
|
296
|
+
init?: (options: Options) => void | Promise<void>;
|
|
271
297
|
/**
|
|
272
298
|
* Method to fetch a client's hit count and reset time.
|
|
273
299
|
*
|
|
@@ -477,6 +503,10 @@ export type Options = {
|
|
|
477
503
|
* If the Store generates an error, allow the request to pass.
|
|
478
504
|
*/
|
|
479
505
|
passOnStoreError: boolean;
|
|
506
|
+
/**
|
|
507
|
+
* The logger to use to log errors. If absent, logs to the console.
|
|
508
|
+
*/
|
|
509
|
+
logger: Logger;
|
|
480
510
|
};
|
|
481
511
|
/**
|
|
482
512
|
* The extended request object that includes information about the client's
|
package/dist/index.mjs
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
import { isIPv6 } from "node:net";
|
|
3
3
|
import { Address6 } from "ip-address";
|
|
4
4
|
function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
5
|
-
if (
|
|
6
|
-
|
|
5
|
+
if (isIPv6(ip)) {
|
|
6
|
+
const address = new Address6(ip);
|
|
7
|
+
if (address.is4()) return address.to4().correctForm();
|
|
8
|
+
if (ipv6Subnet) {
|
|
9
|
+
const subnet = new Address6(`${ip}/${ipv6Subnet}`);
|
|
10
|
+
return subnet.networkForm();
|
|
11
|
+
}
|
|
7
12
|
}
|
|
8
13
|
return ip;
|
|
9
14
|
}
|
|
@@ -167,6 +172,16 @@ var MemoryStore = class {
|
|
|
167
172
|
// source/rate-limit.ts
|
|
168
173
|
import { isIPv6 as isIPv62 } from "node:net";
|
|
169
174
|
|
|
175
|
+
// source/console-logger.ts
|
|
176
|
+
var ConsoleLogger = {
|
|
177
|
+
warn(...args) {
|
|
178
|
+
console.warn(...args.reverse());
|
|
179
|
+
},
|
|
180
|
+
error(...args) {
|
|
181
|
+
console.error(...args.reverse());
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
170
185
|
// source/headers.ts
|
|
171
186
|
import { Buffer } from "node:buffer";
|
|
172
187
|
import { createHash } from "node:crypto";
|
|
@@ -514,7 +529,8 @@ var validations = {
|
|
|
514
529
|
validate: true,
|
|
515
530
|
headers: true,
|
|
516
531
|
max: true,
|
|
517
|
-
passOnStoreError: true
|
|
532
|
+
passOnStoreError: true,
|
|
533
|
+
logger: true
|
|
518
534
|
};
|
|
519
535
|
const validOptions = Object.keys(optionsMap).concat(
|
|
520
536
|
"draft_polli_ratelimit_headers",
|
|
@@ -627,7 +643,15 @@ var validations = {
|
|
|
627
643
|
}
|
|
628
644
|
}
|
|
629
645
|
};
|
|
630
|
-
|
|
646
|
+
function validateLogger(logger) {
|
|
647
|
+
if (typeof logger !== "object" || typeof logger.error !== "function" || typeof logger.warn !== "function") {
|
|
648
|
+
throw new TypeError(
|
|
649
|
+
"Provided logger does not implement the Logger interface"
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
var getValidations = (_enabled, logger) => {
|
|
654
|
+
validateLogger(logger);
|
|
631
655
|
let enabled;
|
|
632
656
|
if (typeof _enabled === "boolean") {
|
|
633
657
|
enabled = {
|
|
@@ -653,8 +677,8 @@ var getValidations = (_enabled) => {
|
|
|
653
677
|
args
|
|
654
678
|
);
|
|
655
679
|
} catch (error) {
|
|
656
|
-
if (error instanceof ChangeWarning)
|
|
657
|
-
else
|
|
680
|
+
if (error instanceof ChangeWarning) logger.warn(error);
|
|
681
|
+
else logger.error(error);
|
|
658
682
|
}
|
|
659
683
|
};
|
|
660
684
|
}
|
|
@@ -707,7 +731,11 @@ var getOptionsFromConfig = (config) => {
|
|
|
707
731
|
};
|
|
708
732
|
var parseOptions = (passedOptions) => {
|
|
709
733
|
const notUndefinedOptions = omitUndefinedProperties(passedOptions);
|
|
710
|
-
const
|
|
734
|
+
const logger = passedOptions.logger ?? ConsoleLogger;
|
|
735
|
+
const validations2 = getValidations(
|
|
736
|
+
notUndefinedOptions?.validate ?? true,
|
|
737
|
+
logger
|
|
738
|
+
);
|
|
711
739
|
validations2.validationsConfig();
|
|
712
740
|
validations2.knownOptions(passedOptions);
|
|
713
741
|
validations2.draftPolliHeaders(
|
|
@@ -782,7 +810,8 @@ var parseOptions = (passedOptions) => {
|
|
|
782
810
|
notUndefinedOptions.store ?? new MemoryStore(validations2)
|
|
783
811
|
),
|
|
784
812
|
// Print an error to the console if a few known misconfigurations are detected.
|
|
785
|
-
validations: validations2
|
|
813
|
+
validations: validations2,
|
|
814
|
+
logger
|
|
786
815
|
};
|
|
787
816
|
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {
|
|
788
817
|
throw new TypeError(
|
|
@@ -803,9 +832,29 @@ var rateLimit = (passedOptions) => {
|
|
|
803
832
|
const options = getOptionsFromConfig(config);
|
|
804
833
|
config.validations.creationStack(config.store);
|
|
805
834
|
config.validations.unsharedStore(config.store);
|
|
806
|
-
if (typeof config.store.init === "function")
|
|
835
|
+
if (typeof config.store.init === "function") {
|
|
836
|
+
try {
|
|
837
|
+
const storeInit = config.store.init(options);
|
|
838
|
+
if (storeInit instanceof Promise) {
|
|
839
|
+
storeInit.catch(
|
|
840
|
+
(error) => config.logger.error(
|
|
841
|
+
error,
|
|
842
|
+
"express-rate-limit: async error during store initialization."
|
|
843
|
+
)
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
} catch (error) {
|
|
847
|
+
config.logger.error(
|
|
848
|
+
error,
|
|
849
|
+
"express-rate-limit: error during store initialization."
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
807
853
|
const middleware = handleAsyncErrors(
|
|
808
854
|
async (request, response, next) => {
|
|
855
|
+
const closePromise = config.skipFailedRequests && new Promise((resolve) => response.once("close", resolve));
|
|
856
|
+
const finishPromise = (config.skipFailedRequests || config.skipSuccessfulRequests) && new Promise((resolve) => response.once("finish", resolve));
|
|
857
|
+
const errorPromise = config.skipFailedRequests && new Promise((resolve) => response.once("error", resolve));
|
|
809
858
|
const skip = await config.skip(request, response);
|
|
810
859
|
if (skip) {
|
|
811
860
|
next();
|
|
@@ -821,9 +870,9 @@ var rateLimit = (passedOptions) => {
|
|
|
821
870
|
resetTime = incrementResult.resetTime;
|
|
822
871
|
} catch (error) {
|
|
823
872
|
if (config.passOnStoreError) {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
error
|
|
873
|
+
config.logger.error(
|
|
874
|
+
error,
|
|
875
|
+
"express-rate-limit: error from store, allowing request without rate-limiting."
|
|
827
876
|
);
|
|
828
877
|
next();
|
|
829
878
|
return;
|
|
@@ -884,22 +933,30 @@ var rateLimit = (passedOptions) => {
|
|
|
884
933
|
}
|
|
885
934
|
};
|
|
886
935
|
if (config.skipFailedRequests) {
|
|
887
|
-
|
|
888
|
-
|
|
936
|
+
if (finishPromise) {
|
|
937
|
+
void finishPromise.then(async () => {
|
|
938
|
+
if (!await config.requestWasSuccessful(request, response))
|
|
939
|
+
await decrementKey();
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
if (closePromise) {
|
|
943
|
+
void closePromise.then(async () => {
|
|
944
|
+
if (!response.writableEnded) await decrementKey();
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
if (errorPromise) {
|
|
948
|
+
void errorPromise.then(async () => {
|
|
889
949
|
await decrementKey();
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if (!response.writableEnded) await decrementKey();
|
|
893
|
-
});
|
|
894
|
-
response.on("error", async () => {
|
|
895
|
-
await decrementKey();
|
|
896
|
-
});
|
|
950
|
+
});
|
|
951
|
+
}
|
|
897
952
|
}
|
|
898
953
|
if (config.skipSuccessfulRequests) {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
await
|
|
902
|
-
|
|
954
|
+
if (finishPromise) {
|
|
955
|
+
void finishPromise.then(async () => {
|
|
956
|
+
if (await config.requestWasSuccessful(request, response))
|
|
957
|
+
await decrementKey();
|
|
958
|
+
});
|
|
959
|
+
}
|
|
903
960
|
}
|
|
904
961
|
}
|
|
905
962
|
config.validations.disable();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "8.2
|
|
3
|
+
"version": "8.5.2",
|
|
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",
|
|
@@ -70,34 +70,38 @@
|
|
|
70
70
|
"test:lib": "jest",
|
|
71
71
|
"test:ext": "cd test/external/ && bash run-all-tests",
|
|
72
72
|
"test": "run-s lint test:lib",
|
|
73
|
+
"format-test": "run-s format test:lib",
|
|
73
74
|
"pre-commit": "lint-staged",
|
|
74
75
|
"prepare": "run-s compile && husky"
|
|
75
76
|
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"ip-address": "^10.2.0"
|
|
79
|
+
},
|
|
76
80
|
"peerDependencies": {
|
|
77
81
|
"express": ">= 4.11"
|
|
78
82
|
},
|
|
79
83
|
"devDependencies": {
|
|
80
|
-
"@biomejs/biome": "2.
|
|
84
|
+
"@biomejs/biome": "2.4.6",
|
|
81
85
|
"@express-rate-limit/prettier": "1.1.1",
|
|
82
86
|
"@express-rate-limit/tsconfig": "1.0.2",
|
|
83
|
-
"@jest/globals": "30.
|
|
84
|
-
"@types/express": "5.0.
|
|
87
|
+
"@jest/globals": "30.4.1",
|
|
88
|
+
"@types/express": "5.0.6",
|
|
85
89
|
"@types/jest": "30.0.0",
|
|
86
|
-
"@types/node": "
|
|
87
|
-
"@types/supertest": "
|
|
88
|
-
"del-cli": "
|
|
90
|
+
"@types/node": "25.7.0",
|
|
91
|
+
"@types/supertest": "7.2.0",
|
|
92
|
+
"del-cli": "7.0.0",
|
|
89
93
|
"dts-bundle-generator": "8.1.2",
|
|
90
|
-
"esbuild": "0.
|
|
91
|
-
"express": "5.1
|
|
94
|
+
"esbuild": "0.28.0",
|
|
95
|
+
"express": "5.2.1",
|
|
92
96
|
"husky": "9.1.7",
|
|
93
|
-
"jest": "30.2
|
|
94
|
-
"lint-staged": "
|
|
95
|
-
"mintlify": "4.2.
|
|
97
|
+
"jest": "30.4.2",
|
|
98
|
+
"lint-staged": "17.0.4",
|
|
99
|
+
"mintlify": "4.2.559",
|
|
96
100
|
"npm-run-all": "4.1.5",
|
|
97
|
-
"prettier": "3.
|
|
101
|
+
"prettier": "3.8.3",
|
|
98
102
|
"ratelimit-header-parser": "0.1.0",
|
|
99
|
-
"supertest": "7.
|
|
100
|
-
"ts-jest": "29.4.
|
|
103
|
+
"supertest": "7.2.2",
|
|
104
|
+
"ts-jest": "29.4.9",
|
|
101
105
|
"ts-node": "10.9.2",
|
|
102
106
|
"typescript": "5.9.3"
|
|
103
107
|
},
|
|
@@ -105,8 +109,5 @@
|
|
|
105
109
|
"lint-staged": {
|
|
106
110
|
"*.{js,ts,json}": "biome check --write",
|
|
107
111
|
"*.{md,yaml}": "prettier --write"
|
|
108
|
-
},
|
|
109
|
-
"dependencies": {
|
|
110
|
-
"ip-address": "10.0.1"
|
|
111
112
|
}
|
|
112
113
|
}
|
package/readme.md
CHANGED
|
@@ -66,24 +66,10 @@ default values.
|
|
|
66
66
|
| [`skipFailedRequests`] | `boolean` | Uncount 4xx/5xx responses. |
|
|
67
67
|
| [`requestWasSuccessful`] | `function` | Used by `skipSuccessfulRequests` and `skipFailedRequests`. |
|
|
68
68
|
| [`validate`] | `boolean` \| `object` | Enable or disable built-in validation checks. |
|
|
69
|
+
| [`logger`] | `Logger` | Custom logger |
|
|
69
70
|
|
|
70
71
|
## Thank You
|
|
71
72
|
|
|
72
|
-
Sponsored by [Zuplo](https://zuplo.link/express-rate-limit) a fully-managed API
|
|
73
|
-
Gateway for developers. Add
|
|
74
|
-
[dynamic rate-limiting](https://zuplo.link/dynamic-rate-limiting),
|
|
75
|
-
authentication and more to any API in minutes. Learn more at
|
|
76
|
-
[zuplo.com](https://zuplo.link/express-rate-limit)
|
|
77
|
-
|
|
78
|
-
<p align="center">
|
|
79
|
-
<a href="https://zuplo.link/express-rate-limit">
|
|
80
|
-
<picture width="322">
|
|
81
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/express-rate-limit/express-rate-limit/assets/114976/cd2f6fa7-eae1-4fbb-be7d-b17df4c6f383">
|
|
82
|
-
<img alt="zuplo-logo" src="https://github.com/express-rate-limit/express-rate-limit/assets/114976/66fd75fa-b39e-4a8c-8d7a-52369bf244dc" width="322">
|
|
83
|
-
</picture>
|
|
84
|
-
</a>
|
|
85
|
-
</p>
|
|
86
|
-
|
|
87
73
|
---
|
|
88
74
|
|
|
89
75
|
Thanks to Mintlify for hosting the documentation at
|
|
@@ -97,7 +83,7 @@ Thanks to Mintlify for hosting the documentation at
|
|
|
97
83
|
|
|
98
84
|
---
|
|
99
85
|
|
|
100
|
-
|
|
86
|
+
And thank you to everyone who's contributed to this project in any way! 🫶
|
|
101
87
|
|
|
102
88
|
## Issues and Contributing
|
|
103
89
|
|
|
@@ -108,7 +94,7 @@ If you need help with something, feel free to
|
|
|
108
94
|
[start a discussion](https://github.com/express-rate-limit/express-rate-limit/discussions/new)!
|
|
109
95
|
|
|
110
96
|
If you wish to contribute to the library, thanks! First, please read
|
|
111
|
-
[the contributing guide](https://express-rate-limit.mintlify.app/
|
|
97
|
+
[the contributing guide](https://express-rate-limit.mintlify.app/guides/contributing).
|
|
112
98
|
Then you can pick up any issue and fix/implement it!
|
|
113
99
|
|
|
114
100
|
## License
|
|
@@ -149,3 +135,5 @@ MIT © [Nathan Friedly](http://nfriedly.com/),
|
|
|
149
135
|
https://express-rate-limit.mintlify.app/reference/configuration#requestwassuccessful
|
|
150
136
|
[`validate`]:
|
|
151
137
|
https://express-rate-limit.mintlify.app/reference/configuration#validate
|
|
138
|
+
[`logger`]:
|
|
139
|
+
https://express-rate-limit.mintlify.app/reference/configuration#logger
|