express-rate-limit 7.5.1 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -21,11 +21,179 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  MemoryStore: () => MemoryStore,
24
- default: () => lib_default,
25
- rateLimit: () => lib_default
24
+ default: () => rate_limit_default,
25
+ ipKeyGenerator: () => ipKeyGenerator,
26
+ rateLimit: () => rate_limit_default
26
27
  });
27
28
  module.exports = __toCommonJS(index_exports);
28
29
 
30
+ // source/ip-key-generator.ts
31
+ var import_node_net = require("node:net");
32
+ var import_ip_address = require("ip-address");
33
+ function ipKeyGenerator(ip, ipv6Subnet = 56) {
34
+ if (ipv6Subnet && (0, import_node_net.isIPv6)(ip)) {
35
+ return `${new import_ip_address.Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;
36
+ }
37
+ return ip;
38
+ }
39
+
40
+ // source/memory-store.ts
41
+ var MemoryStore = class {
42
+ constructor() {
43
+ /**
44
+ * These two maps store usage (requests) and reset time by key (for example, IP
45
+ * addresses or API keys).
46
+ *
47
+ * They are split into two to avoid having to iterate through the entire set to
48
+ * determine which ones need reset. Instead, `Client`s are moved from `previous`
49
+ * to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
50
+ * left in `previous`, i.e., those that have not made any recent requests, are
51
+ * known to be expired and can be deleted in bulk.
52
+ */
53
+ this.previous = /* @__PURE__ */ new Map();
54
+ this.current = /* @__PURE__ */ new Map();
55
+ /**
56
+ * Confirmation that the keys incremented in once instance of MemoryStore
57
+ * cannot affect other instances.
58
+ */
59
+ this.localKeys = true;
60
+ }
61
+ /**
62
+ * Method that initializes the store.
63
+ *
64
+ * @param options {Options} - The options used to setup the middleware.
65
+ */
66
+ init(options) {
67
+ this.windowMs = options.windowMs;
68
+ if (this.interval) clearInterval(this.interval);
69
+ this.interval = setInterval(() => {
70
+ this.clearExpired();
71
+ }, this.windowMs);
72
+ this.interval.unref?.();
73
+ }
74
+ /**
75
+ * Method to fetch a client's hit count and reset time.
76
+ *
77
+ * @param key {string} - The identifier for a client.
78
+ *
79
+ * @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
80
+ *
81
+ * @public
82
+ */
83
+ async get(key) {
84
+ return this.current.get(key) ?? this.previous.get(key);
85
+ }
86
+ /**
87
+ * Method to increment a client's hit counter.
88
+ *
89
+ * @param key {string} - The identifier for a client.
90
+ *
91
+ * @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
92
+ *
93
+ * @public
94
+ */
95
+ async increment(key) {
96
+ const client = this.getClient(key);
97
+ const now = Date.now();
98
+ if (client.resetTime.getTime() <= now) {
99
+ this.resetClient(client, now);
100
+ }
101
+ client.totalHits++;
102
+ return client;
103
+ }
104
+ /**
105
+ * Method to decrement a client's hit counter.
106
+ *
107
+ * @param key {string} - The identifier for a client.
108
+ *
109
+ * @public
110
+ */
111
+ async decrement(key) {
112
+ const client = this.getClient(key);
113
+ if (client.totalHits > 0) client.totalHits--;
114
+ }
115
+ /**
116
+ * Method to reset a client's hit counter.
117
+ *
118
+ * @param key {string} - The identifier for a client.
119
+ *
120
+ * @public
121
+ */
122
+ async resetKey(key) {
123
+ this.current.delete(key);
124
+ this.previous.delete(key);
125
+ }
126
+ /**
127
+ * Method to reset everyone's hit counter.
128
+ *
129
+ * @public
130
+ */
131
+ async resetAll() {
132
+ this.current.clear();
133
+ this.previous.clear();
134
+ }
135
+ /**
136
+ * Method to stop the timer (if currently running) and prevent any memory
137
+ * leaks.
138
+ *
139
+ * @public
140
+ */
141
+ shutdown() {
142
+ clearInterval(this.interval);
143
+ void this.resetAll();
144
+ }
145
+ /**
146
+ * Recycles a client by setting its hit count to zero, and reset time to
147
+ * `windowMs` milliseconds from now.
148
+ *
149
+ * NOT to be confused with `#resetKey()`, which removes a client from both the
150
+ * `current` and `previous` maps.
151
+ *
152
+ * @param client {Client} - The client to recycle.
153
+ * @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
154
+ *
155
+ * @return {Client} - The modified client that was passed in, to allow for chaining.
156
+ */
157
+ resetClient(client, now = Date.now()) {
158
+ client.totalHits = 0;
159
+ client.resetTime.setTime(now + this.windowMs);
160
+ return client;
161
+ }
162
+ /**
163
+ * Retrieves or creates a client, given a key. Also ensures that the client being
164
+ * returned is in the `current` map.
165
+ *
166
+ * @param key {string} - The key under which the client is (or is to be) stored.
167
+ *
168
+ * @returns {Client} - The requested client.
169
+ */
170
+ getClient(key) {
171
+ if (this.current.has(key)) return this.current.get(key);
172
+ let client;
173
+ if (this.previous.has(key)) {
174
+ client = this.previous.get(key);
175
+ this.previous.delete(key);
176
+ } else {
177
+ client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
178
+ this.resetClient(client);
179
+ }
180
+ this.current.set(key, client);
181
+ return client;
182
+ }
183
+ /**
184
+ * Move current clients to previous, create a new map for current.
185
+ *
186
+ * This function is called every `windowMs`.
187
+ */
188
+ clearExpired() {
189
+ this.previous = this.current;
190
+ this.current = /* @__PURE__ */ new Map();
191
+ }
192
+ };
193
+
194
+ // source/rate-limit.ts
195
+ var import_node_net3 = require("node:net");
196
+
29
197
  // source/headers.ts
30
198
  var import_node_buffer = require("node:buffer");
31
199
  var import_node_crypto = require("node:crypto");
@@ -34,12 +202,12 @@ var SUPPORTED_DRAFT_VERSIONS = [
34
202
  "draft-7",
35
203
  "draft-8"
36
204
  ];
37
- var getResetSeconds = (resetTime, windowMs) => {
38
- let resetSeconds = void 0;
205
+ var getResetSeconds = (windowMs, resetTime) => {
206
+ let resetSeconds;
39
207
  if (resetTime) {
40
208
  const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);
41
209
  resetSeconds = Math.max(0, deltaSeconds);
42
- } else if (windowMs) {
210
+ } else {
43
211
  resetSeconds = Math.ceil(windowMs / 1e3);
44
212
  }
45
213
  return resetSeconds;
@@ -65,7 +233,7 @@ var setLegacyHeaders = (response, info) => {
65
233
  var setDraft6Headers = (response, info, windowMs) => {
66
234
  if (response.headersSent) return;
67
235
  const windowSeconds = Math.ceil(windowMs / 1e3);
68
- const resetSeconds = getResetSeconds(info.resetTime);
236
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
69
237
  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
70
238
  response.setHeader("RateLimit-Limit", info.limit.toString());
71
239
  response.setHeader("RateLimit-Remaining", info.remaining.toString());
@@ -75,7 +243,7 @@ var setDraft6Headers = (response, info, windowMs) => {
75
243
  var setDraft7Headers = (response, info, windowMs) => {
76
244
  if (response.headersSent) return;
77
245
  const windowSeconds = Math.ceil(windowMs / 1e3);
78
- const resetSeconds = getResetSeconds(info.resetTime, windowMs);
246
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
79
247
  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
80
248
  response.setHeader(
81
249
  "RateLimit",
@@ -85,21 +253,33 @@ var setDraft7Headers = (response, info, windowMs) => {
85
253
  var setDraft8Headers = (response, info, windowMs, name, key) => {
86
254
  if (response.headersSent) return;
87
255
  const windowSeconds = Math.ceil(windowMs / 1e3);
88
- const resetSeconds = getResetSeconds(info.resetTime, windowMs);
256
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
89
257
  const partitionKey = getPartitionKey(key);
90
- const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;
91
258
  const header = `r=${info.remaining}; t=${resetSeconds}`;
92
- response.append("RateLimit-Policy", `"${name}"; ${policy}`);
259
+ const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;
93
260
  response.append("RateLimit", `"${name}"; ${header}`);
261
+ response.append("RateLimit-Policy", `"${name}"; ${policy}`);
94
262
  };
95
263
  var setRetryAfterHeader = (response, info, windowMs) => {
96
264
  if (response.headersSent) return;
97
- const resetSeconds = getResetSeconds(info.resetTime, windowMs);
265
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
98
266
  response.setHeader("Retry-After", resetSeconds.toString());
99
267
  };
100
268
 
269
+ // source/utils.ts
270
+ var omitUndefinedProperties = (passedOptions) => {
271
+ const omittedOptions = {};
272
+ for (const k of Object.keys(passedOptions)) {
273
+ const key = k;
274
+ if (passedOptions[key] !== void 0) {
275
+ omittedOptions[key] = passedOptions[key];
276
+ }
277
+ }
278
+ return omittedOptions;
279
+ };
280
+
101
281
  // source/validations.ts
102
- var import_node_net = require("node:net");
282
+ var import_node_net2 = require("node:net");
103
283
  var ValidationError = class extends Error {
104
284
  /**
105
285
  * The code must be a string, in snake case and all capital, that starts with
@@ -121,7 +301,6 @@ var ChangeWarning = class extends ValidationError {
121
301
  var usedStores = /* @__PURE__ */ new Set();
122
302
  var singleCountKeys = /* @__PURE__ */ new WeakMap();
123
303
  var validations = {
124
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
125
304
  enabled: {
126
305
  default: true
127
306
  },
@@ -146,7 +325,7 @@ var validations = {
146
325
  `An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
147
326
  );
148
327
  }
149
- if (!(0, import_node_net.isIP)(ip)) {
328
+ if (!(0, import_node_net2.isIP)(ip)) {
150
329
  throw new ValidationError(
151
330
  "ERR_ERL_INVALID_IP_ADDRESS",
152
331
  `An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
@@ -256,7 +435,7 @@ var validations = {
256
435
  if (limit === 0) {
257
436
  throw new ChangeWarning(
258
437
  "WRN_ERL_MAX_ZERO",
259
- `Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7`
438
+ "Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7"
260
439
  );
261
440
  }
262
441
  },
@@ -288,7 +467,7 @@ var validations = {
288
467
  if (onLimitReached) {
289
468
  throw new ChangeWarning(
290
469
  "WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
291
- `The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.`
470
+ "The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7."
292
471
  );
293
472
  }
294
473
  },
@@ -357,7 +536,8 @@ var validations = {
357
536
  const { stack } = new Error(
358
537
  "express-rate-limit validation check (set options.validate.creationStack=false to disable)"
359
538
  );
360
- if (stack?.includes("Layer.handle [as handle_request]")) {
539
+ if (stack?.includes("Layer.handle [as handle_request]") || // express v4
540
+ stack?.includes("Layer.handleRequest")) {
361
541
  if (!store.localKeys) {
362
542
  throw new ValidationError(
363
543
  "ERR_ERL_CREATED_IN_REQUEST_HANDLER",
@@ -366,7 +546,38 @@ var validations = {
366
546
  }
367
547
  throw new ValidationError(
368
548
  "ERR_ERL_CREATED_IN_REQUEST_HANDLER",
369
- `express-rate-limit instance should be created at app initialization, not when responding to a request.`
549
+ "express-rate-limit instance should be created at app initialization, not when responding to a request."
550
+ );
551
+ }
552
+ },
553
+ ipv6Subnet(ipv6Subnet) {
554
+ if (ipv6Subnet === false) {
555
+ return;
556
+ }
557
+ if (!Number.isInteger(ipv6Subnet) || ipv6Subnet < 32 || ipv6Subnet > 64) {
558
+ throw new ValidationError(
559
+ "ERR_ERL_IPV6_SUBNET",
560
+ `Unexpected ipv6Subnet value: ${ipv6Subnet}. Expected an integer between 32 and 64 (usually 48-64).`
561
+ );
562
+ }
563
+ },
564
+ ipv6SubnetOrKeyGenerator(options) {
565
+ if (options.ipv6Subnet !== void 0 && options.keyGenerator) {
566
+ throw new ValidationError(
567
+ "ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR",
568
+ `Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.`
569
+ );
570
+ }
571
+ },
572
+ keyGeneratorIpFallback(keyGenerator) {
573
+ if (!keyGenerator) {
574
+ return;
575
+ }
576
+ const src = keyGenerator.toString();
577
+ if ((src.includes("req.ip") || src.includes("request.ip")) && !src.includes("ipKeyGenerator")) {
578
+ throw new ValidationError(
579
+ "ERR_ERL_KEY_GEN_IPV6",
580
+ "Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
370
581
  );
371
582
  }
372
583
  }
@@ -383,9 +594,7 @@ var getValidations = (_enabled) => {
383
594
  ..._enabled
384
595
  };
385
596
  }
386
- const wrappedValidations = {
387
- enabled
388
- };
597
+ const wrappedValidations = { enabled };
389
598
  for (const [name, validation] of Object.entries(validations)) {
390
599
  if (typeof validation === "function")
391
600
  wrappedValidations[name] = (...args) => {
@@ -407,161 +616,7 @@ var getValidations = (_enabled) => {
407
616
  return wrappedValidations;
408
617
  };
409
618
 
410
- // source/memory-store.ts
411
- var MemoryStore = class {
412
- constructor() {
413
- /**
414
- * These two maps store usage (requests) and reset time by key (for example, IP
415
- * addresses or API keys).
416
- *
417
- * They are split into two to avoid having to iterate through the entire set to
418
- * determine which ones need reset. Instead, `Client`s are moved from `previous`
419
- * to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
420
- * left in `previous`, i.e., those that have not made any recent requests, are
421
- * known to be expired and can be deleted in bulk.
422
- */
423
- this.previous = /* @__PURE__ */ new Map();
424
- this.current = /* @__PURE__ */ new Map();
425
- /**
426
- * Confirmation that the keys incremented in once instance of MemoryStore
427
- * cannot affect other instances.
428
- */
429
- this.localKeys = true;
430
- }
431
- /**
432
- * Method that initializes the store.
433
- *
434
- * @param options {Options} - The options used to setup the middleware.
435
- */
436
- init(options) {
437
- this.windowMs = options.windowMs;
438
- if (this.interval) clearInterval(this.interval);
439
- this.interval = setInterval(() => {
440
- this.clearExpired();
441
- }, this.windowMs);
442
- if (this.interval.unref) this.interval.unref();
443
- }
444
- /**
445
- * Method to fetch a client's hit count and reset time.
446
- *
447
- * @param key {string} - The identifier for a client.
448
- *
449
- * @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
450
- *
451
- * @public
452
- */
453
- async get(key) {
454
- return this.current.get(key) ?? this.previous.get(key);
455
- }
456
- /**
457
- * Method to increment a client's hit counter.
458
- *
459
- * @param key {string} - The identifier for a client.
460
- *
461
- * @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
462
- *
463
- * @public
464
- */
465
- async increment(key) {
466
- const client = this.getClient(key);
467
- const now = Date.now();
468
- if (client.resetTime.getTime() <= now) {
469
- this.resetClient(client, now);
470
- }
471
- client.totalHits++;
472
- return client;
473
- }
474
- /**
475
- * Method to decrement a client's hit counter.
476
- *
477
- * @param key {string} - The identifier for a client.
478
- *
479
- * @public
480
- */
481
- async decrement(key) {
482
- const client = this.getClient(key);
483
- if (client.totalHits > 0) client.totalHits--;
484
- }
485
- /**
486
- * Method to reset a client's hit counter.
487
- *
488
- * @param key {string} - The identifier for a client.
489
- *
490
- * @public
491
- */
492
- async resetKey(key) {
493
- this.current.delete(key);
494
- this.previous.delete(key);
495
- }
496
- /**
497
- * Method to reset everyone's hit counter.
498
- *
499
- * @public
500
- */
501
- async resetAll() {
502
- this.current.clear();
503
- this.previous.clear();
504
- }
505
- /**
506
- * Method to stop the timer (if currently running) and prevent any memory
507
- * leaks.
508
- *
509
- * @public
510
- */
511
- shutdown() {
512
- clearInterval(this.interval);
513
- void this.resetAll();
514
- }
515
- /**
516
- * Recycles a client by setting its hit count to zero, and reset time to
517
- * `windowMs` milliseconds from now.
518
- *
519
- * NOT to be confused with `#resetKey()`, which removes a client from both the
520
- * `current` and `previous` maps.
521
- *
522
- * @param client {Client} - The client to recycle.
523
- * @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
524
- *
525
- * @return {Client} - The modified client that was passed in, to allow for chaining.
526
- */
527
- resetClient(client, now = Date.now()) {
528
- client.totalHits = 0;
529
- client.resetTime.setTime(now + this.windowMs);
530
- return client;
531
- }
532
- /**
533
- * Retrieves or creates a client, given a key. Also ensures that the client being
534
- * returned is in the `current` map.
535
- *
536
- * @param key {string} - The key under which the client is (or is to be) stored.
537
- *
538
- * @returns {Client} - The requested client.
539
- */
540
- getClient(key) {
541
- if (this.current.has(key)) return this.current.get(key);
542
- let client;
543
- if (this.previous.has(key)) {
544
- client = this.previous.get(key);
545
- this.previous.delete(key);
546
- } else {
547
- client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
548
- this.resetClient(client);
549
- }
550
- this.current.set(key, client);
551
- return client;
552
- }
553
- /**
554
- * Move current clients to previous, create a new map for current.
555
- *
556
- * This function is called every `windowMs`.
557
- */
558
- clearExpired() {
559
- this.previous = this.current;
560
- this.current = /* @__PURE__ */ new Map();
561
- }
562
- };
563
-
564
- // source/lib.ts
619
+ // source/rate-limit.ts
565
620
  var isLegacyStore = (store) => (
566
621
  // Check that `incr` exists but `increment` does not - store authors might want
567
622
  // to keep both around for backwards compatibility.
@@ -605,18 +660,8 @@ var getOptionsFromConfig = (config) => {
605
660
  validate: validations2.enabled
606
661
  };
607
662
  };
608
- var omitUndefinedOptions = (passedOptions) => {
609
- const omittedOptions = {};
610
- for (const k of Object.keys(passedOptions)) {
611
- const key = k;
612
- if (passedOptions[key] !== void 0) {
613
- omittedOptions[key] = passedOptions[key];
614
- }
615
- }
616
- return omittedOptions;
617
- };
618
663
  var parseOptions = (passedOptions) => {
619
- const notUndefinedOptions = omitUndefinedOptions(passedOptions);
664
+ const notUndefinedOptions = omitUndefinedProperties(passedOptions);
620
665
  const validations2 = getValidations(notUndefinedOptions?.validate ?? true);
621
666
  validations2.validationsConfig();
622
667
  validations2.draftPolliHeaders(
@@ -624,6 +669,11 @@ var parseOptions = (passedOptions) => {
624
669
  notUndefinedOptions.draft_polli_ratelimit_headers
625
670
  );
626
671
  validations2.onLimitReached(notUndefinedOptions.onLimitReached);
672
+ if (notUndefinedOptions.ipv6Subnet !== void 0 && typeof notUndefinedOptions.ipv6Subnet !== "function") {
673
+ validations2.ipv6Subnet(notUndefinedOptions.ipv6Subnet);
674
+ }
675
+ validations2.keyGeneratorIpFallback(notUndefinedOptions.keyGenerator);
676
+ validations2.ipv6SubnetOrKeyGenerator(notUndefinedOptions);
627
677
  let standardHeaders = notUndefinedOptions.standardHeaders ?? false;
628
678
  if (standardHeaders === true) standardHeaders = "draft-6";
629
679
  const config = {
@@ -652,21 +702,27 @@ var parseOptions = (passedOptions) => {
652
702
  skipSuccessfulRequests: false,
653
703
  requestWasSuccessful: (_request, response) => response.statusCode < 400,
654
704
  skip: (_request, _response) => false,
655
- keyGenerator(request, _response) {
705
+ async keyGenerator(request, response) {
656
706
  validations2.ip(request.ip);
657
707
  validations2.trustProxy(request);
658
708
  validations2.xForwardedForHeader(request);
659
- return request.ip;
709
+ const ip = request.ip;
710
+ let subnet = 56;
711
+ if ((0, import_node_net3.isIPv6)(ip)) {
712
+ subnet = typeof config.ipv6Subnet === "function" ? await config.ipv6Subnet(request, response) : config.ipv6Subnet;
713
+ if (typeof config.ipv6Subnet === "function")
714
+ validations2.ipv6Subnet(subnet);
715
+ }
716
+ return ipKeyGenerator(ip, subnet);
660
717
  },
718
+ ipv6Subnet: 56,
661
719
  async handler(request, response, _next, _optionsUsed) {
662
720
  response.status(config.statusCode);
663
721
  const message = typeof config.message === "function" ? await config.message(
664
722
  request,
665
723
  response
666
724
  ) : config.message;
667
- if (!response.writableEnded) {
668
- response.send(message);
669
- }
725
+ if (!response.writableEnded) response.send(message);
670
726
  },
671
727
  passOnStoreError: false,
672
728
  // Allow the default options to be overridden by the passed options.
@@ -734,7 +790,8 @@ var rateLimit = (passedOptions) => {
734
790
  limit,
735
791
  used: totalHits,
736
792
  remaining: Math.max(limit - totalHits, 0),
737
- resetTime
793
+ resetTime,
794
+ key
738
795
  };
739
796
  Object.defineProperty(info, "current", {
740
797
  configurable: false,
@@ -814,10 +871,11 @@ var rateLimit = (passedOptions) => {
814
871
  middleware.getKey = typeof config.store.get === "function" ? config.store.get.bind(config.store) : getThrowFn;
815
872
  return middleware;
816
873
  };
817
- var lib_default = rateLimit;
874
+ var rate_limit_default = rateLimit;
818
875
  // Annotate the CommonJS export names for ESM import in node:
819
876
  0 && (module.exports = {
820
877
  MemoryStore,
878
+ ipKeyGenerator,
821
879
  rateLimit
822
880
  });
823
- module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;
881
+ module.exports = Object.assign(rateLimit, module.exports);