/** * Jeton kovası (token bucket) hız sınırlayıcı. Saat dışarıdan verilir (test edilebilirlik). * Kova en fazla `capacity` jeton tutar ve saniyede `refillPerSec` jeton dolar. */ class RateLimiter { constructor({ capacity, refillPerSec, now = () => Date.now() }) { if (!(capacity > 0)) throw new RangeError('capacity pozitif olmalı'); if (!(refillPerSec >= 0)) throw new RangeError('refillPerSec negatif olamaz'); this.capacity = capacity; this.refillPerSec = refillPerSec; this.now = now; this.tokens = capacity; this.last = now(); } _refill() { const t = this.now(); const elapsed = (t - this.last) / 1000; if (elapsed > 0) { this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillPerSec); this.last = t; } } /** Yeterli jeton varsa harcar ve true döner; yoksa hiçbir şey harcamadan false. */ tryRemove(cost = 1) { if (cost <= 0) throw new RangeError('cost pozitif olmalı'); this._refill(); if (this.tokens >= cost) { this.tokens -= cost; return true; } return false; } /** Bir sonraki `cost` jetonluk istek için beklenmesi gereken süre (ms, yukarı yuvarlanmış). */ waitTime(cost = 1) { this._refill(); if (this.tokens >= cost) return 0; if (this.refillPerSec === 0) return Infinity; return Math.ceil(((cost - this.tokens) / this.refillPerSec) * 1000); } } module.exports = { RateLimiter };