export const DEFAULT_IMAGE_CACHE = 'images-v1' const objectUrlMap = new Map() export async function getCachedImageUrl( imageId: string, cacheName = DEFAULT_IMAGE_CACHE, ): Promise { if (!imageId) throw new Error('imageId required') // reuse existing object URL if created in this session const existing = objectUrlMap.get(imageId) if (existing) return existing const requestUrl = `/api/image/request/${imageId}` // Try Cache Storage first let response: Response | undefined if ('caches' in window) { const cache = await caches.open(cacheName) response = await cache.match(requestUrl) if (!response) { const fetched = await fetch(requestUrl) if (!fetched.ok) throw new Error(`HTTP ${fetched.status}`) // store a clone in Cache Storage (non-blocking) cache.put(requestUrl, fetched.clone()).catch((e) => { console.warn('Cache put failed:', e) }) response = fetched } } else { const fetched = await fetch(requestUrl) if (!fetched.ok) throw new Error(`HTTP ${fetched.status}`) response = fetched } const blob = await response.blob() const objectUrl = URL.createObjectURL(blob) objectUrlMap.set(imageId, objectUrl) return objectUrl } export function revokeImageUrl(imageId: string) { const url = objectUrlMap.get(imageId) if (url) { try { URL.revokeObjectURL(url) } catch (e) { /* ignore */ } objectUrlMap.delete(imageId) } } export function revokeAllImageUrls() { for (const url of objectUrlMap.values()) { try { URL.revokeObjectURL(url) } catch (e) { /* ignore */ } } objectUrlMap.clear() }