tempest-react-sdk 0.82.0 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/bin/lib/openapi/generate.hostile.test.mjs +91 -0
- package/bin/lib/openapi/generate.mjs +42 -3
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +135 -132
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +124 -75
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/upload-with-progress.cjs +1 -1
- package/dist/http/upload-with-progress.cjs.map +1 -1
- package/dist/http/upload-with-progress.js +100 -44
- package/dist/http/upload-with-progress.js.map +1 -1
- package/dist/query/offline-persistence.cjs +1 -1
- package/dist/query/offline-persistence.cjs.map +1 -1
- package/dist/query/offline-persistence.js +57 -44
- package/dist/query/offline-persistence.js.map +1 -1
- package/dist/sw/cache.cjs +1 -1
- package/dist/sw/cache.cjs.map +1 -1
- package/dist/sw/cache.js +82 -32
- package/dist/sw/cache.js.map +1 -1
- package/dist/sw.d.ts +21 -1
- package/dist/tempest-react-sdk.d.ts +90 -6
- package/dist/vite/base-url.cjs +1 -1
- package/dist/vite/base-url.cjs.map +1 -1
- package/dist/vite/base-url.js +5 -1
- package/dist/vite/base-url.js.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.js +96 -31
- package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
- package/dist/vite/tempest-pwa-manifest.cjs +2 -2
- package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
- package/dist/vite/tempest-pwa-manifest.js +32 -16
- package/dist/vite/tempest-pwa-manifest.js.map +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.d.ts +63 -12
- package/dist/vite.js +4 -4
- package/package.json +1 -1
package/dist/sw/cache.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.cjs","names":["credentialHeaderSet","require_credential_headers","getSwScope","precacheName","precachedPaths","CACHED_AT_HEADER","isExpired","maxAgeSeconds","stamp","response","dateHeader","storedAt","cacheControlDirectives","directive","varyNames","vary","name","isStorable","directives","route","isCredentialed","request","credentialsOn","store","headers","varies","missing","copy","cache","trimCache","maxEntries","cacheName","keys","key","cacheFirst","cached","networkFirst","network","timeoutMs","timeout","extend","reject","staleWhileRevalidate","fresh","runRoute","routeMatches","url","createPartialResponse","rangeHeader","match","buffer","total","start","end","suffix","slice","installRuntimeCache","event","routes","candidate","promise","full","stripRange","out","installPrecache","sw","options","manifest","manifestUrl","skipWaiting","navigationPreload","navigateFallbackDenylist","re","shell","navigateFallback"],"sources":["../../src/sw/cache.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines — precache, runtime strategies (cache-first, network-\n * first, stale-while-revalidate), expiry and the inspect/clear helpers all address\n * the same Cache Storage namespace, and the naming scheme that keeps them from\n * evicting each other is the file's whole contract.\n */\n/**\n * Service-worker caching helpers — a small, dependency-free subset of what\n * Workbox provides: precaching of the build's app shell (so the app launches\n * offline) plus runtime caching strategies for fonts, APIs and images.\n *\n * Import these inside your own `sw.ts`. They run in the service-worker global\n * scope, not the main thread. Pair `installPrecache` with the\n * `tempestPwaManifest()` Vite plugin (from `tempest-react-sdk/vite`), which\n * emits the `precache-manifest.json` this reads at install time.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installRuntimeCache, installPrecache } from \"tempest-react-sdk/sw\";\n *\n * // Register specific routes FIRST so they win over the precache catch-all.\n * installRuntimeCache([\n * { match: /\\/api\\//, strategy: \"network-first\", cacheName: \"tempest-api\", maxAgeSeconds: 300 },\n * ]);\n * installPrecache();\n */\n\nimport { credentialHeaderSet } from \"./credential-headers\";\n\n/** Minimal shape of the events we touch, to avoid pulling in `lib.webworker`. */\ninterface ExtendableEventLike {\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface FetchEventLike extends ExtendableEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n /** Resolves to the navigation-preload response when it is enabled, else `undefined`. */\n preloadResponse?: Promise<Response | undefined>;\n}\n\n/** Minimal shape of `registration.navigationPreload` (Navigation Preload API). */\ninterface NavigationPreloadManagerLike {\n enable(): Promise<void>;\n disable(): Promise<void>;\n}\n\ninterface CacheSwGlobal {\n addEventListener(type: \"install\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"activate\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: FetchEventLike) => void): void;\n clients: { claim(): Promise<void> };\n skipWaiting(): Promise<void>;\n location: { origin: string };\n registration?: { navigationPreload?: NavigationPreloadManagerLike };\n}\n\nfunction getSwScope(): CacheSwGlobal {\n return globalThis as unknown as CacheSwGlobal;\n}\n\n/** Caching strategy for a runtime route. Mirrors the common Workbox trio. */\nexport type RuntimeStrategy = \"cache-first\" | \"network-first\" | \"stale-while-revalidate\";\n\n/** A single runtime-caching rule, matched against each `GET` request. */\nexport interface RuntimeRoute {\n /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */\n match: RegExp | ((url: URL, request: Request) => boolean);\n /** How to resolve a match. */\n strategy: RuntimeStrategy;\n /** Cache bucket name for this route. */\n cacheName: string;\n /** Trim the cache to at most this many entries (FIFO) after each write. */\n maxEntries?: number;\n /** Treat a cached response older than this (seconds) as a miss. */\n maxAgeSeconds?: number;\n /** For `network-first`: fall back to cache after this timeout (seconds). */\n networkTimeoutSeconds?: number;\n /**\n * Serve HTTP `Range` requests (206 Partial Content) by slicing the cached\n * full response. Enable for audio/video so seeking works offline. The full\n * resource is cached once (the `Range` header is stripped before caching).\n */\n rangeRequests?: boolean;\n /**\n * Opt in to caching credentialed requests — those that carry a credential\n * header (see `credentialHeaders`) or are a CORS-mode `fetch` sent with\n * `credentials: \"include\"` — and responses marked `Cache-Control: private`.\n * Default `false`.\n *\n * Cache Storage is keyed by URL and outlives the session: without this\n * gate, a `GET /api/me` stored for one account was served to the next\n * account on the same browser whenever the network failed or exceeded\n * `networkTimeoutSeconds` (reproduced in Chromium 153, 11/10/2026). With\n * the default, credentialed requests bypass the route entirely and reach\n * the network as if no service worker were installed.\n *\n * When enabled, every stored entry gets `Vary` on each credential header the\n * request carried, so the Cache API only matches it for the same credential —\n * another account (or no account) misses instead of reading it. A token\n * rotated while online simply repopulates the entry. Pair it with\n * `clearSessionData()` on logout, which removes the entries themselves.\n * `Cache-Control: no-store` is never cached, opt-in or not.\n *\n * A cookie session is only partly visible here: the worker cannot see\n * `Cookie`. What it can see is a `fetch` that asked for cookies explicitly —\n * `mode: \"cors\"` with `credentials: \"include\"`, which is what `ApiClient`\n * sends under `withCredentials` — and since 0.82.0 that request is treated\n * as credentialed too (#531). `<img>`, `<audio>` and `<video>` also send\n * `credentials: \"include\"`, but in `no-cors` mode, so they keep caching. A\n * plain same-origin `fetch` (`credentials: \"same-origin\"`, the default)\n * carries the cookie without saying so; such an API stays private only\n * through `Cache-Control: private`/`no-store` on its responses and\n * `clearSessionData()` on logout. Opting a cookie request in caches it by\n * URL — there is no credential header to vary on — so `clearSessionData()`\n * is then its only isolation.\n */\n cacheAuthenticated?: boolean;\n /**\n * Header names that mark a request as credentialed. Default\n * `DEFAULT_CREDENTIAL_HEADERS` (`authorization`, `x-api-key`,\n * `x-auth-token`, …) — the same list the background-sync queue strips.\n */\n credentialHeaders?: readonly string[];\n}\n\n/** Options for {@link installPrecache}. */\nexport interface InstallPrecacheOptions {\n /** URL of the manifest emitted by `tempestPwaManifest()`. Default `/precache-manifest.json`. */\n manifestUrl?: string;\n /** Cache name prefix; the manifest `version` is appended. Default `tempest-precache`. */\n cacheName?: string;\n /** App-shell document served for navigation requests offline. Default `/index.html`. */\n navigateFallback?: string;\n /** Navigation paths that should NOT use the fallback (e.g. `[/^\\/api\\//]`). */\n navigateFallbackDenylist?: RegExp[];\n /** Activate the new worker immediately after precaching. Default `true`. */\n skipWaiting?: boolean;\n /**\n * Enable the Navigation Preload API on `activate`, so the browser fetches\n * the navigation request in parallel with the worker boot and the handler\n * serves `event.preloadResponse` — cutting first-navigation latency after\n * the worker starts. Silently ignored where unsupported. Default `true`.\n */\n navigationPreload?: boolean;\n}\n\ninterface PrecacheManifest {\n version: string;\n urls: string[];\n}\n\nlet precacheName = \"\";\nconst precachedPaths = new Set<string>();\n\n/**\n * Header stamped on every runtime-cache entry with the epoch milliseconds of the\n * write. `Date` alone is not enough: it is not a CORS-safelisted response\n * header, so for a cross-origin API the worker reads `null` and `maxAgeSeconds`\n * would never expire anything.\n */\nconst CACHED_AT_HEADER = \"x-tempest-cached-at\";\n\n/**\n * Whether a cached response has aged past `maxAgeSeconds`. Reads the\n * {@link CACHED_AT_HEADER} stamp written by the runtime cache, and falls back to\n * `Date` for entries written before the stamp existed.\n */\nfunction isExpired(response: Response, maxAgeSeconds?: number): boolean {\n if (!maxAgeSeconds) return false;\n const stamp = Number(response.headers.get(CACHED_AT_HEADER));\n const dateHeader = response.headers.get(\"date\");\n const storedAt = stamp > 0 ? stamp : dateHeader ? new Date(dateHeader).getTime() : NaN;\n if (Number.isNaN(storedAt)) return false;\n return (Date.now() - storedAt) / 1000 > maxAgeSeconds;\n}\n\n/** The `Cache-Control` directives of a response, lower-cased. */\nfunction cacheControlDirectives(response: Response): string[] {\n return (response.headers.get(\"cache-control\") ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((directive) => directive.trim().split(\"=\")[0]);\n}\n\n/** The comma-separated names of a response's `Vary` header, trimmed and lower-cased. */\nfunction varyNames(vary: string | null): string[] {\n return (vary ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((name) => name.trim());\n}\n\n/**\n * Whether a network response may be written to the route's cache: it must be\n * `ok`, never `no-store`, never `Vary: *`, and `private` only under\n * `cacheAuthenticated`.\n *\n * `Vary: *` says no later request can be proven to match, and `Cache.put`\n * rejects it with a `TypeError`. Before 0.82.0 that rejection escaped the\n * strategy, so a response the network had delivered became the offline\n * fallback path (network-first), an error (cache-first) or nothing at all\n * (stale-while-revalidate) (#534).\n */\nfunction isStorable(response: Response, route: RuntimeRoute): boolean {\n if (!response.ok) return false;\n if (varyNames(response.headers.get(\"vary\")).includes(\"*\")) return false;\n const directives = cacheControlDirectives(response);\n if (directives.includes(\"no-store\")) return false;\n return route.cacheAuthenticated === true || !directives.includes(\"private\");\n}\n\n/**\n * Whether a request is session-bound for its route: it carries a credential\n * header, or it is a CORS `fetch` that asked for cookies with\n * `credentials: \"include\"` (`ApiClient` under `withCredentials`). A `no-cors`\n * request — `<img>`, `<audio>`, `<video>` — also sends `include` by default and\n * is not counted, or every image would bypass the cache.\n */\nfunction isCredentialed(request: Request, route: RuntimeRoute): boolean {\n if (request.mode === \"cors\" && request.credentials === \"include\") return true;\n return credentialsOn(request, route).length > 0;\n}\n\n/** The credential headers, lower-cased, that a request carries for its route. */\nfunction credentialsOn(request: Request, route: RuntimeRoute): string[] {\n return [...credentialHeaderSet(route.credentialHeaders)].filter((name) =>\n request.headers.has(name),\n );\n}\n\n/**\n * Write a response to the route's cache with the {@link CACHED_AT_HEADER} stamp\n * and, under `cacheAuthenticated`, `Vary` on each credential header the request\n * carried, so the entry only matches the credential that produced it. Skips\n * responses that {@link isStorable} rejects, then trims the cache to\n * `maxEntries`.\n */\nasync function store(\n cache: Cache,\n request: Request,\n response: Response,\n route: RuntimeRoute,\n): Promise<void> {\n if (!isStorable(response, route)) return;\n const headers = new Headers(response.headers);\n headers.set(CACHED_AT_HEADER, String(Date.now()));\n if (route.cacheAuthenticated) {\n const vary = headers.get(\"vary\");\n const varies = varyNames(vary);\n const missing = credentialsOn(request, route).filter((name) => !varies.includes(name));\n if (missing.length > 0) {\n headers.set(\"vary\", [vary, ...missing].filter(Boolean).join(\", \"));\n }\n }\n const copy = response.clone();\n await cache.put(\n request,\n new Response(copy.body, { status: copy.status, statusText: copy.statusText, headers }),\n );\n await trimCache(route.cacheName, route.maxEntries);\n}\n\n/** Drop oldest entries until the cache holds at most `maxEntries`. */\nasync function trimCache(cacheName: string, maxEntries?: number): Promise<void> {\n if (!maxEntries) return;\n const cache = await caches.open(cacheName);\n const keys = await cache.keys();\n if (keys.length <= maxEntries) return;\n for (const key of keys.slice(0, keys.length - maxEntries)) {\n await cache.delete(key);\n }\n}\n\nasync function cacheFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n}\n\n/**\n * Network-first. When the timeout wins, the network request keeps running to\n * refresh the cache, and is handed to `extend` (`event.waitUntil`) so the\n * worker is not stopped mid-write.\n */\nasync function networkFirst(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n\n const network = (async (): Promise<Response> => {\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n })();\n\n const timeoutMs = (route.networkTimeoutSeconds ?? 0) * 1000;\n try {\n if (timeoutMs > 0) {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => {\n extend(network.catch(() => undefined));\n reject(new Error(\"network-timeout\"));\n }, timeoutMs),\n );\n return await Promise.race([network, timeout]);\n }\n return await network;\n } catch {\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n throw new Error(\"network-first: no network and no cache\");\n }\n}\n\n/**\n * Stale-while-revalidate. A fresh cached response is served at once, and the\n * revalidation is handed to `extend` (`event.waitUntil`): without it the\n * browser may stop the worker before the refreshed response is written.\n */\nasync function staleWhileRevalidate(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n\n const network = fetch(request)\n .then(async (response) => {\n await store(cache, request, response, route);\n return response;\n })\n .catch(() => undefined);\n\n const fresh = cached && !isExpired(cached, route.maxAgeSeconds) ? cached : undefined;\n if (fresh) {\n extend(network);\n return fresh;\n }\n const response = await network;\n if (response) return response;\n throw new Error(\"stale-while-revalidate: no network and no cache\");\n}\n\nfunction runRoute(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n switch (route.strategy) {\n case \"cache-first\":\n return cacheFirst(request, route);\n case \"network-first\":\n return networkFirst(request, route, extend);\n case \"stale-while-revalidate\":\n return staleWhileRevalidate(request, route, extend);\n }\n}\n\nfunction routeMatches(route: RuntimeRoute, url: URL, request: Request): boolean {\n return typeof route.match === \"function\"\n ? route.match(url, request)\n : route.match.test(url.href);\n}\n\n/**\n * Build a `206 Partial Content` response from a full one for an HTTP `Range`\n * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix\n * `bytes=-suffixLength` (the last N bytes). Returns the original response when there is no usable\n * `Range` header, or a `416` when the range is unsatisfiable.\n *\n * @param request The incoming request (its `Range` header drives the slice).\n * @param response The full (200) response to slice.\n */\nexport async function createPartialResponse(\n request: Request,\n response: Response,\n): Promise<Response> {\n const rangeHeader = request.headers.get(\"range\");\n if (!rangeHeader) return response;\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match || (match[1] === \"\" && match[2] === \"\")) {\n return new Response(null, { status: 416, statusText: \"Range Not Satisfiable\" });\n }\n\n const buffer = await response.clone().arrayBuffer();\n const total = buffer.byteLength;\n\n let start: number;\n let end: number;\n if (match[1] === \"\") {\n const suffix = Number(match[2]);\n start = Math.max(0, total - suffix);\n end = total - 1;\n } else {\n start = Number(match[1]);\n end = match[2] === \"\" ? total - 1 : Math.min(Number(match[2]), total - 1);\n }\n\n if (start > end || start >= total) {\n return new Response(null, {\n status: 416,\n statusText: \"Range Not Satisfiable\",\n headers: { \"Content-Range\": `bytes */${total}` },\n });\n }\n\n const slice = buffer.slice(start, end + 1);\n const headers = new Headers(response.headers);\n headers.set(\"Content-Range\", `bytes ${start}-${end}/${total}`);\n headers.set(\"Content-Length\", String(slice.byteLength));\n headers.set(\"Accept-Ranges\", \"bytes\");\n\n return new Response(slice, {\n status: 206,\n statusText: \"Partial Content\",\n headers,\n });\n}\n\n/**\n * Install a `fetch` handler that resolves matching `GET` requests with the\n * given runtime strategies. Non-matching requests are left untouched (no\n * `respondWith`), so a later {@link installPrecache} can handle them.\n *\n * A request carrying a credential header (`Authorization`, `X-Api-Key`, … — the\n * route's `credentialHeaders`), or a CORS `fetch` sent with\n * `credentials: \"include\"`, is also left untouched unless its route sets\n * `cacheAuthenticated`, and a response with `Cache-Control: no-store`\n * (or `private`, without that opt-in) or `Vary: *` is served but never stored. `maxAgeSeconds`\n * applies to every read, including the offline fallback: an expired entry is a\n * miss, never a last resort. Under `rangeRequests`, the full resource is cached\n * once (the `Range` header stripped) and each range is sliced from it.\n *\n * Register this BEFORE `installPrecache` so specific routes win over the\n * precache catch-all.\n *\n * @param routes Ordered rules; the first whose `match` passes handles the request.\n */\nexport function installRuntimeCache(routes: RuntimeRoute[]): void {\n const sw = getSwScope();\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n const route = routes.find((candidate) => routeMatches(candidate, url, request));\n if (!route) return;\n if (!route.cacheAuthenticated && isCredentialed(request, route)) return;\n const extend = (promise: Promise<unknown>): void => event.waitUntil(promise);\n\n if (route.rangeRequests && request.headers.has(\"range\")) {\n const full = new Request(request.url, {\n headers: stripRange(request.headers),\n credentials: request.credentials,\n mode: request.mode === \"navigate\" ? \"same-origin\" : request.mode,\n });\n event.respondWith(\n runRoute(full, route, extend).then((response) =>\n createPartialResponse(request, response),\n ),\n );\n return;\n }\n\n event.respondWith(runRoute(request, route, extend));\n });\n}\n\n/** Copy headers without the `Range` header (so the cached entry is the full file). */\nfunction stripRange(headers: Headers): Headers {\n const out = new Headers(headers);\n out.delete(\"range\");\n return out;\n}\n\n/**\n * Precache the app shell at `install` and serve it offline:\n * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),\n * - caches every listed URL under a versioned cache,\n * - on `activate`, deletes stale precache versions and claims open clients,\n * - on `fetch`, serves precached assets cache-first and falls back to the\n * `navigateFallback` document for offline navigations (SPA routing).\n *\n * Same-origin only. Register this LAST, after any {@link installRuntimeCache}.\n */\nexport function installPrecache(options: InstallPrecacheOptions = {}): void {\n const sw = getSwScope();\n const {\n manifestUrl = \"/precache-manifest.json\",\n cacheName = \"tempest-precache\",\n navigateFallback = \"/index.html\",\n navigateFallbackDenylist = [],\n skipWaiting = true,\n navigationPreload = true,\n } = options;\n\n sw.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const response = await fetch(manifestUrl, { cache: \"no-cache\" });\n const manifest = (await response.json()) as PrecacheManifest;\n precacheName = `${cacheName}-${manifest.version}`;\n\n const cache = await caches.open(precacheName);\n await cache.addAll(manifest.urls);\n for (const url of manifest.urls) {\n precachedPaths.add(new URL(url, sw.location.origin).pathname);\n }\n if (skipWaiting) await sw.skipWaiting();\n })(),\n );\n });\n\n sw.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const keys = await caches.keys();\n await Promise.all(\n keys\n .filter((key) => key.startsWith(`${cacheName}-`) && key !== precacheName)\n .map((key) => caches.delete(key)),\n );\n if (navigationPreload && sw.registration?.navigationPreload) {\n await sw.registration.navigationPreload.enable();\n }\n await sw.clients.claim();\n })(),\n );\n });\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n if (url.origin !== sw.location.origin) return;\n\n // SPA navigations: serve the cached app shell when offline.\n if (request.mode === \"navigate\") {\n if (navigateFallbackDenylist.some((re) => re.test(url.pathname))) return;\n event.respondWith(\n (async () => {\n try {\n const preloaded = await event.preloadResponse;\n if (preloaded) return preloaded;\n return await fetch(request);\n } catch {\n const cache = await caches.open(precacheName);\n const shell = await cache.match(navigateFallback);\n if (shell) return shell;\n throw new Error(\"offline and no cached app shell\");\n }\n })(),\n );\n return;\n }\n\n // Precached static assets: cache-first.\n if (precachedPaths.has(url.pathname)) {\n event.respondWith(\n (async () => {\n const cache = await caches.open(precacheName);\n const cached = await cache.match(request);\n return cached ?? fetch(request);\n })(),\n );\n }\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache.cjs","names":["credentialHeaderSet","require_credential_headers","getSwScope","answeredByRuntime","PRECACHE_STATE_PATH","CACHED_AT_HEADER","isExpired","maxAgeSeconds","stamp","response","dateHeader","storedAt","cacheControlDirectives","directive","varyNames","vary","name","isStorable","directives","route","isCredentialed","request","credentialsOn","store","headers","varies","missing","copy","cache","trimCache","maxEntries","cacheName","keys","key","cacheFirst","cached","networkFirst","network","timeoutMs","timeout","extend","reject","staleWhileRevalidate","fresh","runRoute","routeMatches","url","createPartialResponse","rangeHeader","match","buffer","total","start","end","suffix","slice","installRuntimeCache","event","routes","candidate","promise","full","stripRange","out","readPrecacheState","writePrecacheState","state","latestPrecache","latest","at","pick","describePrecache","path","fromPrecache","installPrecache","sw","options","installedName","active","known","loading","loadActive","found","manifest","manifestUrl","skipWaiting","previous","navigationPreload","navigateFallbackDenylist","re","precache","shell","navigateFallback"],"sources":["../../src/sw/cache.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines — precache, runtime strategies (cache-first, network-\n * first, stale-while-revalidate), expiry and the inspect/clear helpers all address\n * the same Cache Storage namespace, and the naming scheme that keeps them from\n * evicting each other is the file's whole contract.\n */\n/**\n * Service-worker caching helpers — a small, dependency-free subset of what\n * Workbox provides: precaching of the build's app shell (so the app launches\n * offline) plus runtime caching strategies for fonts, APIs and images.\n *\n * Import these inside your own `sw.ts`. They run in the service-worker global\n * scope, not the main thread. Pair `installPrecache` with the\n * `tempestPwaManifest()` Vite plugin (from `tempest-react-sdk/vite`), which\n * emits the `precache-manifest.json` this reads at install time.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installRuntimeCache, installPrecache } from \"tempest-react-sdk/sw\";\n *\n * // Register specific routes FIRST so they win over the precache catch-all.\n * installRuntimeCache([\n * { match: /\\/api\\//, strategy: \"network-first\", cacheName: \"tempest-api\", maxAgeSeconds: 300 },\n * ]);\n * installPrecache();\n */\n\nimport { credentialHeaderSet } from \"./credential-headers\";\n\n/** Minimal shape of the events we touch, to avoid pulling in `lib.webworker`. */\ninterface ExtendableEventLike {\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface FetchEventLike extends ExtendableEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n /** Resolves to the navigation-preload response when it is enabled, else `undefined`. */\n preloadResponse?: Promise<Response | undefined>;\n}\n\n/** Minimal shape of `registration.navigationPreload` (Navigation Preload API). */\ninterface NavigationPreloadManagerLike {\n enable(): Promise<void>;\n disable(): Promise<void>;\n}\n\ninterface CacheSwGlobal {\n addEventListener(type: \"install\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"activate\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: FetchEventLike) => void): void;\n clients: { claim(): Promise<void> };\n skipWaiting(): Promise<void>;\n location: { origin: string };\n registration?: { navigationPreload?: NavigationPreloadManagerLike };\n}\n\nfunction getSwScope(): CacheSwGlobal {\n return globalThis as unknown as CacheSwGlobal;\n}\n\n/** Caching strategy for a runtime route. Mirrors the common Workbox trio. */\nexport type RuntimeStrategy = \"cache-first\" | \"network-first\" | \"stale-while-revalidate\";\n\n/** A single runtime-caching rule, matched against each `GET` request. */\nexport interface RuntimeRoute {\n /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */\n match: RegExp | ((url: URL, request: Request) => boolean);\n /** How to resolve a match. */\n strategy: RuntimeStrategy;\n /** Cache bucket name for this route. */\n cacheName: string;\n /** Trim the cache to at most this many entries (FIFO) after each write. */\n maxEntries?: number;\n /** Treat a cached response older than this (seconds) as a miss. */\n maxAgeSeconds?: number;\n /** For `network-first`: fall back to cache after this timeout (seconds). */\n networkTimeoutSeconds?: number;\n /**\n * Serve HTTP `Range` requests (206 Partial Content) by slicing the cached\n * full response. Enable for audio/video so seeking works offline. The full\n * resource is cached once (the `Range` header is stripped before caching).\n */\n rangeRequests?: boolean;\n /**\n * Opt in to caching credentialed requests — those that carry a credential\n * header (see `credentialHeaders`) or are a CORS-mode `fetch` sent with\n * `credentials: \"include\"` — and responses marked `Cache-Control: private`.\n * Default `false`.\n *\n * Cache Storage is keyed by URL and outlives the session: without this\n * gate, a `GET /api/me` stored for one account was served to the next\n * account on the same browser whenever the network failed or exceeded\n * `networkTimeoutSeconds` (reproduced in Chromium 153, 11/10/2026). With\n * the default, credentialed requests bypass the route entirely and reach\n * the network as if no service worker were installed.\n *\n * When enabled, every stored entry gets `Vary` on each credential header the\n * request carried, so the Cache API only matches it for the same credential —\n * another account (or no account) misses instead of reading it. A token\n * rotated while online simply repopulates the entry. Pair it with\n * `clearSessionData()` on logout, which removes the entries themselves.\n * `Cache-Control: no-store` is never cached, opt-in or not.\n *\n * A cookie session is only partly visible here: the worker cannot see\n * `Cookie`. What it can see is a `fetch` that asked for cookies explicitly —\n * `mode: \"cors\"` with `credentials: \"include\"`, which is what `ApiClient`\n * sends under `withCredentials` — and since 0.82.0 that request is treated\n * as credentialed too (#531). `<img>`, `<audio>` and `<video>` also send\n * `credentials: \"include\"`, but in `no-cors` mode, so they keep caching. A\n * plain same-origin `fetch` (`credentials: \"same-origin\"`, the default)\n * carries the cookie without saying so; such an API stays private only\n * through `Cache-Control: private`/`no-store` on its responses and\n * `clearSessionData()` on logout. Opting a cookie request in caches it by\n * URL — there is no credential header to vary on — so `clearSessionData()`\n * is then its only isolation.\n */\n cacheAuthenticated?: boolean;\n /**\n * Header names that mark a request as credentialed. Default\n * `DEFAULT_CREDENTIAL_HEADERS` (`authorization`, `x-api-key`,\n * `x-auth-token`, …) — the same list the background-sync queue strips.\n */\n credentialHeaders?: readonly string[];\n}\n\n/** Options for {@link installPrecache}. */\nexport interface InstallPrecacheOptions {\n /** URL of the manifest emitted by `tempestPwaManifest()`. Default `/precache-manifest.json`. */\n manifestUrl?: string;\n /** Cache name prefix; the manifest `version` is appended. Default `tempest-precache`. */\n cacheName?: string;\n /** App-shell document served for navigation requests offline. Default `/index.html`. */\n navigateFallback?: string;\n /** Navigation paths that should NOT use the fallback (e.g. `[/^\\/api\\//]`). */\n navigateFallbackDenylist?: RegExp[];\n /** Activate the new worker immediately after precaching. Default `true`. */\n skipWaiting?: boolean;\n /**\n * Enable the Navigation Preload API on `activate`, so the browser fetches\n * the navigation request in parallel with the worker boot and the handler\n * serves `event.preloadResponse` — cutting first-navigation latency after\n * the worker starts. Silently ignored where unsupported. Default `true`.\n */\n navigationPreload?: boolean;\n}\n\ninterface PrecacheManifest {\n version: string;\n urls: string[];\n}\n\n/**\n * Fetch events a runtime route already answered. The precache listener skips\n * them: a second `respondWith` on the same event throws `InvalidStateError`,\n * which a precached path also matched by a runtime route used to raise.\n */\nconst answeredByRuntime = new WeakSet<object>();\n\n/**\n * Synthetic key, inside each versioned precache, of the entry that records when\n * that version was installed and activated. It never collides with a built\n * asset, and living inside the versioned cache means it is deleted with it and\n * survives `clearSessionData()`, which keeps the precache.\n */\nconst PRECACHE_STATE_PATH = \"/__tempest-precache-state__\";\n\n/** What {@link PRECACHE_STATE_PATH} holds for one precache version. */\ninterface PrecacheState {\n installedAt: number;\n activatedAt?: number;\n}\n\n/** The precache a worker serves: its cache name and the pathnames it holds. */\ninterface ActivePrecache {\n name: string;\n paths: Set<string>;\n}\n\n/**\n * Header stamped on every runtime-cache entry with the epoch milliseconds of the\n * write. `Date` alone is not enough: it is not a CORS-safelisted response\n * header, so for a cross-origin API the worker reads `null` and `maxAgeSeconds`\n * would never expire anything.\n */\nconst CACHED_AT_HEADER = \"x-tempest-cached-at\";\n\n/**\n * Whether a cached response has aged past `maxAgeSeconds`. Reads the\n * {@link CACHED_AT_HEADER} stamp written by the runtime cache, and falls back to\n * `Date` for entries written before the stamp existed.\n */\nfunction isExpired(response: Response, maxAgeSeconds?: number): boolean {\n if (!maxAgeSeconds) return false;\n const stamp = Number(response.headers.get(CACHED_AT_HEADER));\n const dateHeader = response.headers.get(\"date\");\n const storedAt = stamp > 0 ? stamp : dateHeader ? new Date(dateHeader).getTime() : NaN;\n if (Number.isNaN(storedAt)) return false;\n return (Date.now() - storedAt) / 1000 > maxAgeSeconds;\n}\n\n/** The `Cache-Control` directives of a response, lower-cased. */\nfunction cacheControlDirectives(response: Response): string[] {\n return (response.headers.get(\"cache-control\") ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((directive) => directive.trim().split(\"=\")[0]);\n}\n\n/** The comma-separated names of a response's `Vary` header, trimmed and lower-cased. */\nfunction varyNames(vary: string | null): string[] {\n return (vary ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((name) => name.trim());\n}\n\n/**\n * Whether a network response may be written to the route's cache: it must be\n * `ok`, never `no-store`, never `Vary: *`, and `private` only under\n * `cacheAuthenticated`.\n *\n * `Vary: *` says no later request can be proven to match, and `Cache.put`\n * rejects it with a `TypeError`. Before 0.82.0 that rejection escaped the\n * strategy, so a response the network had delivered became the offline\n * fallback path (network-first), an error (cache-first) or nothing at all\n * (stale-while-revalidate) (#534).\n */\nfunction isStorable(response: Response, route: RuntimeRoute): boolean {\n if (!response.ok) return false;\n if (varyNames(response.headers.get(\"vary\")).includes(\"*\")) return false;\n const directives = cacheControlDirectives(response);\n if (directives.includes(\"no-store\")) return false;\n return route.cacheAuthenticated === true || !directives.includes(\"private\");\n}\n\n/**\n * Whether a request is session-bound for its route: it carries a credential\n * header, or it is a CORS `fetch` that asked for cookies with\n * `credentials: \"include\"` (`ApiClient` under `withCredentials`). A `no-cors`\n * request — `<img>`, `<audio>`, `<video>` — also sends `include` by default and\n * is not counted, or every image would bypass the cache.\n */\nfunction isCredentialed(request: Request, route: RuntimeRoute): boolean {\n if (request.mode === \"cors\" && request.credentials === \"include\") return true;\n return credentialsOn(request, route).length > 0;\n}\n\n/** The credential headers, lower-cased, that a request carries for its route. */\nfunction credentialsOn(request: Request, route: RuntimeRoute): string[] {\n return [...credentialHeaderSet(route.credentialHeaders)].filter((name) =>\n request.headers.has(name),\n );\n}\n\n/**\n * Write a response to the route's cache with the {@link CACHED_AT_HEADER} stamp\n * and, under `cacheAuthenticated`, `Vary` on each credential header the request\n * carried, so the entry only matches the credential that produced it. Skips\n * responses that {@link isStorable} rejects, then trims the cache to\n * `maxEntries`.\n */\nasync function store(\n cache: Cache,\n request: Request,\n response: Response,\n route: RuntimeRoute,\n): Promise<void> {\n if (!isStorable(response, route)) return;\n const headers = new Headers(response.headers);\n headers.set(CACHED_AT_HEADER, String(Date.now()));\n if (route.cacheAuthenticated) {\n const vary = headers.get(\"vary\");\n const varies = varyNames(vary);\n const missing = credentialsOn(request, route).filter((name) => !varies.includes(name));\n if (missing.length > 0) {\n headers.set(\"vary\", [vary, ...missing].filter(Boolean).join(\", \"));\n }\n }\n const copy = response.clone();\n await cache.put(\n request,\n new Response(copy.body, { status: copy.status, statusText: copy.statusText, headers }),\n );\n await trimCache(route.cacheName, route.maxEntries);\n}\n\n/** Drop oldest entries until the cache holds at most `maxEntries`. */\nasync function trimCache(cacheName: string, maxEntries?: number): Promise<void> {\n if (!maxEntries) return;\n const cache = await caches.open(cacheName);\n const keys = await cache.keys();\n if (keys.length <= maxEntries) return;\n for (const key of keys.slice(0, keys.length - maxEntries)) {\n await cache.delete(key);\n }\n}\n\nasync function cacheFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n}\n\n/**\n * Network-first. When the timeout wins, the network request keeps running to\n * refresh the cache, and is handed to `extend` (`event.waitUntil`) so the\n * worker is not stopped mid-write.\n */\nasync function networkFirst(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n\n const network = (async (): Promise<Response> => {\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n })();\n\n const timeoutMs = (route.networkTimeoutSeconds ?? 0) * 1000;\n try {\n if (timeoutMs > 0) {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => {\n extend(network.catch(() => undefined));\n reject(new Error(\"network-timeout\"));\n }, timeoutMs),\n );\n return await Promise.race([network, timeout]);\n }\n return await network;\n } catch {\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n throw new Error(\"network-first: no network and no cache\");\n }\n}\n\n/**\n * Stale-while-revalidate. A fresh cached response is served at once, and the\n * revalidation is handed to `extend` (`event.waitUntil`): without it the\n * browser may stop the worker before the refreshed response is written.\n */\nasync function staleWhileRevalidate(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n\n const network = fetch(request)\n .then(async (response) => {\n await store(cache, request, response, route);\n return response;\n })\n .catch(() => undefined);\n\n const fresh = cached && !isExpired(cached, route.maxAgeSeconds) ? cached : undefined;\n if (fresh) {\n extend(network);\n return fresh;\n }\n const response = await network;\n if (response) return response;\n throw new Error(\"stale-while-revalidate: no network and no cache\");\n}\n\nfunction runRoute(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n switch (route.strategy) {\n case \"cache-first\":\n return cacheFirst(request, route);\n case \"network-first\":\n return networkFirst(request, route, extend);\n case \"stale-while-revalidate\":\n return staleWhileRevalidate(request, route, extend);\n }\n}\n\nfunction routeMatches(route: RuntimeRoute, url: URL, request: Request): boolean {\n return typeof route.match === \"function\"\n ? route.match(url, request)\n : route.match.test(url.href);\n}\n\n/**\n * Build a `206 Partial Content` response from a full one for an HTTP `Range`\n * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix\n * `bytes=-suffixLength` (the last N bytes). Returns the original response when there is no usable\n * `Range` header, or a `416` when the range is unsatisfiable.\n *\n * @param request The incoming request (its `Range` header drives the slice).\n * @param response The full (200) response to slice.\n */\nexport async function createPartialResponse(\n request: Request,\n response: Response,\n): Promise<Response> {\n const rangeHeader = request.headers.get(\"range\");\n if (!rangeHeader) return response;\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match || (match[1] === \"\" && match[2] === \"\")) {\n return new Response(null, { status: 416, statusText: \"Range Not Satisfiable\" });\n }\n\n const buffer = await response.clone().arrayBuffer();\n const total = buffer.byteLength;\n\n let start: number;\n let end: number;\n if (match[1] === \"\") {\n const suffix = Number(match[2]);\n start = Math.max(0, total - suffix);\n end = total - 1;\n } else {\n start = Number(match[1]);\n end = match[2] === \"\" ? total - 1 : Math.min(Number(match[2]), total - 1);\n }\n\n if (start > end || start >= total) {\n return new Response(null, {\n status: 416,\n statusText: \"Range Not Satisfiable\",\n headers: { \"Content-Range\": `bytes */${total}` },\n });\n }\n\n const slice = buffer.slice(start, end + 1);\n const headers = new Headers(response.headers);\n headers.set(\"Content-Range\", `bytes ${start}-${end}/${total}`);\n headers.set(\"Content-Length\", String(slice.byteLength));\n headers.set(\"Accept-Ranges\", \"bytes\");\n\n return new Response(slice, {\n status: 206,\n statusText: \"Partial Content\",\n headers,\n });\n}\n\n/**\n * Install a `fetch` handler that resolves matching `GET` requests with the\n * given runtime strategies. Non-matching requests are left untouched (no\n * `respondWith`), so a later {@link installPrecache} can handle them.\n *\n * A request carrying a credential header (`Authorization`, `X-Api-Key`, … — the\n * route's `credentialHeaders`), or a CORS `fetch` sent with\n * `credentials: \"include\"`, is also left untouched unless its route sets\n * `cacheAuthenticated`, and a response with `Cache-Control: no-store`\n * (or `private`, without that opt-in) or `Vary: *` is served but never stored. `maxAgeSeconds`\n * applies to every read, including the offline fallback: an expired entry is a\n * miss, never a last resort. Under `rangeRequests`, the full resource is cached\n * once (the `Range` header stripped) and each range is sliced from it.\n *\n * Register this BEFORE `installPrecache` so specific routes win over the\n * precache catch-all.\n *\n * @param routes Ordered rules; the first whose `match` passes handles the request.\n */\nexport function installRuntimeCache(routes: RuntimeRoute[]): void {\n const sw = getSwScope();\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n const route = routes.find((candidate) => routeMatches(candidate, url, request));\n if (!route) return;\n if (!route.cacheAuthenticated && isCredentialed(request, route)) return;\n answeredByRuntime.add(event);\n const extend = (promise: Promise<unknown>): void => event.waitUntil(promise);\n\n if (route.rangeRequests && request.headers.has(\"range\")) {\n const full = new Request(request.url, {\n headers: stripRange(request.headers),\n credentials: request.credentials,\n mode: request.mode === \"navigate\" ? \"same-origin\" : request.mode,\n });\n event.respondWith(\n runRoute(full, route, extend).then((response) =>\n createPartialResponse(request, response),\n ),\n );\n return;\n }\n\n event.respondWith(runRoute(request, route, extend));\n });\n}\n\n/** Copy headers without the `Range` header (so the cached entry is the full file). */\nfunction stripRange(headers: Headers): Headers {\n const out = new Headers(headers);\n out.delete(\"range\");\n return out;\n}\n\n/** Read the {@link PrecacheState} a versioned precache recorded, if any. */\nasync function readPrecacheState(cache: Cache): Promise<PrecacheState | undefined> {\n const response = await cache.match(PRECACHE_STATE_PATH);\n if (!response) return undefined;\n try {\n return (await response.json()) as PrecacheState;\n } catch {\n return undefined;\n }\n}\n\nasync function writePrecacheState(cache: Cache, state: PrecacheState): Promise<void> {\n await cache.put(\n PRECACHE_STATE_PATH,\n new Response(JSON.stringify(state), { headers: { \"content-type\": \"application/json\" } }),\n );\n}\n\n/**\n * The name of the precache version whose `pick` timestamp is the latest, among\n * the `${cacheName}-*` caches that recorded a {@link PrecacheState}.\n */\nasync function latestPrecache(\n cacheName: string,\n pick: (state: PrecacheState) => number | undefined,\n): Promise<string | undefined> {\n let latest: { name: string; at: number } | undefined;\n for (const name of await caches.keys()) {\n if (!name.startsWith(`${cacheName}-`)) continue;\n const state = await readPrecacheState(await caches.open(name));\n const at = state ? pick(state) : undefined;\n if (at !== undefined && (!latest || at > latest.at)) latest = { name, at };\n }\n return latest?.name;\n}\n\n/** The pathnames a precache holds, read back from the cache itself. */\nasync function describePrecache(name: string): Promise<ActivePrecache> {\n const keys = await (await caches.open(name)).keys();\n const paths = new Set(\n keys\n .map((request) => new URL(request.url).pathname)\n .filter((path) => path !== PRECACHE_STATE_PATH),\n );\n return { name, paths };\n}\n\nasync function fromPrecache(name: string, request: Request): Promise<Response> {\n const cached = await (await caches.open(name)).match(request);\n return cached ?? fetch(request);\n}\n\n/**\n * Precache the app shell at `install` and serve it offline:\n * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),\n * - caches every listed URL under a versioned cache,\n * - on `activate`, deletes stale precache versions and claims open clients,\n * - on `fetch`, serves precached assets cache-first and falls back to the\n * `navigateFallback` document for offline navigations (SPA routing).\n *\n * Survives the browser stopping and restarting the worker, which it does\n * whenever the worker sits idle and which never repeats `install`. Nothing the\n * fetch handler needs lives only in memory: `install` and `activate` record, in\n * an entry inside the versioned cache, when that version was installed and\n * activated, and a restarted worker reads back the activated version and its\n * pathnames from Cache Storage. Before 0.83.0 the cache name and the precached\n * paths were module variables filled only by `install`, so after the first\n * restart offline navigations failed with \"offline and no cached app shell\",\n * precached assets went to the network, and an `activate` in a run without\n * `install` deleted every precache, the current one included (#572).\n *\n * `activate` deletes the other `${cacheName}-*` caches only once it knows which\n * version it is activating; when no version recorded an install it deletes\n * nothing. In the first request after a restart, while the worker is still\n * reading which paths are precached, a same-origin `GET` that a runtime route\n * did not answer is answered with `fetch(request)` when it is not precached —\n * the decision to call `respondWith` cannot wait for Cache Storage. From then on\n * a non-precached request is left to the browser, as before.\n *\n * Same-origin only. Register this LAST, after any {@link installRuntimeCache}:\n * a request a runtime route answered is never answered again here.\n */\nexport function installPrecache(options: InstallPrecacheOptions = {}): void {\n const sw = getSwScope();\n const {\n manifestUrl = \"/precache-manifest.json\",\n cacheName = \"tempest-precache\",\n navigateFallback = \"/index.html\",\n navigateFallbackDenylist = [],\n skipWaiting = true,\n navigationPreload = true,\n } = options;\n\n let installedName: string | undefined;\n let active: ActivePrecache | undefined;\n let known = false;\n let loading: Promise<ActivePrecache | undefined> | undefined;\n\n const loadActive = (): Promise<ActivePrecache | undefined> => {\n if (known) return Promise.resolve(active);\n loading ??= (async () => {\n const name = await latestPrecache(cacheName, (state) => state.activatedAt);\n const found = name ? await describePrecache(name) : undefined;\n if (!known) {\n active = found;\n known = true;\n }\n return active;\n })().finally(() => {\n loading = undefined;\n });\n return loading;\n };\n\n sw.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const response = await fetch(manifestUrl, { cache: \"no-cache\" });\n const manifest = (await response.json()) as PrecacheManifest;\n const name = `${cacheName}-${manifest.version}`;\n\n const cache = await caches.open(name);\n await cache.addAll(manifest.urls);\n const previous = await readPrecacheState(cache);\n await writePrecacheState(cache, { ...previous, installedAt: Date.now() });\n installedName = name;\n if (skipWaiting) await sw.skipWaiting();\n })(),\n );\n });\n\n sw.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const name =\n installedName ??\n (await latestPrecache(cacheName, (state) =>\n state.installedAt > (state.activatedAt ?? -Infinity)\n ? state.installedAt\n : undefined,\n ));\n if (name) {\n const cache = await caches.open(name);\n const previous = await readPrecacheState(cache);\n await writePrecacheState(cache, {\n installedAt: Date.now(),\n ...previous,\n activatedAt: Date.now(),\n });\n active = await describePrecache(name);\n known = true;\n const keys = await caches.keys();\n await Promise.all(\n keys\n .filter((key) => key.startsWith(`${cacheName}-`) && key !== name)\n .map((key) => caches.delete(key)),\n );\n }\n if (navigationPreload && sw.registration?.navigationPreload) {\n await sw.registration.navigationPreload.enable();\n }\n await sw.clients.claim();\n })(),\n );\n });\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n if (answeredByRuntime.has(event)) return;\n\n const url = new URL(request.url);\n if (url.origin !== sw.location.origin) return;\n\n if (request.mode === \"navigate\") {\n if (navigateFallbackDenylist.some((re) => re.test(url.pathname))) return;\n event.respondWith(\n (async () => {\n try {\n const preloaded = await event.preloadResponse;\n if (preloaded) return preloaded;\n return await fetch(request);\n } catch {\n const precache = await loadActive();\n const shell = precache\n ? await (await caches.open(precache.name)).match(navigateFallback)\n : undefined;\n if (shell) return shell;\n throw new Error(\"offline and no cached app shell\");\n }\n })(),\n );\n return;\n }\n\n if (known) {\n if (active?.paths.has(url.pathname)) {\n event.respondWith(fromPrecache(active.name, request));\n }\n return;\n }\n\n event.respondWith(\n loadActive()\n .catch(() => undefined)\n .then((precache) =>\n precache?.paths.has(url.pathname)\n ? fromPrecache(precache.name, request)\n : fetch(request),\n ),\n );\n });\n}\n"],"mappings":"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"}
|
package/dist/sw/cache.js
CHANGED
|
@@ -3,7 +3,7 @@ import { credentialHeaderSet as e } from "./credential-headers.js";
|
|
|
3
3
|
function t() {
|
|
4
4
|
return globalThis;
|
|
5
5
|
}
|
|
6
|
-
var n =
|
|
6
|
+
var n = /* @__PURE__ */ new WeakSet(), r = "/__tempest-precache-state__", i = "x-tempest-cached-at";
|
|
7
7
|
function a(e, t) {
|
|
8
8
|
if (!t) return !1;
|
|
9
9
|
let n = Number(e.headers.get(i)), r = e.headers.get("date"), a = n > 0 ? n : r ? new Date(r).getTime() : NaN;
|
|
@@ -114,60 +114,110 @@ async function v(e, t) {
|
|
|
114
114
|
}
|
|
115
115
|
function y(e) {
|
|
116
116
|
t().addEventListener("fetch", (t) => {
|
|
117
|
-
let
|
|
118
|
-
if (
|
|
119
|
-
let
|
|
120
|
-
if (!
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
let r = t.request;
|
|
118
|
+
if (r.method !== "GET") return;
|
|
119
|
+
let i = new URL(r.url), a = e.find((e) => _(e, i, r));
|
|
120
|
+
if (!a || !a.cacheAuthenticated && l(r, a)) return;
|
|
121
|
+
n.add(t);
|
|
122
|
+
let o = (e) => t.waitUntil(e);
|
|
123
|
+
if (a.rangeRequests && r.headers.has("range")) {
|
|
124
|
+
let e = new Request(r.url, {
|
|
125
|
+
headers: b(r.headers),
|
|
126
|
+
credentials: r.credentials,
|
|
127
|
+
mode: r.mode === "navigate" ? "same-origin" : r.mode
|
|
127
128
|
});
|
|
128
|
-
t.respondWith(g(e,
|
|
129
|
-
} else t.respondWith(g(
|
|
129
|
+
t.respondWith(g(e, a, o).then((e) => v(r, e)));
|
|
130
|
+
} else t.respondWith(g(r, a, o));
|
|
130
131
|
});
|
|
131
132
|
}
|
|
132
133
|
function b(e) {
|
|
133
134
|
let t = new Headers(e);
|
|
134
135
|
return t.delete("range"), t;
|
|
135
136
|
}
|
|
136
|
-
function x(e
|
|
137
|
-
let
|
|
138
|
-
|
|
137
|
+
async function x(e) {
|
|
138
|
+
let t = await e.match(r);
|
|
139
|
+
if (t) try {
|
|
140
|
+
return await t.json();
|
|
141
|
+
} catch {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function S(e, t) {
|
|
146
|
+
await e.put(r, new Response(JSON.stringify(t), { headers: { "content-type": "application/json" } }));
|
|
147
|
+
}
|
|
148
|
+
async function C(e, t) {
|
|
149
|
+
let n;
|
|
150
|
+
for (let r of await caches.keys()) {
|
|
151
|
+
if (!r.startsWith(`${e}-`)) continue;
|
|
152
|
+
let i = await x(await caches.open(r)), a = i ? t(i) : void 0;
|
|
153
|
+
a !== void 0 && (!n || a > n.at) && (n = {
|
|
154
|
+
name: r,
|
|
155
|
+
at: a
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return n?.name;
|
|
159
|
+
}
|
|
160
|
+
async function w(e) {
|
|
161
|
+
let t = await (await caches.open(e)).keys();
|
|
162
|
+
return {
|
|
163
|
+
name: e,
|
|
164
|
+
paths: new Set(t.map((e) => new URL(e.url).pathname).filter((e) => e !== r))
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async function T(e, t) {
|
|
168
|
+
return await (await caches.open(e)).match(t) ?? fetch(t);
|
|
169
|
+
}
|
|
170
|
+
function E(e = {}) {
|
|
171
|
+
let r = t(), { manifestUrl: i = "/precache-manifest.json", cacheName: a = "tempest-precache", navigateFallback: o = "/index.html", navigateFallbackDenylist: s = [], skipWaiting: c = !0, navigationPreload: l = !0 } = e, u, d, f = !1, p, m = () => f ? Promise.resolve(d) : (p ??= (async () => {
|
|
172
|
+
let e = await C(a, (e) => e.activatedAt), t = e ? await w(e) : void 0;
|
|
173
|
+
return f ||= (d = t, !0), d;
|
|
174
|
+
})().finally(() => {
|
|
175
|
+
p = void 0;
|
|
176
|
+
}), p);
|
|
177
|
+
r.addEventListener("install", (e) => {
|
|
139
178
|
e.waitUntil((async () => {
|
|
140
|
-
let e = await (await fetch(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
179
|
+
let e = await (await fetch(i, { cache: "no-cache" })).json(), t = `${a}-${e.version}`, n = await caches.open(t);
|
|
180
|
+
await n.addAll(e.urls), await S(n, {
|
|
181
|
+
...await x(n),
|
|
182
|
+
installedAt: Date.now()
|
|
183
|
+
}), u = t, c && await r.skipWaiting();
|
|
144
184
|
})());
|
|
145
|
-
}),
|
|
185
|
+
}), r.addEventListener("activate", (e) => {
|
|
146
186
|
e.waitUntil((async () => {
|
|
147
|
-
let e = await
|
|
148
|
-
|
|
187
|
+
let e = u ?? await C(a, (e) => e.installedAt > (e.activatedAt ?? -Infinity) ? e.installedAt : void 0);
|
|
188
|
+
if (e) {
|
|
189
|
+
let t = await caches.open(e), n = await x(t);
|
|
190
|
+
await S(t, {
|
|
191
|
+
installedAt: Date.now(),
|
|
192
|
+
...n,
|
|
193
|
+
activatedAt: Date.now()
|
|
194
|
+
}), d = await w(e), f = !0;
|
|
195
|
+
let r = await caches.keys();
|
|
196
|
+
await Promise.all(r.filter((t) => t.startsWith(`${a}-`) && t !== e).map((e) => caches.delete(e)));
|
|
197
|
+
}
|
|
198
|
+
l && r.registration?.navigationPreload && await r.registration.navigationPreload.enable(), await r.clients.claim();
|
|
149
199
|
})());
|
|
150
|
-
}),
|
|
200
|
+
}), r.addEventListener("fetch", (e) => {
|
|
151
201
|
let t = e.request;
|
|
152
|
-
if (t.method !== "GET") return;
|
|
153
|
-
let
|
|
154
|
-
if (
|
|
202
|
+
if (t.method !== "GET" || n.has(e)) return;
|
|
203
|
+
let i = new URL(t.url);
|
|
204
|
+
if (i.origin === r.location.origin) {
|
|
155
205
|
if (t.mode === "navigate") {
|
|
156
|
-
if (
|
|
206
|
+
if (s.some((e) => e.test(i.pathname))) return;
|
|
157
207
|
e.respondWith((async () => {
|
|
158
208
|
try {
|
|
159
209
|
return await e.preloadResponse || await fetch(t);
|
|
160
210
|
} catch {
|
|
161
|
-
let e = await (await caches.open(
|
|
162
|
-
if (
|
|
211
|
+
let e = await m(), t = e ? await (await caches.open(e.name)).match(o) : void 0;
|
|
212
|
+
if (t) return t;
|
|
163
213
|
throw Error("offline and no cached app shell");
|
|
164
214
|
}
|
|
165
215
|
})());
|
|
166
|
-
} else
|
|
216
|
+
} else f ? d?.paths.has(i.pathname) && e.respondWith(T(d.name, t)) : e.respondWith(m().catch(() => void 0).then((e) => e?.paths.has(i.pathname) ? T(e.name, t) : fetch(t)));
|
|
167
217
|
}
|
|
168
218
|
});
|
|
169
219
|
}
|
|
170
220
|
//#endregion
|
|
171
|
-
export { v as createPartialResponse,
|
|
221
|
+
export { v as createPartialResponse, E as installPrecache, y as installRuntimeCache };
|
|
172
222
|
|
|
173
223
|
//# sourceMappingURL=cache.js.map
|
package/dist/sw/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","names":["getSwScope","precacheName","precachedPaths","CACHED_AT_HEADER","isExpired","maxAgeSeconds","stamp","response","dateHeader","storedAt","cacheControlDirectives","directive","varyNames","vary","name","isStorable","directives","route","isCredentialed","request","credentialsOn","credentialHeaderSet","store","headers","varies","missing","copy","cache","trimCache","maxEntries","cacheName","keys","key","cacheFirst","cached","networkFirst","network","timeoutMs","timeout","extend","reject","staleWhileRevalidate","fresh","runRoute","routeMatches","url","createPartialResponse","rangeHeader","match","buffer","total","start","end","suffix","slice","installRuntimeCache","event","routes","candidate","promise","full","stripRange","out","installPrecache","sw","options","manifest","manifestUrl","skipWaiting","navigationPreload","navigateFallbackDenylist","re","shell","navigateFallback"],"sources":["../../src/sw/cache.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines — precache, runtime strategies (cache-first, network-\n * first, stale-while-revalidate), expiry and the inspect/clear helpers all address\n * the same Cache Storage namespace, and the naming scheme that keeps them from\n * evicting each other is the file's whole contract.\n */\n/**\n * Service-worker caching helpers — a small, dependency-free subset of what\n * Workbox provides: precaching of the build's app shell (so the app launches\n * offline) plus runtime caching strategies for fonts, APIs and images.\n *\n * Import these inside your own `sw.ts`. They run in the service-worker global\n * scope, not the main thread. Pair `installPrecache` with the\n * `tempestPwaManifest()` Vite plugin (from `tempest-react-sdk/vite`), which\n * emits the `precache-manifest.json` this reads at install time.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installRuntimeCache, installPrecache } from \"tempest-react-sdk/sw\";\n *\n * // Register specific routes FIRST so they win over the precache catch-all.\n * installRuntimeCache([\n * { match: /\\/api\\//, strategy: \"network-first\", cacheName: \"tempest-api\", maxAgeSeconds: 300 },\n * ]);\n * installPrecache();\n */\n\nimport { credentialHeaderSet } from \"./credential-headers\";\n\n/** Minimal shape of the events we touch, to avoid pulling in `lib.webworker`. */\ninterface ExtendableEventLike {\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface FetchEventLike extends ExtendableEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n /** Resolves to the navigation-preload response when it is enabled, else `undefined`. */\n preloadResponse?: Promise<Response | undefined>;\n}\n\n/** Minimal shape of `registration.navigationPreload` (Navigation Preload API). */\ninterface NavigationPreloadManagerLike {\n enable(): Promise<void>;\n disable(): Promise<void>;\n}\n\ninterface CacheSwGlobal {\n addEventListener(type: \"install\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"activate\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: FetchEventLike) => void): void;\n clients: { claim(): Promise<void> };\n skipWaiting(): Promise<void>;\n location: { origin: string };\n registration?: { navigationPreload?: NavigationPreloadManagerLike };\n}\n\nfunction getSwScope(): CacheSwGlobal {\n return globalThis as unknown as CacheSwGlobal;\n}\n\n/** Caching strategy for a runtime route. Mirrors the common Workbox trio. */\nexport type RuntimeStrategy = \"cache-first\" | \"network-first\" | \"stale-while-revalidate\";\n\n/** A single runtime-caching rule, matched against each `GET` request. */\nexport interface RuntimeRoute {\n /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */\n match: RegExp | ((url: URL, request: Request) => boolean);\n /** How to resolve a match. */\n strategy: RuntimeStrategy;\n /** Cache bucket name for this route. */\n cacheName: string;\n /** Trim the cache to at most this many entries (FIFO) after each write. */\n maxEntries?: number;\n /** Treat a cached response older than this (seconds) as a miss. */\n maxAgeSeconds?: number;\n /** For `network-first`: fall back to cache after this timeout (seconds). */\n networkTimeoutSeconds?: number;\n /**\n * Serve HTTP `Range` requests (206 Partial Content) by slicing the cached\n * full response. Enable for audio/video so seeking works offline. The full\n * resource is cached once (the `Range` header is stripped before caching).\n */\n rangeRequests?: boolean;\n /**\n * Opt in to caching credentialed requests — those that carry a credential\n * header (see `credentialHeaders`) or are a CORS-mode `fetch` sent with\n * `credentials: \"include\"` — and responses marked `Cache-Control: private`.\n * Default `false`.\n *\n * Cache Storage is keyed by URL and outlives the session: without this\n * gate, a `GET /api/me` stored for one account was served to the next\n * account on the same browser whenever the network failed or exceeded\n * `networkTimeoutSeconds` (reproduced in Chromium 153, 11/10/2026). With\n * the default, credentialed requests bypass the route entirely and reach\n * the network as if no service worker were installed.\n *\n * When enabled, every stored entry gets `Vary` on each credential header the\n * request carried, so the Cache API only matches it for the same credential —\n * another account (or no account) misses instead of reading it. A token\n * rotated while online simply repopulates the entry. Pair it with\n * `clearSessionData()` on logout, which removes the entries themselves.\n * `Cache-Control: no-store` is never cached, opt-in or not.\n *\n * A cookie session is only partly visible here: the worker cannot see\n * `Cookie`. What it can see is a `fetch` that asked for cookies explicitly —\n * `mode: \"cors\"` with `credentials: \"include\"`, which is what `ApiClient`\n * sends under `withCredentials` — and since 0.82.0 that request is treated\n * as credentialed too (#531). `<img>`, `<audio>` and `<video>` also send\n * `credentials: \"include\"`, but in `no-cors` mode, so they keep caching. A\n * plain same-origin `fetch` (`credentials: \"same-origin\"`, the default)\n * carries the cookie without saying so; such an API stays private only\n * through `Cache-Control: private`/`no-store` on its responses and\n * `clearSessionData()` on logout. Opting a cookie request in caches it by\n * URL — there is no credential header to vary on — so `clearSessionData()`\n * is then its only isolation.\n */\n cacheAuthenticated?: boolean;\n /**\n * Header names that mark a request as credentialed. Default\n * `DEFAULT_CREDENTIAL_HEADERS` (`authorization`, `x-api-key`,\n * `x-auth-token`, …) — the same list the background-sync queue strips.\n */\n credentialHeaders?: readonly string[];\n}\n\n/** Options for {@link installPrecache}. */\nexport interface InstallPrecacheOptions {\n /** URL of the manifest emitted by `tempestPwaManifest()`. Default `/precache-manifest.json`. */\n manifestUrl?: string;\n /** Cache name prefix; the manifest `version` is appended. Default `tempest-precache`. */\n cacheName?: string;\n /** App-shell document served for navigation requests offline. Default `/index.html`. */\n navigateFallback?: string;\n /** Navigation paths that should NOT use the fallback (e.g. `[/^\\/api\\//]`). */\n navigateFallbackDenylist?: RegExp[];\n /** Activate the new worker immediately after precaching. Default `true`. */\n skipWaiting?: boolean;\n /**\n * Enable the Navigation Preload API on `activate`, so the browser fetches\n * the navigation request in parallel with the worker boot and the handler\n * serves `event.preloadResponse` — cutting first-navigation latency after\n * the worker starts. Silently ignored where unsupported. Default `true`.\n */\n navigationPreload?: boolean;\n}\n\ninterface PrecacheManifest {\n version: string;\n urls: string[];\n}\n\nlet precacheName = \"\";\nconst precachedPaths = new Set<string>();\n\n/**\n * Header stamped on every runtime-cache entry with the epoch milliseconds of the\n * write. `Date` alone is not enough: it is not a CORS-safelisted response\n * header, so for a cross-origin API the worker reads `null` and `maxAgeSeconds`\n * would never expire anything.\n */\nconst CACHED_AT_HEADER = \"x-tempest-cached-at\";\n\n/**\n * Whether a cached response has aged past `maxAgeSeconds`. Reads the\n * {@link CACHED_AT_HEADER} stamp written by the runtime cache, and falls back to\n * `Date` for entries written before the stamp existed.\n */\nfunction isExpired(response: Response, maxAgeSeconds?: number): boolean {\n if (!maxAgeSeconds) return false;\n const stamp = Number(response.headers.get(CACHED_AT_HEADER));\n const dateHeader = response.headers.get(\"date\");\n const storedAt = stamp > 0 ? stamp : dateHeader ? new Date(dateHeader).getTime() : NaN;\n if (Number.isNaN(storedAt)) return false;\n return (Date.now() - storedAt) / 1000 > maxAgeSeconds;\n}\n\n/** The `Cache-Control` directives of a response, lower-cased. */\nfunction cacheControlDirectives(response: Response): string[] {\n return (response.headers.get(\"cache-control\") ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((directive) => directive.trim().split(\"=\")[0]);\n}\n\n/** The comma-separated names of a response's `Vary` header, trimmed and lower-cased. */\nfunction varyNames(vary: string | null): string[] {\n return (vary ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((name) => name.trim());\n}\n\n/**\n * Whether a network response may be written to the route's cache: it must be\n * `ok`, never `no-store`, never `Vary: *`, and `private` only under\n * `cacheAuthenticated`.\n *\n * `Vary: *` says no later request can be proven to match, and `Cache.put`\n * rejects it with a `TypeError`. Before 0.82.0 that rejection escaped the\n * strategy, so a response the network had delivered became the offline\n * fallback path (network-first), an error (cache-first) or nothing at all\n * (stale-while-revalidate) (#534).\n */\nfunction isStorable(response: Response, route: RuntimeRoute): boolean {\n if (!response.ok) return false;\n if (varyNames(response.headers.get(\"vary\")).includes(\"*\")) return false;\n const directives = cacheControlDirectives(response);\n if (directives.includes(\"no-store\")) return false;\n return route.cacheAuthenticated === true || !directives.includes(\"private\");\n}\n\n/**\n * Whether a request is session-bound for its route: it carries a credential\n * header, or it is a CORS `fetch` that asked for cookies with\n * `credentials: \"include\"` (`ApiClient` under `withCredentials`). A `no-cors`\n * request — `<img>`, `<audio>`, `<video>` — also sends `include` by default and\n * is not counted, or every image would bypass the cache.\n */\nfunction isCredentialed(request: Request, route: RuntimeRoute): boolean {\n if (request.mode === \"cors\" && request.credentials === \"include\") return true;\n return credentialsOn(request, route).length > 0;\n}\n\n/** The credential headers, lower-cased, that a request carries for its route. */\nfunction credentialsOn(request: Request, route: RuntimeRoute): string[] {\n return [...credentialHeaderSet(route.credentialHeaders)].filter((name) =>\n request.headers.has(name),\n );\n}\n\n/**\n * Write a response to the route's cache with the {@link CACHED_AT_HEADER} stamp\n * and, under `cacheAuthenticated`, `Vary` on each credential header the request\n * carried, so the entry only matches the credential that produced it. Skips\n * responses that {@link isStorable} rejects, then trims the cache to\n * `maxEntries`.\n */\nasync function store(\n cache: Cache,\n request: Request,\n response: Response,\n route: RuntimeRoute,\n): Promise<void> {\n if (!isStorable(response, route)) return;\n const headers = new Headers(response.headers);\n headers.set(CACHED_AT_HEADER, String(Date.now()));\n if (route.cacheAuthenticated) {\n const vary = headers.get(\"vary\");\n const varies = varyNames(vary);\n const missing = credentialsOn(request, route).filter((name) => !varies.includes(name));\n if (missing.length > 0) {\n headers.set(\"vary\", [vary, ...missing].filter(Boolean).join(\", \"));\n }\n }\n const copy = response.clone();\n await cache.put(\n request,\n new Response(copy.body, { status: copy.status, statusText: copy.statusText, headers }),\n );\n await trimCache(route.cacheName, route.maxEntries);\n}\n\n/** Drop oldest entries until the cache holds at most `maxEntries`. */\nasync function trimCache(cacheName: string, maxEntries?: number): Promise<void> {\n if (!maxEntries) return;\n const cache = await caches.open(cacheName);\n const keys = await cache.keys();\n if (keys.length <= maxEntries) return;\n for (const key of keys.slice(0, keys.length - maxEntries)) {\n await cache.delete(key);\n }\n}\n\nasync function cacheFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n}\n\n/**\n * Network-first. When the timeout wins, the network request keeps running to\n * refresh the cache, and is handed to `extend` (`event.waitUntil`) so the\n * worker is not stopped mid-write.\n */\nasync function networkFirst(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n\n const network = (async (): Promise<Response> => {\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n })();\n\n const timeoutMs = (route.networkTimeoutSeconds ?? 0) * 1000;\n try {\n if (timeoutMs > 0) {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => {\n extend(network.catch(() => undefined));\n reject(new Error(\"network-timeout\"));\n }, timeoutMs),\n );\n return await Promise.race([network, timeout]);\n }\n return await network;\n } catch {\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n throw new Error(\"network-first: no network and no cache\");\n }\n}\n\n/**\n * Stale-while-revalidate. A fresh cached response is served at once, and the\n * revalidation is handed to `extend` (`event.waitUntil`): without it the\n * browser may stop the worker before the refreshed response is written.\n */\nasync function staleWhileRevalidate(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n\n const network = fetch(request)\n .then(async (response) => {\n await store(cache, request, response, route);\n return response;\n })\n .catch(() => undefined);\n\n const fresh = cached && !isExpired(cached, route.maxAgeSeconds) ? cached : undefined;\n if (fresh) {\n extend(network);\n return fresh;\n }\n const response = await network;\n if (response) return response;\n throw new Error(\"stale-while-revalidate: no network and no cache\");\n}\n\nfunction runRoute(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n switch (route.strategy) {\n case \"cache-first\":\n return cacheFirst(request, route);\n case \"network-first\":\n return networkFirst(request, route, extend);\n case \"stale-while-revalidate\":\n return staleWhileRevalidate(request, route, extend);\n }\n}\n\nfunction routeMatches(route: RuntimeRoute, url: URL, request: Request): boolean {\n return typeof route.match === \"function\"\n ? route.match(url, request)\n : route.match.test(url.href);\n}\n\n/**\n * Build a `206 Partial Content` response from a full one for an HTTP `Range`\n * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix\n * `bytes=-suffixLength` (the last N bytes). Returns the original response when there is no usable\n * `Range` header, or a `416` when the range is unsatisfiable.\n *\n * @param request The incoming request (its `Range` header drives the slice).\n * @param response The full (200) response to slice.\n */\nexport async function createPartialResponse(\n request: Request,\n response: Response,\n): Promise<Response> {\n const rangeHeader = request.headers.get(\"range\");\n if (!rangeHeader) return response;\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match || (match[1] === \"\" && match[2] === \"\")) {\n return new Response(null, { status: 416, statusText: \"Range Not Satisfiable\" });\n }\n\n const buffer = await response.clone().arrayBuffer();\n const total = buffer.byteLength;\n\n let start: number;\n let end: number;\n if (match[1] === \"\") {\n const suffix = Number(match[2]);\n start = Math.max(0, total - suffix);\n end = total - 1;\n } else {\n start = Number(match[1]);\n end = match[2] === \"\" ? total - 1 : Math.min(Number(match[2]), total - 1);\n }\n\n if (start > end || start >= total) {\n return new Response(null, {\n status: 416,\n statusText: \"Range Not Satisfiable\",\n headers: { \"Content-Range\": `bytes */${total}` },\n });\n }\n\n const slice = buffer.slice(start, end + 1);\n const headers = new Headers(response.headers);\n headers.set(\"Content-Range\", `bytes ${start}-${end}/${total}`);\n headers.set(\"Content-Length\", String(slice.byteLength));\n headers.set(\"Accept-Ranges\", \"bytes\");\n\n return new Response(slice, {\n status: 206,\n statusText: \"Partial Content\",\n headers,\n });\n}\n\n/**\n * Install a `fetch` handler that resolves matching `GET` requests with the\n * given runtime strategies. Non-matching requests are left untouched (no\n * `respondWith`), so a later {@link installPrecache} can handle them.\n *\n * A request carrying a credential header (`Authorization`, `X-Api-Key`, … — the\n * route's `credentialHeaders`), or a CORS `fetch` sent with\n * `credentials: \"include\"`, is also left untouched unless its route sets\n * `cacheAuthenticated`, and a response with `Cache-Control: no-store`\n * (or `private`, without that opt-in) or `Vary: *` is served but never stored. `maxAgeSeconds`\n * applies to every read, including the offline fallback: an expired entry is a\n * miss, never a last resort. Under `rangeRequests`, the full resource is cached\n * once (the `Range` header stripped) and each range is sliced from it.\n *\n * Register this BEFORE `installPrecache` so specific routes win over the\n * precache catch-all.\n *\n * @param routes Ordered rules; the first whose `match` passes handles the request.\n */\nexport function installRuntimeCache(routes: RuntimeRoute[]): void {\n const sw = getSwScope();\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n const route = routes.find((candidate) => routeMatches(candidate, url, request));\n if (!route) return;\n if (!route.cacheAuthenticated && isCredentialed(request, route)) return;\n const extend = (promise: Promise<unknown>): void => event.waitUntil(promise);\n\n if (route.rangeRequests && request.headers.has(\"range\")) {\n const full = new Request(request.url, {\n headers: stripRange(request.headers),\n credentials: request.credentials,\n mode: request.mode === \"navigate\" ? \"same-origin\" : request.mode,\n });\n event.respondWith(\n runRoute(full, route, extend).then((response) =>\n createPartialResponse(request, response),\n ),\n );\n return;\n }\n\n event.respondWith(runRoute(request, route, extend));\n });\n}\n\n/** Copy headers without the `Range` header (so the cached entry is the full file). */\nfunction stripRange(headers: Headers): Headers {\n const out = new Headers(headers);\n out.delete(\"range\");\n return out;\n}\n\n/**\n * Precache the app shell at `install` and serve it offline:\n * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),\n * - caches every listed URL under a versioned cache,\n * - on `activate`, deletes stale precache versions and claims open clients,\n * - on `fetch`, serves precached assets cache-first and falls back to the\n * `navigateFallback` document for offline navigations (SPA routing).\n *\n * Same-origin only. Register this LAST, after any {@link installRuntimeCache}.\n */\nexport function installPrecache(options: InstallPrecacheOptions = {}): void {\n const sw = getSwScope();\n const {\n manifestUrl = \"/precache-manifest.json\",\n cacheName = \"tempest-precache\",\n navigateFallback = \"/index.html\",\n navigateFallbackDenylist = [],\n skipWaiting = true,\n navigationPreload = true,\n } = options;\n\n sw.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const response = await fetch(manifestUrl, { cache: \"no-cache\" });\n const manifest = (await response.json()) as PrecacheManifest;\n precacheName = `${cacheName}-${manifest.version}`;\n\n const cache = await caches.open(precacheName);\n await cache.addAll(manifest.urls);\n for (const url of manifest.urls) {\n precachedPaths.add(new URL(url, sw.location.origin).pathname);\n }\n if (skipWaiting) await sw.skipWaiting();\n })(),\n );\n });\n\n sw.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const keys = await caches.keys();\n await Promise.all(\n keys\n .filter((key) => key.startsWith(`${cacheName}-`) && key !== precacheName)\n .map((key) => caches.delete(key)),\n );\n if (navigationPreload && sw.registration?.navigationPreload) {\n await sw.registration.navigationPreload.enable();\n }\n await sw.clients.claim();\n })(),\n );\n });\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n if (url.origin !== sw.location.origin) return;\n\n // SPA navigations: serve the cached app shell when offline.\n if (request.mode === \"navigate\") {\n if (navigateFallbackDenylist.some((re) => re.test(url.pathname))) return;\n event.respondWith(\n (async () => {\n try {\n const preloaded = await event.preloadResponse;\n if (preloaded) return preloaded;\n return await fetch(request);\n } catch {\n const cache = await caches.open(precacheName);\n const shell = await cache.match(navigateFallback);\n if (shell) return shell;\n throw new Error(\"offline and no cached app shell\");\n }\n })(),\n );\n return;\n }\n\n // Precached static assets: cache-first.\n if (precachedPaths.has(url.pathname)) {\n event.respondWith(\n (async () => {\n const cache = await caches.open(precacheName);\n const cached = await cache.match(request);\n return cached ?? fetch(request);\n })(),\n );\n }\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache.js","names":["getSwScope","answeredByRuntime","PRECACHE_STATE_PATH","CACHED_AT_HEADER","isExpired","maxAgeSeconds","stamp","response","dateHeader","storedAt","cacheControlDirectives","directive","varyNames","vary","name","isStorable","directives","route","isCredentialed","request","credentialsOn","credentialHeaderSet","store","headers","varies","missing","copy","cache","trimCache","maxEntries","cacheName","keys","key","cacheFirst","cached","networkFirst","network","timeoutMs","timeout","extend","reject","staleWhileRevalidate","fresh","runRoute","routeMatches","url","createPartialResponse","rangeHeader","match","buffer","total","start","end","suffix","slice","installRuntimeCache","event","routes","candidate","promise","full","stripRange","out","readPrecacheState","writePrecacheState","state","latestPrecache","latest","at","pick","describePrecache","path","fromPrecache","installPrecache","sw","options","installedName","active","known","loading","loadActive","found","manifest","manifestUrl","skipWaiting","previous","navigationPreload","navigateFallbackDenylist","re","precache","shell","navigateFallback"],"sources":["../../src/sw/cache.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines — precache, runtime strategies (cache-first, network-\n * first, stale-while-revalidate), expiry and the inspect/clear helpers all address\n * the same Cache Storage namespace, and the naming scheme that keeps them from\n * evicting each other is the file's whole contract.\n */\n/**\n * Service-worker caching helpers — a small, dependency-free subset of what\n * Workbox provides: precaching of the build's app shell (so the app launches\n * offline) plus runtime caching strategies for fonts, APIs and images.\n *\n * Import these inside your own `sw.ts`. They run in the service-worker global\n * scope, not the main thread. Pair `installPrecache` with the\n * `tempestPwaManifest()` Vite plugin (from `tempest-react-sdk/vite`), which\n * emits the `precache-manifest.json` this reads at install time.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installRuntimeCache, installPrecache } from \"tempest-react-sdk/sw\";\n *\n * // Register specific routes FIRST so they win over the precache catch-all.\n * installRuntimeCache([\n * { match: /\\/api\\//, strategy: \"network-first\", cacheName: \"tempest-api\", maxAgeSeconds: 300 },\n * ]);\n * installPrecache();\n */\n\nimport { credentialHeaderSet } from \"./credential-headers\";\n\n/** Minimal shape of the events we touch, to avoid pulling in `lib.webworker`. */\ninterface ExtendableEventLike {\n waitUntil(promise: Promise<unknown>): void;\n}\n\ninterface FetchEventLike extends ExtendableEventLike {\n request: Request;\n respondWith(response: Response | Promise<Response>): void;\n /** Resolves to the navigation-preload response when it is enabled, else `undefined`. */\n preloadResponse?: Promise<Response | undefined>;\n}\n\n/** Minimal shape of `registration.navigationPreload` (Navigation Preload API). */\ninterface NavigationPreloadManagerLike {\n enable(): Promise<void>;\n disable(): Promise<void>;\n}\n\ninterface CacheSwGlobal {\n addEventListener(type: \"install\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"activate\", listener: (event: ExtendableEventLike) => void): void;\n addEventListener(type: \"fetch\", listener: (event: FetchEventLike) => void): void;\n clients: { claim(): Promise<void> };\n skipWaiting(): Promise<void>;\n location: { origin: string };\n registration?: { navigationPreload?: NavigationPreloadManagerLike };\n}\n\nfunction getSwScope(): CacheSwGlobal {\n return globalThis as unknown as CacheSwGlobal;\n}\n\n/** Caching strategy for a runtime route. Mirrors the common Workbox trio. */\nexport type RuntimeStrategy = \"cache-first\" | \"network-first\" | \"stale-while-revalidate\";\n\n/** A single runtime-caching rule, matched against each `GET` request. */\nexport interface RuntimeRoute {\n /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */\n match: RegExp | ((url: URL, request: Request) => boolean);\n /** How to resolve a match. */\n strategy: RuntimeStrategy;\n /** Cache bucket name for this route. */\n cacheName: string;\n /** Trim the cache to at most this many entries (FIFO) after each write. */\n maxEntries?: number;\n /** Treat a cached response older than this (seconds) as a miss. */\n maxAgeSeconds?: number;\n /** For `network-first`: fall back to cache after this timeout (seconds). */\n networkTimeoutSeconds?: number;\n /**\n * Serve HTTP `Range` requests (206 Partial Content) by slicing the cached\n * full response. Enable for audio/video so seeking works offline. The full\n * resource is cached once (the `Range` header is stripped before caching).\n */\n rangeRequests?: boolean;\n /**\n * Opt in to caching credentialed requests — those that carry a credential\n * header (see `credentialHeaders`) or are a CORS-mode `fetch` sent with\n * `credentials: \"include\"` — and responses marked `Cache-Control: private`.\n * Default `false`.\n *\n * Cache Storage is keyed by URL and outlives the session: without this\n * gate, a `GET /api/me` stored for one account was served to the next\n * account on the same browser whenever the network failed or exceeded\n * `networkTimeoutSeconds` (reproduced in Chromium 153, 11/10/2026). With\n * the default, credentialed requests bypass the route entirely and reach\n * the network as if no service worker were installed.\n *\n * When enabled, every stored entry gets `Vary` on each credential header the\n * request carried, so the Cache API only matches it for the same credential —\n * another account (or no account) misses instead of reading it. A token\n * rotated while online simply repopulates the entry. Pair it with\n * `clearSessionData()` on logout, which removes the entries themselves.\n * `Cache-Control: no-store` is never cached, opt-in or not.\n *\n * A cookie session is only partly visible here: the worker cannot see\n * `Cookie`. What it can see is a `fetch` that asked for cookies explicitly —\n * `mode: \"cors\"` with `credentials: \"include\"`, which is what `ApiClient`\n * sends under `withCredentials` — and since 0.82.0 that request is treated\n * as credentialed too (#531). `<img>`, `<audio>` and `<video>` also send\n * `credentials: \"include\"`, but in `no-cors` mode, so they keep caching. A\n * plain same-origin `fetch` (`credentials: \"same-origin\"`, the default)\n * carries the cookie without saying so; such an API stays private only\n * through `Cache-Control: private`/`no-store` on its responses and\n * `clearSessionData()` on logout. Opting a cookie request in caches it by\n * URL — there is no credential header to vary on — so `clearSessionData()`\n * is then its only isolation.\n */\n cacheAuthenticated?: boolean;\n /**\n * Header names that mark a request as credentialed. Default\n * `DEFAULT_CREDENTIAL_HEADERS` (`authorization`, `x-api-key`,\n * `x-auth-token`, …) — the same list the background-sync queue strips.\n */\n credentialHeaders?: readonly string[];\n}\n\n/** Options for {@link installPrecache}. */\nexport interface InstallPrecacheOptions {\n /** URL of the manifest emitted by `tempestPwaManifest()`. Default `/precache-manifest.json`. */\n manifestUrl?: string;\n /** Cache name prefix; the manifest `version` is appended. Default `tempest-precache`. */\n cacheName?: string;\n /** App-shell document served for navigation requests offline. Default `/index.html`. */\n navigateFallback?: string;\n /** Navigation paths that should NOT use the fallback (e.g. `[/^\\/api\\//]`). */\n navigateFallbackDenylist?: RegExp[];\n /** Activate the new worker immediately after precaching. Default `true`. */\n skipWaiting?: boolean;\n /**\n * Enable the Navigation Preload API on `activate`, so the browser fetches\n * the navigation request in parallel with the worker boot and the handler\n * serves `event.preloadResponse` — cutting first-navigation latency after\n * the worker starts. Silently ignored where unsupported. Default `true`.\n */\n navigationPreload?: boolean;\n}\n\ninterface PrecacheManifest {\n version: string;\n urls: string[];\n}\n\n/**\n * Fetch events a runtime route already answered. The precache listener skips\n * them: a second `respondWith` on the same event throws `InvalidStateError`,\n * which a precached path also matched by a runtime route used to raise.\n */\nconst answeredByRuntime = new WeakSet<object>();\n\n/**\n * Synthetic key, inside each versioned precache, of the entry that records when\n * that version was installed and activated. It never collides with a built\n * asset, and living inside the versioned cache means it is deleted with it and\n * survives `clearSessionData()`, which keeps the precache.\n */\nconst PRECACHE_STATE_PATH = \"/__tempest-precache-state__\";\n\n/** What {@link PRECACHE_STATE_PATH} holds for one precache version. */\ninterface PrecacheState {\n installedAt: number;\n activatedAt?: number;\n}\n\n/** The precache a worker serves: its cache name and the pathnames it holds. */\ninterface ActivePrecache {\n name: string;\n paths: Set<string>;\n}\n\n/**\n * Header stamped on every runtime-cache entry with the epoch milliseconds of the\n * write. `Date` alone is not enough: it is not a CORS-safelisted response\n * header, so for a cross-origin API the worker reads `null` and `maxAgeSeconds`\n * would never expire anything.\n */\nconst CACHED_AT_HEADER = \"x-tempest-cached-at\";\n\n/**\n * Whether a cached response has aged past `maxAgeSeconds`. Reads the\n * {@link CACHED_AT_HEADER} stamp written by the runtime cache, and falls back to\n * `Date` for entries written before the stamp existed.\n */\nfunction isExpired(response: Response, maxAgeSeconds?: number): boolean {\n if (!maxAgeSeconds) return false;\n const stamp = Number(response.headers.get(CACHED_AT_HEADER));\n const dateHeader = response.headers.get(\"date\");\n const storedAt = stamp > 0 ? stamp : dateHeader ? new Date(dateHeader).getTime() : NaN;\n if (Number.isNaN(storedAt)) return false;\n return (Date.now() - storedAt) / 1000 > maxAgeSeconds;\n}\n\n/** The `Cache-Control` directives of a response, lower-cased. */\nfunction cacheControlDirectives(response: Response): string[] {\n return (response.headers.get(\"cache-control\") ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((directive) => directive.trim().split(\"=\")[0]);\n}\n\n/** The comma-separated names of a response's `Vary` header, trimmed and lower-cased. */\nfunction varyNames(vary: string | null): string[] {\n return (vary ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((name) => name.trim());\n}\n\n/**\n * Whether a network response may be written to the route's cache: it must be\n * `ok`, never `no-store`, never `Vary: *`, and `private` only under\n * `cacheAuthenticated`.\n *\n * `Vary: *` says no later request can be proven to match, and `Cache.put`\n * rejects it with a `TypeError`. Before 0.82.0 that rejection escaped the\n * strategy, so a response the network had delivered became the offline\n * fallback path (network-first), an error (cache-first) or nothing at all\n * (stale-while-revalidate) (#534).\n */\nfunction isStorable(response: Response, route: RuntimeRoute): boolean {\n if (!response.ok) return false;\n if (varyNames(response.headers.get(\"vary\")).includes(\"*\")) return false;\n const directives = cacheControlDirectives(response);\n if (directives.includes(\"no-store\")) return false;\n return route.cacheAuthenticated === true || !directives.includes(\"private\");\n}\n\n/**\n * Whether a request is session-bound for its route: it carries a credential\n * header, or it is a CORS `fetch` that asked for cookies with\n * `credentials: \"include\"` (`ApiClient` under `withCredentials`). A `no-cors`\n * request — `<img>`, `<audio>`, `<video>` — also sends `include` by default and\n * is not counted, or every image would bypass the cache.\n */\nfunction isCredentialed(request: Request, route: RuntimeRoute): boolean {\n if (request.mode === \"cors\" && request.credentials === \"include\") return true;\n return credentialsOn(request, route).length > 0;\n}\n\n/** The credential headers, lower-cased, that a request carries for its route. */\nfunction credentialsOn(request: Request, route: RuntimeRoute): string[] {\n return [...credentialHeaderSet(route.credentialHeaders)].filter((name) =>\n request.headers.has(name),\n );\n}\n\n/**\n * Write a response to the route's cache with the {@link CACHED_AT_HEADER} stamp\n * and, under `cacheAuthenticated`, `Vary` on each credential header the request\n * carried, so the entry only matches the credential that produced it. Skips\n * responses that {@link isStorable} rejects, then trims the cache to\n * `maxEntries`.\n */\nasync function store(\n cache: Cache,\n request: Request,\n response: Response,\n route: RuntimeRoute,\n): Promise<void> {\n if (!isStorable(response, route)) return;\n const headers = new Headers(response.headers);\n headers.set(CACHED_AT_HEADER, String(Date.now()));\n if (route.cacheAuthenticated) {\n const vary = headers.get(\"vary\");\n const varies = varyNames(vary);\n const missing = credentialsOn(request, route).filter((name) => !varies.includes(name));\n if (missing.length > 0) {\n headers.set(\"vary\", [vary, ...missing].filter(Boolean).join(\", \"));\n }\n }\n const copy = response.clone();\n await cache.put(\n request,\n new Response(copy.body, { status: copy.status, statusText: copy.statusText, headers }),\n );\n await trimCache(route.cacheName, route.maxEntries);\n}\n\n/** Drop oldest entries until the cache holds at most `maxEntries`. */\nasync function trimCache(cacheName: string, maxEntries?: number): Promise<void> {\n if (!maxEntries) return;\n const cache = await caches.open(cacheName);\n const keys = await cache.keys();\n if (keys.length <= maxEntries) return;\n for (const key of keys.slice(0, keys.length - maxEntries)) {\n await cache.delete(key);\n }\n}\n\nasync function cacheFirst(request: Request, route: RuntimeRoute): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n}\n\n/**\n * Network-first. When the timeout wins, the network request keeps running to\n * refresh the cache, and is handed to `extend` (`event.waitUntil`) so the\n * worker is not stopped mid-write.\n */\nasync function networkFirst(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n\n const network = (async (): Promise<Response> => {\n const response = await fetch(request);\n await store(cache, request, response, route);\n return response;\n })();\n\n const timeoutMs = (route.networkTimeoutSeconds ?? 0) * 1000;\n try {\n if (timeoutMs > 0) {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => {\n extend(network.catch(() => undefined));\n reject(new Error(\"network-timeout\"));\n }, timeoutMs),\n );\n return await Promise.race([network, timeout]);\n }\n return await network;\n } catch {\n const cached = await cache.match(request);\n if (cached && !isExpired(cached, route.maxAgeSeconds)) return cached;\n throw new Error(\"network-first: no network and no cache\");\n }\n}\n\n/**\n * Stale-while-revalidate. A fresh cached response is served at once, and the\n * revalidation is handed to `extend` (`event.waitUntil`): without it the\n * browser may stop the worker before the refreshed response is written.\n */\nasync function staleWhileRevalidate(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n const cache = await caches.open(route.cacheName);\n const cached = await cache.match(request);\n\n const network = fetch(request)\n .then(async (response) => {\n await store(cache, request, response, route);\n return response;\n })\n .catch(() => undefined);\n\n const fresh = cached && !isExpired(cached, route.maxAgeSeconds) ? cached : undefined;\n if (fresh) {\n extend(network);\n return fresh;\n }\n const response = await network;\n if (response) return response;\n throw new Error(\"stale-while-revalidate: no network and no cache\");\n}\n\nfunction runRoute(\n request: Request,\n route: RuntimeRoute,\n extend: (promise: Promise<unknown>) => void,\n): Promise<Response> {\n switch (route.strategy) {\n case \"cache-first\":\n return cacheFirst(request, route);\n case \"network-first\":\n return networkFirst(request, route, extend);\n case \"stale-while-revalidate\":\n return staleWhileRevalidate(request, route, extend);\n }\n}\n\nfunction routeMatches(route: RuntimeRoute, url: URL, request: Request): boolean {\n return typeof route.match === \"function\"\n ? route.match(url, request)\n : route.match.test(url.href);\n}\n\n/**\n * Build a `206 Partial Content` response from a full one for an HTTP `Range`\n * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix\n * `bytes=-suffixLength` (the last N bytes). Returns the original response when there is no usable\n * `Range` header, or a `416` when the range is unsatisfiable.\n *\n * @param request The incoming request (its `Range` header drives the slice).\n * @param response The full (200) response to slice.\n */\nexport async function createPartialResponse(\n request: Request,\n response: Response,\n): Promise<Response> {\n const rangeHeader = request.headers.get(\"range\");\n if (!rangeHeader) return response;\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match || (match[1] === \"\" && match[2] === \"\")) {\n return new Response(null, { status: 416, statusText: \"Range Not Satisfiable\" });\n }\n\n const buffer = await response.clone().arrayBuffer();\n const total = buffer.byteLength;\n\n let start: number;\n let end: number;\n if (match[1] === \"\") {\n const suffix = Number(match[2]);\n start = Math.max(0, total - suffix);\n end = total - 1;\n } else {\n start = Number(match[1]);\n end = match[2] === \"\" ? total - 1 : Math.min(Number(match[2]), total - 1);\n }\n\n if (start > end || start >= total) {\n return new Response(null, {\n status: 416,\n statusText: \"Range Not Satisfiable\",\n headers: { \"Content-Range\": `bytes */${total}` },\n });\n }\n\n const slice = buffer.slice(start, end + 1);\n const headers = new Headers(response.headers);\n headers.set(\"Content-Range\", `bytes ${start}-${end}/${total}`);\n headers.set(\"Content-Length\", String(slice.byteLength));\n headers.set(\"Accept-Ranges\", \"bytes\");\n\n return new Response(slice, {\n status: 206,\n statusText: \"Partial Content\",\n headers,\n });\n}\n\n/**\n * Install a `fetch` handler that resolves matching `GET` requests with the\n * given runtime strategies. Non-matching requests are left untouched (no\n * `respondWith`), so a later {@link installPrecache} can handle them.\n *\n * A request carrying a credential header (`Authorization`, `X-Api-Key`, … — the\n * route's `credentialHeaders`), or a CORS `fetch` sent with\n * `credentials: \"include\"`, is also left untouched unless its route sets\n * `cacheAuthenticated`, and a response with `Cache-Control: no-store`\n * (or `private`, without that opt-in) or `Vary: *` is served but never stored. `maxAgeSeconds`\n * applies to every read, including the offline fallback: an expired entry is a\n * miss, never a last resort. Under `rangeRequests`, the full resource is cached\n * once (the `Range` header stripped) and each range is sliced from it.\n *\n * Register this BEFORE `installPrecache` so specific routes win over the\n * precache catch-all.\n *\n * @param routes Ordered rules; the first whose `match` passes handles the request.\n */\nexport function installRuntimeCache(routes: RuntimeRoute[]): void {\n const sw = getSwScope();\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n\n const url = new URL(request.url);\n const route = routes.find((candidate) => routeMatches(candidate, url, request));\n if (!route) return;\n if (!route.cacheAuthenticated && isCredentialed(request, route)) return;\n answeredByRuntime.add(event);\n const extend = (promise: Promise<unknown>): void => event.waitUntil(promise);\n\n if (route.rangeRequests && request.headers.has(\"range\")) {\n const full = new Request(request.url, {\n headers: stripRange(request.headers),\n credentials: request.credentials,\n mode: request.mode === \"navigate\" ? \"same-origin\" : request.mode,\n });\n event.respondWith(\n runRoute(full, route, extend).then((response) =>\n createPartialResponse(request, response),\n ),\n );\n return;\n }\n\n event.respondWith(runRoute(request, route, extend));\n });\n}\n\n/** Copy headers without the `Range` header (so the cached entry is the full file). */\nfunction stripRange(headers: Headers): Headers {\n const out = new Headers(headers);\n out.delete(\"range\");\n return out;\n}\n\n/** Read the {@link PrecacheState} a versioned precache recorded, if any. */\nasync function readPrecacheState(cache: Cache): Promise<PrecacheState | undefined> {\n const response = await cache.match(PRECACHE_STATE_PATH);\n if (!response) return undefined;\n try {\n return (await response.json()) as PrecacheState;\n } catch {\n return undefined;\n }\n}\n\nasync function writePrecacheState(cache: Cache, state: PrecacheState): Promise<void> {\n await cache.put(\n PRECACHE_STATE_PATH,\n new Response(JSON.stringify(state), { headers: { \"content-type\": \"application/json\" } }),\n );\n}\n\n/**\n * The name of the precache version whose `pick` timestamp is the latest, among\n * the `${cacheName}-*` caches that recorded a {@link PrecacheState}.\n */\nasync function latestPrecache(\n cacheName: string,\n pick: (state: PrecacheState) => number | undefined,\n): Promise<string | undefined> {\n let latest: { name: string; at: number } | undefined;\n for (const name of await caches.keys()) {\n if (!name.startsWith(`${cacheName}-`)) continue;\n const state = await readPrecacheState(await caches.open(name));\n const at = state ? pick(state) : undefined;\n if (at !== undefined && (!latest || at > latest.at)) latest = { name, at };\n }\n return latest?.name;\n}\n\n/** The pathnames a precache holds, read back from the cache itself. */\nasync function describePrecache(name: string): Promise<ActivePrecache> {\n const keys = await (await caches.open(name)).keys();\n const paths = new Set(\n keys\n .map((request) => new URL(request.url).pathname)\n .filter((path) => path !== PRECACHE_STATE_PATH),\n );\n return { name, paths };\n}\n\nasync function fromPrecache(name: string, request: Request): Promise<Response> {\n const cached = await (await caches.open(name)).match(request);\n return cached ?? fetch(request);\n}\n\n/**\n * Precache the app shell at `install` and serve it offline:\n * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),\n * - caches every listed URL under a versioned cache,\n * - on `activate`, deletes stale precache versions and claims open clients,\n * - on `fetch`, serves precached assets cache-first and falls back to the\n * `navigateFallback` document for offline navigations (SPA routing).\n *\n * Survives the browser stopping and restarting the worker, which it does\n * whenever the worker sits idle and which never repeats `install`. Nothing the\n * fetch handler needs lives only in memory: `install` and `activate` record, in\n * an entry inside the versioned cache, when that version was installed and\n * activated, and a restarted worker reads back the activated version and its\n * pathnames from Cache Storage. Before 0.83.0 the cache name and the precached\n * paths were module variables filled only by `install`, so after the first\n * restart offline navigations failed with \"offline and no cached app shell\",\n * precached assets went to the network, and an `activate` in a run without\n * `install` deleted every precache, the current one included (#572).\n *\n * `activate` deletes the other `${cacheName}-*` caches only once it knows which\n * version it is activating; when no version recorded an install it deletes\n * nothing. In the first request after a restart, while the worker is still\n * reading which paths are precached, a same-origin `GET` that a runtime route\n * did not answer is answered with `fetch(request)` when it is not precached —\n * the decision to call `respondWith` cannot wait for Cache Storage. From then on\n * a non-precached request is left to the browser, as before.\n *\n * Same-origin only. Register this LAST, after any {@link installRuntimeCache}:\n * a request a runtime route answered is never answered again here.\n */\nexport function installPrecache(options: InstallPrecacheOptions = {}): void {\n const sw = getSwScope();\n const {\n manifestUrl = \"/precache-manifest.json\",\n cacheName = \"tempest-precache\",\n navigateFallback = \"/index.html\",\n navigateFallbackDenylist = [],\n skipWaiting = true,\n navigationPreload = true,\n } = options;\n\n let installedName: string | undefined;\n let active: ActivePrecache | undefined;\n let known = false;\n let loading: Promise<ActivePrecache | undefined> | undefined;\n\n const loadActive = (): Promise<ActivePrecache | undefined> => {\n if (known) return Promise.resolve(active);\n loading ??= (async () => {\n const name = await latestPrecache(cacheName, (state) => state.activatedAt);\n const found = name ? await describePrecache(name) : undefined;\n if (!known) {\n active = found;\n known = true;\n }\n return active;\n })().finally(() => {\n loading = undefined;\n });\n return loading;\n };\n\n sw.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const response = await fetch(manifestUrl, { cache: \"no-cache\" });\n const manifest = (await response.json()) as PrecacheManifest;\n const name = `${cacheName}-${manifest.version}`;\n\n const cache = await caches.open(name);\n await cache.addAll(manifest.urls);\n const previous = await readPrecacheState(cache);\n await writePrecacheState(cache, { ...previous, installedAt: Date.now() });\n installedName = name;\n if (skipWaiting) await sw.skipWaiting();\n })(),\n );\n });\n\n sw.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const name =\n installedName ??\n (await latestPrecache(cacheName, (state) =>\n state.installedAt > (state.activatedAt ?? -Infinity)\n ? state.installedAt\n : undefined,\n ));\n if (name) {\n const cache = await caches.open(name);\n const previous = await readPrecacheState(cache);\n await writePrecacheState(cache, {\n installedAt: Date.now(),\n ...previous,\n activatedAt: Date.now(),\n });\n active = await describePrecache(name);\n known = true;\n const keys = await caches.keys();\n await Promise.all(\n keys\n .filter((key) => key.startsWith(`${cacheName}-`) && key !== name)\n .map((key) => caches.delete(key)),\n );\n }\n if (navigationPreload && sw.registration?.navigationPreload) {\n await sw.registration.navigationPreload.enable();\n }\n await sw.clients.claim();\n })(),\n );\n });\n\n sw.addEventListener(\"fetch\", (event) => {\n const request = event.request;\n if (request.method !== \"GET\") return;\n if (answeredByRuntime.has(event)) return;\n\n const url = new URL(request.url);\n if (url.origin !== sw.location.origin) return;\n\n if (request.mode === \"navigate\") {\n if (navigateFallbackDenylist.some((re) => re.test(url.pathname))) return;\n event.respondWith(\n (async () => {\n try {\n const preloaded = await event.preloadResponse;\n if (preloaded) return preloaded;\n return await fetch(request);\n } catch {\n const precache = await loadActive();\n const shell = precache\n ? await (await caches.open(precache.name)).match(navigateFallback)\n : undefined;\n if (shell) return shell;\n throw new Error(\"offline and no cached app shell\");\n }\n })(),\n );\n return;\n }\n\n if (known) {\n if (active?.paths.has(url.pathname)) {\n event.respondWith(fromPrecache(active.name, request));\n }\n return;\n }\n\n event.respondWith(\n loadActive()\n .catch(() => undefined)\n .then((precache) =>\n precache?.paths.has(url.pathname)\n ? fromPrecache(precache.name, request)\n : fetch(request),\n ),\n );\n });\n}\n"],"mappings":"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"}
|
package/dist/sw.d.ts
CHANGED
|
@@ -365,7 +365,27 @@ export declare interface InstallNotificationClickHandlerOptions {
|
|
|
365
365
|
* - on `fetch`, serves precached assets cache-first and falls back to the
|
|
366
366
|
* `navigateFallback` document for offline navigations (SPA routing).
|
|
367
367
|
*
|
|
368
|
-
*
|
|
368
|
+
* Survives the browser stopping and restarting the worker, which it does
|
|
369
|
+
* whenever the worker sits idle and which never repeats `install`. Nothing the
|
|
370
|
+
* fetch handler needs lives only in memory: `install` and `activate` record, in
|
|
371
|
+
* an entry inside the versioned cache, when that version was installed and
|
|
372
|
+
* activated, and a restarted worker reads back the activated version and its
|
|
373
|
+
* pathnames from Cache Storage. Before 0.83.0 the cache name and the precached
|
|
374
|
+
* paths were module variables filled only by `install`, so after the first
|
|
375
|
+
* restart offline navigations failed with "offline and no cached app shell",
|
|
376
|
+
* precached assets went to the network, and an `activate` in a run without
|
|
377
|
+
* `install` deleted every precache, the current one included (#572).
|
|
378
|
+
*
|
|
379
|
+
* `activate` deletes the other `${cacheName}-*` caches only once it knows which
|
|
380
|
+
* version it is activating; when no version recorded an install it deletes
|
|
381
|
+
* nothing. In the first request after a restart, while the worker is still
|
|
382
|
+
* reading which paths are precached, a same-origin `GET` that a runtime route
|
|
383
|
+
* did not answer is answered with `fetch(request)` when it is not precached —
|
|
384
|
+
* the decision to call `respondWith` cannot wait for Cache Storage. From then on
|
|
385
|
+
* a non-precached request is left to the browser, as before.
|
|
386
|
+
*
|
|
387
|
+
* Same-origin only. Register this LAST, after any {@link installRuntimeCache}:
|
|
388
|
+
* a request a runtime route answered is never answered again here.
|
|
369
389
|
*/
|
|
370
390
|
export declare function installPrecache(options?: InstallPrecacheOptions): void;
|
|
371
391
|
|