const STATIC_CACHE = "md-hub-static-v2"; const DB_NAME = "md-hub-cache"; const DB_VERSION = 3; const STORE_PAGES = "pages"; const STATIC_ASSETS = [ "/static/site.css", "/static/cache.js", "/static/sync.js", "/static/realtime.js", "/static/editor.js", "/static/render.js", "/static/tags.js", "/static/favicon.png", "/static/cairnquire%20logo%402x.webp", "/", "/docs", ]; self.addEventListener("install", function (event) { event.waitUntil( caches.open(STATIC_CACHE).then(function (cache) { return cache.addAll(STATIC_ASSETS); }).then(function () { return self.skipWaiting(); }) ); }); self.addEventListener("activate", function (event) { event.waitUntil( caches.keys().then(function (keys) { return Promise.all( keys.map(function (key) { if (key === STATIC_CACHE) { return null; } return caches.delete(key); }) ); }).then(function () { return self.clients.claim(); }) ); }); self.addEventListener("fetch", function (event) { const request = event.request; if (request.method !== "GET") { return; } const url = new URL(request.url); if (url.origin !== self.location.origin) { return; } if (request.mode === "navigate") { event.respondWith(handleNavigation(request, url)); return; } if (url.pathname.startsWith("/static/")) { event.respondWith(handleStaticAsset(request)); } }); async function handleNavigation(request, url) { const cacheKey = url.pathname + url.search; try { const response = await fetch(request); cachePageResponse(cacheKey, response.clone()); return response; } catch (_error) { const cachedPage = await getCachedPage(cacheKey); if (cachedPage && cachedPage.html) { return new Response(markOfflineHTML(cachedPage.html), { headers: { "Content-Type": "text/html; charset=utf-8", "X-MDHub-Cache": "offline", }, status: 200, }); } if (cacheKey !== "/") { const fallback = await getCachedPage("/"); if (fallback && fallback.html) { return new Response(markOfflineHTML(fallback.html), { headers: { "Content-Type": "text/html; charset=utf-8", "X-MDHub-Cache": "offline", }, status: 200, }); } } const fallback = await caches.match("/"); if (fallback) { return fallback; } return new Response("Offline", { status: 503, headers: { "Content-Type": "text/plain; charset=utf-8" }, }); } } async function handleStaticAsset(request) { const cache = await caches.open(STATIC_CACHE); const cached = await cache.match(request); if (cached) { fetch(request).then(function (response) { if (response && response.ok) { cache.put(request, response.clone()); } }).catch(function () {}); return cached; } const response = await fetch(request); if (response && response.ok) { cache.put(request, response.clone()); } return response; } async function cachePageResponse(pathname, response) { if (!response || !response.ok) { return; } const contentType = response.headers.get("Content-Type") || ""; if (contentType.indexOf("text/html") === -1) { return; } try { const html = await response.text(); await putCachedPage(normalizePath(pathname), { path: normalizePath(pathname), html: html, title: extractTitle(html), cachedAt: Date.now(), }); } catch (_error) {} } function markOfflineHTML(html) { const marker = "