/** * DID cache using in-memory Map with TTL. * Replaces Cloudflare Workers Cache API from Cirrus. */ import { defs, type DidDocument } from "@atcute/identity"; export interface CacheResult { did: string; doc: DidDocument; updatedAt: number; stale: boolean; expired: boolean; } export interface DidCache { cacheDid( did: string, doc: DidDocument, prevResult?: CacheResult, ): Promise; checkCache(did: string): Promise; refreshCache( did: string, getDoc: () => Promise, prevResult?: CacheResult, ): Promise; clearEntry(did: string): Promise; clear(): Promise; } const STALE_TTL = 60 * 60 * 1000; // 1 hour const MAX_TTL = 24 * 60 * 60 * 1000; // 24 hours interface CacheEntry { doc: DidDocument; cachedAt: number; } export class InMemoryDidCache implements DidCache { private cache = new Map(); async cacheDid(did: string, doc: DidDocument): Promise { this.cache.set(did, { doc, cachedAt: Date.now() }); } async checkCache(did: string): Promise { const entry = this.cache.get(did); if (!entry) return null; const now = Date.now(); const age = now - entry.cachedAt; // Validate document const parsed = defs.didDocument.try(entry.doc); if (!parsed.ok || parsed.value.id !== did) { this.cache.delete(did); return null; } return { did, doc: parsed.value, updatedAt: entry.cachedAt, stale: age > STALE_TTL, expired: age > MAX_TTL, }; } async refreshCache( did: string, getDoc: () => Promise, ): Promise { // Background refresh (fire and forget) getDoc().then((doc) => { if (doc) { this.cacheDid(did, doc); } }); } async clearEntry(did: string): Promise { this.cache.delete(did); } async clear(): Promise { this.cache.clear(); } }