import { LRUCache as LRU } from 'lru-cache' import { CallRequestDTO } from '../CallRequestDTO' import { CallResponseDTO } from '../CallResponseDTO' import { Cache } from './Cache' import { makeCallRequestKey } from '../utils/cache-utils' import { GetDebugLogger } from '../utils/debug-logger' const debug = GetDebugLogger('rpc:InMemoryCache') interface InMemoryCacheOptions { cacheMaxSize?: number cacheMaxAgeMs?: number } export class InMemoryCache implements Cache { static DEFAULT_CACHE_MAX_AGE_MS = 1000 * 60 * 15 static DEFAULT_CACHE_MAX_SIZE = 200 private cachedResponseByParams: LRU | undefined constructor(cacheOptions?: InMemoryCacheOptions) { this.cachedResponseByParams = new LRU({ max: cacheOptions?.cacheMaxSize || InMemoryCache.DEFAULT_CACHE_MAX_SIZE, ttl: cacheOptions?.cacheMaxAgeMs || InMemoryCache.DEFAULT_CACHE_MAX_AGE_MS, }) } clearCache = (request?: CallRequestDTO): void => { debug('clearCache') if (request) { this.cachedResponseByParams?.delete(makeCallRequestKey(request)) return } this.cachedResponseByParams?.clear() } getCachedResponse = ( request: CallRequestDTO, ): CallResponseDTO | undefined => { debug('getCachedResponse, key: ', request) let cachedResponse = this.cachedResponseByParams?.get( makeCallRequestKey(request), ) if (typeof cachedResponse === 'string') { cachedResponse = JSON.parse(cachedResponse) } return cachedResponse } setCachedResponse = (request: CallRequestDTO, response: CallResponseDTO) => { debug('setCachedResponse', request, response) const requestKey = makeCallRequestKey(request) if (!response) { return this.cachedResponseByParams?.delete(requestKey) } // Strip correlationId from cached response - it's request-specific and will // be restored during deduplication (see ClientManager.manageClientRequest). // This ensures each caller gets their unique correlationId even for cached responses. const cachedResponse = { ...response } delete cachedResponse.correlationId this.cachedResponseByParams?.set( requestKey, cachedResponse ? JSON.stringify(cachedResponse) : undefined, ) } }