2025-03-24 22:56:10 +01:00

164 lines
4.9 KiB
JavaScript

"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;