const CACHE_NAME = 'clinicpro-admin-v1'; const BUILD_CACHE = 'clinicpro-build-v1'; const SHELL_URLS = [ '/admin', '/manifest.json', '/icons/icon-192.png', '/icons/icon-512.png', ]; // ── Install: cache the app shell ────────────────────────────────────────────── self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => cache.addAll(SHELL_URLS)) ); self.skipWaiting(); }); // ── Activate: clean up old caches ───────────────────────────────────────────── self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keys) => Promise.all( keys .filter((k) => k !== CACHE_NAME && k !== BUILD_CACHE) .map((k) => caches.delete(k)) ) ) ); self.clients.claim(); }); // ── Fetch ───────────────────────────────────────────────────────────────────── self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); // Only handle same-origin requests if (url.origin !== self.location.origin) return; // API calls → Network only, never cache if (url.pathname.startsWith('/api/')) { return; } // Build assets (/build/) → Cache First if (url.pathname.startsWith('/build/')) { event.respondWith( caches.open(BUILD_CACHE).then(async (cache) => { const cached = await cache.match(request); if (cached) return cached; const response = await fetch(request); if (response.ok) cache.put(request, response.clone()); return response; }) ); return; } // Navigation to /admin/* → Network First, fallback to cached shell if (request.mode === 'navigate' && url.pathname.startsWith('/admin')) { event.respondWith( fetch(request) .then((response) => { const clone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); return response; }) // Offline: fall back to the cached shell. If it was never cached, return a // real offline Response — respondWith(undefined) throws "Failed to convert // value to 'Response'" and surfaces as a network error in the console. .catch(async () => (await caches.match('/admin')) || new Response('آفلاین هستید', { status: 503, headers: { 'Content-Type': 'text/plain; charset=utf-8' }, }) ) ); return; } // Everything else → Network First with cache fallback event.respondWith( fetch(request).catch(async () => (await caches.match(request)) || Response.error() ) ); });