All files / src/pwa serviceWorker.ts

93.47% Statements 43/46
66.66% Branches 12/18
100% Functions 12/12
93.47% Lines 43/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162                            1x 1x   1x             3x               4x 1x 1x     3x 3x 2x 2x   1x 1x               1x 1x                 2x   2x 2x                 2x   2x 1x       1x                   1x 1x 1x 1x             1x 1x                     1x     1x 1x                 1x 1x   2x 1x                   1x   1x 1x         1x       1x 1x 1x 1x              
/// <reference lib="webworker" />
 
/**
 * Service Worker Registration and Management
 *
 * Handles PWA service worker lifecycle:
 * - Registration on app load
 * - Caching strategies for offline support
 * - Service worker updates
 * - Cache management
 *
 * Used by: main.tsx (app initialization)
 */
 
const CACHE_NAME = 'batbern-v1';
const OFFLINE_URL = '/offline.html';
 
let swRegistration: ServiceWorkerRegistration | null = null;
 
/**
 * Get the current cache name
 * @returns Current cache name with version
 */
export function getCacheName(): string {
  return CACHE_NAME;
}
 
/**
 * Register the service worker
 * @returns Service worker registration or undefined if not supported
 */
export async function registerServiceWorker(): Promise<ServiceWorkerRegistration | undefined> {
  if (!('serviceWorker' in navigator)) {
    console.warn('Service Worker not supported in this browser');
    return undefined;
  }
 
  try {
    swRegistration = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
    console.log('Service Worker registered successfully:', swRegistration);
    return swRegistration;
  } catch (error) {
    console.error('Service Worker registration failed:', error);
    return undefined;
  }
}
 
/**
 * Check for service worker updates
 */
export async function checkForUpdates(): Promise<void> {
  Eif (swRegistration) {
    await swRegistration.update();
  }
}
 
/**
 * Cache assets for offline use
 * @param cacheName - Name of the cache
 */
export async function cacheAssets(cacheName: string): Promise<void> {
  const urlsToCache = ['/', '/index.html', '/offline.html', '/manifest.json'];
 
  const cache = await caches.open(cacheName);
  await cache.addAll(urlsToCache);
}
 
/**
 * Get cached response for a request
 * @param request - The request to check
 * @returns Cached response or fetched response
 */
export async function getCachedResponse(request: Request): Promise<Response> {
  const cachedResponse = await caches.match(request);
 
  if (cachedResponse) {
    return cachedResponse;
  }
 
  // If not in cache, fetch from network
  return fetch(request);
}
 
/**
 * Fetch from network and update cache
 * @param request - The request to fetch
 * @param cacheName - Name of the cache
 * @returns Fetched response
 */
export async function fetchAndCache(request: Request, cacheName: string): Promise<Response> {
  const response = await fetch(request);
  const cache = await caches.open(cacheName);
  await cache.put(request, response.clone());
  return response;
}
 
/**
 * Skip waiting and activate new service worker immediately
 */
export async function skipWaiting(): Promise<void> {
  Eif ('skipWaiting' in self) {
    await (
      self as unknown as ServiceWorkerGlobalScope & { skipWaiting: () => Promise<void> }
    ).skipWaiting();
  }
}
 
/**
 * Claim clients immediately after activation
 */
export async function claimClients(): Promise<void> {
  // Access clients through self (ServiceWorkerGlobalScope)
  const sw = self as unknown as ServiceWorkerGlobalScope & {
    clients: { claim: () => Promise<void> };
  };
  Eif (sw.clients && sw.clients.claim) {
    await sw.clients.claim();
  }
}
 
/**
 * Delete old caches during activation
 * @param currentCacheName - The current cache name to keep
 */
export async function deleteOldCaches(currentCacheName: string): Promise<void> {
  const cacheNames = await caches.keys();
  await Promise.all(
    cacheNames
      .filter((cacheName) => cacheName !== currentCacheName)
      .map((cacheName) => caches.delete(cacheName))
  );
}
 
/**
 * Handle fetch events with caching strategies
 * @param request - The request to handle
 * @returns Response from cache or network
 */
export async function handleFetch(request: Request): Promise<Response> {
  try {
    // Try cache first
    const cachedResponse = await caches.match(request);
    Iif (cachedResponse) {
      return cachedResponse;
    }
 
    // Try network
    const networkResponse = await fetch(request);
    return networkResponse;
  } catch (error) {
    // If both fail, return offline page for navigation requests
    Eif (request.mode === 'navigate') {
      const offlineResponse = await caches.match(OFFLINE_URL);
      Eif (offlineResponse) {
        return offlineResponse;
      }
    }
 
    throw error;
  }
}