"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RateLimit = exports.Sema = void 0; const events_1 = __importDefault(require("events")); function arrayMove(src, srcIndex, dst, dstIndex, len) { for (let j = 0; j < len; ++j) { dst[j + dstIndex] = src[j + srcIndex]; src[j + srcIndex] = void 0; } } function pow2AtLeast(n) { n = n >>> 0; n = n - 1; n = n | (n >> 1); n = n | (n >> 2); n = n | (n >> 4); n = n | (n >> 8); n = n | (n >> 16); return n + 1; } function getCapacity(capacity) { return pow2AtLeast(Math.min(Math.max(16, capacity), 1073741824)); } // Deque is based on https://github.com/petkaantonov/deque/blob/master/js/deque.js // Released under the MIT License: https://github.com/petkaantonov/deque/blob/6ef4b6400ad3ba82853fdcc6531a38eb4f78c18c/LICENSE class Deque { constructor(capacity) { this._capacity = getCapacity(capacity); this._length = 0; this._front = 0; this.arr = []; } push(item) { const length = this._length; this.checkCapacity(length + 1); const i = (this._front + length) & (this._capacity - 1); this.arr[i] = item; this._length = length + 1; return length + 1; } pop() { const length = this._length; if (length === 0) { return void 0; } const i = (this._front + length - 1) & (this._capacity - 1); const ret = this.arr[i]; this.arr[i] = void 0; this._length = length - 1; return ret; } shift() { const length = this._length; if (length === 0) { return void 0; } const front = this._front; const ret = this.arr[front]; this.arr[front] = void 0; this._front = (front + 1) & (this._capacity - 1); this._length = length - 1; return ret; } get length() { return this._length; } checkCapacity(size) { if (this._capacity < size) { this.resizeTo(getCapacity(this._capacity * 1.5 + 16)); } } resizeTo(capacity) { const oldCapacity = this._capacity; this._capacity = capacity; const front = this._front; const length = this._length; if (front + length > oldCapacity) { const moveItemsCount = (front + length) & (oldCapacity - 1); arrayMove(this.arr, 0, this.arr, oldCapacity, moveItemsCount); } } } class ReleaseEmitter extends events_1.default { } function isFn(x) { return typeof x === 'function'; } function defaultInit() { return '1'; } class Sema { constructor(nr, { initFn = defaultInit, pauseFn, resumeFn, capacity = 10, } = {}) { if (isFn(pauseFn) !== isFn(resumeFn)) { throw new Error('pauseFn and resumeFn must be both set for pausing'); } this.nrTokens = nr; this.free = new Deque(nr); this.waiting = new Deque(capacity); this.releaseEmitter = new ReleaseEmitter(); this.noTokens = initFn === defaultInit; this.pauseFn = pauseFn; this.resumeFn = resumeFn; this.paused = false; this.releaseEmitter.on('release', (token) => { const p = this.waiting.shift(); if (p) { p.resolve(token); } else { if (this.resumeFn && this.paused) { this.paused = false; this.resumeFn(); } this.free.push(token); } }); for (let i = 0; i < nr; i++) { this.free.push(initFn()); } } tryAcquire() { return this.free.pop(); } async acquire() { let token = this.tryAcquire(); if (token !== void 0) { return token; } return new Promise((resolve, reject) => { if (this.pauseFn && !this.paused) { this.paused = true; this.pauseFn(); } this.waiting.push({ resolve, reject }); }); } release(token) { this.releaseEmitter.emit('release', this.noTokens ? '1' : token); } drain() { const a = new Array(this.nrTokens); for (let i = 0; i < this.nrTokens; i++) { a[i] = this.acquire(); } return Promise.all(a); } nrWaiting() { return this.waiting.length; } } exports.Sema = Sema; function RateLimit(rps, { timeUnit = 1000, uniformDistribution = false, } = {}) { const sema = new Sema(uniformDistribution ? 1 : rps); const delay = uniformDistribution ? timeUnit / rps : timeUnit; return async function rl() { await sema.acquire(); setTimeout(() => sema.release(), delay); }; } exports.RateLimit = RateLimit;