splitwise / src /lib /server /auth /rate-limit.ts
assafvayner's picture
assafvayner HF Staff
fix(ui): shared safe-next validation, no lockout on success, logout origin check, tab/feed polish
b11153a
Raw History Blame Contribute Delete
1.18 kB
/** Above this many tracked keys, prune stale entries on the next `allow` before inserting. */
const MAX_TRACKED_KEYS = 10_000;
/** Sliding-window limiter: at most `max` allowed calls per key within `windowMs`. In-memory only. */
export class RateLimiter {
private hits = new Map<string, number[]>();
constructor(
private readonly max: number,
private readonly windowMs: number,
private readonly now: () => number = Date.now
) {}
/** Number of keys currently tracked. Exposed for tests. */
get size(): number {
return this.hits.size;
}
allow(key: string): boolean {
const t = this.now();
const recent = (this.hits.get(key) ?? []).filter((h) => t - h < this.windowMs);
if (recent.length >= this.max) {
if (recent.length === 0) this.hits.delete(key);
else this.hits.set(key, recent);
return false;
}
if (this.hits.size > MAX_TRACKED_KEYS) {
for (const [k, hs] of this.hits) {
const mostRecent = hs.length > 0 ? Math.max(...hs) : -Infinity;
if (t - mostRecent >= this.windowMs) this.hits.delete(k);
}
}
recent.push(t);
this.hits.set(key, recent);
return true;
}
reset(key: string): void {
this.hits.delete(key);
}
}