@dbx-tools/email 0.6.44 → 0.6.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,59 +0,0 @@
1
- /**
2
- * A small in-memory fixed-window rate limiter for the email-OTP gate.
3
- *
4
- * Keyed by an arbitrary string (an email address or a client IP). Each key gets
5
- * `max` hits per `windowMs`; the window resets on first use after it elapses.
6
- * `hit()` returns whether the call is allowed and, when not, how many seconds
7
- * until the window resets so a caller can surface a cooldown.
8
- *
9
- * In-memory is intentional and sufficient for a single-app-instance gate: an
10
- * app behind a portr tunnel serves from one process. It is NOT a distributed
11
- * limiter; a multi-replica deployment would need shared state. Entries are
12
- * pruned lazily on access, so an idle key costs nothing after its window.
13
- *
14
- * @module
15
- */
16
-
17
- interface Window {
18
- count: number;
19
- resetAt: number;
20
- }
21
-
22
- /** A fixed-window rate limiter over string keys. */
23
- export class RateLimiter {
24
- private readonly windows = new Map<string, Window>();
25
-
26
- constructor(
27
- private readonly max: number,
28
- private readonly windowMs: number,
29
- ) {}
30
-
31
- /**
32
- * Record a hit for `key`. Returns `{ allowed }`, plus `retryAfter` (seconds)
33
- * when the limit is exceeded. A limit of `<= 0` disables limiting (always
34
- * allowed), which lets a config turn it off without special-casing callers.
35
- */
36
- hit(key: string, now: number = Date.now()): { allowed: boolean; retryAfter?: number } {
37
- if (this.max <= 0) return { allowed: true };
38
- const existing = this.windows.get(key);
39
- if (!existing || now >= existing.resetAt) {
40
- this.windows.set(key, { count: 1, resetAt: now + this.windowMs });
41
- return { allowed: true };
42
- }
43
- if (existing.count < this.max) {
44
- existing.count += 1;
45
- return { allowed: true };
46
- }
47
- return { allowed: false, retryAfter: Math.ceil((existing.resetAt - now) / 1000) };
48
- }
49
-
50
- /** Forget a key (e.g. clear a caller's window after a successful verify). */
51
- reset(key: string): void {
52
- this.windows.delete(key);
53
- }
54
-
55
- /** Drop every window (tests). */
56
- clear(): void {
57
- this.windows.clear();
58
- }
59
- }