class ExpiringCache implements Cache { #cache: Cache; constructor(cache: Cache) { this.#cache = cache; } add(request: RequestInfo | URL): Promise { return this.#cache.add(request); } addAll(requests: RequestInfo[]): Promise { return this.#cache.addAll(requests); } keys(request?: RequestInfo | URL | undefined, options?: CacheQueryOptions | undefined): Promise { return this.#cache.keys(request, options); } matchAll( request?: RequestInfo | URL | undefined, options?: CacheQueryOptions | undefined, ): Promise { return this.#cache.matchAll(request, options); } put(request: RequestInfo | URL, response: Response): Promise { return this.#cache.put(request, response); } putExpiring(request: RequestInfo | URL, response: Response, expiresIn: number): Promise { const expires = Date.now() + expiresIn; const clone = new Response(response.body, { status: response.status, headers: { expires: new Date(expires).toUTCString(), ...Object.fromEntries(response.headers.entries()), }, }); return this.#cache.put(request, clone); } async match(request: RequestInfo | URL, options?: CacheQueryOptions | undefined): Promise { const response = await this.#cache.match(request, options); const expires = response?.headers.get('Expires'); if (response && expires) { if (new Date(expires).getTime() > Date.now()) { return response; } else { await Promise.all([ this.delete(request), response.text(), // Prevent memory leaks ]); } } else if (response) { return response; } } delete(request: RequestInfo | URL, options?: CacheQueryOptions | undefined): Promise { return this.#cache.delete(request, options); } } export default ExpiringCache;