UNPKG

rate-keeper

Version:

A lightweight utility for easily adding rate limiting to functions, ideal for preventing API rate limit violations.

88 lines 3.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DropPolicy = void 0; exports.default = RateKeeper; const globalRateData = new Map(); /** * @enum {DropPolicy} Defines the behavior of the queue when the maximum size has been reached. */ var DropPolicy; (function (DropPolicy) { DropPolicy[DropPolicy["Reject"] = 0] = "Reject"; DropPolicy[DropPolicy["DropOldest"] = 1] = "DropOldest"; })(DropPolicy || (exports.DropPolicy = DropPolicy = {})); class LimitData { constructor(settings) { this.queue = []; this.timer = null; this.settings = settings; } } function getRateData(settings) { const { id } = settings; if (globalRateData.has(id)) { return globalRateData.get(id); } else { const newLimitData = new LimitData(settings); globalRateData.set(id, newLimitData); return newLimitData; } } /** * @param {(...args: Args) => Result} action The action to be rate-limited. * @param {number} rateLimit The minimum interval in milliseconds between each execution. * @param {QueueSettings} settings Optional. Queue settings for rate limiting and execution. * @returns {(...args: Args) => CancelablePromise<Result>} An asynchronous function that executes the action and returns a promise with the result and a cancel method. */ function RateKeeper(action, rateLimit, settings = { id: 0 }) { const limitData = settings.id === 0 ? new LimitData(settings) : getRateData(settings); function processQueue() { const next = limitData.queue.shift(); if (next) { next.action(); } if (limitData.queue.length === 0 && limitData.timer !== null) { clearInterval(limitData.timer); limitData.timer = null; } } function publicFunc(...args) { var _a; const { maxQueueSize, dropPolicy } = limitData.settings; let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); const actionEntry = { action: () => resolve(action(...args)), reject: (reason) => { reject(reason); }, id: settings.id }; promise.cancel = (reason) => { const index = limitData.queue.indexOf(actionEntry); if (index !== -1) { limitData.queue.splice(index, 1); actionEntry.reject(reason || new Error("Cancelled by user.")); } }; if (maxQueueSize !== undefined && limitData.queue.length >= maxQueueSize) { if (dropPolicy === DropPolicy.Reject) { return Promise.reject(new Error("Queue is at max capacity.")); } else if (dropPolicy === DropPolicy.DropOldest) { (_a = limitData.queue.shift()) === null || _a === void 0 ? void 0 : _a.reject(new Error("Queue is at max capacity.")); } } limitData.queue.push(actionEntry); if (limitData.timer === null) { processQueue(); limitData.timer = setInterval(processQueue, rateLimit); } return promise; } return publicFunc; } //# sourceMappingURL=index.js.map