@plinthjs/rate-limiter
v0.1.0
Published
Mason rate limiter: attempts, decay, lockout (the Illuminate\Cache\RateLimiter equivalent).
Maintainers
Readme
@plinthjs/rate-limiter
Mason's rate limiter — the Illuminate\Cache\RateLimiter equivalent. Tracks attempt counts per
string key inside a decay window, reports lockout, and resets automatically once the window passes.
Time is injected ({ now }, epoch ms) so window expiry is deterministic in tests.
Usage
import { RateLimiter, Limit } from '@plinthjs/rate-limiter'
const limiter = new RateLimiter() // production reads Date.now
// Count attempts within a 60s window.
limiter.hit('login|127.0.0.1') // => 1
limiter.hit('login|127.0.0.1') // => 2
limiter.attempts('login|127.0.0.1') // => 2
limiter.tooManyAttempts('login|127.0.0.1', 5) // => false
limiter.remaining('login|127.0.0.1', 5) // => 3
limiter.availableIn('login|127.0.0.1') // => seconds until reset
// Run work only while under the limit (returns false when locked out).
const result = await limiter.attempt('send-mail', 3, () => sendMail(), 60)
limiter.clear('login|127.0.0.1') // reset attempts + lockoutNamed limiters
limiter.for('uploads', () => Limit.perHour(100))
const limit = limiter.limiter('uploads') // => Limit { maxAttempts: 100, decaySeconds: 3600 }
Limit.perMinute(60) // 60 / minute
Limit.perHour(1000) // 1000 / hour
Limit.perDay(5000) // 5000 / day
Limit.none() // unlimitedDeterministic clock (tests)
const clock = { at: 1_000_000, now: () => clock.at }
const limiter = new RateLimiter({ now: clock.now })
limiter.hit('k', 60)
clock.at += 60_000 // advance past the window
limiter.attempts('k') // => 0 (window elapsed)