ns-ui / diff-unified-viewer

Pull request #482 — rate limiter

Left-rail markers stand in for red/green: a solid bar for additions, a hairline hatch for deletions, both derived from the same ink. Any line can carry a widget — an agent's review note, a teammate's comment, or a suggested edit with its own controls.

src/lib/rate-limiter.ts+9 5
Unified view
diff --git a/src/lib/rate-limiter.ts b/src/lib/rate-limiter.ts
index 4b1f9a2..7c3e881 100644
src/lib/rate-limiter.ts
@@ -1,17 +1,21 @@ export interface RateLimiterOptions {
11export interface RateLimiterOptions {
22 windowMs: number;
33 max: number;
4 keyPrefix?: string;

Agent review

Consider documenting this with a JSDoc comment, since it changes the public options shape callers pass in.
45}
56 
67export class RateLimiter {
78 private hits = new Map<string, number[]>();
9 private readonly prefix: string;
810 
911 constructor(private options: RateLimiterOptions) {
12 this.prefix = options.keyPrefix ?? "rl";
1013 }
1114 
12 check(key: string): boolean {

Comment · @priya

Renaming the param to rawKey here reads well, but check callers of check() outside this file too.
13 const now = Date.now();
14 const bucket = this.hits.get(key) ?? [];
15 check(rawKey: string): boolean {
16 const key = `${this.prefix}:${rawKey}`;

Suggested edit

Guard against an empty rawKey so the derived key can never collide with the bare prefix.

17 const now = Date.now();
18 const bucket = this.hits.get(key) ?? [];
1519 const cutoff = now - this.options.windowMs;
1620 const recent = bucket.filter((t) => t > cutoff);
1721 
@@ -18,7 +22,7 @@ export class RateLimiter {
1822 return true;
1923 }
2024 
21 reset(key: string): void {
22 this.hits.delete(key);
25 reset(rawKey: string): void {
26 this.hits.delete(`${this.prefix}:${rawKey}`);
2327 }
2428}