express-rate-limit 6.11.2 → 7.0.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.mjs CHANGED
@@ -1,10 +1,3 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => {
4
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
- return value;
6
- };
7
-
8
1
  // source/headers.ts
9
2
  var getResetSeconds = (resetTime, windowMs) => {
10
3
  let resetSeconds = void 0;
@@ -71,9 +64,6 @@ var ValidationError = class extends Error {
71
64
  constructor(code, message) {
72
65
  const url = `https://express-rate-limit.github.io/${code}/`;
73
66
  super(`${message} See ${url} for more information.`);
74
- __publicField(this, "name");
75
- __publicField(this, "code");
76
- __publicField(this, "help");
77
67
  this.name = this.constructor.name;
78
68
  this.code = code;
79
69
  this.help = url;
@@ -81,18 +71,17 @@ var ValidationError = class extends Error {
81
71
  };
82
72
  var ChangeWarning = class extends ValidationError {
83
73
  };
84
- var _Validations = class _Validations {
85
- constructor(enabled) {
86
- // eslint-disable-next-line @typescript-eslint/parameter-properties
87
- __publicField(this, "enabled");
88
- this.enabled = enabled;
89
- }
90
- enable() {
91
- this.enabled = true;
92
- }
74
+ var singleCountKeys = /* @__PURE__ */ new WeakMap();
75
+ var validations = {
76
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
+ enabled: {
78
+ default: true
79
+ },
80
+ // Should be EnabledValidations type, but that's a circular reference
93
81
  disable() {
94
- this.enabled = false;
95
- }
82
+ for (const k of Object.keys(this.enabled))
83
+ this.enabled[k] = false;
84
+ },
96
85
  /**
97
86
  * Checks whether the IP address is valid, and that it does not have a port
98
87
  * number in it.
@@ -104,21 +93,19 @@ var _Validations = class _Validations {
104
93
  * @returns {void}
105
94
  */
106
95
  ip(ip) {
107
- this.wrap(() => {
108
- if (ip === void 0) {
109
- throw new ValidationError(
110
- "ERR_ERL_UNDEFINED_IP_ADDRESS",
111
- `An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
112
- );
113
- }
114
- if (!isIP(ip)) {
115
- throw new ValidationError(
116
- "ERR_ERL_INVALID_IP_ADDRESS",
117
- `An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
118
- );
119
- }
120
- });
121
- }
96
+ if (ip === void 0) {
97
+ throw new ValidationError(
98
+ "ERR_ERL_UNDEFINED_IP_ADDRESS",
99
+ `An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
100
+ );
101
+ }
102
+ if (!isIP(ip)) {
103
+ throw new ValidationError(
104
+ "ERR_ERL_INVALID_IP_ADDRESS",
105
+ `An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
106
+ );
107
+ }
108
+ },
122
109
  /**
123
110
  * Makes sure the trust proxy setting is not set to `true`.
124
111
  *
@@ -129,15 +116,13 @@ var _Validations = class _Validations {
129
116
  * @returns {void}
130
117
  */
131
118
  trustProxy(request) {
132
- this.wrap(() => {
133
- if (request.app.get("trust proxy") === true) {
134
- throw new ValidationError(
135
- "ERR_ERL_PERMISSIVE_TRUST_PROXY",
136
- `The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`
137
- );
138
- }
139
- });
140
- }
119
+ if (request.app.get("trust proxy") === true) {
120
+ throw new ValidationError(
121
+ "ERR_ERL_PERMISSIVE_TRUST_PROXY",
122
+ `The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`
123
+ );
124
+ }
125
+ },
141
126
  /**
142
127
  * Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
143
128
  * header is present.
@@ -149,31 +134,26 @@ var _Validations = class _Validations {
149
134
  * @returns {void}
150
135
  */
151
136
  xForwardedForHeader(request) {
152
- this.wrap(() => {
153
- if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {
154
- throw new ValidationError(
155
- "ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",
156
- `The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`
157
- );
158
- }
159
- });
160
- }
137
+ if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {
138
+ throw new ValidationError(
139
+ "ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",
140
+ `The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`
141
+ );
142
+ }
143
+ },
161
144
  /**
162
145
  * Ensures totalHits value from store is a positive integer.
163
146
  *
164
147
  * @param hits {any} - The `totalHits` returned by the store.
165
148
  */
166
149
  positiveHits(hits) {
167
- this.wrap(() => {
168
- if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {
169
- throw new ValidationError(
170
- "ERR_ERL_INVALID_HITS",
171
- `The totalHits value returned from the store must be a positive integer, got ${hits}`
172
- // eslint-disable-line @typescript-eslint/restrict-template-expressions
173
- );
174
- }
175
- });
176
- }
150
+ if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {
151
+ throw new ValidationError(
152
+ "ERR_ERL_INVALID_HITS",
153
+ `The totalHits value returned from the store must be a positive integer, got ${hits}`
154
+ );
155
+ }
156
+ },
177
157
  /**
178
158
  * Ensures a given key is incremented only once per request.
179
159
  *
@@ -184,83 +164,74 @@ var _Validations = class _Validations {
184
164
  * @returns {void}
185
165
  */
