2024-04-22 11:47:34 +00:00
// source/headers.ts
var getResetSeconds = ( resetTime , windowMs ) => {
let resetSeconds = void 0 ;
if ( resetTime ) {
const deltaSeconds = Math . ceil ( ( resetTime . getTime ( ) - Date . now ( ) ) / 1e3 ) ;
resetSeconds = Math . max ( 0 , deltaSeconds ) ;
} else if ( windowMs ) {
resetSeconds = Math . ceil ( windowMs / 1e3 ) ;
}
return resetSeconds ;
} ;
var setLegacyHeaders = ( response , info ) => {
if ( response . headersSent )
return ;
response . setHeader ( "X-RateLimit-Limit" , info . limit . toString ( ) ) ;
response . setHeader ( "X-RateLimit-Remaining" , info . remaining . toString ( ) ) ;
if ( info . resetTime instanceof Date ) {
response . setHeader ( "Date" , ( /* @__PURE__ */ new Date ( ) ) . toUTCString ( ) ) ;
response . setHeader (
"X-RateLimit-Reset" ,
Math . ceil ( info . resetTime . getTime ( ) / 1e3 ) . toString ( )
) ;
}
} ;
var setDraft6Headers = ( response , info , windowMs ) => {
if ( response . headersSent )
return ;
const windowSeconds = Math . ceil ( windowMs / 1e3 ) ;
const resetSeconds = getResetSeconds ( info . resetTime ) ;
response . setHeader ( "RateLimit-Policy" , ` ${ info . limit } ;w= ${ windowSeconds } ` ) ;
response . setHeader ( "RateLimit-Limit" , info . limit . toString ( ) ) ;
response . setHeader ( "RateLimit-Remaining" , info . remaining . toString ( ) ) ;
if ( resetSeconds )
response . setHeader ( "RateLimit-Reset" , resetSeconds . toString ( ) ) ;
} ;
var setDraft7Headers = ( response , info , windowMs ) => {
if ( response . headersSent )
return ;
const windowSeconds = Math . ceil ( windowMs / 1e3 ) ;
const resetSeconds = getResetSeconds ( info . resetTime , windowMs ) ;
response . setHeader ( "RateLimit-Policy" , ` ${ info . limit } ;w= ${ windowSeconds } ` ) ;
response . setHeader (
"RateLimit" ,
` limit= ${ info . limit } , remaining= ${ info . remaining } , reset= ${ resetSeconds } `
) ;
} ;
var setRetryAfterHeader = ( response , info , windowMs ) => {
if ( response . headersSent )
return ;
const resetSeconds = getResetSeconds ( info . resetTime , windowMs ) ;
response . setHeader ( "Retry-After" , resetSeconds . toString ( ) ) ;
} ;
// source/validations.ts
import { isIP } from "net" ;
var ValidationError = class extends Error {
/ * *
* The code must be a string , in snake case and all capital , that starts with
* the substring ` ERR_ERL_ ` .
*
* The message must be a string , starting with an uppercase character ,
* describing the issue in detail .
* /
constructor ( code , message ) {
const url = ` https://express-rate-limit.github.io/ ${ code } / ` ;
super ( ` ${ message } See ${ url } for more information. ` ) ;
this . name = this . constructor . name ;
this . code = code ;
this . help = url ;
}
} ;
var ChangeWarning = class extends ValidationError {
} ;
var singleCountKeys = /* @__PURE__ */ new WeakMap ( ) ;
var validations = {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
enabled : {
default : true
} ,
// Should be EnabledValidations type, but that's a circular reference
disable ( ) {
for ( const k of Object . keys ( this . enabled ) )
this . enabled [ k ] = false ;
} ,
/ * *
* Checks whether the IP address is valid , and that it does not have a port
* number in it .
*
* See https : //github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
*
* @ param ip { string | undefined } - The IP address provided by Express as request . ip .
*
* @ returns { void }
* /
ip ( ip ) {
if ( ip === void 0 ) {
throw new ValidationError (
"ERR_ERL_UNDEFINED_IP_ADDRESS" ,
` An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely. `
) ;
}
if ( ! isIP ( ip ) ) {
throw new ValidationError (
"ERR_ERL_INVALID_IP_ADDRESS" ,
` An invalid 'request.ip' ( ${ ip } ) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter. `
) ;
}
} ,
/ * *
* Makes sure the trust proxy setting is not set to ` true ` .
*
* See https : //github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
*
* @ param request { Request } - The Express request object .
*
* @ returns { void }
* /
trustProxy ( request ) {
if ( request . app . get ( "trust proxy" ) === true ) {
throw new ValidationError (
"ERR_ERL_PERMISSIVE_TRUST_PROXY" ,
` The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting. `
) ;
}
} ,
/ * *
* Makes sure the trust proxy setting is set in case the ` X-Forwarded-For `
* header is present .
*
* See https : //github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
*
* @ param request { Request } - The Express request object .
*
* @ returns { void }
* /
xForwardedForHeader ( request ) {
if ( request . headers [ "x-forwarded-for" ] && request . app . get ( "trust proxy" ) === false ) {
throw new ValidationError (
"ERR_ERL_UNEXPECTED_X_FORWARDED_FOR" ,
` 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. `
) ;
}
} ,
/ * *
* Ensures totalHits value from store is a positive integer .
*
* @ param hits { any } - The ` totalHits ` returned by the store .
* /
positiveHits ( hits ) {
if ( typeof hits !== "number" || hits < 1 || hits !== Math . round ( hits ) ) {
throw new ValidationError (
"ERR_ERL_INVALID_HITS" ,
` The totalHits value returned from the store must be a positive integer, got ${ hits } `
) ;
}
} ,
/ * *
* Ensures a given key is incremented only once per request .
*
* @ param request { Request } - The Express request object .
* @ param store { Store } - The store class .
* @ param key { string } - The key used to store the client ' s hit count .
*
* @ returns { void }
* /
singleCount ( request , store , key ) {
let storeKeys = singleCountKeys . get ( request ) ;
if ( ! storeKeys ) {
storeKeys = /* @__PURE__ */ new Map ( ) ;
singleCountKeys . set ( request , storeKeys ) ;
}
const storeKey = store . localKeys ? store : store . constructor . name ;
let keys = storeKeys . get ( storeKey ) ;
if ( ! keys ) {
keys = [ ] ;
storeKeys . set ( storeKey , keys ) ;
}
const prefixedKey = ` ${ store . prefix ? ? "" } ${ key } ` ;
if ( keys . includes ( prefixedKey ) ) {
throw new ValidationError (
"ERR_ERL_DOUBLE_COUNT" ,
` The hit count for ${ key } was incremented more than once for a single request. `
) ;
}
keys . push ( prefixedKey ) ;
} ,
/ * *
* Warns the user that the behaviour for ` max: 0 ` / ` limit: 0 ` is changing in the next
* major release .
*
* @ param limit { number } - The maximum number of hits per client .
*
* @ returns { void }
* /
limit ( limit ) {
if ( limit === 0 ) {
throw new ChangeWarning (
"WRN_ERL_MAX_ZERO" ,
` 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 `
) ;
}
} ,
/ * *
* Warns the user that the ` draft_polli_ratelimit_headers ` option is deprecated
* and will be removed in the next major release .
*
* @ param draft _polli _ratelimit _headers { any | undefined } - The now - deprecated setting that was used to enable standard headers .
*
* @ returns { void }
* /
draftPolliHeaders ( draft _polli _ratelimit _headers ) {
if ( draft _polli _ratelimit _headers ) {
throw new ChangeWarning (
"WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS" ,
` The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead. `
) ;
}
} ,
/ * *
* Warns the user that the ` onLimitReached ` option is deprecated and will be removed in the next
* major release .
*
* @ param onLimitReached { any | undefined } - The maximum number of hits per client .
*
* @ returns { void }
* /
onLimitReached ( onLimitReached ) {
if ( onLimitReached ) {
throw new ChangeWarning (
"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED" ,
` The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7. `
) ;
}
} ,
/ * *
* Warns the user when the selected headers option requires a reset time but
* the store does not provide one .
*
* @ param resetTime { Date | undefined } - The timestamp when the client ' s hit count will be reset .
*
* @ returns { void }
* /
headersResetTime ( resetTime ) {
if ( ! resetTime ) {
throw new ValidationError (
"ERR_ERL_HEADERS_NO_RESET" ,
` 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. `
) ;
}
} ,
/ * *
* Checks the options . validate setting to ensure that only recognized validations are enabled or disabled .
*
* If any unrecognized values are found , an error is logged that includes the list of supported vaidations .
* /
validationsConfig ( ) {
const supportedValidations = Object . keys ( this ) . filter (
( k ) => ! [ "enabled" , "disable" ] . includes ( k )
) ;
supportedValidations . push ( "default" ) ;
for ( const key of Object . keys ( this . enabled ) ) {
if ( ! supportedValidations . includes ( key ) ) {
throw new ValidationError (
"ERR_ERL_UNKNOWN_VALIDATION" ,
` options.validate. ${ key } is not recognized. Supported validate options are: ${ supportedValidations . join (
", "
) } . `
) ;
}
}
} ,
/ * *
* Checks to see if the instance was created inside of a request handler , which would prevent it from working correctly .
* /
creationStack ( ) {
const { stack } = new Error (
"express-rate-limit validation check (set options.validate.creationStack=false to disable)"
) ;
if ( stack ? . includes ( "Layer.handle [as handle_request]" ) ) {
throw new ValidationError (
"ERR_ERL_CREATED_IN_REQUEST_HANDLER" ,
` express-rate-limit instance should be created at app initialization, not when responding to a request. `
) ;
}
}
2022-01-09 15:20:59 +00:00
} ;
2024-04-22 11:47:34 +00:00
var getValidations = ( _enabled ) => {
let enabled ;
if ( typeof _enabled === "boolean" ) {
enabled = {
default : _enabled
} ;
} else {
enabled = {
default : true ,
... _enabled
} ;
}
const wrappedValidations = {
enabled
} ;
for ( const [ name , validation ] of Object . entries ( validations ) ) {
if ( typeof validation === "function" )
wrappedValidations [ name ] = ( ... args ) => {
if ( ! ( enabled [ name ] ? ? enabled . default ) ) {
return ;
}
try {
;
validation . apply (
wrappedValidations ,
args
) ;
} catch ( error ) {
if ( error instanceof ChangeWarning )
console . warn ( error ) ;
else
console . error ( error ) ;
}
} ;
}
return wrappedValidations ;
} ;
// source/memory-store.ts
2022-01-09 15:20:59 +00:00
var MemoryStore = class {
2024-04-22 11:47:34 +00:00
constructor ( ) {
/ * *
* These two maps store usage ( requests ) and reset time by key ( for example , IP
* addresses or API keys ) .
*
* They are split into two to avoid having to iterate through the entire set to
* determine which ones need reset . Instead , ` Client ` s are moved from ` previous `
* to ` current ` as they hit the endpoint . Once ` windowMs ` has elapsed , all clients
* left in ` previous ` , i . e . , those that have not made any recent requests , are
* known to be expired and can be deleted in bulk .
* /
this . previous = /* @__PURE__ */ new Map ( ) ;
this . current = /* @__PURE__ */ new Map ( ) ;
/ * *
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances .
* /
this . localKeys = true ;
}
/ * *
* Method that initializes the store .
*
* @ param options { Options } - The options used to setup the middleware .
* /
2022-01-09 15:20:59 +00:00
init ( options ) {
this . windowMs = options . windowMs ;
2024-04-22 11:47:34 +00:00
if ( this . interval )
clearInterval ( this . interval ) ;
this . interval = setInterval ( ( ) => {
this . clearExpired ( ) ;
2022-01-09 15:20:59 +00:00
} , this . windowMs ) ;
2022-12-21 23:26:55 +00:00
if ( this . interval . unref )
this . interval . unref ( ) ;
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
/ * *
* Method to fetch a client ' s hit count and reset time .
*
* @ param key { string } - The identifier for a client .
*
* @ returns { ClientRateLimitInfo | undefined } - The number of hits and reset time for that client .
*
* @ public
* /
async get ( key ) {
return this . current . get ( key ) ? ? this . previous . get ( key ) ;
}
/ * *
* Method to increment a client ' s hit counter .
*
* @ param key { string } - The identifier for a client .
*
* @ returns { ClientRateLimitInfo } - The number of hits and reset time for that client .
*
* @ public
* /
2022-01-09 15:20:59 +00:00
async increment ( key ) {
2024-04-22 11:47:34 +00:00
const client = this . getClient ( key ) ;
const now = Date . now ( ) ;
if ( client . resetTime . getTime ( ) <= now ) {
this . resetClient ( client , now ) ;
}
client . totalHits ++ ;
return client ;
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
/ * *
* Method to decrement a client ' s hit counter .
*
* @ param key { string } - The identifier for a client .
*
* @ public
* /
2022-01-09 15:20:59 +00:00
async decrement ( key ) {
2024-04-22 11:47:34 +00:00
const client = this . getClient ( key ) ;
if ( client . totalHits > 0 )
client . totalHits -- ;
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
/ * *
* Method to reset a client ' s hit counter .
*
* @ param key { string } - The identifier for a client .
*
* @ public
* /
2022-01-09 15:20:59 +00:00
async resetKey ( key ) {
2024-04-22 11:47:34 +00:00
this . current . delete ( key ) ;
this . previous . delete ( key ) ;
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
/ * *
* Method to reset everyone ' s hit counter .
*
* @ public
* /
2022-01-09 15:20:59 +00:00
async resetAll ( ) {
2024-04-22 11:47:34 +00:00
this . current . clear ( ) ;
this . previous . clear ( ) ;
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
/ * *
* Method to stop the timer ( if currently running ) and prevent any memory
* leaks .
*
* @ public
* /
2022-12-21 23:26:55 +00:00
shutdown ( ) {
clearInterval ( this . interval ) ;
2024-04-22 11:47:34 +00:00
void this . resetAll ( ) ;
}
/ * *
* Recycles a client by setting its hit count to zero , and reset time to
* ` windowMs ` milliseconds from now .
*
* NOT to be confused with ` #resetKey() ` , which removes a client from both the
* ` current ` and ` previous ` maps .
*
* @ param client { Client } - The client to recycle .
* @ param now { number } - The current time , to which the ` windowMs ` is added to get the ` resetTime ` for the client .
*
* @ return { Client } - The modified client that was passed in , to allow for chaining .
* /
resetClient ( client , now = Date . now ( ) ) {
client . totalHits = 0 ;
client . resetTime . setTime ( now + this . windowMs ) ;
return client ;
}
/ * *
* Retrieves or creates a client , given a key . Also ensures that the client being
* returned is in the ` current ` map .
*
* @ param key { string } - The key under which the client is ( or is to be ) stored .
*
* @ returns { Client } - The requested client .
* /
getClient ( key ) {
if ( this . current . has ( key ) )
return this . current . get ( key ) ;
let client ;
if ( this . previous . has ( key ) ) {
client = this . previous . get ( key ) ;
this . previous . delete ( key ) ;
} else {
client = { totalHits : 0 , resetTime : /* @__PURE__ */ new Date ( ) } ;
this . resetClient ( client ) ;
}
this . current . set ( key , client ) ;
return client ;
}
/ * *
* Move current clients to previous , create a new map for current .
*
* This function is called every ` windowMs ` .
* /
clearExpired ( ) {
this . previous = this . current ;
this . current = /* @__PURE__ */ new Map ( ) ;
2022-12-21 23:26:55 +00:00
}
2022-01-09 15:20:59 +00:00
} ;
// source/lib.ts
2024-04-22 11:47:34 +00:00
var isLegacyStore = ( store ) => (
// Check that `incr` exists but `increment` does not - store authors might want
// to keep both around for backwards compatibility.
typeof store . incr === "function" && typeof store . increment !== "function"
) ;
2022-01-09 15:20:59 +00:00
var promisifyStore = ( passedStore ) => {
if ( ! isLegacyStore ( passedStore ) ) {
return passedStore ;
}
const legacyStore = passedStore ;
class PromisifiedStore {
async increment ( key ) {
return new Promise ( ( resolve , reject ) => {
2022-12-21 23:26:55 +00:00
legacyStore . incr (
key ,
( error , totalHits , resetTime ) => {
if ( error )
reject ( error ) ;
resolve ( { totalHits , resetTime } ) ;
}
) ;
2022-01-09 15:20:59 +00:00
} ) ;
}
async decrement ( key ) {
2022-12-21 23:26:55 +00:00
return legacyStore . decrement ( key ) ;
2022-01-09 15:20:59 +00:00
}
async resetKey ( key ) {
2022-12-21 23:26:55 +00:00
return legacyStore . resetKey ( key ) ;
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
/* istanbul ignore next */
2022-01-09 15:20:59 +00:00
async resetAll ( ) {
if ( typeof legacyStore . resetAll === "function" )
2022-12-21 23:26:55 +00:00
return legacyStore . resetAll ( ) ;
2022-01-09 15:20:59 +00:00
}
}
return new PromisifiedStore ( ) ;
} ;
2024-04-22 11:47:34 +00:00
var getOptionsFromConfig = ( config ) => {
const { validations : validations2 , ... directlyPassableEntries } = config ;
return {
... directlyPassableEntries ,
validate : validations2 . enabled
} ;
} ;
var omitUndefinedOptions = ( passedOptions ) => {
const omittedOptions = { } ;
for ( const k of Object . keys ( passedOptions ) ) {
const key = k ;
if ( passedOptions [ key ] !== void 0 ) {
omittedOptions [ key ] = passedOptions [ key ] ;
}
}
return omittedOptions ;
} ;
2022-01-09 15:20:59 +00:00
var parseOptions = ( passedOptions ) => {
2022-12-21 23:26:55 +00:00
const notUndefinedOptions = omitUndefinedOptions ( passedOptions ) ;
2024-04-22 11:47:34 +00:00
const validations2 = getValidations ( notUndefinedOptions ? . validate ? ? true ) ;
validations2 . validationsConfig ( ) ;
validations2 . draftPolliHeaders (
// @ts-expect-error see the note above.
notUndefinedOptions . draft _polli _ratelimit _headers
) ;
validations2 . onLimitReached ( notUndefinedOptions . onLimitReached ) ;
let standardHeaders = notUndefinedOptions . standardHeaders ? ? false ;
if ( standardHeaders === true )
standardHeaders = "draft-6" ;
2022-12-21 23:26:55 +00:00
const config = {
2022-01-09 15:20:59 +00:00
windowMs : 60 * 1e3 ,
2024-04-22 11:47:34 +00:00
limit : passedOptions . max ? ? 5 ,
// `max` is deprecated, but support it anyways.
2022-01-09 15:20:59 +00:00
message : "Too many requests, please try again later." ,
statusCode : 429 ,
2024-04-22 11:47:34 +00:00
legacyHeaders : passedOptions . headers ? ? true ,
2022-01-09 15:20:59 +00:00
requestPropertyName : "rateLimit" ,
skipFailedRequests : false ,
skipSuccessfulRequests : false ,
requestWasSuccessful : ( _request , response ) => response . statusCode < 400 ,
skip : ( _request , _response ) => false ,
2022-12-21 23:26:55 +00:00
keyGenerator ( request , _response ) {
2024-04-22 11:47:34 +00:00
validations2 . ip ( request . ip ) ;
validations2 . trustProxy ( request ) ;
validations2 . xForwardedForHeader ( request ) ;
2022-01-09 15:20:59 +00:00
return request . ip ;
} ,
2022-12-21 23:26:55 +00:00
async handler ( request , response , _next , _optionsUsed ) {
response . status ( config . statusCode ) ;
const message = typeof config . message === "function" ? await config . message (
request ,
response
) : config . message ;
if ( ! response . writableEnded ) {
2024-04-22 11:47:34 +00:00
response . send ( message ) ;
2022-12-21 23:26:55 +00:00
}
2022-01-09 15:20:59 +00:00
} ,
2024-04-22 11:47:34 +00:00
// Allow the default options to be overriden by the options passed to the middleware.
2022-12-21 23:26:55 +00:00
... notUndefinedOptions ,
2024-04-22 11:47:34 +00:00
// `standardHeaders` is resolved into a draft version above, use that.
standardHeaders ,
// Note that this field is declared after the user's options are spread in,
// so that this field doesn't get overriden with an un-promisified store!
store : promisifyStore ( notUndefinedOptions . store ? ? new MemoryStore ( ) ) ,
// Print an error to the console if a few known misconfigurations are detected.
validations : validations2
2022-01-09 15:20:59 +00:00
} ;
2024-04-22 11:47:34 +00:00
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" ) {
2022-12-21 23:26:55 +00:00
throw new TypeError (
"An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
) ;
2022-01-09 15:20:59 +00:00
}
2022-12-21 23:26:55 +00:00
return config ;
2022-01-09 15:20:59 +00:00
} ;
var handleAsyncErrors = ( fn ) => async ( request , response , next ) => {
try {
await Promise . resolve ( fn ( request , response , next ) ) . catch ( next ) ;
} catch ( error ) {
next ( error ) ;
}
} ;
var rateLimit = ( passedOptions ) => {
2024-04-22 11:47:34 +00:00
const config = parseOptions ( passedOptions ? ? { } ) ;
const options = getOptionsFromConfig ( config ) ;
config . validations . creationStack ( ) ;
if ( typeof config . store . init === "function" )
config . store . init ( options ) ;
2022-12-21 23:26:55 +00:00
const middleware = handleAsyncErrors (
async ( request , response , next ) => {
2024-04-22 11:47:34 +00:00
const skip = await config . skip ( request , response ) ;
2022-12-21 23:26:55 +00:00
if ( skip ) {
next ( ) ;
return ;
2022-01-09 15:20:59 +00:00
}
2022-12-21 23:26:55 +00:00
const augmentedRequest = request ;
2024-04-22 11:47:34 +00:00
const key = await config . keyGenerator ( request , response ) ;
const { totalHits , resetTime } = await config . store . increment ( key ) ;
config . validations . positiveHits ( totalHits ) ;
config . validations . singleCount ( request , config . store , key ) ;
const retrieveLimit = typeof config . limit === "function" ? config . limit ( request , response ) : config . limit ;
const limit = await retrieveLimit ;
config . validations . limit ( limit ) ;
const info = {
limit ,
used : totalHits ,
remaining : Math . max ( limit - totalHits , 0 ) ,
2022-12-21 23:26:55 +00:00
resetTime
} ;
2024-04-22 11:47:34 +00:00
Object . defineProperty ( info , "current" , {
configurable : false ,
enumerable : false ,
value : totalHits
} ) ;
augmentedRequest [ config . requestPropertyName ] = info ;
if ( config . legacyHeaders && ! response . headersSent ) {
setLegacyHeaders ( response , info ) ;
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
if ( config . standardHeaders && ! response . headersSent ) {
if ( config . standardHeaders === "draft-6" ) {
setDraft6Headers ( response , info , config . windowMs ) ;
} else if ( config . standardHeaders === "draft-7" ) {
config . validations . headersResetTime ( info . resetTime ) ;
setDraft7Headers ( response , info , config . windowMs ) ;
2022-01-09 15:20:59 +00:00
}
}
2024-04-22 11:47:34 +00:00
if ( config . skipFailedRequests || config . skipSuccessfulRequests ) {
2022-12-21 23:26:55 +00:00
let decremented = false ;
const decrementKey = async ( ) => {
if ( ! decremented ) {
2024-04-22 11:47:34 +00:00
await config . store . decrement ( key ) ;
2022-12-21 23:26:55 +00:00
decremented = true ;
}
} ;
2024-04-22 11:47:34 +00:00
if ( config . skipFailedRequests ) {
2022-12-21 23:26:55 +00:00
response . on ( "finish" , async ( ) => {
2024-04-22 11:47:34 +00:00
if ( ! await config . requestWasSuccessful ( request , response ) )
2022-12-21 23:26:55 +00:00
await decrementKey ( ) ;
} ) ;
response . on ( "close" , async ( ) => {
if ( ! response . writableEnded )
await decrementKey ( ) ;
} ) ;
response . on ( "error" , async ( ) => {
2022-01-09 15:20:59 +00:00
await decrementKey ( ) ;
2022-12-21 23:26:55 +00:00
} ) ;
}
2024-04-22 11:47:34 +00:00
if ( config . skipSuccessfulRequests ) {
2022-12-21 23:26:55 +00:00
response . on ( "finish" , async ( ) => {
2024-04-22 11:47:34 +00:00
if ( await config . requestWasSuccessful ( request , response ) )
2022-12-21 23:26:55 +00:00
await decrementKey ( ) ;
} ) ;
}
2022-01-09 15:20:59 +00:00
}
2024-04-22 11:47:34 +00:00
config . validations . disable ( ) ;
if ( totalHits > limit ) {
if ( config . legacyHeaders || config . standardHeaders ) {
setRetryAfterHeader ( response , info , config . windowMs ) ;
2022-12-21 23:26:55 +00:00
}
2024-04-22 11:47:34 +00:00
config . handler ( request , response , next , options ) ;
2022-12-21 23:26:55 +00:00
return ;
}
next ( ) ;
2022-01-09 15:20:59 +00:00
}
2022-12-21 23:26:55 +00:00
) ;
2024-04-22 11:47:34 +00:00
const getThrowFn = ( ) => {
throw new Error ( "The current store does not support the get/getKey method" ) ;
} ;
middleware . resetKey = config . store . resetKey . bind ( config . store ) ;
middleware . getKey = typeof config . store . get === "function" ? config . store . get . bind ( config . store ) : getThrowFn ;
2022-01-09 15:20:59 +00:00
return middleware ;
} ;
var lib _default = rateLimit ;
export {
2022-12-21 23:26:55 +00:00
MemoryStore ,
2022-01-18 16:39:42 +00:00
lib _default as default ,
lib _default as rateLimit
2022-01-09 15:20:59 +00:00
} ;