diff --git a/docs/sw.js b/docs/sw.js index fceffa2..46bc6e6 100644 --- a/docs/sw.js +++ b/docs/sw.js @@ -1,18 +1,68 @@ -self.addEventListener("install", (event) => { +const DB_NAME = "sune-sw-db"; +const STORE = "kv"; +const KEY = "lastSession"; +function idbOpen() { + return new Promise((resolve, reject) => { + const r = indexedDB.open(DB_NAME, 1); + r.onupgradeneeded = () => { + r.result.createObjectStore(STORE); + }; + r.onsuccess = () => resolve(r.result); + r.onerror = () => reject(r.error || new Error("idb open error")); + }); +} +function idbGet(key) { + return idbOpen().then((db) => new Promise((res, rej) => { + const tx = db.transaction(STORE, "readonly"); + const req = tx.objectStore(STORE).get(key); + req.onsuccess = () => res(req.result); + req.onerror = () => rej(req.error); + })); +} +function idbSet(key, val) { + return idbOpen().then((db) => new Promise((res, rej) => { + const tx = db.transaction(STORE, "readwrite"); + const req = tx.objectStore(STORE).put(val, key); + req.onsuccess = () => res(true); + req.onerror = () => rej(req.error); + })); +} +const SESSION_ID = Date.now().toString(36) + "-" + Math.floor(Math.random() * 1e9).toString(36); +const STARTED_AT = Date.now(); +self.addEventListener("install", (ev) => { self.skipWaiting(); }); -self.addEventListener("activate", (event) => { - event.waitUntil(self.clients.claim()); +self.addEventListener("activate", (ev) => { + ev.waitUntil(self.clients.claim()); }); -self.addEventListener("message", (event) => { - const data = event.data || {}; - if (data?.type === "PING") { - if (event.ports && event.ports[0]) { - event.ports[0].postMessage({ type: "PONG", ts: Date.now(), ok: true }); - } else { - self.clients.matchAll().then((clients) => { - clients.forEach((c) => c.postMessage({ type: "PONG", ts: Date.now(), ok: true })); - }); +self.addEventListener("message", (ev) => { + const data = ev.data || {}; + if (data.type !== "PING") return; + const respond = async () => { + try { + const last = await idbGet(KEY); + const restarted = !!last && last !== SESSION_ID; + await idbSet(KEY, SESSION_ID); + const payload = { + type: "PONG", + ts: Date.now(), + sessionId: SESSION_ID, + lastSessionId: last || null, + restarted, + uptimeMs: Date.now() - STARTED_AT, + ok: true + }; + if (ev.ports && ev.ports[0]) { + ev.ports[0].postMessage(payload); + } else { + const clients = await self.clients.matchAll({ includeUncontrolled: true }); + clients.forEach((c) => c.postMessage(payload)); + } + } catch (err) { + const errPayload = { type: "PONG", ok: false, error: String(err), ts: Date.now() }; + if (ev.ports && ev.ports[0]) ev.ports[0].postMessage(errPayload); } - } + }; + if (ev.waitUntil) ev.waitUntil(Promise.resolve(respond())); + else respond(); });