{"version":3,"file":"index.cjs","sources":["../src/request.ts","../src/constants.ts","../src/store/utils.ts","../src/store/memory.ts","../src/utils/options.ts","../src/handler.ts","../src/module.ts"],"sourcesContent":["import type { Request } from 'routup';\nimport type { RateLimitInfo } from './type';\n\nconst symbol = Symbol.for('ReqRateLimit');\nexport function useRequestRateLimitInfo(req: Request) : RateLimitInfo;\nexport function useRequestRateLimitInfo<K extends keyof RateLimitInfo>(req: Request, key: K) : RateLimitInfo[K];\nexport function useRequestRateLimitInfo(req: Request, key?: string) {\n    if (symbol in req) {\n        if (typeof key === 'string') {\n            return (req as any)[symbol][key];\n        }\n\n        return (req as any)[symbol];\n    }\n\n    return {};\n}\n\nexport function setRequestRateLimitInfo<K extends keyof RateLimitInfo>(\n    req: Request,\n    key: K,\n    value: RateLimitInfo[K]\n) : void;\nexport function setRequestRateLimitInfo(req: Request, record: RateLimitInfo) : void;\nexport function setRequestRateLimitInfo(req: Request, key: RateLimitInfo | string, value?: unknown) : void {\n    if (symbol in req) {\n        if (typeof key === 'object') {\n            (req as any)[symbol] = key;\n        } else {\n            (req as any)[symbol][key] = value;\n        }\n\n        return;\n    }\n\n    if (typeof key === 'object') {\n        (req as any)[symbol] = key;\n        return;\n    }\n\n    (req as any)[symbol] = {\n        [key]: value,\n    };\n}\n","export const RETRY_AGAIN_MESSAGE = 'Too many requests, please try again later.';\n","export function calculateNextResetTime(windowMs: number): Date {\n    const resetTime = new Date();\n    resetTime.setMilliseconds(resetTime.getMilliseconds() + windowMs);\n    return resetTime;\n}\n","import type { IncrementResponse, Options } from '../type';\nimport type { Store } from './type';\nimport { calculateNextResetTime } from './utils';\n\nexport class MemoryStore implements Store {\n    /**\n     * The duration of time before which all hit counts are reset (in milliseconds).\n     */\n    windowMs!: number;\n\n    /**\n     * The map that stores the number of hits for each client in memory.\n     */\n    hits!: {\n        [key: string]: number | undefined\n    };\n\n    /**\n     * The time at which all hit counts will be reset.\n     */\n    resetTime!: Date;\n\n    /**\n     * Reference to the active timer.\n     */\n    interval?: NodeJS.Timer;\n\n    /**\n     * Method that initializes the store.\n     *\n     * @param options {Options} - The options used to setup the middleware.\n     */\n    init(options: Options): void {\n        // Get the duration of a window from the options.\n        this.windowMs = options.windowMs;\n\n        // Then calculate the reset time using that.\n        this.resetTime = calculateNextResetTime(this.windowMs);\n\n        // Initialise the hit counter map.\n        this.hits = {};\n\n        // Reset hit counts for ALL clients every `windowMs` - this will also\n        // re-calculate the `resetTime`\n        this.interval = setInterval(async () => {\n            await this.resetAll();\n        }, this.windowMs);\n\n        // Cleaning up the interval will be taken care of by the `shutdown` method.\n        if (this.interval.unref) this.interval.unref();\n    }\n\n    /**\n     * Method to increment a client's hit counter.\n     *\n     * @param key {string} - The identifier for a client.\n     *\n     * @returns {IncrementResponse} - The number of hits and reset time for that client.\n     *\n     * @public\n     */\n    async increment(key: string): Promise<IncrementResponse> {\n        const totalHits = (this.hits[key] ?? 0) + 1;\n        this.hits[key] = totalHits;\n\n        return {\n            totalHits,\n            resetTime: this.resetTime,\n        };\n    }\n\n    /**\n     * Method to decrement a client's hit counter.\n     *\n     * @param key {string} - The identifier for a client.\n     *\n     * @public\n     */\n    async decrement(key: string): Promise<void> {\n        const current = this.hits[key];\n\n        if (current) this.hits[key] = current - 1;\n    }\n\n    /**\n     * Method to reset a client's hit counter.\n     *\n     * @param key {string} - The identifier for a client.\n     *\n     * @public\n     */\n    async reset(key: string): Promise<void> {\n        delete this.hits[key];\n    }\n\n    /**\n     * Method to reset everyone's hit counter.\n     *\n     * @public\n     */\n    /* istanbul ignore next */\n    async resetAll(): Promise<void> {\n        this.hits = {};\n        this.resetTime = calculateNextResetTime(this.windowMs);\n    }\n}\n","import type { Next, Request, Response } from 'routup';\nimport { getRequestIP, send } from 'routup';\nimport { RETRY_AGAIN_MESSAGE } from '../constants';\nimport { MemoryStore } from '../store';\nimport type { Options, OptionsInput, ValueDeterminingMiddleware } from '../type';\n\nexport function buildHandlerOptions(input?: OptionsInput) : Options {\n    input = input || {};\n\n    const options : Options = {\n        windowMs: 60 * 1000,\n        max: 5,\n        message: RETRY_AGAIN_MESSAGE,\n        statusCode: 429,\n        skipFailedRequest: false,\n        skipSuccessfulRequest: false,\n        requestWasSuccessful: (request: Request, response: Response): boolean => response.statusCode < 400,\n        skip: (_request: Request, _response: Response): boolean => false,\n        keyGenerator: (request: Request, _response: Response): string => getRequestIP(request, { trustProxy: true }),\n        async handler(\n            request: Request,\n            response: Response,\n            _next: Next,\n            _optionsUsed: Options,\n        ): Promise<void> {\n            // Set the response status code\n            response.statusCode = options.statusCode;\n            // Call the `message` if it is a function.\n            const message: unknown = typeof options.message === 'function' ?\n                await (options.message as ValueDeterminingMiddleware<any>)(\n                    request,\n                    response,\n                ) :\n                options.message;\n\n            // Send the response if writable.\n            if (!response.writableEnded) {\n                send(response, message ?? 'Too many requests, please try again later.');\n            }\n        },\n        ...input,\n\n        store: input.store || new MemoryStore(),\n    };\n\n    return options;\n}\n","import { HeaderName, coreHandler } from 'routup';\nimport { setRequestRateLimitInfo, useRequestRateLimitInfo } from './request';\nimport type { OptionsInput } from './type';\nimport { buildHandlerOptions } from './utils';\n\nexport function createHandler(input?: OptionsInput) {\n    const options = buildHandlerOptions({\n        ...(input || {}),\n    });\n\n    if (typeof options.store.init === 'function') {\n        options.store.init(options);\n    }\n\n    return coreHandler(async (req, res, next) => {\n        const skip = await options.skip(req, res);\n        if (skip) {\n            next();\n            return;\n        }\n\n        const key = await options.keyGenerator(req, res);\n\n        const { totalHits, resetTime } = await options.store.increment(key);\n\n        const retrieveQuota = typeof options.max === 'function' ?\n            options.max(req, res) :\n            options.max;\n\n        const maxHits = await retrieveQuota;\n\n        setRequestRateLimitInfo(req, {\n            limit: maxHits,\n            current: totalHits,\n            remaining: Math.max(maxHits - totalHits, 0),\n            resetTime,\n        });\n\n        if (!res.headersSent) {\n            res.setHeader(HeaderName.RATE_LIMIT_LIMIT, maxHits);\n            res.setHeader(\n                HeaderName.RATE_LIMIT_REMAINING,\n                useRequestRateLimitInfo(req, 'remaining'),\n            );\n\n            if (resetTime) {\n                const deltaSeconds = Math.ceil(\n                    (resetTime.getTime() - Date.now()) / 1000,\n                );\n                res.setHeader(HeaderName.RATE_LIMIT_RESET, Math.max(0, deltaSeconds));\n            }\n        }\n\n        if (\n            options.skipFailedRequest ||\n            options.skipSuccessfulRequest\n        ) {\n            let decremented = false;\n            const decrementKey = async () => {\n                if (!decremented) {\n                    await options.store.decrement(key);\n                    decremented = true;\n\n                    setRequestRateLimitInfo(req, 'remaining', Math.max(maxHits - totalHits - 1, 0));\n                }\n            };\n\n            if (options.skipFailedRequest) {\n                res.on('finish', async () => {\n                    if (!options.requestWasSuccessful(req, res)) {\n                        await decrementKey();\n                    }\n                });\n\n                res.on('close', async () => {\n                    if (!res.writableEnded) {\n                        await decrementKey();\n                    }\n                });\n\n                res.on('error', async () => {\n                    await decrementKey();\n                });\n            }\n\n            if (options.skipSuccessfulRequest) {\n                res.on('finish', async () => {\n                    if (options.requestWasSuccessful(req, res)) {\n                        await decrementKey();\n                    }\n                });\n            }\n        }\n\n        if (\n            maxHits &&\n            totalHits > maxHits\n        ) {\n            if (!res.headersSent) {\n                res.setHeader(\n                    HeaderName.RETRY_AFTER,\n                    Math.ceil(options.windowMs / 1000),\n                );\n            }\n\n            options.handler(req, res, next, options);\n            return;\n        }\n\n        next();\n    });\n}\n","import type { Plugin } from 'routup';\nimport { createHandler } from './handler';\nimport type { OptionsInput } from './type';\n\nexport function rateLimit(options?: OptionsInput) : Plugin {\n    return {\n        name: 'rateLimit',\n        install: (router) => {\n            router.use(createHandler(options));\n        },\n    };\n}\n"],"names":["symbol","Symbol","for","useRequestRateLimitInfo","req","key","setRequestRateLimitInfo","value","RETRY_AGAIN_MESSAGE","calculateNextResetTime","windowMs","resetTime","Date","setMilliseconds","getMilliseconds","MemoryStore","init","options","hits","interval","setInterval","resetAll","unref","increment","totalHits","decrement","current","reset","buildHandlerOptions","input","max","message","statusCode","skipFailedRequest","skipSuccessfulRequest","requestWasSuccessful","request","response","skip","_request","_response","keyGenerator","getRequestIP","trustProxy","handler","_next","_optionsUsed","writableEnded","send","store","createHandler","coreHandler","res","next","retrieveQuota","maxHits","limit","remaining","Math","headersSent","setHeader","HeaderName","RATE_LIMIT_LIMIT","RATE_LIMIT_REMAINING","deltaSeconds","ceil","getTime","now","RATE_LIMIT_RESET","decremented","decrementKey","on","RETRY_AFTER","rateLimit","name","install","router","use"],"mappings":"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"}