limiter.ts (1015B)
1 import { Ephemeral } from "#core/domain/ephemeral.ts"; 2 import { DomainError } from "#core/domain/error.ts"; 3 4 export class ExceedingLimit extends DomainError { 5 constructor(readonly delay: number) { 6 super(`Exceeding limit`); 7 } 8 } 9 10 export class Limiter { 11 private _counter: Ephemeral<number>; 12 13 constructor( 14 readonly limit: number, 15 readonly ttl: number, 16 count: number = 0, 17 expire: number = 0, 18 ) { 19 this._counter = new Ephemeral(count, expire); 20 } 21 22 get count(): number { 23 return this._counter.orElse(0); 24 } 25 26 get expire(): number { 27 return this._counter.expire; 28 } 29 30 get delay() { 31 if (this.remaining > 0) return 0; 32 return this._counter.ttl; 33 } 34 35 get remaining(): number { 36 return Math.max(this.limit - this.count, 0); 37 } 38 39 increment(): void { 40 if (this.remaining === 0) { 41 throw new ExceedingLimit(this.delay); 42 } 43 const ttl = this._counter.ttl; 44 this._counter = Ephemeral.of( 45 this.count + 1, 46 ttl > 0 ? ttl : this.ttl, 47 ); 48 } 49 }