diff --git a/public/service-worker.js b/public/service-worker.js
index 8f4e86f..108657e 100644
--- a/public/service-worker.js
+++ b/public/service-worker.js
@@ -1,55 +1,74 @@
-// public/sw.js
+const CACHE_NAME = "devtools-hub-v1";
-const CACHE_NAME = 'devtools-hub-v1'
+// cache only safe static assets
+const PRECACHE_ASSETS = ["/", "/icon.png", "/manifest.json"];
-// basic assets to cache right away
-const PRECACHE_ASSETS = ['/', '/icon.png', '/manifest.json']
-
-// install event: cache static assets
-self.addEventListener('install', (event) => {
+self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
- return cache.addAll(PRECACHE_ASSETS)
+ return cache.addAll(PRECACHE_ASSETS);
}),
- )
- // force the waiting service worker to become the active service worker
- self.skipWaiting()
-})
+ );
+
+ self.skipWaiting();
+});
-// activate event: clean up old caches
-self.addEventListener('activate', (event) => {
+self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
- return Promise.all(cacheNames.filter((name) => name !== CACHE_NAME).map((name) => caches.delete(name)))
+ return Promise.all(
+ cacheNames
+ .filter((name) => name !== CACHE_NAME)
+ .map((name) => caches.delete(name)),
+ );
}),
- )
- self.clients.claim()
-})
+ );
+
+ self.clients.claim();
+});
-// fetch event: network first, fallback to cache
-self.addEventListener('fetch', (event) => {
- // only handle GET requests
- if (event.request.method !== 'GET') return
+self.addEventListener("fetch", (event) => {
+ const request = event.request;
- // skip non-http(s) requests (e.g. chrome-extension://, file://)
- if (!event.request.url.startsWith('http')) return
+ // only GET requests
+ if (request.method !== "GET") return;
+
+ // ignore non-http(s)
+ if (!request.url.startsWith("http")) return;
- // network-first strategy for smooth updates but offline reliability
event.respondWith(
- fetch(event.request)
- .then((response) => {
- // if valid response, clone and cache it
- if (response && response.status === 200 && response.type === 'basic') {
- const responseToCache = response.clone()
- caches.open(CACHE_NAME).then((cache) => {
- cache.put(event.request, responseToCache)
- })
+ (async () => {
+ try {
+ // network first
+ const networkResponse = await fetch(request);
+
+ // only cache valid responses
+ if (networkResponse && networkResponse.ok) {
+ const cache = await caches.open(CACHE_NAME);
+ cache.put(request, networkResponse.clone());
}
- return response
- })
- .catch(() => {
- // network failed, fallback to cache
- return caches.match(event.request)
- }),
- )
-})
+
+ return networkResponse;
+ } catch (error) {
+ // fallback to cache
+ const cached = await caches.match(request);
+
+ // IMPORTANT: always return a Response
+ if (cached) return cached;
+
+ // safe fallback for navigation requests only
+ if (request.mode === "navigate") {
+ return new Response("offline", {
+ status: 200,
+ headers: { "Content-Type": "text/plain" },
+ });
+ }
+
+ // final safe fallback
+ return new Response("", {
+ status: 204,
+ });
+ }
+ })(),
+ );
+});
diff --git a/src/components/Analytics.astro b/src/components/Analytics.astro
index d90978b..edb0efd 100644
--- a/src/components/Analytics.astro
+++ b/src/components/Analytics.astro
@@ -1,13 +1,11 @@
---
const gaId = import.meta.env.PUBLIC_GA_ID;
const clarityId = import.meta.env.PUBLIC_CLARITY_ID;
-
const isProd = import.meta.env.PROD;
---
{isProd && gaId && (
<>
-
>
)}
{isProd && clarityId && (
-
)}
\ No newline at end of file