| 125 | const cache = new Map<string, { readonly allowed: boolean; readonly expiresAtMs: number }>(); |
| 126 | |
| 127 | const writeCache = (organizationId: string, allowed: boolean, nowMs: number): void => { |
| 128 | if (cache.size >= BALANCE_CACHE_MAX_ENTRIES) { |
| 129 | for (const [key, entry] of cache) { |
| 130 | if (entry.expiresAtMs <= nowMs) cache.delete(key); |
| 131 | } |
| 132 | // Still saturated after dropping expired entries: reset rather than grow. |
| 133 | if (cache.size >= BALANCE_CACHE_MAX_ENTRIES) cache.clear(); |
| 134 | } |
| 135 | cache.set(organizationId, { allowed, expiresAtMs: nowMs + BALANCE_CACHE_TTL_MS }); |
| 136 | }; |
| 137 | |
| 138 | const toDecision = (organizationId: string, allowed: boolean): GateDecision => |
| 139 | allowed |