an atproto based link aggregator
at main 97 lines 2.6 kB view raw
1/// <reference no-default-lib="true"/> 2/// <reference lib="esnext" /> 3/// <reference lib="webworker" /> 4/// <reference types="@sveltejs/kit" /> 5 6import { build, files, version } from '$service-worker'; 7 8// This gives `self` the correct types 9const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (globalThis.self)); 10 11// Create a unique cache name for this deployment 12const CACHE = `cache-${version}`; 13 14// Assets to cache immediately (app shell) 15const ASSETS = [ 16 ...build, // the app itself 17 ...files // everything in static 18]; 19 20// Install: cache app shell 21sw.addEventListener('install', (event) => { 22 async function addFilesToCache() { 23 const cache = await caches.open(CACHE); 24 // In dev mode, ASSETS is empty - skip caching 25 if (ASSETS.length > 0) { 26 await cache.addAll(ASSETS); 27 } 28 } 29 30 event.waitUntil(addFilesToCache()); 31}); 32 33// Activate: clean up old caches 34sw.addEventListener('activate', (event) => { 35 async function deleteOldCaches() { 36 for (const key of await caches.keys()) { 37 if (key !== CACHE) await caches.delete(key); 38 } 39 } 40 41 event.waitUntil(deleteOldCaches()); 42}); 43 44// Fetch: network-first with cache fallback for pages, cache-first for assets 45sw.addEventListener('fetch', (event) => { 46 // Only handle GET requests 47 if (event.request.method !== 'GET') return; 48 49 const url = new URL(event.request.url); 50 51 // Skip cross-origin requests 52 if (url.origin !== sw.location.origin) return; 53 54 // Skip API routes - always go to network 55 if (url.pathname.startsWith('/api/')) return; 56 57 async function respond() { 58 const cache = await caches.open(CACHE); 59 60 // For static assets (build/files), serve from cache first 61 if (ASSETS.includes(url.pathname)) { 62 const cached = await cache.match(url.pathname); 63 if (cached) return cached; 64 } 65 66 // For pages, try network first, fall back to cache 67 try { 68 const response = await fetch(event.request); 69 70 // if we're offline, fetch can return a value that is not a Response 71 if (!(response instanceof Response)) { 72 throw new Error('invalid response from fetch'); 73 } 74 75 // Cache successful responses 76 if (response.status === 200) { 77 cache.put(event.request, response.clone()); 78 } 79 80 return response; 81 } catch (err) { 82 // Offline: try to serve from cache 83 const cached = await cache.match(event.request); 84 if (cached) return cached; 85 86 // If no cache and it's a navigation request, show offline page 87 if (event.request.mode === 'navigate') { 88 const offlinePage = await cache.match('/'); 89 if (offlinePage) return offlinePage; 90 } 91 92 throw err; 93 } 94 } 95 96 event.respondWith(respond()); 97});