express-rate-limit 8.5.2 → 8.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,12 +17,24 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // source/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ DAY: () => DAY,
34
+ HOUR: () => HOUR,
35
+ MINUTE: () => MINUTE,
23
36
  MemoryStore: () => MemoryStore,
37
+ SECOND: () => SECOND,
24
38
  default: () => rate_limit_default,
25
39
  ipKeyGenerator: () => ipKeyGenerator,
26
40
  rateLimit: () => rate_limit_default
@@ -200,6 +214,7 @@ var MemoryStore = class {
200
214
 
201
215
  // source/rate-limit.ts
202
216
  var import_node_net3 = require("node:net");
217
+ var import_debug = __toESM(require("debug"), 1);
203
218
 
204
219
  // source/console-logger.ts
205
220
  var ConsoleLogger = {
@@ -699,6 +714,7 @@ var getValidations = (_enabled, logger) => {
699
714
  if (!(enabled[name] ?? enabled.default)) {
700
715
  return;
701
716
  }
717
+ enabled[name] = false;
702
718
  try {
703
719
  ;
704
720
  validation.apply(
@@ -711,6 +727,9 @@ var getValidations = (_enabled, logger) => {
711
727
  }
712
728
  };
713
729
  }
730
+ const inspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
731
+ if (inspect)
732
+ wrappedValidations[inspect] = () => wrappedValidations.enabled;
714
733
  return wrappedValidations;
715
734
  };
716
735
 
@@ -859,9 +878,14 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
859
878
  var rateLimit = (passedOptions) => {
860
879
  const config = parseOptions(passedOptions ?? {});
861
880
  const options = getOptionsFromConfig(config);
881
+ const debug = (0, import_debug.default)("express-rate-limit");
882
+ debug("creating new rate limiter with %o", config.store.constructor.name);
883
+ for (const [key, val] of Object.entries(config))
884
+ debug("set %s to %o", key, val);
862
885
  config.validations.creationStack(config.store);
863
886
  config.validations.unsharedStore(config.store);
864
887
  if (typeof config.store.init === "function") {
888
+ debug("executing init for store");
865
889
  try {
866
890
  const storeInit = config.store.init(options);
867
891
  if (storeInit instanceof Promise) {
@@ -884,13 +908,18 @@ var rateLimit = (passedOptions) => {
884
908
  const closePromise = config.skipFailedRequests && new Promise((resolve) => response.once("close", resolve));
885
909
  const finishPromise = (config.skipFailedRequests || config.skipSuccessfulRequests) && new Promise((resolve) => response.once("finish", resolve));
886
910
  const errorPromise = config.skipFailedRequests && new Promise((resolve) => response.once("error", resolve));
911
+ debug("requested %o", request.originalUrl);
912
+ debug("request from ip %o", request.ip);
887
913
  const skip = await config.skip(request, response);
888
914
  if (skip) {
915
+ debug("skipping request");
889
916
  next();
890
917
  return;
891
918
  }
892
919
  const augmentedRequest = request;
893
920
  const key = await config.keyGenerator(request, response);
921
+ debug("computed key %o", key);
922
+ debug("incrementing count");
894
923
  let totalHits = 0;
895
924
  let resetTime;
896
925
  try {
@@ -920,6 +949,13 @@ var rateLimit = (passedOptions) => {
920
949
  resetTime,
921
950
  key
922
951
  };
952
+ for (const [key2, val] of Object.entries(info))
953
+ debug(
954
+ "set request.%s.%s to be %o",
955
+ config.requestPropertyName,
956
+ key2,
957
+ val
958
+ );
923
959
  Object.defineProperty(info, "current", {
924
960
  configurable: false,
925
961
  enumerable: false,
@@ -927,15 +963,18 @@ var rateLimit = (passedOptions) => {
927
963
  });
928
964
  augmentedRequest[config.requestPropertyName] = info;
929
965
  if (config.legacyHeaders && !response.headersSent) {
966
+ debug("set legacy headers");
930
967
  setLegacyHeaders(response, info);
931
968
  }
932
969
  if (config.standardHeaders && !response.headersSent) {
933
970
  switch (config.standardHeaders) {
934
971
  case "draft-6": {
972
+ debug("set ietf draft 6 headers");
935
973
  setDraft6Headers(response, info, config.windowMs);
936
974
  break;
937
975
  }
938
976
  case "draft-7": {
977
+ debug("set ietf draft 7 headers");
939
978
  config.validations.headersResetTime(info.resetTime);
940
979
  setDraft7Headers(response, info, config.windowMs);
941
980
  break;
@@ -943,6 +982,8 @@ var rateLimit = (passedOptions) => {
943
982
  case "draft-8": {
944
983
  const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;
945
984
  const name = await retrieveName;
985
+ debug("set ietf draft 8 headers");
986
+ debug("set name to %o", name);
946
987
  config.validations.headersResetTime(info.resetTime);
947
988
  setDraft8Headers(response, info, config.windowMs, name, key);
948
989
  break;
@@ -957,6 +998,10 @@ var rateLimit = (passedOptions) => {
957
998
  let decremented = false;
958
999
  const decrementKey = async () => {
959
1000
  if (!decremented) {
1001
+ if (resetTime && Date.now() >= resetTime.getTime()) {
1002
+ return;
1003
+ }
1004
+ debug("decrementing count");
960
1005
  await config.store.decrement(key);
961
1006
  decremented = true;
962
1007
  }
@@ -964,8 +1009,12 @@ var rateLimit = (passedOptions) => {
964
1009
  if (config.skipFailedRequests) {
965
1010
  if (finishPromise) {
966
1011
  void finishPromise.then(async () => {
967
- if (!await config.requestWasSuccessful(request, response))
968
- await decrementKey();
1012
+ const success = await config.requestWasSuccessful(
1013
+ request,
1014
+ response
1015
+ );
1016
+ debug("computed requestWasSuccessful as %o", success);
1017
+ if (!success) await decrementKey();
969
1018
  });
970
1019
  }
971
1020
  if (closePromise) {
@@ -982,15 +1031,20 @@ var rateLimit = (passedOptions) => {
982
1031
  if (config.skipSuccessfulRequests) {
983
1032
  if (finishPromise) {
984
1033
  void finishPromise.then(async () => {
985
- if (await config.requestWasSuccessful(request, response))
986
- await decrementKey();
1034
+ const success = await config.requestWasSuccessful(
1035
+ request,
1036
+ response
1037
+ );
1038
+ debug("computed requestWasSuccessful as %o", success);
1039
+ if (success) await decrementKey();
987
1040
  });
988
1041
  }
989
1042
  }
990
1043
  }
991
- config.validations.disable();
992
1044
  if (totalHits > limit) {
1045
+ debug("limit exceeded");
993
1046
  if (config.legacyHeaders || config.standardHeaders) {
1047
+ debug("set retry-after header");
994
1048
  setRetryAfterHeader(response, info, config.windowMs);
995
1049
  }
996
1050
  config.handler(request, response, next, options);
@@ -1007,9 +1061,19 @@ var rateLimit = (passedOptions) => {
1007
1061
  return middleware;
1008
1062
  };
1009
1063
  var rate_limit_default = rateLimit;
1064
+
1065
+ // source/time-constants.ts
1066
+ var SECOND = 1e3;
1067
+ var MINUTE = 60 * SECOND;
1068
+ var HOUR = 60 * MINUTE;
1069
+ var DAY = 24 * HOUR;
1010
1070
  // Annotate the CommonJS export names for ESM import in node:
1011
1071
  0 && (module.exports = {
1072
+ DAY,
1073
+ HOUR,
1074
+ MINUTE,
1012
1075
  MemoryStore,
1076
+ SECOND,
1013
1077
  ipKeyGenerator,
1014
1078
  rateLimit
1015
1079
  });
package/dist/index.d.cts CHANGED
@@ -664,6 +664,24 @@ export declare class MemoryStore implements Store {
664
664
  * @public
665
665
  */
666
666
  export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
667
+ /**
668
+ * Time constants in milliseconds
669
+ *
670
+ * Use these with rate limit window calculations:
671
+ * @example
672
+ * // 5 minute window
673
+ * windowMs: 5 * MINUTE
674
+ *
675
+ * // 2 hour window
676
+ * windowMs: 2 * HOUR
677
+ *
678
+ * // 1 day window
679
+ * windowMs: 1 * DAY
680
+ */
681
+ export declare const SECOND: number;
682
+ export declare const MINUTE: number;
683
+ export declare const HOUR: number;
684
+ export declare const DAY: number;
667
685
 
668
686
  export {
669
687
  rateLimit as default,
package/dist/index.d.mts CHANGED
@@ -664,6 +664,24 @@ export declare class MemoryStore implements Store {
664
664
  * @public
665
665
  */
666
666
  export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
667
+ /**
668
+ * Time constants in milliseconds
669
+ *
670
+ * Use these with rate limit window calculations:
671
+ * @example
672
+ * // 5 minute window
673
+ * windowMs: 5 * MINUTE
674
+ *
675
+ * // 2 hour window
676
+ * windowMs: 2 * HOUR
677
+ *
678
+ * // 1 day window
679
+ * windowMs: 1 * DAY
680
+ */
681
+ export declare const SECOND: number;
682
+ export declare const MINUTE: number;
683
+ export declare const HOUR: number;
684
+ export declare const DAY: number;
667
685
 
668
686
  export {
669
687
  rateLimit as default,
package/dist/index.d.ts CHANGED
@@ -664,6 +664,24 @@ export declare class MemoryStore implements Store {
664
664
  * @public
665
665
  */
666
666
  export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
667
+ /**
668
+ * Time constants in milliseconds
669
+ *
670
+ * Use these with rate limit window calculations:
671
+ * @example
672
+ * // 5 minute window
673
+ * windowMs: 5 * MINUTE
674
+ *
675
+ * // 2 hour window
676
+ * windowMs: 2 * HOUR
677
+ *
678
+ * // 1 day window
679
+ * windowMs: 1 * DAY
680
+ */
681
+ export declare const SECOND: number;
682
+ export declare const MINUTE: number;
683
+ export declare const HOUR: number;
684
+ export declare const DAY: number;
667
685
 
668
686
  export {
669
687
  rateLimit as default,
package/dist/index.mjs CHANGED
@@ -171,6 +171,7 @@ var MemoryStore = class {
171
171
 
172
172
  // source/rate-limit.ts
173
173
  import { isIPv6 as isIPv62 } from "node:net";
174
+ import createDebugLogger from "debug";
174
175
 
175
176
  // source/console-logger.ts
176
177
  var ConsoleLogger = {
@@ -670,6 +671,7 @@ var getValidations = (_enabled, logger) => {
670
671
  if (!(enabled[name] ?? enabled.default)) {
671
672
  return;
672
673
  }
674
+ enabled[name] = false;
673
675
  try {
674
676
  ;
675
677
  validation.apply(
@@ -682,6 +684,9 @@ var getValidations = (_enabled, logger) => {
682
684
  }
683
685
  };
684
686
  }
687
+ const inspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
688
+ if (inspect)
689
+ wrappedValidations[inspect] = () => wrappedValidations.enabled;
685
690
  return wrappedValidations;
686
691
  };
687
692
 
@@ -830,9 +835,14 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
830
835
  var rateLimit = (passedOptions) => {
831
836
  const config = parseOptions(passedOptions ?? {});
832
837
  const options = getOptionsFromConfig(config);
838
+ const debug = createDebugLogger("express-rate-limit");
839
+ debug("creating new rate limiter with %o", config.store.constructor.name);
840
+ for (const [key, val] of Object.entries(config))
841
+ debug("set %s to %o", key, val);
833
842
  config.validations.creationStack(config.store);
834
843
  config.validations.unsharedStore(config.store);
835
844
  if (typeof config.store.init === "function") {
845
+ debug("executing init for store");
836
846
  try {
837
847
  const storeInit = config.store.init(options);
838
848
  if (storeInit instanceof Promise) {
@@ -855,13 +865,18 @@ var rateLimit = (passedOptions) => {
855
865
  const closePromise = config.skipFailedRequests && new Promise((resolve) => response.once("close", resolve));
856
866
  const finishPromise = (config.skipFailedRequests || config.skipSuccessfulRequests) && new Promise((resolve) => response.once("finish", resolve));
857
867
  const errorPromise = config.skipFailedRequests && new Promise((resolve) => response.once("error", resolve));
868
+ debug("requested %o", request.originalUrl);
869
+ debug("request from ip %o", request.ip);
858
870
  const skip = await config.skip(request, response);
859
871
  if (skip) {
872
+ debug("skipping request");
860
873
  next();
861
874
  return;
862
875
  }
863
876
  const augmentedRequest = request;
864
877
  const key = await config.keyGenerator(request, response);
878
+ debug("computed key %o", key);
879
+ debug("incrementing count");
865
880
  let totalHits = 0;
866
881
  let resetTime;
867
882
  try {
@@ -891,6 +906,13 @@ var rateLimit = (passedOptions) => {
891
906
  resetTime,
892
907
  key
893
908
  };
909
+ for (const [key2, val] of Object.entries(info))
910
+ debug(
911
+ "set request.%s.%s to be %o",
912
+ config.requestPropertyName,
913
+ key2,
914
+ val
915
+ );
894
916
  Object.defineProperty(info, "current", {
895
917
  configurable: false,
896
918
  enumerable: false,
@@ -898,15 +920,18 @@ var rateLimit = (passedOptions) => {
898
920
  });
899
921
  augmentedRequest[config.requestPropertyName] = info;
900
922
  if (config.legacyHeaders && !response.headersSent) {
923
+ debug("set legacy headers");
901
924
  setLegacyHeaders(response, info);
902
925
  }
903
926
  if (config.standardHeaders && !response.headersSent) {
904
927
  switch (config.standardHeaders) {
905
928
  case "draft-6": {
929
+ debug("set ietf draft 6 headers");
906
930
  setDraft6Headers(response, info, config.windowMs);
907
931
  break;
908
932
  }
909
933
  case "draft-7": {
934
+ debug("set ietf draft 7 headers");
910
935
  config.validations.headersResetTime(info.resetTime);
911
936
  setDraft7Headers(response, info, config.windowMs);
912
937
  break;
@@ -914,6 +939,8 @@ var rateLimit = (passedOptions) => {
914
939
  case "draft-8": {
915
940
  const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;
916
941
  const name = await retrieveName;
942
+ debug("set ietf draft 8 headers");
943
+ debug("set name to %o", name);
917
944
  config.validations.headersResetTime(info.resetTime);
918
945
  setDraft8Headers(response, info, config.windowMs, name, key);
919
946
  break;
@@ -928,6 +955,10 @@ var rateLimit = (passedOptions) => {
928
955
  let decremented = false;
929
956
  const decrementKey = async () => {
930
957
  if (!decremented) {
958
+ if (resetTime && Date.now() >= resetTime.getTime()) {
959
+ return;
960
+ }
961
+ debug("decrementing count");
931
962
  await config.store.decrement(key);
932
963
  decremented = true;
933
964
  }
@@ -935,8 +966,12 @@ var rateLimit = (passedOptions) => {
935
966
  if (config.skipFailedRequests) {
936
967
  if (finishPromise) {
937
968
  void finishPromise.then(async () => {
938
- if (!await config.requestWasSuccessful(request, response))
939
- await decrementKey();
969
+ const success = await config.requestWasSuccessful(
970
+ request,
971
+ response
972
+ );
973
+ debug("computed requestWasSuccessful as %o", success);
974
+ if (!success) await decrementKey();
940
975
  });
941
976
  }
942
977
  if (closePromise) {
@@ -953,15 +988,20 @@ var rateLimit = (passedOptions) => {
953
988
  if (config.skipSuccessfulRequests) {
954
989
  if (finishPromise) {
955
990
  void finishPromise.then(async () => {
956
- if (await config.requestWasSuccessful(request, response))
957
- await decrementKey();
991
+ const success = await config.requestWasSuccessful(
992
+ request,
993
+ response
994
+ );
995
+ debug("computed requestWasSuccessful as %o", success);
996
+ if (success) await decrementKey();
958
997
  });
959
998
  }
960
999
  }
961
1000
  }
962
- config.validations.disable();
963
1001
  if (totalHits > limit) {
1002
+ debug("limit exceeded");
964
1003
  if (config.legacyHeaders || config.standardHeaders) {
1004
+ debug("set retry-after header");
965
1005
  setRetryAfterHeader(response, info, config.windowMs);
966
1006
  }
967
1007
  config.handler(request, response, next, options);
@@ -978,8 +1018,18 @@ var rateLimit = (passedOptions) => {
978
1018
  return middleware;
979
1019
  };
980
1020
  var rate_limit_default = rateLimit;
1021
+
1022
+ // source/time-constants.ts
1023
+ var SECOND = 1e3;
1024
+ var MINUTE = 60 * SECOND;
1025
+ var HOUR = 60 * MINUTE;
1026
+ var DAY = 24 * HOUR;
981
1027
  export {
1028
+ DAY,
1029
+ HOUR,
1030
+ MINUTE,
982
1031
  MemoryStore,
1032
+ SECOND,
983
1033
  rate_limit_default as default,
984
1034
  ipKeyGenerator,
985
1035
  rate_limit_default as rateLimit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-rate-limit",
3
- "version": "8.5.2",
3
+ "version": "8.6.0",
4
4
  "description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
5
5
  "author": {
6
6
  "name": "Nathan Friedly",
@@ -75,33 +75,36 @@
75
75
  "prepare": "run-s compile && husky"
76
76
  },
77
77
  "dependencies": {
78
+ "debug": "^4.4.3",
78
79
  "ip-address": "^10.2.0"
79
80
  },
80
81
  "peerDependencies": {
81
82
  "express": ">= 4.11"
82
83
  },
83
84
  "devDependencies": {
85
+ "@asyncapi/specs": "6.8.1",
84
86
  "@biomejs/biome": "2.4.6",
85
87
  "@express-rate-limit/prettier": "1.1.1",
86
- "@express-rate-limit/tsconfig": "1.0.2",
88
+ "@express-rate-limit/tsconfig": "1.0.5",
87
89
  "@jest/globals": "30.4.1",
90
+ "@types/debug": "4.1.13",
88
91
  "@types/express": "5.0.6",
89
92
  "@types/jest": "30.0.0",
90
- "@types/node": "25.7.0",
93
+ "@types/node": "26.1.1",
91
94
  "@types/supertest": "7.2.0",
92
95
  "del-cli": "7.0.0",
93
96
  "dts-bundle-generator": "8.1.2",
94
- "esbuild": "0.28.0",
97
+ "esbuild": "0.28.1",
95
98
  "express": "5.2.1",
96
99
  "husky": "9.1.7",
97
100
  "jest": "30.4.2",
98
- "lint-staged": "17.0.4",
99
- "mintlify": "4.2.559",
101
+ "lint-staged": "17.0.8",
102
+ "mintlify": "4.2.688",
100
103
  "npm-run-all": "4.1.5",
101
- "prettier": "3.8.3",
104
+ "prettier": "3.9.5",
102
105
  "ratelimit-header-parser": "0.1.0",
103
106
  "supertest": "7.2.2",
104
- "ts-jest": "29.4.9",
107
+ "ts-jest": "29.4.11",
105
108
  "ts-node": "10.9.2",
106
109
  "typescript": "5.9.3"
107
110
  },
package/readme.md CHANGED
@@ -21,10 +21,10 @@ The [full documentation](https://express-rate-limit.mintlify.app/overview) is
21
21
  available on-line.
22
22
 
23
23
  ```ts
24
- import { rateLimit } from 'express-rate-limit'
24
+ import { rateLimit, MINUTE } from 'express-rate-limit'
25
25
 
26
26
  const limiter = rateLimit({
27
- windowMs: 15 * 60 * 1000, // 15 minutes
27
+ windowMs: 15 * MINUTE, // SECOND, MINUTE, HOUR, and DAY constants are available, or a use bare number for milliseconds
28
28
  limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
29
29
  standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
30
30
  legacyHeaders: false, // Disable the `X-RateLimit-*` headers.