186
166
  singleCount(request, store, key) {
187
- this.wrap(() => {
188
- var _a;
189
- let storeKeys = _Validations.singleCountKeys.get(request);
190
- if (!storeKeys) {
191
- storeKeys = /* @__PURE__ */ new Map();
192
- _Validations.singleCountKeys.set(request, storeKeys);
193
- }
194
- const storeKey = store.localKeys ? store : store.constructor.name;
195
- let keys = storeKeys.get(storeKey);
196
- if (!keys) {
197
- keys = [];
198
- storeKeys.set(storeKey, keys);
199
- }
200
- const prefixedKey = `${(_a = store.prefix) != null ? _a : ""}${key}`;
201
- if (keys.includes(prefixedKey)) {
202
- throw new ValidationError(
203
- "ERR_ERL_DOUBLE_COUNT",
204
- `The hit count for ${key} was incremented more than once for a single request.`
205
- );
206
- }
207
- keys.push(prefixedKey);
208
- });
209
- }
167
+ let storeKeys = singleCountKeys.get(request);
168
+ if (!storeKeys) {
169
+ storeKeys = /* @__PURE__ */ new Map();
170
+ singleCountKeys.set(request, storeKeys);
171
+ }
172
+ const storeKey = store.localKeys ? store : store.constructor.name;
173
+ let keys = storeKeys.get(storeKey);
174
+ if (!keys) {
175
+ keys = [];
176
+ storeKeys.set(storeKey, keys);
177
+ }
178
+ const prefixedKey = `${store.prefix ?? ""}${key}`;
179
+ if (keys.includes(prefixedKey)) {
180
+ throw new ValidationError(
181
+ "ERR_ERL_DOUBLE_COUNT",
182
+ `The hit count for ${key} was incremented more than once for a single request.`
183
+ );
184
+ }
185
+ keys.push(prefixedKey);
186
+ },
210
187
  /**
211
- * Warns the user that the behaviour for `max: 0` is changing in the next
188
+ * Warns the user that the behaviour for `max: 0` / `limit: 0` is changing in the next
212
189
  * major release.
213
190
  *
214
- * @param max {number} - The maximum number of hits per client.
191
+ * @param limit {number} - The maximum number of hits per client.
215
192
  *
216
193
  * @returns {void}
217
194
  */
218
- max(max) {
219
- this.wrap(() => {
220
- if (max === 0) {
221
- throw new ChangeWarning(
222
- "WRN_ERL_MAX_ZERO",
223
- `Setting max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7`
224
- );
225
- }
226
- });
227
- }
195
+ limit(limit) {
196
+ if (limit === 0) {
197
+ throw new ChangeWarning(
198
+ "WRN_ERL_MAX_ZERO",
199
+ `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`
200
+ );
201
+ }
202
+ },
228
203
  /**
229
204
  * Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
230
205
  * and will be removed in the next major release.
231
206
  *
232
- * @param draft_polli_ratelimit_headers {boolean|undefined} - The now-deprecated setting that was used to enable standard headers.
207
+ * @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
233
208
  *
234
209
  * @returns {void}
235
210
  */
236
211
  draftPolliHeaders(draft_polli_ratelimit_headers) {
237
- this.wrap(() => {
238
- if (draft_polli_ratelimit_headers) {
239
- throw new ChangeWarning(
240
- "WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",
241
- `The draft_polli_ratelimit_headers configuration option is deprecated and will be removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`
242
- );
243
- }
244
- });
245
- }
212
+ if (draft_polli_ratelimit_headers) {
213
+ throw new ChangeWarning(
214
+ "WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",
215
+ `The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`
216
+ );
217
+ }
218
+ },
246
219
  /**
247
220
  * Warns the user that the `onLimitReached` option is deprecated and will be removed in the next
248
221
  * major release.
249
222
  *
250
- * @param onLimitReached {function|undefined} - The maximum number of hits per client.
223
+ * @param onLimitReached {any | undefined} - The maximum number of hits per client.
251
224
  *
252
225
  * @returns {void}
253
226
  */
