| 43 | } |
| 44 | |
| 45 | class FileLearnSessionCacheStore implements LearnSessionCacheStore { |
| 46 | private readonly cacheFilePath: string; |
| 47 | private readonly ttlMs: number; |
| 48 | private readonly now: () => number; |
| 49 | |
| 50 | constructor(options: FileLearnSessionCacheStoreOptions) { |
| 51 | this.cacheFilePath = options.cacheFilePath ?? getDefaultCacheFilePath(); |
| 52 | this.ttlMs = options.ttlMs ?? DEFAULT_CACHE_TTL_MS; |
| 53 | this.now = options.now ?? Date.now; |
| 54 | } |
| 55 | |
| 56 | async read(endpoint: string): Promise<LearnSessionCacheValue | undefined> { |
| 57 | const normalizedEndpoint = normalizeEndpoint(endpoint); |
| 58 | const cache = await this.readCacheFile(); |
| 59 | const entry = cache.entries[normalizedEndpoint]; |
| 60 | |
| 61 | if (!entry) { |
| 62 | return undefined; |
| 63 | } |
| 64 | |
| 65 | if (new Date(entry.expiresAt).getTime() <= this.now()) { |
| 66 | delete cache.entries[normalizedEndpoint]; |
| 67 | await this.writeCacheFile(cache); |
| 68 | return undefined; |
| 69 | } |
| 70 | |
| 71 | return entry; |
| 72 | } |
| 73 | |
| 74 | async write(value: { endpoint: string; sessionId?: string; tools?: ListedTool[] }): Promise<LearnSessionCacheValue> { |
| 75 | const normalizedEndpoint = normalizeEndpoint(value.endpoint); |
| 76 | const cache = await this.readCacheFile(); |
| 77 | const storedValue: LearnSessionCacheValue = { |
| 78 | endpoint: normalizedEndpoint, |
| 79 | sessionId: value.sessionId, |
| 80 | tools: value.tools ? [...value.tools] : undefined, |
| 81 | updatedAt: new Date(this.now()).toISOString(), |
| 82 | expiresAt: new Date(this.now() + this.ttlMs).toISOString(), |
| 83 | }; |
| 84 | |
| 85 | cache.entries[normalizedEndpoint] = storedValue; |
| 86 | await this.writeCacheFile(cache); |
| 87 | return storedValue; |
| 88 | } |
| 89 | |
| 90 | async clear(endpoint: string): Promise<void> { |
| 91 | const normalizedEndpoint = normalizeEndpoint(endpoint); |
| 92 | const cache = await this.readCacheFile(); |
| 93 | |
| 94 | if (!(normalizedEndpoint in cache.entries)) { |
| 95 | return; |
| 96 | } |
| 97 | |
| 98 | delete cache.entries[normalizedEndpoint]; |
| 99 | |
| 100 | if (Object.keys(cache.entries).length === 0) { |
| 101 | await rm(this.cacheFilePath, { force: true }).catch(() => undefined); |
| 102 | return; |
nothing calls this directly
no outgoing calls
no test coverage detected