| 211 | type WorkersCacheStorage = CacheStorage & { readonly default?: Cache }; |
| 212 | |
| 213 | const workersCacheStore = (staleMaxMs: number): JwksStore | null => { |
| 214 | if (typeof caches === "undefined") return null; |
| 215 | const open = (): Cache | null => (caches as WorkersCacheStorage).default ?? null; |
| 216 | |
| 217 | return { |
| 218 | get: async (url) => { |
| 219 | const cache = open(); |
| 220 | if (!cache) return null; |
| 221 | const hit = await cache.match(url.toString()); |
| 222 | if (!hit) return null; |
| 223 | const fetchedAtHeader = hit.headers.get(STORE_HEADER_FETCHED_AT); |
| 224 | const fetchedAt = fetchedAtHeader === null ? Number.NaN : Number(fetchedAtHeader); |
| 225 | if (!Number.isFinite(fetchedAt)) return null; |
| 226 | const body = await hit.json(); |
| 227 | // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: a corrupt cache entry must degrade to a miss, never fail the verify |
| 228 | try { |
| 229 | await decodeJsonWebKeySetPayload(body); |
| 230 | } catch { |
| 231 | return null; |
| 232 | } |
| 233 | return { jwks: body as JSONWebKeySet, fetchedAt }; |
| 234 | }, |
| 235 | put: async (url, stored) => { |
| 236 | const cache = open(); |
| 237 | if (!cache) return; |
| 238 | const maxAgeSeconds = Math.max(1, Math.floor(staleMaxMs / 1000)); |
| 239 | await cache.put( |
| 240 | url.toString(), |
| 241 | new Response(JSON.stringify(stored.jwks), { |
| 242 | status: 200, |
| 243 | headers: { |
| 244 | "content-type": "application/json", |
| 245 | "cache-control": `max-age=${maxAgeSeconds}`, |
| 246 | [STORE_HEADER_FETCHED_AT]: String(stored.fetchedAt), |
| 247 | }, |
| 248 | }), |
| 249 | ); |
| 250 | }, |
| 251 | }; |
| 252 | }; |
| 253 | |
| 254 | /** |
| 255 | * Creates a cached, single-flight, force-refreshable JWKS resolver compatible |