254
227
  onLimitReached(onLimitReached) {
255
- this.wrap(() => {
256
- if (onLimitReached) {
257
- throw new ChangeWarning(
258
- "WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
259
- `The onLimitReached configuration option is deprecated and will be removed in express-rate-limit v7.`
260
- );
261
- }
262
- });
263
- }
228
+ if (onLimitReached) {
229
+ throw new ChangeWarning(
230
+ "WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
231
+ `The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.`
232
+ );
233
+ }
234
+ },
264
235
  /**
265
236
  * Warns the user when the selected headers option requires a reset time but
266
237
  * the store does not provide one.
@@ -270,71 +241,93 @@ var _Validations = class _Validations {
270
241
  * @returns {void}
271
242
  */
272
243
  headersResetTime(resetTime) {
273
- this.wrap(() => {
274
- if (!resetTime) {
244
+ if (!resetTime) {
245
+ throw new ValidationError(
246
+ "ERR_ERL_HEADERS_NO_RESET",
247
+ `standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`
248
+ );
249
+ }
250
+ },
251
+ /**
252
+ * Checks the options.validate setting to ensure that only recognized validations are enabled or disabled.
253
+ *
254
+ * If any unrecognized values are found, an error is logged that includes the list of supported vaidations.
255
+ */
256
+ validationsConfig() {
257
+ const supportedValidations = Object.keys(this).filter(
258
+ (k) => !["enabled", "disable"].includes(k)
259
+ );
260
+ supportedValidations.push("default");
261
+ for (const key of Object.keys(this.enabled)) {
262
+ if (!supportedValidations.includes(key)) {
275
263
  throw new ValidationError(
276
- "ERR_ERL_HEADERS_NO_RESET",
277
- `standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`
264
+ "ERR_ERL_UNKNOWN_VALIDATION",
265
+ `options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(
266
+ ", "
267
+ )}.`
278
268
  );
279
269
  }
280
- });
281
- }
282
- wrap(validation) {
283
- if (!this.enabled) {
284
- return;
285
- }
286
- try {
287
- validation.call(this);
288
- } catch (error) {
289
- if (error instanceof ChangeWarning)
290
- console.warn(error);
291
- else
292
- console.error(error);
293
270
  }
294
271
  }
295
272
  };
296
- /**
297
- * Maps the key used in a store for a certain request, and ensures that the
298
- * same key isn't used more than once per request.
299
- *
300
- * The store can be any one of the following:
301
- * - An instance, for stores like the MemoryStore where two instances do not
302
- * share state.
303
- * - A string (class name), for stores where multiple instances
304
- * typically share state, such as the Redis store.
305
- */
306
- __publicField(_Validations, "singleCountKeys", /* @__PURE__ */ new WeakMap());
307
- var Validations = _Validations;
273
+ var getValidations = (_enabled) => {
274
+ let enabled;
275
+ if (typeof _enabled === "boolean") {
276
+ enabled = {
277
+ default: _enabled
278
+ };
279
+ } else {
280
+ enabled = {
281
+ default: true,
282
+ ..._enabled
283
+ };
284
+ }
285
+ const wrappedValidations = {
286
+ enabled
287
+ };
288
+ for (const [name, validation] of Object.entries(validations)) {
289
+ if (typeof validation === "function")
290
+ wrappedValidations[name] = (...args) => {
291
+ if (!(enabled[name] ?? enabled.default)) {
292
+ return;
293
+ }
294
+ try {
295
+ ;
296
+ validation.apply(
297
+ wrappedValidations,
298
+ args
299
+ );
300
+ } catch (error) {
301
+ if (error instanceof ChangeWarning)
302
+ console.warn(error);
303
+ else
304
+ console.error(error);
305
+ }
306
+ };
307
+ }
308
+ return wrappedValidations;
309
+ };
308
310
 
309
311
  // source/memory-store.ts
310
- var calculateNextResetTime = (windowMs) => {
311
- const resetTime = /* @__PURE__ */ new Date();
312
- resetTime.setMilliseconds(resetTime.getMilliseconds() + windowMs);
313
- return resetTime;
314
- };
315
312
  var MemoryStore = class {
316
313
  constructor() {
317
314
  /**
318
- * The duration of time before which all hit counts are reset (in milliseconds).
319
- */
320
- __publicField(this, "windowMs");
321
- /**
322
- * The map that stores the number of hits for each client in memory.
323
- */
324
- __publicField(this, "hits");
325
- /**
326
- * The time at which all hit counts will be reset.
327
- */
328
- __publicField(this, "resetTime");
329
- /**
330
- * Reference to the active timer.
315
+ * These two maps store usage (requests) and reset time by key (for example, IP
316
+ * addresses or API keys).
317
+ *
318
+ * They are split into two to avoid having to iterate through the entire set to
319
+ * determine which ones need reset. Instead, `Client`s are moved from `previous`
320
+ * to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
321
+ * left in `previous`, i.e., those that have not made any recent requests, are
322
+ * known to be expired and can be deleted in bulk.
331
323
  */
332
- __publicField(this, "interval");
324
+ this.previous = /* @__PURE__ */ new Map();
325
+ this.current = /* @__PURE__ */ new Map();
333
326
  /**
334
327
  * Confirmation that the keys incremented in once instance of MemoryStore
335
328
  * cannot affect other instances.
336
329
  */
337
- __publicField(this, "localKeys", true);
330
+ this.localKeys = true;
338
331
  }
339
332
  /**
340
333
  * Method that initializes the store.
@@ -343,10 +336,10 @@ var MemoryStore = class {
343
336
  */
344
337
  init(options) {
345
338
  this.windowMs = options.windowMs;
346
- this.resetTime = calculateNextResetTime(this.windowMs);
347
- this.hits = {};
348
- this.interval = setInterval(async () => {
349
- await this.resetAll();
339
+ if (this.interval)
340
+ clearInterval(this.interval);
341
+ this.interval = setInterval(() => {
342
+ this.clearExpired();
350
343
  }, this.windowMs);
351
344
  if (this.interval.unref)
352
345
  this.interval.unref();
@@ -361,12 +354,7 @@ var MemoryStore = class {
361
354
  * @public
362
355
  */
363
356
  async get(key) {
364
- if (this.hits[key] !== void 0)
365
- return {
366
- totalHits: this.hits[key],
367
- resetTime: this.resetTime
368
- };
369
- return void 0;
357
+ return this.current.get(key) ?? this.previous.get(key);
370
358
  }
371
359
  /**
372
360
  * Method to increment a client's hit counter.
@@ -378,13 +366,13 @@ var MemoryStore = class {
378
366
  * @public
379
367
  */
380
368
  async increment(key) {
381
- var _a;
382
- const totalHits = ((_a = this.hits[key]) != null ? _a : 0) + 1;
383
- this.hits[key] = totalHits;
384
- return {
385
- totalHits,
386
- resetTime: this.resetTime
387
- };
369
+ const client = this.getClient(key);
370
+ const now = Date.now();
371
+ if (client.resetTime.getTime() <= now) {
372
+ this.resetClient(client, now);
373
+ }
374
+ client.totalHits++;
375
+ return client;
388
376
  }
389
377
  /**
390
378
  * Method to decrement a client's hit counter.
@@ -394,9 +382,9 @@ var MemoryStore = class {
394
382
  * @public
395
383
  */
396
384
  async decrement(key) {
397
- const current = this.hits[key];
398
- if (current)
399
- this.hits[key] = current - 1;
385
+ const client = this.getClient(key);
386
+ if (client.totalHits > 0)
387
+ client.totalHits--;
400
388
  }
401
389
  /**
402
390
  * Method to reset a client's hit counter.
@@ -406,7 +394,8 @@ var MemoryStore = class {
406
394
  * @public
407
395
  */
408
396
  async resetKey(key) {
409
- delete this.hits[key];
397
+ this.current.delete(key);
398
+ this.previous.delete(key);
410
399
  }
411
400
  /**
412
401
  * Method to reset everyone's hit counter.
@@ -414,8 +403,8 @@ var MemoryStore = class {
414
403
  * @public
415
404
  */
416
405
  async resetAll() {
417
- this.hits = {};
418
- this.resetTime = calculateNextResetTime(this.windowMs);
406
+ this.current.clear();
407
+ this.previous.clear();
419
408
  }
420
409
  /**
421
410
  * Method to stop the timer (if currently running) and prevent any memory
@@ -425,6 +414,55 @@ var MemoryStore = class {
425
414
  */
426
415
  shutdown() {
427
416
  clearInterval(this.interval);
417
+ void this.resetAll();
418
+ }
419
+ /**
420
+ * Recycles a client by setting its hit count to zero, and reset time to
421
+ * `windowMs` milliseconds from now.
422
+ *
423
+ * NOT to be confused with `#resetKey()`, which removes a client from both the
424
+ * `current` and `previous` maps.
425
+ *
426
+ * @param client {Client} - The client to recycle.
427
+ * @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
428
+ *
429
+ * @return {Client} - The modified client that was passed in, to allow for chaining.
430
+ */
431
+ resetClient(client, now = Date.now()) {
432
+ client.totalHits = 0;
433
+ client.resetTime.setTime(now + this.windowMs);
434
+ return client;
435
+ }
436
+ /**
437
+ * Retrieves or creates a client, given a key. Also ensures that the client being
438
+ * returned is in the `current` map.
439
+ *
440
+ * @param key {string} - The key under which the client is (or is to be) stored.
441
+ *
442
+ * @returns {Client} - The requested client.
443
+ */
444
+ getClient(key) {
445
+ if (this.current.has(key))
446
+ return this.current.get(key);
447
+ let client;
448
+ if (this.previous.has(key)) {
449
+ client = this.previous.get(key);
450
+ this.previous.delete(key);
451
+ } else {
452
+ client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
453
+ this.resetClient(client);
454
+ }
455
+ this.current.set(key, client);
456
+ return client;
457
+ }
458
+ /**
459
+ * Move current clients to previous, create a new map for current.
460
+ *
461
+ * This function is called every `windowMs`.
462
+ */
463
+ clearExpired() {
464
+ this.previous = this.current;
465
+ this.current = /* @__PURE__ */ new Map();
428
466
  }
429
467
  };
430
468
 
@@ -471,10 +509,10 @@ var promisifyStore = (passedStore) => {
471
509
  return new PromisifiedStore();
472
510
  };
473
511
  var getOptionsFromConfig = (config) => {
474
- const { validations, ...directlyPassableEntries } = config;
512
+ const { validations: validations2, ...directlyPassableEntries } = config;
475
513
  return {
476
514
  ...directlyPassableEntries,
477
- validate: validations.enabled
515
+ validate: validations2.enabled
478
516
  };
479
517
  };
480
518
  var omitUndefinedOptions = (passedOptions) => {
@@ -488,32 +526,33 @@ var omitUndefinedOptions = (passedOptions) => {
488
526
  return omittedOptions;
489
527
  };
490
528
  var parseOptions = (passedOptions) => {
491
- var _a, _b, _c, _d;
492
529
  const notUndefinedOptions = omitUndefinedOptions(passedOptions);
493
- const validations = new Validations((_a = notUndefinedOptions == null ? void 0 : notUndefinedOptions.validate) != null ? _a : true);
494
- validations.draftPolliHeaders(
530
+ const validations2 = getValidations(notUndefinedOptions?.validate ?? true);
531
+ validations2.validationsConfig();
532
+ validations2.draftPolliHeaders(
533
+ // @ts-expect-error see the note above.
495
534
  notUndefinedOptions.draft_polli_ratelimit_headers
496
535
  );
497
- validations.onLimitReached(notUndefinedOptions.onLimitReached);
498
- let standardHeaders = (_b = notUndefinedOptions.standardHeaders) != null ? _b : false;
499
- if (standardHeaders === true || standardHeaders === void 0 && notUndefinedOptions.draft_polli_ratelimit_headers) {
536
+ validations2.onLimitReached(notUndefinedOptions.onLimitReached);
537
+ let standardHeaders = notUndefinedOptions.standardHeaders ?? false;
538
+ if (standardHeaders === true)
500
539
  standardHeaders = "draft-6";
501
- }
502
540
  const config = {
503
541
  windowMs: 60 * 1e3,
504
- max: 5,
542
+ limit: passedOptions.max ?? 5,
543
+ // `max` is deprecated, but support it anyways.
505
544
  message: "Too many requests, please try again later.",
506
545
  statusCode: 429,
507
- legacyHeaders: (_c = passedOptions.headers) != null ? _c : true,
546
+ legacyHeaders: passedOptions.headers ?? true,
508
547
  requestPropertyName: "rateLimit",
509
548
  skipFailedRequests: false,
510
549
  skipSuccessfulRequests: false,
511
550
  requestWasSuccessful: (_request, response) => response.statusCode < 400,
512
551
  skip: (_request, _response) => false,
513
552
  keyGenerator(request, _response) {
514
- validations.ip(request.ip);
515
- validations.trustProxy(request);
516
- validations.xForwardedForHeader(request);
553
+ validations2.ip(request.ip);
554
+ validations2.trustProxy(request);
555
+ validations2.xForwardedForHeader(request);
517
556
  return request.ip;
518
557
  },
519
558
  async handler(request, response, _next, _optionsUsed) {
@@ -526,17 +565,15 @@ var parseOptions = (passedOptions) => {
526
565
  response.send(message);
527
566
  }
528
567
  },
529
- onLimitReached(_request, _response, _optionsUsed) {
530
- },
531
568
  // Allow the default options to be overriden by the options passed to the middleware.
532
569
  ...notUndefinedOptions,
533
570
  // `standardHeaders` is resolved into a draft version above, use that.
534
571
  standardHeaders,
535
572
  // Note that this field is declared after the user's options are spread in,
536
573
  // so that this field doesn't get overriden with an un-promisified store!
537
- store: promisifyStore((_d = notUndefinedOptions.store) != null ? _d : new MemoryStore()),
574
+ store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore()),
538
575
  // Print an error to the console if a few known misconfigurations are detected.
539
- validations
576
+ validations: validations2
540
577
  };
541
578
  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") {
542
579
  throw new TypeError(
@@ -553,8 +590,7 @@ var handleAsyncErrors = (fn) => async (request, response, next) => {
553
590
  }
554
591
  };
555
592
  var rateLimit = (passedOptions) => {
556
- var _a;
557
- const config = parseOptions(passedOptions != null ? passedOptions : {});
593
+ const config = parseOptions(passedOptions ?? {});
558
594
  const options = getOptionsFromConfig(config);
559
595
  if (typeof config.store.init === "function")
560
596
  config.store.init(options);
@@ -570,15 +606,20 @@ var rateLimit = (passedOptions) => {
570
606
  const { totalHits, resetTime } = await config.store.increment(key);
571
607
  config.validations.positiveHits(totalHits);
572
608
  config.validations.singleCount(request, config.store, key);
573
- const retrieveQuota = typeof config.max === "function" ? config.max(request, response) : config.max;
574
- const maxHits = await retrieveQuota;
575
- config.validations.max(maxHits);
609
+ const retrieveLimit = typeof config.limit === "function" ? config.limit(request, response) : config.limit;
610
+ const limit = await retrieveLimit;
611
+ config.validations.limit(limit);
576
612
  const info = {
577
- limit: maxHits,
578
- current: totalHits,
579
- remaining: Math.max(maxHits - totalHits, 0),
613
+ limit,
614
+ used: totalHits,
615
+ remaining: Math.max(limit - totalHits, 0),
580
616
  resetTime
581
617
  };
618
+ Object.defineProperty(info, "current", {
619
+ configurable: false,
620
+ enumerable: false,
621
+ value: totalHits
622
+ });
582
623
  augmentedRequest[config.requestPropertyName] = info;
583
624
  if (config.legacyHeaders && !response.headersSent) {
584
625
  setLegacyHeaders(response, info);
@@ -619,11 +660,8 @@ var rateLimit = (passedOptions) => {
619
660
  });
620
661
  }
621
662
  }
622
- if (maxHits && totalHits === maxHits + 1) {
623
- config.onLimitReached(request, response, options);
624
- }
625
663
  config.validations.disable();
626
- if (maxHits && totalHits > maxHits) {
664
+ if (totalHits > limit) {
627
665
  if (config.legacyHeaders || config.standardHeaders) {
628
666
  setRetryAfterHeader(response, info, config.windowMs);
629
667
  }
@@ -634,7 +672,7 @@ var rateLimit = (passedOptions) => {
634
672
  }
635
673
  );
636
674
  middleware.resetKey = config.store.resetKey.bind(config.store);
637
- middleware.getKey = (_a = config.store.get) == null ? void 0 : _a.bind(
675
+ middleware.getKey = config.store.get?.bind(
638
676
  config.store
639
677
  );
640
678
  return middleware;