diff --git a/src/service-worker.ts b/src/service-worker.ts
index b3b745b..ca9e1ce 100644
--- a/src/service-worker.ts
+++ b/src/service-worker.ts
@@ -1,13 +1,20 @@
-///
+///
+///
+///
///
-// Standard workbox imports - let the build process handle these
-import { cleanupOutdatedCaches, createHandlerBoundToURL, precacheAndRoute } from 'workbox-precaching';
-import { NavigationRoute, registerRoute } from 'workbox-routing';
+import { build, files, version } from '$service-worker';
declare let self: ServiceWorkerGlobalScope;
-// Global error handler for service worker
+// Create a unique cache name for this deployment
+const CACHE = `cache-${version}`;
+const ASSETS = [
+ ...build, // the app itself
+ ...files // everything in `static`
+];
+
+// Global error handlers (preserve existing)
self.addEventListener('error', (event) => {
console.error('[SW] Global error:', event.error);
console.error('[SW] Error details:', {
@@ -19,7 +26,6 @@ self.addEventListener('error', (event) => {
});
});
-// Unhandled promise rejection handler
self.addEventListener('unhandledrejection', (event) => {
console.error('[SW] Unhandled promise rejection:', event.reason);
event.preventDefault(); // Prevent default browser behavior
@@ -27,90 +33,81 @@ self.addEventListener('unhandledrejection', (event) => {
console.log('[SW] Service worker script loading...');
-// Get the workbox manifest - this will be injected by the build process
-const workboxManifest = self.__WB_MANIFEST;
+// Install event - cache all assets
+self.addEventListener('install', (event) => {
+ console.log('[SW] Installing service worker...');
+
+ async function addFilesToCache() {
+ const cache = await caches.open(CACHE);
+ await cache.addAll(ASSETS);
+ console.log(`[SW] Cached ${ASSETS.length} assets`);
+ }
-// Wrap workbox initialization in try-catch with granular error handling
-try {
- console.log('[SW] Initializing workbox...');
+ event.waitUntil(addFilesToCache());
+});
+
+// Activate event - clean up old caches
+self.addEventListener('activate', (event) => {
+ console.log('[SW] Activating service worker...');
- // Check if workbox functions are available
- if (typeof precacheAndRoute !== 'function' || typeof cleanupOutdatedCaches !== 'function') {
- throw new Error('Workbox functions not available');
- }
-
- // Detect environment - in production, workbox manifest should be injected
- const isDevelopment = !workboxManifest || (workboxManifest && workboxManifest.length === 0);
- console.log(`[SW] Running in ${isDevelopment ? 'development' : 'production'} mode`);
-
- // Enhanced manifest validation with detailed logging
- if (!workboxManifest) {
- if (isDevelopment) {
- console.info('[SW] Workbox manifest not injected - running in development mode, precaching disabled');
- } else {
- console.warn('[SW] Workbox manifest not found in production build - this may be a build issue');
+ async function deleteOldCaches() {
+ for (const key of await caches.keys()) {
+ if (key !== CACHE) {
+ console.log('[SW] Deleting old cache:', key);
+ await caches.delete(key);
+ }
}
- } else if (!Array.isArray(workboxManifest)) {
- console.error('[SW] Workbox manifest exists but is invalid format:', typeof workboxManifest);
- } else if (workboxManifest.length === 0) {
- console.warn('[SW] Workbox manifest is empty - no assets to precache');
- } else {
- console.log(`[SW] Workbox manifest found with ${workboxManifest.length} entries`);
- console.debug('[SW] Manifest entries:', workboxManifest.slice(0, 5)); // Log first 5 for debugging
-
+ }
+
+ event.waitUntil(deleteOldCaches());
+});
+
+// Fetch event - serve from cache with network fallback
+self.addEventListener('fetch', (event) => {
+ // ignore POST requests etc
+ if (event.request.method !== 'GET') return;
+
+ async function respond() {
+ const url = new URL(event.request.url);
+ const cache = await caches.open(CACHE);
+
+ // `build`/`files` can always be served from the cache
+ if (ASSETS.includes(url.pathname)) {
+ const response = await cache.match(url.pathname);
+ if (response) {
+ return response;
+ }
+ }
+
+ // for everything else, try the network first, but
+ // fall back to the cache if we're offline
try {
- precacheAndRoute(workboxManifest);
- console.log('[SW] Precaching completed successfully');
- } catch (precacheError) {
- console.error('[SW] Error during precaching:', precacheError);
+ const response = await fetch(event.request);
+
+ // if we're offline, fetch can return a value that is not a Response
+ // instead of throwing - and we can't pass this non-Response to respondWith
+ if (!(response instanceof Response)) {
+ throw new Error('invalid response from fetch');
+ }
+
+ if (response.status === 200) {
+ cache.put(event.request, response.clone());
+ }
+ return response;
+ } catch (err) {
+ const response = await cache.match(event.request);
+ if (response) {
+ return response;
+ }
+
+ // if there's no cache, then just error out
+ // as there is nothing we can do to respond to this request
+ throw err;
}
}
-
- // Always try to cleanup outdated caches
- try {
- cleanupOutdatedCaches();
- console.log('[SW] Cache cleanup completed');
- } catch (cleanupError) {
- console.error('[SW] Error during cache cleanup:', cleanupError);
- }
-
- // Handle navigation requests with additional error handling
- try {
- console.log('[SW] Setting up navigation routing...');
- if (typeof createHandlerBoundToURL === 'function' && typeof NavigationRoute === 'function' && typeof registerRoute === 'function') {
- const handler = createHandlerBoundToURL('/');
- const navigationRoute = new NavigationRoute(handler, {
- denylist: [/^\/api/]
- });
- registerRoute(navigationRoute);
- console.log('[SW] Navigation routing configured successfully');
- } else {
- throw new Error('Navigation routing functions not available');
- }
- } catch (routingError) {
- console.error('[SW] Error setting up navigation routing:', routingError);
- // Continue without navigation routing if it fails
- }
-
- console.log('[SW] Workbox initialization completed');
-} catch (error) {
- console.error('[SW] Critical error initializing workbox:', error);
- console.error('[SW] Error details:', {
- name: error.name,
- message: error.message,
- stack: error.stack
- });
-
- // In development mode, this is expected behavior
- if (!workboxManifest || (Array.isArray(workboxManifest) && workboxManifest.length === 0)) {
- console.info('[SW] Continuing with limited functionality in development mode');
- } else {
- console.error('[SW] Production build should have workbox manifest - check build configuration');
- }
-
- // Continue with service worker registration even if workbox fails
- // This allows push notifications and other features to still work
-}
+
+ event.respondWith(respond());
+});
// Push notification handling
self.addEventListener('push', (event) => {