Skip to main content
Glama
InMemoryCacheProvider.ts3.74 kB
/** * InMemoryCacheProvider.ts * * @semantic-intent Infrastructure adapter implementing ICacheProvider port * In-memory caching with TTL support for domain entities * * @observable-anchoring * - Uses Map for O(1) key-value lookup * - Stores expiration timestamps as observable markers * - Checks Date.now() for expiration (observable time) * * @intent-preservation * - TTL semantics preserved through expiration timestamps * - Cache entries maintain immutability of stored entities * - Expired entries treated as non-existent (semantic consistency) * * @semantic-over-structural * - Focuses on cache freshness semantics, not memory optimization * - Expiration based on semantic time intent, not access patterns * * @immutability-protection * - Stores values as-is (expects frozen entities) * - No mutation of cache state during reads */ import { ICacheProvider } from '../../application/ports/ICacheProvider'; /** * Cache entry with expiration metadata * * Observable properties: * - value: Cached domain entity * - expiresAt: Observable expiration timestamp (milliseconds since epoch) */ interface CacheEntry<T> { value: T; expiresAt: number; // Timestamp in milliseconds (Date.now()) } /** * In-memory cache provider with TTL support * * Semantic Intent: Fast, process-local caching for domain entities * Observable Anchoring: Expiration based on Date.now() comparison */ export class InMemoryCacheProvider implements ICacheProvider { private readonly cache: Map<string, CacheEntry<unknown>>; constructor() { this.cache = new Map(); Object.freeze(this); } /** * Get cached value by key * * Semantic: Returns undefined if expired (treats as non-existent) */ async get<T>(key: string): Promise<T | undefined> { const entry = this.cache.get(key); if (!entry) { return undefined; } // Observable: Check if expired based on current time const now = Date.now(); if (entry.expiresAt > 0 && now >= entry.expiresAt) { // Semantic: Expired entries are removed this.cache.delete(key); return undefined; } return entry.value as T; } /** * Set cache value with TTL * * Semantic: TTL of 0 means cache indefinitely (expiresAt = 0) */ async set<T>(key: string, value: T, ttl: number): Promise<void> { if (ttl < 0) { throw new Error('TTL cannot be negative'); } // Observable: Calculate expiration timestamp const expiresAt = ttl === 0 ? 0 : Date.now() + ttl * 1000; const entry: CacheEntry<T> = { value, expiresAt, }; this.cache.set(key, entry); } /** * Delete cached value by key * * Semantic: Idempotent - no error if key doesn't exist */ async delete(key: string): Promise<void> { this.cache.delete(key); } /** * Clear all cached values * * Semantic: Removes all cache entries */ async clear(): Promise<void> { this.cache.clear(); } /** * Check if key exists in cache (and not expired) * * Observable: Existence checked via get() which handles expiration */ async has(key: string): Promise<boolean> { const value = await this.get(key); return value !== undefined; } /** * Get cache size (number of entries, including expired) * * Observable: Map size is directly observable * Note: May include expired entries that haven't been accessed yet */ getSize(): number { return this.cache.size; } /** * Clean up expired entries * * Semantic: Removes all expired entries to free memory * Observable: Iterates through cache and checks expiration timestamps */ cleanup(): void { const now = Date.now(); for (const [key, entry] of this.cache.entries()) { if (entry.expiresAt > 0 && now >= entry.expiresAt) { this.cache.delete(key); } } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/semanticintent/semantic-d1-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server