Files
healer-man/public/content-cache-sw.js
T
2026-09-11 14:26:15 -04:00

124 lines
5.3 KiB
JavaScript

const CONTENT_CACHE_NAME = "healer-man-content-v1";
const OBJECT_HEADER = "x-healer-man-content-object";
const ENCODING_HEADER = "x-healer-man-content-encoding";
let bundledManifest;
function mimeType(path) {
const extension = path.split(".").pop();
return ({ glb: "model/gltf-binary", json: "application/json", js: "text/javascript", wasm: "application/wasm",
ktx2: "image/ktx2", png: "image/png", webp: "image/webp", avif: "image/avif", svg: "image/svg+xml",
mp3: "audio/mpeg", ogg: "audio/ogg", wav: "audio/wav", webm: "video/webm" })[extension] ?? "application/octet-stream";
}
async function decodedResponse(response, path, encoding) {
if (encoding !== "gzip") return response;
const headers = new Headers(response.headers);
headers.set("content-type", mimeType(path));
headers.delete("content-length");
headers.delete("content-encoding");
headers.delete(ENCODING_HEADER);
return new Response(response.body.pipeThrough(new DecompressionStream("gzip")), { status: 200, headers });
}
async function bundledAsset(path) {
if (!bundledManifest) {
bundledManifest = fetch("/content/bootstrap-manifest.json", { cache: "no-cache" })
.then((response) => response.ok ? response.json() : null)
.catch(() => null);
}
const manifest = await bundledManifest;
if (!manifest) { bundledManifest = null; return null; }
const entry = manifest.files?.[path];
if (!entry) return null;
const object = entry.download ?? entry;
// Older bootstrap catalogs served their original logical paths directly.
if (!object.url.startsWith("/content/bundled/")) return fetch(path);
const response = await fetch(object.url);
if (!response.ok) return response;
if (entry.download) return decodedResponse(response, path, entry.download.encoding);
return new Response(response.body, { headers: { "content-type": mimeType(path) } });
}
const MANAGED_ASSET_PREFIXES = [
"/assets/characters/",
"/assets/creatures/",
"/assets/equipment/",
"/assets/game/",
"/assets/ui/",
"/basis/",
"/data/",
];
const BUNDLED_ASSET_PREFIXES = [
"/assets/ui/",
"/assets/equipment/",
"/assets/characters/wow335a-v1/manifest.json",
"/assets/characters/wow335a-v1/human/",
"/assets/creatures/wailing-caverns/",
"/assets/game/arenas/",
"/assets/game/dungeons/wailing-caverns/",
];
const BUNDLED_ASSET_PATHS = new Set([
"/assets/characters/wow335a-v1/textures/character/human/faciallowerhair00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/facialupperhair00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/female/humanfemalefacelower00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/female/humanfemalefaceupper00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/female/humanfemalenakedpelvisskin00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/female/humanfemalenakedtorsoskin00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/female/humanfemaleskin00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/hair00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/male/humanmalefacelower00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/male/humanmalefaceupper00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/male/humanmalenakedpelvisskin00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/male/humanmaleskin00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/scalplowerhair00_00.webp",
"/assets/characters/wow335a-v1/textures/character/human/scalpupperhair00_00.webp",
]);
self.addEventListener("install", (event) => {
event.waitUntil(self.skipWaiting());
});
self.addEventListener("message", (event) => {
if (event.data?.type === "refresh-content-bootstrap") bundledManifest = null;
});
self.addEventListener("activate", (event) => {
event.waitUntil((async () => {
const names = await caches.keys();
await Promise.all(names
.filter((name) => name.startsWith(`${CONTENT_CACHE_NAME}-staging-`))
.map((name) => caches.delete(name)));
await self.clients.claim();
})());
});
self.addEventListener("fetch", (event) => {
const { request } = event;
if (request.method !== "GET") return;
const url = new URL(request.url);
if (url.origin !== self.location.origin
|| !MANAGED_ASSET_PREFIXES.some((prefix) => url.pathname.startsWith(prefix))) return;
event.respondWith((async () => {
const cache = await caches.open(CONTENT_CACHE_NAME);
const cached = await cache.match(request, { ignoreSearch: true });
if (cached) {
const objectPath = cached.headers.get(OBJECT_HEADER);
if (!objectPath) return cached;
if (objectPath.startsWith("/content/cache-objects/")) {
const object = await cache.match(new URL(objectPath, self.location.origin).toString());
if (object) return decodedResponse(object, url.pathname, object.headers.get(ENCODING_HEADER));
}
}
const bundled = await bundledAsset(url.pathname);
if (bundled) return bundled;
if (BUNDLED_ASSET_PATHS.has(url.pathname)
|| BUNDLED_ASSET_PREFIXES.some((prefix) => url.pathname.startsWith(prefix))) {
return fetch(request);
}
return new Response("Optional content is not installed.", {
status: 404,
headers: { "content-type": "text/plain; charset=utf-8" },
});
})());
});