First commit

This commit is contained in:
2025-12-25 11:16:59 +01:00
commit 0c5ca09a63
720 changed files with 329234 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
module.exports = class BlockedKeys {
constructor() {
this._keys = {}; // {'key': 1526279430331}
this._addedKeysAmount = 0;
}
collectExpired() {
const now = Date.now();
Object.keys(this._keys).forEach((key) => {
if (this._keys[key] <= now) {
delete this._keys[key];
}
});
this._addedKeysAmount = Object.keys(this._keys).length;
}
/**
* Add new blocked key
*
* @param key String
* @param sec Number
*/
add(key, sec) {
this.addMs(key, sec * 1000);
}
/**
* Add new blocked key for ms
*
* @param key String
* @param ms Number
*/
addMs(key, ms) {
this._keys[key] = Date.now() + ms;
this._addedKeysAmount++;
if (this._addedKeysAmount > 999) {
this.collectExpired();
}
}
/**
* 0 means not blocked
*
* @param key
* @returns {number}
*/
msBeforeExpire(key) {
const expire = this._keys[key];
if (expire && expire >= Date.now()) {
this.collectExpired();
const now = Date.now();
return expire >= now ? expire - now : 0;
}
return 0;
}
/**
* If key is not given, delete all data in memory
*
* @param {string|undefined} key
*/
delete(key) {
if (key) {
delete this._keys[key];
} else {
Object.keys(this._keys).forEach((key) => {
delete this._keys[key];
});
}
}
};

View File

@@ -0,0 +1,3 @@
const BlockedKeys = require('./BlockedKeys');
module.exports = BlockedKeys;

View File

@@ -0,0 +1,83 @@
const Record = require('./Record');
const RateLimiterRes = require('../../RateLimiterRes');
module.exports = class MemoryStorage {
constructor() {
/**
* @type {Object.<string, Record>}
* @private
*/
this._storage = {};
}
incrby(key, value, durationSec) {
if (this._storage[key]) {
const msBeforeExpires = this._storage[key].expiresAt
? this._storage[key].expiresAt.getTime() - new Date().getTime()
: -1;
if (msBeforeExpires !== 0) {
// Change value
this._storage[key].value = this._storage[key].value + value;
return new RateLimiterRes(0, msBeforeExpires, this._storage[key].value, false);
}
return this.set(key, value, durationSec);
}
return this.set(key, value, durationSec);
}
set(key, value, durationSec) {
const durationMs = durationSec * 1000;
if (this._storage[key] && this._storage[key].timeoutId) {
clearTimeout(this._storage[key].timeoutId);
}
this._storage[key] = new Record(
value,
durationMs > 0 ? new Date(Date.now() + durationMs) : null
);
if (durationMs > 0) {
this._storage[key].timeoutId = setTimeout(() => {
delete this._storage[key];
}, durationMs);
if (this._storage[key].timeoutId.unref) {
this._storage[key].timeoutId.unref();
}
}
return new RateLimiterRes(0, durationMs === 0 ? -1 : durationMs, this._storage[key].value, true);
}
/**
*
* @param key
* @returns {*}
*/
get(key) {
if (this._storage[key]) {
const msBeforeExpires = this._storage[key].expiresAt
? this._storage[key].expiresAt.getTime() - new Date().getTime()
: -1;
return new RateLimiterRes(0, msBeforeExpires, this._storage[key].value, false);
}
return null;
}
/**
*
* @param key
* @returns {boolean}
*/
delete(key) {
if (this._storage[key]) {
if (this._storage[key].timeoutId) {
clearTimeout(this._storage[key].timeoutId);
}
delete this._storage[key];
return true;
}
return false;
}
};

View File

@@ -0,0 +1,40 @@
module.exports = class Record {
/**
*
* @param value int
* @param expiresAt Date|int
* @param timeoutId
*/
constructor(value, expiresAt, timeoutId = null) {
this.value = value;
this.expiresAt = expiresAt;
this.timeoutId = timeoutId;
}
get value() {
return this._value;
}
set value(value) {
this._value = parseInt(value);
}
get expiresAt() {
return this._expiresAt;
}
set expiresAt(value) {
if (!(value instanceof Date) && Number.isInteger(value)) {
value = new Date(value);
}
this._expiresAt = value;
}
get timeoutId() {
return this._timeoutId;
}
set timeoutId(value) {
this._timeoutId = value;
}
};

View File

@@ -0,0 +1,3 @@
const MemoryStorage = require('./MemoryStorage');
module.exports = MemoryStorage;

View File

@@ -0,0 +1,13 @@
module.exports = class RateLimiterQueueError extends Error {
constructor(message, extra) {
super();
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
this.name = 'CustomError';
this.message = message;
if (extra) {
this.extra = extra;
}
}
};

View File

@@ -0,0 +1,9 @@
export class RateLimiterQueueError extends Error {
constructor(message?: string, extra?: string);
readonly name: string;
readonly message: string;
readonly extra: string;
}