express-rate-limit 6.7.0 → 6.7.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/changelog.md CHANGED
@@ -6,15 +6,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [6.7.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.7.1)
10
+
11
+ ### Fixed
12
+
13
+ - Fixed compatibility with TypeScript's TypeScript new `node16` module
14
+ resolution strategy (See
15
+ [#355](https://github.com/express-rate-limit/express-rate-limit/issues/355))
16
+
17
+ ### Changed
18
+
19
+ - Bumped development dependencies.
20
+ - Added `node` 20 to list of versions the CI jobs run on.
21
+
22
+ No functional changes.
23
+
9
24
  ## [6.7.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.7.0)
10
25
 
11
26
  ### Changed
12
27
 
13
- - Updated links to point to new express-rate-limit organization on GitHub.
14
- - Added advertisement to Readme for project sponsor
28
+ - Updated links to point to the new `express-rate-limit` organization on GitHub.
29
+ - Added advertisement to `readme.md` for project sponsor
15
30
  [Zuplo](https://zuplo.link/express-rate-limit).
16
- - Updated TypeScript version and other dev dependencies
17
- - Changed CI test suite: dropped node.js 12, added node.js 19
31
+ - Updated to `typescript` version 5 and bumped other dependencies.
32
+ - Dropped `node` 12, and added `node` 19 to the list of versions the CI jobs run
33
+ on.
18
34
 
19
35
  No functional changes.
20
36
 
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
7
  var __export = (target, all) => {
7
8
  for (var name in all)
8
9
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -16,6 +17,10 @@ var __copyProps = (to, from, except, desc) => {
16
17
  return to;
17
18
  };
18
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => {
21
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
22
+ return value;
23
+ };
19
24
 
20
25
  // source/index.ts
21
26
  var source_exports = {};
@@ -28,11 +33,34 @@ module.exports = __toCommonJS(source_exports);
28
33
 
29
34
  // source/memory-store.ts
30
35
  var calculateNextResetTime = (windowMs) => {
31
- const resetTime = new Date();
36
+ const resetTime = /* @__PURE__ */ new Date();
32
37
  resetTime.setMilliseconds(resetTime.getMilliseconds() + windowMs);
33
38
  return resetTime;
34
39
  };
35
40
  var MemoryStore = class {
41
+ constructor() {
42
+ /**
43
+ * The duration of time before which all hit counts are reset (in milliseconds).
44
+ */
45
+ __publicField(this, "windowMs");
46
+ /**
47
+ * The map that stores the number of hits for each client in memory.
48
+ */
49
+ __publicField(this, "hits");
50
+ /**
51
+ * The time at which all hit counts will be reset.
52
+ */
53
+ __publicField(this, "resetTime");
54
+ /**
55
+ * Reference to the active timer.
56
+ */
57
+ __publicField(this, "interval");
58
+ }
59
+ /**
60
+ * Method that initializes the store.
61
+ *
62
+ * @param options {Options} - The options used to setup the middleware.
63
+ */
36
64
  init(options) {
37
65
  this.windowMs = options.windowMs;
38
66
  this.resetTime = calculateNextResetTime(this.windowMs);
@@ -43,6 +71,15 @@ var MemoryStore = class {
43
71
  if (this.interval.unref)
44
72
  this.interval.unref();
45
73
  }
74
+ /**
75
+ * Method to increment a client's hit counter.
76
+ *
77
+ * @param key {string} - The identifier for a client.
78
+ *
79
+ * @returns {IncrementResponse} - The number of hits and reset time for that client.
80
+ *
81
+ * @public
82
+ */
46
83
  async increment(key) {
47
84
  var _a;
48
85
  const totalHits = ((_a = this.hits[key]) != null ? _a : 0) + 1;
@@ -52,25 +89,54 @@ var MemoryStore = class {
52
89
  resetTime: this.resetTime
53
90
  };
54
91
  }
92
+ /**
93
+ * Method to decrement a client's hit counter.
94
+ *
95
+ * @param key {string} - The identifier for a client.
96
+ *
97
+ * @public
98
+ */
55
99
  async decrement(key) {
56
100
  const current = this.hits[key];
57
101
  if (current)
58
102
  this.hits[key] = current - 1;
59
103
  }
104
+ /**
105
+ * Method to reset a client's hit counter.
106
+ *
107
+ * @param key {string} - The identifier for a client.
108
+ *
109
+ * @public
110
+ */
60
111
  async resetKey(key) {
61
112
  delete this.hits[key];
62
113
  }
114
+ /**
115
+ * Method to reset everyone's hit counter.
116
+ *
117
+ * @public
118
+ */
63
119
  async resetAll() {
64
120
  this.hits = {};
65
121
  this.resetTime = calculateNextResetTime(this.windowMs);
66
122
  }
123
+ /**
124
+ * Method to stop the timer (if currently running) and prevent any memory
125
+ * leaks.
126
+ *
127
+ * @public
128
+ */
67
129
  shutdown() {
68
130
  clearInterval(this.interval);
69
131
  }
70
132
  };
71
133
 
72
134
  // source/lib.ts
73
- var isLegacyStore = (store) => typeof store.incr === "function" && typeof store.increment !== "function";
135
+ var isLegacyStore = (store) => (
136
+ // Check that `incr` exists but `increment` does not - store authors might want
137
+ // to keep both around for backwards compatibility.
138
+ typeof store.incr === "function" && typeof store.increment !== "function"
139
+ );
74
140
  var promisifyStore = (passedStore) => {
75
141
  if (!isLegacyStore(passedStore)) {
76
142
  return passedStore;
@@ -137,10 +203,13 @@ var parseOptions = (passedOptions) => {
137
203
  },
138
204
  onLimitReached(_request, _response, _optionsUsed) {
139
205
  },
206
+ // Allow the options object to be overriden by the options passed to the middleware.
140
207
  ...notUndefinedOptions,
208
+ // Note that this field is declared after the user's options are spread in,
209
+ // so that this field doesn't get overriden with an un-promisified store!
141
210
  store: promisifyStore((_c = notUndefinedOptions.store) != null ? _c : new MemoryStore())
142
211
  };
143
- if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || typeof config.store.resetAll !== "undefined" && typeof config.store.resetAll !== "function" || typeof config.store.init !== "undefined" && typeof config.store.init !== "function") {
212
+ 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") {
144
213
  throw new TypeError(
145
214
  "An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
146
215
  );
@@ -183,7 +252,7 @@ var rateLimit = (passedOptions) => {
183
252
  augmentedRequest[options.requestPropertyName].remaining
184
253
  );
185
254
  if (resetTime instanceof Date) {
186
- response.setHeader("Date", new Date().toUTCString());
255
+ response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());
187
256
  response.setHeader(
188
257
  "X-RateLimit-Reset",
189
258
  Math.ceil(resetTime.getTime() / 1e3)
@@ -0,0 +1,359 @@
1
+ // Generated by dts-bundle-generator v7.0.0
2
+
3
+ import { NextFunction, Request, RequestHandler, Response } from 'express';
4
+
5
+ /**
6
+ * Callback that fires when a client's hit counter is incremented.
7
+ *
8
+ * @param error {Error | undefined} - The error that occurred, if any.
9
+ * @param totalHits {number} - The number of hits for that client so far.
10
+ * @param resetTime {Date | undefined} - The time when the counter resets.
11
+ */
12
+ export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
13
+ /**
14
+ * Method (in the form of middleware) to generate/retrieve a value based on the
15
+ * incoming request.
16
+ *
17
+ * @param request {Request} - The Express request object.
18
+ * @param response {Response} - The Express response object.
19
+ *
20
+ * @returns {T} - The value needed.
21
+ */
22
+ export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
23
+ /**
24
+ * Express request handler that sends back a response when a client is
25
+ * rate-limited.
26
+ *
27
+ * @param request {Request} - The Express request object.
28
+ * @param response {Response} - The Express response object.
29
+ * @param next {NextFunction} - The Express `next` function, can be called to skip responding.
30
+ * @param optionsUsed {Options} - The options used to set up the middleware.
31
+ */
32
+ export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
33
+ /**
34
+ * Event callback that is triggered on a client's first request that exceeds the limit
35
+ * but not for subsequent requests. May be used for logging, etc. Should *not*
36
+ * send a response.
37
+ *
38
+ * @param request {Request} - The Express request object.
39
+ * @param response {Response} - The Express response object.
40
+ * @param optionsUsed {Options} - The options used to set up the middleware.
41
+ */
42
+ export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
43
+ /**
44
+ * Data returned from the `Store` when a client's hit counter is incremented.
45
+ *
46
+ * @property totalHits {number} - The number of hits for that client so far.
47
+ * @property resetTime {Date | undefined} - The time when the counter resets.
48
+ */
49
+ export type IncrementResponse = {
50
+ totalHits: number;
51
+ resetTime: Date | undefined;
52
+ };
53
+ /**
54
+ * A modified Express request handler with the rate limit functions.
55
+ */
56
+ export type RateLimitRequestHandler = RequestHandler & {
57
+ /**
58
+ * Method to reset a client's hit counter.
59
+ *
60
+ * @param key {string} - The identifier for a client.
61
+ */
62
+ resetKey: (key: string) => void;
63
+ };
64
+ /**
65
+ * An interface that all hit counter stores must implement.
66
+ *
67
+ * @deprecated 6.x - Implement the `Store` interface instead.
68
+ */
69
+ export type LegacyStore = {
70
+ /**
71
+ * Method to increment a client's hit counter.
72
+ *
73
+ * @param key {string} - The identifier for a client.
74
+ * @param callback {IncrementCallback} - The callback to call once the counter is incremented.
75
+ */
76
+ incr: (key: string, callback: IncrementCallback) => void;
77
+ /**
78
+ * Method to decrement a client's hit counter.
79
+ *
80
+ * @param key {string} - The identifier for a client.
81
+ */
82
+ decrement: (key: string) => void;
83
+ /**
84
+ * Method to reset a client's hit counter.
85
+ *
86
+ * @param key {string} - The identifier for a client.
87
+ */
88
+ resetKey: (key: string) => void;
89
+ /**
90
+ * Method to reset everyone's hit counter.
91
+ */
92
+ resetAll?: () => void;
93
+ };
94
+ /**
95
+ * An interface that all hit counter stores must implement.
96
+ */
97
+ export type Store = {
98
+ /**
99
+ * Method that initializes the store, and has access to the options passed to
100
+ * the middleware too.
101
+ *
102
+ * @param options {Options} - The options used to setup the middleware.
103
+ */
104
+ init?: (options: Options) => void;
105
+ /**
106
+ * Method to increment a client's hit counter.
107
+ *
108
+ * @param key {string} - The identifier for a client.
109
+ *
110
+ * @returns {IncrementResponse} - The number of hits and reset time for that client.
111
+ */
112
+ increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
113
+ /**
114
+ * Method to decrement a client's hit counter.
115
+ *
116
+ * @param key {string} - The identifier for a client.
117
+ */
118
+ decrement: (key: string) => Promise<void> | void;
119
+ /**
120
+ * Method to reset a client's hit counter.
121
+ *
122
+ * @param key {string} - The identifier for a client.
123
+ */
124
+ resetKey: (key: string) => Promise<void> | void;
125
+ /**
126
+ * Method to reset everyone's hit counter.
127
+ */
128
+ resetAll?: () => Promise<void> | void;
129
+ /**
130
+ * Method to shutdown the store, stop timers, and release all resources.
131
+ */
132
+ shutdown?: () => Promise<void> | void;
133
+ };
134
+ /**
135
+ * The configuration options for the rate limiter.
136
+ */
137
+ export type Options = {
138
+ /**
139
+ * How long we should remember the requests.
140
+ *
141
+ * Defaults to `60000` ms (= 1 minute).
142
+ */
143
+ readonly windowMs: number;
144
+ /**
145
+ * The maximum number of connections to allow during the `window` before
146
+ * rate limiting the client.
147
+ *
148
+ * Can be the limit itself as a number or express middleware that parses
149
+ * the request and then figures out the limit.
150
+ *
151
+ * Defaults to `5`.
152
+ */
153
+ readonly max: number | ValueDeterminingMiddleware<number>;
154
+ /**
155
+ * The response body to send back when a client is rate limited.
156
+ *
157
+ * Defaults to `'Too many requests, please try again later.'`
158
+ */
159
+ readonly message: any | ValueDeterminingMiddleware<any>;
160
+ /**
161
+ * The HTTP status code to send back when a client is rate limited.
162
+ *
163
+ * Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
164
+ */
165
+ readonly statusCode: number;
166
+ /**
167
+ * Whether to send `X-RateLimit-*` headers with the rate limit and the number
168
+ * of requests.
169
+ *
170
+ * Defaults to `true` (for backward compatibility).
171
+ */
172
+ readonly legacyHeaders: boolean;
173
+ /**
174
+ * Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
175
+ *
176
+ * Defaults to `false` (for backward compatibility, but its use is recommended).
177
+ */
178
+ readonly standardHeaders: boolean;
179
+ /**
180
+ * The name of the property on the request object to store the rate limit info.
181
+ *
182
+ * Defaults to `rateLimit`.
183
+ */
184
+ readonly requestPropertyName: string;
185
+ /**
186
+ * If `true`, the library will (by default) skip all requests that have a 4XX
187
+ * or 5XX status.
188
+ *
189
+ * Defaults to `false`.
190
+ */
191
+ readonly skipFailedRequests: boolean;
192
+ /**
193
+ * If `true`, the library will (by default) skip all requests that have a
194
+ * status code less than 400.
195
+ *
196
+ * Defaults to `false`.
197
+ */
198
+ readonly skipSuccessfulRequests: boolean;
199
+ /**
200
+ * Method to generate custom identifiers for clients.
201
+ *
202
+ * By default, the client's IP address is used.
203
+ */
204
+ readonly keyGenerator: ValueDeterminingMiddleware<string>;
205
+ /**
206
+ * Express request handler that sends back a response when a client is
207
+ * rate-limited.
208
+ *
209
+ * By default, sends back the `statusCode` and `message` set via the options.
210
+ */
211
+ readonly handler: RateLimitExceededEventHandler;
212
+ /**
213
+ * Express request handler that sends back a response when a client has
214
+ * reached their rate limit, and will be rate limited on their next request.
215
+ *
216
+ * @deprecated 6.x - Please use a custom `handler` that checks the number of
217
+ * hits instead.
218
+ */
219
+ readonly onLimitReached: RateLimitReachedEventHandler;
220
+ /**
221
+ * Method (in the form of middleware) to determine whether or not this request
222
+ * counts towards a client's quota.
223
+ *
224
+ * By default, skips no requests.
225
+ */
226
+ readonly skip: ValueDeterminingMiddleware<boolean>;
227
+ /**
228
+ * Method to determine whether or not the request counts as 'succesful'. Used
229
+ * when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
230
+ *
231
+ * By default, requests with a response status code less than 400 are considered
232
+ * successful.
233
+ */
234
+ readonly requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
235
+ /**
236
+ * The `Store` to use to store the hit count for each client.
237
+ *
238
+ * By default, the built-in `MemoryStore` will be used.
239
+ */
240
+ store: Store | LegacyStore;
241
+ /**
242
+ * Whether to send `X-RateLimit-*` headers with the rate limit and the number
243
+ * of requests.
244
+ *
245
+ * @deprecated 6.x - This option was renamed to `legacyHeaders`.
246
+ */
247
+ headers?: boolean;
248
+ /**
249
+ * Whether to send `RateLimit-*` headers with the rate limit and the number
250
+ * of requests.
251
+ *
252
+ * @deprecated 6.x - This option was renamed to `standardHeaders`.
253
+ */
254
+ draft_polli_ratelimit_headers?: boolean;
255
+ };
256
+ /**
257
+ * The extended request object that includes information about the client's
258
+ * rate limit.
259
+ */
260
+ export type AugmentedRequest = Request & {
261
+ [key: string]: RateLimitInfo;
262
+ };
263
+ /**
264
+ * The rate limit related information for each client included in the
265
+ * Express request object.
266
+ */
267
+ export type RateLimitInfo = {
268
+ readonly limit: number;
269
+ readonly current: number;
270
+ readonly remaining: number;
271
+ readonly resetTime: Date | undefined;
272
+ };
273
+ /**
274
+ *
275
+ * Create an instance of IP rate-limiting middleware for Express.
276
+ *
277
+ * @param passedOptions {Options} - Options to configure the rate limiter.
278
+ *
279
+ * @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
280
+ *
281
+ * @public
282
+ */
283
+ export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
284
+ /**
285
+ * A `Store` that stores the hit count for each client in memory.
286
+ *
287
+ * @public
288
+ */
289
+ export declare class MemoryStore implements Store {
290
+ /**
291
+ * The duration of time before which all hit counts are reset (in milliseconds).
292
+ */
293
+ windowMs: number;
294
+ /**
295
+ * The map that stores the number of hits for each client in memory.
296
+ */
297
+ hits: {
298
+ [key: string]: number | undefined;
299
+ };
300
+ /**
301
+ * The time at which all hit counts will be reset.
302
+ */
303
+ resetTime: Date;
304
+ /**
305
+ * Reference to the active timer.
306
+ */
307
+ interval?: NodeJS.Timer;
308
+ /**
309
+ * Method that initializes the store.
310
+ *
311
+ * @param options {Options} - The options used to setup the middleware.
312
+ */
313
+ init(options: Options): void;
314
+ /**
315
+ * Method to increment a client's hit counter.
316
+ *
317
+ * @param key {string} - The identifier for a client.
318
+ *
319
+ * @returns {IncrementResponse} - The number of hits and reset time for that client.
320
+ *
321
+ * @public
322
+ */
323
+ increment(key: string): Promise<IncrementResponse>;
324
+ /**
325
+ * Method to decrement a client's hit counter.
326
+ *
327
+ * @param key {string} - The identifier for a client.
328
+ *
329
+ * @public
330
+ */
331
+ decrement(key: string): Promise<void>;
332
+ /**
333
+ * Method to reset a client's hit counter.
334
+ *
335
+ * @param key {string} - The identifier for a client.
336
+ *
337
+ * @public
338
+ */
339
+ resetKey(key: string): Promise<void>;
340
+ /**
341
+ * Method to reset everyone's hit counter.
342
+ *
343
+ * @public
344
+ */
345
+ resetAll(): Promise<void>;
346
+ /**
347
+ * Method to stop the timer (if currently running) and prevent any memory
348
+ * leaks.
349
+ *
350
+ * @public
351
+ */
352
+ shutdown(): void;
353
+ }
354
+
355
+ export {
356
+ rateLimit as default,
357
+ };
358
+
359
+ export {};
@@ -0,0 +1,359 @@
1
+ // Generated by dts-bundle-generator v7.0.0
2
+
3
+ import { NextFunction, Request, RequestHandler, Response } from 'express';
4
+
5
+ /**
6
+ * Callback that fires when a client's hit counter is incremented.
7
+ *
8
+ * @param error {Error | undefined} - The error that occurred, if any.
9
+ * @param totalHits {number} - The number of hits for that client so far.
10
+ * @param resetTime {Date | undefined} - The time when the counter resets.
11
+ */
12
+ export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
13
+ /**
14
+ * Method (in the form of middleware) to generate/retrieve a value based on the
15
+ * incoming request.
16
+ *
17
+ * @param request {Request} - The Express request object.
18
+ * @param response {Response} - The Express response object.
19
+ *
20
+ * @returns {T} - The value needed.
21
+ */
22
+ export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
23
+ /**
24
+ * Express request handler that sends back a response when a client is
25
+ * rate-limited.
26
+ *
27
+ * @param request {Request} - The Express request object.
28
+ * @param response {Response} - The Express response object.
29
+ * @param next {NextFunction} - The Express `next` function, can be called to skip responding.
30
+ * @param optionsUsed {Options} - The options used to set up the middleware.
31
+ */
32
+ export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
33
+ /**
34
+ * Event callback that is triggered on a client's first request that exceeds the limit
35
+ * but not for subsequent requests. May be used for logging, etc. Should *not*
36
+ * send a response.
37
+ *
38
+ * @param request {Request} - The Express request object.
39
+ * @param response {Response} - The Express response object.
40
+ * @param optionsUsed {Options} - The options used to set up the middleware.
41
+ */
42
+ export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
43
+ /**
44
+ * Data returned from the `Store` when a client's hit counter is incremented.
45
+ *
46
+ * @property totalHits {number} - The number of hits for that client so far.
47
+ * @property resetTime {Date | undefined} - The time when the counter resets.
48
+ */
49
+ export type IncrementResponse = {
50
+ totalHits: number;
51
+ resetTime: Date | undefined;
52
+ };
53
+ /**
54
+ * A modified Express request handler with the rate limit functions.
55
+ */
56
+ export type RateLimitRequestHandler = RequestHandler & {
57
+ /**
58
+ * Method to reset a client's hit counter.
59
+ *
60
+ * @param key {string} - The identifier for a client.
61
+ */
62
+ resetKey: (key: string) => void;
63
+ };
64
+ /**
65
+ * An interface that all hit counter stores must implement.
66
+ *
67
+ * @deprecated 6.x - Implement the `Store` interface instead.
68
+ */
69
+ export type LegacyStore = {
70
+ /**
71
+ * Method to increment a client's hit counter.
72
+ *
73
+ * @param key {string} - The identifier for a client.
74
+ * @param callback {IncrementCallback} - The callback to call once the counter is incremented.
75
+ */
76
+ incr: (key: string, callback: IncrementCallback) => void;
77
+ /**
78
+ * Method to decrement a client's hit counter.
79
+ *
80
+ * @param key {string} - The identifier for a client.
81
+ */
82
+ decrement: (key: string) => void;
83
+ /**
84
+ * Method to reset a client's hit counter.
85
+ *
86
+ * @param key {string} - The identifier for a client.
87
+ */
88
+ resetKey: (key: string) => void;
89
+ /**
90
+ * Method to reset everyone's hit counter.
91
+ */
92
+ resetAll?: () => void;
93
+ };
94
+ /**
95
+ * An interface that all hit counter stores must implement.
96
+ */
97
+ export type Store = {
98
+ /**
99
+ * Method that initializes the store, and has access to the options passed to
100
+ * the middleware too.
101
+ *
102
+ * @param options {Options} - The options used to setup the middleware.
103
+ */
104
+ init?: (options: Options) => void;
105
+ /**
106
+ * Method to increment a client's hit counter.
107
+ *
108
+ * @param key {string} - The identifier for a client.
109
+ *
110
+ * @returns {IncrementResponse} - The number of hits and reset time for that client.
111
+ */
112
+ increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
113
+ /**
114
+ * Method to decrement a client's hit counter.
115
+ *
116
+ * @param key {string} - The identifier for a client.
117
+ */
118
+ decrement: (key: string) => Promise<void> | void;
119
+ /**
120
+ * Method to reset a client's hit counter.
121
+ *
122
+ * @param key {string} - The identifier for a client.
123
+ */
124
+ resetKey: (key: string) => Promise<void> | void;
125
+ /**
126
+ * Method to reset everyone's hit counter.
127
+ */
128
+ resetAll?: () => Promise<void> | void;
129
+ /**
130
+ * Method to shutdown the store, stop timers, and release all resources.
131
+ */
132
+ shutdown?: () => Promise<void> | void;
133
+ };
134
+ /**
135
+ * The configuration options for the rate limiter.
136
+ */
137
+ export type Options = {
138
+ /**
139
+ * How long we should remember the requests.
140
+ *
141
+ * Defaults to `60000` ms (= 1 minute).
142
+ */
143
+ readonly windowMs: number;
144
+ /**
145
+ * The maximum number of connections to allow during the `window` before
146
+ * rate limiting the client.
147
+ *
148
+ * Can be the limit itself as a number or express middleware that parses
149
+ * the request and then figures out the limit.
150
+ *
151
+ * Defaults to `5`.
152
+ */
153
+ readonly max: number | ValueDeterminingMiddleware<number>;
154
+ /**
155
+ * The response body to send back when a client is rate limited.
156
+ *
157
+ * Defaults to `'Too many requests, please try again later.'`
158
+ */
159
+ readonly message: any | ValueDeterminingMiddleware<any>;
160
+ /**
161
+ * The HTTP status code to send back when a client is rate limited.
162
+ *
163
+ * Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
164
+ */
165
+ readonly statusCode: number;
166
+ /**
167
+ * Whether to send `X-RateLimit-*` headers with the rate limit and the number
168
+ * of requests.
169
+ *
170
+ * Defaults to `true` (for backward compatibility).
171
+ */
172
+ readonly legacyHeaders: boolean;
173
+ /**
174
+ * Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
175
+ *
176
+ * Defaults to `false` (for backward compatibility, but its use is recommended).
177
+ */
178
+ readonly standardHeaders: boolean;
179
+ /**
180
+ * The name of the property on the request object to store the rate limit info.
181
+ *
182
+ * Defaults to `rateLimit`.
183
+ */
184
+ readonly requestPropertyName: string;
185
+ /**
186
+ * If `true`, the library will (by default) skip all requests that have a 4XX
187
+ * or 5XX status.
188
+ *
189
+ * Defaults to `false`.
190
+ */
191
+ readonly skipFailedRequests: boolean;
192
+ /**
193
+ * If `true`, the library will (by default) skip all requests that have a
194
+ * status code less than 400.
195
+ *
196
+ * Defaults to `false`.
197
+ */
198
+ readonly skipSuccessfulRequests: boolean;
199
+ /**
200
+ * Method to generate custom identifiers for clients.
201
+ *
202
+ * By default, the client's IP address is used.
203
+ */
204
+ readonly keyGenerator: ValueDeterminingMiddleware<string>;
205
+ /**
206
+ * Express request handler that sends back a response when a client is
207
+ * rate-limited.
208
+ *
209
+ * By default, sends back the `statusCode` and `message` set via the options.
210
+ */
211
+ readonly handler: RateLimitExceededEventHandler;
212
+ /**
213
+ * Express request handler that sends back a response when a client has
214
+ * reached their rate limit, and will be rate limited on their next request.
215
+ *
216
+ * @deprecated 6.x - Please use a custom `handler` that checks the number of
217
+ * hits instead.
218
+ */
219
+ readonly onLimitReached: RateLimitReachedEventHandler;
220
+ /**
221
+ * Method (in the form of middleware) to determine whether or not this request
222
+ * counts towards a client's quota.
223
+ *
224
+ * By default, skips no requests.
225
+ */
226
+ readonly skip: ValueDeterminingMiddleware<boolean>;
227
+ /**
228
+ * Method to determine whether or not the request counts as 'succesful'. Used
229
+ * when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
230
+ *
231
+ * By default, requests with a response status code less than 400 are considered
232
+ * successful.
233
+ */
234
+ readonly requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
235
+ /**
236
+ * The `Store` to use to store the hit count for each client.
237
+ *
238
+ * By default, the built-in `MemoryStore` will be used.
239
+ */
240
+ store: Store | LegacyStore;
241
+ /**
242
+ * Whether to send `X-RateLimit-*` headers with the rate limit and the number
243
+ * of requests.
244
+ *
245
+ * @deprecated 6.x - This option was renamed to `legacyHeaders`.
246
+ */
247
+ headers?: boolean;
248
+ /**
249
+ * Whether to send `RateLimit-*` headers with the rate limit and the number
250
+ * of requests.
251
+ *
252
+ * @deprecated 6.x - This option was renamed to `standardHeaders`.
253
+ */
254
+ draft_polli_ratelimit_headers?: boolean;
255
+ };
256
+ /**
257
+ * The extended request object that includes information about the client's
258
+ * rate limit.
259
+ */
260
+ export type AugmentedRequest = Request & {
261
+ [key: string]: RateLimitInfo;
262
+ };
263
+ /**
264
+ * The rate limit related information for each client included in the
265
+ * Express request object.
266
+ */
267
+ export type RateLimitInfo = {
268
+ readonly limit: number;
269
+ readonly current: number;
270
+ readonly remaining: number;
271
+ readonly resetTime: Date | undefined;
272
+ };
273
+ /**
274
+ *
275
+ * Create an instance of IP rate-limiting middleware for Express.
276
+ *
277
+ * @param passedOptions {Options} - Options to configure the rate limiter.
278
+ *
279
+ * @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
280
+ *
281
+ * @public
282
+ */
283
+ export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
284
+ /**
285
+ * A `Store` that stores the hit count for each client in memory.
286
+ *
287
+ * @public
288
+ */
289
+ export declare class MemoryStore implements Store {
290
+ /**
291
+ * The duration of time before which all hit counts are reset (in milliseconds).
292
+ */
293
+ windowMs: number;
294
+ /**
295
+ * The map that stores the number of hits for each client in memory.
296
+ */
297
+ hits: {
298
+ [key: string]: number | undefined;
299
+ };
300
+ /**
301
+ * The time at which all hit counts will be reset.
302
+ */
303
+ resetTime: Date;
304
+ /**
305
+ * Reference to the active timer.
306
+ */
307
+ interval?: NodeJS.Timer;
308
+ /**
309
+ * Method that initializes the store.
310
+ *
311
+ * @param options {Options} - The options used to setup the middleware.
312
+ */
313
+ init(options: Options): void;
314
+ /**
315
+ * Method to increment a client's hit counter.
316
+ *
317
+ * @param key {string} - The identifier for a client.
318
+ *
319
+ * @returns {IncrementResponse} - The number of hits and reset time for that client.
320
+ *
321
+ * @public
322
+ */
323
+ increment(key: string): Promise<IncrementResponse>;
324
+ /**
325
+ * Method to decrement a client's hit counter.
326
+ *
327
+ * @param key {string} - The identifier for a client.
328
+ *
329
+ * @public
330
+ */
331
+ decrement(key: string): Promise<void>;
332
+ /**
333
+ * Method to reset a client's hit counter.
334
+ *
335
+ * @param key {string} - The identifier for a client.
336
+ *
337
+ * @public
338
+ */
339
+ resetKey(key: string): Promise<void>;
340
+ /**
341
+ * Method to reset everyone's hit counter.
342
+ *
343
+ * @public
344
+ */
345
+ resetAll(): Promise<void>;
346
+ /**
347
+ * Method to stop the timer (if currently running) and prevent any memory
348
+ * leaks.
349
+ *
350
+ * @public
351
+ */
352
+ shutdown(): void;
353
+ }
354
+
355
+ export {
356
+ rateLimit as default,
357
+ };
358
+
359
+ export {};
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ import { NextFunction, Request, RequestHandler, Response } from 'express';
9
9
  * @param totalHits {number} - The number of hits for that client so far.
10
10
  * @param resetTime {Date | undefined} - The time when the counter resets.
11
11
  */
12
- export declare type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
12
+ export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
13
13
  /**
14
14
  * Method (in the form of middleware) to generate/retrieve a value based on the
15
15
  * incoming request.
@@ -19,7 +19,7 @@ export declare type IncrementCallback = (error: Error | undefined, totalHits: nu
19
19
  *
20
20
  * @returns {T} - The value needed.
21
21
  */
22
- export declare type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
22
+ export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
23
23
  /**
24
24
  * Express request handler that sends back a response when a client is
25
25
  * rate-limited.
@@ -29,7 +29,7 @@ export declare type ValueDeterminingMiddleware<T> = (request: Request, response:
29
29
  * @param next {NextFunction} - The Express `next` function, can be called to skip responding.
30
30
  * @param optionsUsed {Options} - The options used to set up the middleware.
31
31
  */
32
- export declare type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
32
+ export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
33
33
  /**
34
34
  * Event callback that is triggered on a client's first request that exceeds the limit
35
35
  * but not for subsequent requests. May be used for logging, etc. Should *not*
@@ -39,21 +39,21 @@ export declare type RateLimitExceededEventHandler = (request: Request, response:
39
39
  * @param response {Response} - The Express response object.
40
40
  * @param optionsUsed {Options} - The options used to set up the middleware.
41
41
  */
42
- export declare type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
42
+ export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
43
43
  /**
44
44
  * Data returned from the `Store` when a client's hit counter is incremented.
45
45
  *
46
46
  * @property totalHits {number} - The number of hits for that client so far.
47
47
  * @property resetTime {Date | undefined} - The time when the counter resets.
48
48
  */
49
- export declare type IncrementResponse = {
49
+ export type IncrementResponse = {
50
50
  totalHits: number;
51
51
  resetTime: Date | undefined;
52
52
  };
53
53
  /**
54
54
  * A modified Express request handler with the rate limit functions.
55
55
  */
56
- export declare type RateLimitRequestHandler = RequestHandler & {
56
+ export type RateLimitRequestHandler = RequestHandler & {
57
57
  /**
58
58
  * Method to reset a client's hit counter.
59
59
  *
@@ -66,7 +66,7 @@ export declare type RateLimitRequestHandler = RequestHandler & {
66
66
  *
67
67
  * @deprecated 6.x - Implement the `Store` interface instead.
68
68
  */
69
- export declare type LegacyStore = {
69
+ export type LegacyStore = {
70
70
  /**
71
71
  * Method to increment a client's hit counter.
72
72
  *
@@ -94,7 +94,7 @@ export declare type LegacyStore = {
94
94
  /**
95
95
  * An interface that all hit counter stores must implement.
96
96
  */
97
- export declare type Store = {
97
+ export type Store = {
98
98
  /**
99
99
  * Method that initializes the store, and has access to the options passed to
100
100
  * the middleware too.
@@ -134,7 +134,7 @@ export declare type Store = {
134
134
  /**
135
135
  * The configuration options for the rate limiter.
136
136
  */
137
- export declare type Options = {
137
+ export type Options = {
138
138
  /**
139
139
  * How long we should remember the requests.
140
140
  *
@@ -257,14 +257,14 @@ export declare type Options = {
257
257
  * The extended request object that includes information about the client's
258
258
  * rate limit.
259
259
  */
260
- export declare type AugmentedRequest = Request & {
260
+ export type AugmentedRequest = Request & {
261
261
  [key: string]: RateLimitInfo;
262
262
  };
263
263
  /**
264
264
  * The rate limit related information for each client included in the
265
265
  * Express request object.
266
266
  */
267
- export declare type RateLimitInfo = {
267
+ export type RateLimitInfo = {
268
268
  readonly limit: number;
269
269
  readonly current: number;
270
270
  readonly remaining: number;
package/dist/index.mjs CHANGED
@@ -1,10 +1,40 @@
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
+
1
8
  // source/memory-store.ts
2
9
  var calculateNextResetTime = (windowMs) => {
3
- const resetTime = new Date();
10
+ const resetTime = /* @__PURE__ */ new Date();
4
11
  resetTime.setMilliseconds(resetTime.getMilliseconds() + windowMs);
5
12
  return resetTime;
6
13
  };
7
14
  var MemoryStore = class {
15
+ constructor() {
16
+ /**
17
+ * The duration of time before which all hit counts are reset (in milliseconds).
18
+ */
19
+ __publicField(this, "windowMs");
20
+ /**
21
+ * The map that stores the number of hits for each client in memory.
22
+ */
23
+ __publicField(this, "hits");
24
+ /**
25
+ * The time at which all hit counts will be reset.
26
+ */
27
+ __publicField(this, "resetTime");
28
+ /**
29
+ * Reference to the active timer.
30
+ */
31
+ __publicField(this, "interval");
32
+ }
33
+ /**
34
+ * Method that initializes the store.
35
+ *
36
+ * @param options {Options} - The options used to setup the middleware.
37
+ */
8
38
  init(options) {
9
39
  this.windowMs = options.windowMs;
10
40
  this.resetTime = calculateNextResetTime(this.windowMs);
@@ -15,6 +45,15 @@ var MemoryStore = class {
15
45
  if (this.interval.unref)
16
46
  this.interval.unref();
17
47
  }
48
+ /**
49
+ * Method to increment a client's hit counter.
50
+ *
51
+ * @param key {string} - The identifier for a client.
52
+ *
53
+ * @returns {IncrementResponse} - The number of hits and reset time for that client.
54
+ *
55
+ * @public
56
+ */
18
57
  async increment(key) {
19
58
  var _a;
20
59
  const totalHits = ((_a = this.hits[key]) != null ? _a : 0) + 1;
@@ -24,25 +63,54 @@ var MemoryStore = class {
24
63
  resetTime: this.resetTime
25
64
  };
26
65
  }
66
+ /**
67
+ * Method to decrement a client's hit counter.
68
+ *
69
+ * @param key {string} - The identifier for a client.
70
+ *
71
+ * @public
72
+ */
27
73
  async decrement(key) {
28
74
  const current = this.hits[key];
29
75
  if (current)
30
76
  this.hits[key] = current - 1;
31
77
  }
78
+ /**
79
+ * Method to reset a client's hit counter.
80
+ *
81
+ * @param key {string} - The identifier for a client.
82
+ *
83
+ * @public
84
+ */
32
85
  async resetKey(key) {
33
86
  delete this.hits[key];
34
87
  }
88
+ /**
89
+ * Method to reset everyone's hit counter.
90
+ *
91
+ * @public
92
+ */
35
93
  async resetAll() {
36
94
  this.hits = {};
37
95
  this.resetTime = calculateNextResetTime(this.windowMs);
38
96
  }
97
+ /**
98
+ * Method to stop the timer (if currently running) and prevent any memory
99
+ * leaks.
100
+ *
101
+ * @public
102
+ */
39
103
  shutdown() {
40
104
  clearInterval(this.interval);
41
105
  }
42
106
  };
43
107
 
44
108
  // source/lib.ts
45
- var isLegacyStore = (store) => typeof store.incr === "function" && typeof store.increment !== "function";
109
+ var isLegacyStore = (store) => (
110
+ // Check that `incr` exists but `increment` does not - store authors might want
111
+ // to keep both around for backwards compatibility.
112
+ typeof store.incr === "function" && typeof store.increment !== "function"
113
+ );
46
114
  var promisifyStore = (passedStore) => {
47
115
  if (!isLegacyStore(passedStore)) {
48
116
  return passedStore;
@@ -109,10 +177,13 @@ var parseOptions = (passedOptions) => {
109
177
  },
110
178
  onLimitReached(_request, _response, _optionsUsed) {
111
179
  },
180
+ // Allow the options object to be overriden by the options passed to the middleware.
112
181
  ...notUndefinedOptions,
182
+ // Note that this field is declared after the user's options are spread in,
183
+ // so that this field doesn't get overriden with an un-promisified store!
113
184
  store: promisifyStore((_c = notUndefinedOptions.store) != null ? _c : new MemoryStore())
114
185
  };
115
- if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || typeof config.store.resetAll !== "undefined" && typeof config.store.resetAll !== "function" || typeof config.store.init !== "undefined" && typeof config.store.init !== "function") {
186
+ 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") {
116
187
  throw new TypeError(
117
188
  "An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
118
189
  );
@@ -155,7 +226,7 @@ var rateLimit = (passedOptions) => {
155
226
  augmentedRequest[options.requestPropertyName].remaining
156
227
  );
157
228
  if (resetTime instanceof Date) {
158
- response.setHeader("Date", new Date().toUTCString());
229
+ response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());
159
230
  response.setHeader(
160
231
  "X-RateLimit-Reset",
161
232
  Math.ceil(resetTime.getTime() / 1e3)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-rate-limit",
3
- "version": "6.7.0",
3
+ "version": "6.7.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",
@@ -30,9 +30,14 @@
30
30
  "type": "module",
31
31
  "exports": {
32
32
  ".": {
33
- "types": "./dist/index.d.ts",
34
- "import": "./dist/index.mjs",
35
- "require": "./dist/index.cjs"
33
+ "import": {
34
+ "types": "./dist/index.d.mts",
35
+ "default": "./dist/index.mjs"
36
+ },
37
+ "require": {
38
+ "types": "./dist/index.d.cts",
39
+ "default": "./dist/index.cjs"
40
+ }
36
41
  }
37
42
  },
38
43
  "main": "./dist/index.cjs",
@@ -47,19 +52,19 @@
47
52
  "changelog.md"
48
53
  ],
49
54
  "engines": {
50
- "node": ">= 12.9.0"
55
+ "node": ">= 14.0.0"
51
56
  },
52
57
  "scripts": {
53
58
  "clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
54
59
  "build:cjs": "esbuild --bundle --target=es2019 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;\" source/index.ts",
55
60
  "build:esm": "esbuild --bundle --target=es2019 --format=esm --outfile=dist/index.mjs source/index.ts",
56
- "build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts",
61
+ "build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts && cp dist/index.d.ts dist/index.d.cts && cp dist/index.d.ts dist/index.d.mts",
57
62
  "compile": "run-s clean build:*",
58
63
  "lint:code": "xo --ignore test/external/",
59
64
  "lint:rest": "prettier --ignore-path .gitignore --ignore-unknown --check .",
60
65
  "lint": "run-s lint:*",
61
- "autofix:code": "xo --ignore test/external/ --fix",
62
- "autofix:rest": "prettier --ignore-path .gitignore --ignore-unknown --write .",
66
+ "autofix:code": "run-s lint:code --fix",
67
+ "autofix:rest": "run-s lint:rest --write .",
63
68
  "autofix": "run-s autofix:*",
64
69
  "test:lib": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
65
70
  "test:ext": "cd test/external/ && bash run-all-tests",
@@ -71,25 +76,25 @@
71
76
  "express": "^4 || ^5"
72
77
  },
73
78
  "devDependencies": {
74
- "@jest/globals": "29.3.1",
75
- "@types/express": "4.17.14",
76
- "@types/jest": "29.2.3",
77
- "@types/node": "18.11.9",
79
+ "@jest/globals": "29.6.1",
80
+ "@types/express": "4.17.17",
81
+ "@types/jest": "29.5.2",
82
+ "@types/node": "20.4.0",
78
83
  "@types/supertest": "2.0.12",
79
84
  "cross-env": "7.0.3",
80
85
  "del-cli": "5.0.0",
81
86
  "dts-bundle-generator": "7.0.0",
82
- "esbuild": "0.15.14",
87
+ "esbuild": "0.18.11",
83
88
  "express": "4.18.2",
84
- "husky": "8.0.2",
85
- "jest": "29.3.1",
86
- "lint-staged": "13.0.3",
89
+ "husky": "8.0.3",
90
+ "jest": "29.6.1",
91
+ "lint-staged": "13.2.3",
87
92
  "npm-run-all": "4.1.5",
88
- "supertest": "6.3.1",
89
- "ts-jest": "29.0.3",
93
+ "supertest": "6.3.3",
94
+ "ts-jest": "29.1.1",
90
95
  "ts-node": "10.9.1",
91
- "typescript": "4.8.4",
92
- "xo": "0.49.0"
96
+ "typescript": "4.9.5",
97
+ "xo": "0.54.2"
93
98
  },
94
99
  "xo": {
95
100
  "prettier": true,
package/readme.md CHANGED
@@ -355,7 +355,8 @@ Defaults to `false`.
355
355
 
356
356
  > `function`
357
357
 
358
- Method to generate custom identifiers for clients.
358
+ Method to retrieve custom identifiers for clients, such as their IP address,
359
+ username, or API Key.
359
360
 
360
361
  Should be a (sync/async) function that accepts the Express `request` and
361
362
  `response` objects and then returns a string.
@@ -369,6 +370,10 @@ const limiter = rateLimit({
369
370
  })
370
371
  ```
371
372
 
373
+ > **Note** If a `keyGenerator` returns the same value for every user, it becomes
374
+ > a global rate limiter. This could be combined with a second instance of
375
+ > `express-rate-limit` to have both global and per-user limits.
376
+
372
377
  ### `handler`
373
378
 
374
379
  > `function`