{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/is-object.ts","../src/constants.ts","../src/store/utils.ts","../src/store/memory.ts","../src/utils/options.ts","../src/request.ts","../src/handler.ts","../src/module.ts","../src/index.ts"],"sourcesContent":["export function isObject(item: unknown) : item is Record<string, any> {\n    return (\n        !!item &&\n        typeof item === 'object' &&\n        !Array.isArray(item)\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.Timeout;\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 { IAppEvent } from 'routup';\nimport { getRequestIP } from 'routup';\nimport { RETRY_AGAIN_MESSAGE } from '../constants';\nimport { MemoryStore } from '../store';\nimport type { Options, OptionsInput, ValueDeterminingMiddleware } from '../type';\n\nexport function normalizeHandlerOptions(input: OptionsInput = {}) : Options {\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: (_event: IAppEvent, response: Response): boolean => response.status < 400,\n        skip: (_event: IAppEvent): boolean => false,\n        keyGenerator: (event: IAppEvent): string => getRequestIP(event, { trustProxy: true }) || '127.0.0.1',\n        async handler(\n            event: IAppEvent,\n            _optionsUsed: Options,\n        ): Promise<unknown> {\n            const message: unknown = typeof options.message === 'function' ?\n                await (options.message as ValueDeterminingMiddleware<any>)(\n                    event,\n                ) :\n                options.message;\n\n            event.response.status = options.statusCode;\n\n            return message ?? 'Too many requests, please try again later.';\n        },\n        ...input,\n\n        store: input.store || new MemoryStore(),\n    };\n\n    return options;\n}\n","import type { IAppEvent } from 'routup';\nimport type { RateLimitInfo } from './type';\nimport { isObject } from './utils';\n\nconst RateLimitSymbol = Symbol.for('@routup/rate-limit:ReqRateLimit');\n\nexport function useRequestRateLimitInfo(event: IAppEvent) : Partial<RateLimitInfo>;\nexport function useRequestRateLimitInfo<K extends keyof RateLimitInfo>(event: IAppEvent, key: K) : RateLimitInfo[K] | undefined;\nexport function useRequestRateLimitInfo(event: IAppEvent, key?: string) {\n    if (RateLimitSymbol in event.store) {\n        if (typeof key === 'string') {\n            return (event.store[RateLimitSymbol] as Record<string, unknown>)[key];\n        }\n\n        return event.store[RateLimitSymbol];\n    }\n\n    return typeof key === 'string' ?\n        undefined :\n        {};\n}\n\nexport function setRequestRateLimitInfo<K extends keyof RateLimitInfo>(\n    event: IAppEvent,\n    key: K,\n    value: RateLimitInfo[K],\n) : void;\nexport function setRequestRateLimitInfo(event: IAppEvent, record: RateLimitInfo) : void;\nexport function setRequestRateLimitInfo(event: IAppEvent, key: RateLimitInfo | string, value?: unknown) : void {\n    const existing = RateLimitSymbol in event.store ?\n        event.store[RateLimitSymbol] as Record<string, unknown> :\n        undefined;\n\n    if (isObject(key)) {\n        event.store[RateLimitSymbol] = existing ? { ...existing, ...key } : key;\n    } else if (existing) {\n        existing[key] = value;\n    } else {\n        event.store[RateLimitSymbol] = { [key]: value };\n    }\n}\n","import { HeaderName, defineCoreHandler } from 'routup';\nimport { setRequestRateLimitInfo } from './request';\nimport type { OptionsInput } from './type';\nimport { normalizeHandlerOptions } from './utils';\n\nexport function createHandler(input?: OptionsInput) {\n    const options = normalizeHandlerOptions({ ...(input || {}) });\n\n    if (typeof options.store.init === 'function') {\n        options.store.init(options);\n    }\n\n    return defineCoreHandler(async (event) => {\n        const skip = await options.skip(event);\n        if (skip) {\n            return event.next();\n        }\n\n        const key = await options.keyGenerator(event);\n\n        const { totalHits, resetTime } = await options.store.increment(key);\n\n        const retrieveQuota = typeof options.max === 'function' ?\n            options.max(event) :\n            options.max;\n\n        const maxHits = await retrieveQuota;\n\n        setRequestRateLimitInfo(event, {\n            limit: maxHits,\n            current: totalHits,\n            remaining: Math.max(maxHits - totalHits, 0),\n            resetTime,\n        });\n\n        event.response.headers.set(HeaderName.RATE_LIMIT_LIMIT, String(maxHits));\n        event.response.headers.set(\n            HeaderName.RATE_LIMIT_REMAINING,\n            String(Math.max(maxHits - totalHits, 0)),\n        );\n\n        if (resetTime) {\n            const deltaSeconds = Math.ceil(\n                (resetTime.getTime() - Date.now()) / 1000,\n            );\n            event.response.headers.set(HeaderName.RATE_LIMIT_RESET, String(Math.max(0, deltaSeconds)));\n        }\n\n        if (\n            maxHits &&\n            totalHits > maxHits\n        ) {\n            event.response.headers.set(\n                HeaderName.RETRY_AFTER,\n                String(Math.ceil(options.windowMs / 1000)),\n            );\n\n            return options.handler(event, options);\n        }\n\n        const response = await event.next();\n\n        if (\n            options.skipFailedRequest ||\n            options.skipSuccessfulRequest\n        ) {\n            const wasSuccessful = response ?\n                options.requestWasSuccessful(event, response) :\n                false;\n\n            if (\n                (options.skipFailedRequest && !wasSuccessful) ||\n                (options.skipSuccessfulRequest && wasSuccessful)\n            ) {\n                await options.store.decrement(key);\n                setRequestRateLimitInfo(event, 'remaining', Math.max(maxHits - totalHits + 1, 0));\n            }\n        }\n\n        return response;\n    });\n}\n","import type { Handler } from 'routup';\nimport { createHandler } from './handler';\nimport type { OptionsInput } from './type';\n\nexport function rateLimit(options?: OptionsInput) : Handler {\n    return createHandler(options);\n}\n","import { rateLimit } from './module';\n\nexport * from './constants';\nexport * from './handler';\nexport * from './module';\nexport * from './request';\nexport * from './store';\nexport * from './type';\nexport * from './utils';\nexport default rateLimit;\n"],"mappings":"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"}