Skip to main content

Overview

The Mini POS System implements a service worker (sw.js) that provides offline-first functionality through intelligent caching. The service worker enables the application to:
  • Work offline after the first visit
  • Load instantly from cache
  • Update automatically when new versions are deployed
  • Fallback gracefully when network requests fail

Cache Versioning

The cache version is controlled by the CACHE constant:
const CACHE = 'mini-pos-v2';
To force a cache update:
  1. Increment the version number (e.g., mini-pos-v3)
  2. Deploy the updated sw.js file
  3. The service worker will automatically clear old caches on activation

Cached Assets

The ASSETS array defines all files cached during installation:
const ASSETS = [
  './',
  './index.html',
  './products.html',
  './pos.html',
  './reports.html',
  './manifest.json',
  './css/style.css',
  './js/db.js',
  './js/shared.js',
  './js/dashboard.js',
  './js/products.js',
  './js/pos.js',
  './js/reports.js',
  './icons/icon-192.png',
  './icons/icon-512.png',
  // CDN resources must not be cached here due to CORS restrictions.
  // Use local compiled CSS instead (css/style.css is already included above).
];
CDN resources are intentionally excluded from the cache due to CORS restrictions. All required CSS is bundled locally in css/style.css.

Service Worker Lifecycle

Install Event

The install event caches all application assets when the service worker is first installed or updated:
self.addEventListener('install', (event) => {
  // Try to cache all assets but don't fail installation if some external
  // resources are unavailable (e.g., CORS-blocked CDN). We already
  // prefer local files (css/style.css) so it's safe to ignore individual failures.
  event.waitUntil(
    caches.open(CACHE).then((cache) =>
      Promise.allSettled(
        ASSETS.map((asset) => cache.add(asset).catch(() => {
          // ignore individual asset cache errors
        }))
      )
    )
  );
  self.skipWaiting();
});
Key behaviors:
  • Uses Promise.allSettled() to handle individual asset failures gracefully
  • Calls self.skipWaiting() to activate immediately without waiting for old tabs to close
  • Does not fail installation if some assets can’t be cached

Activate Event

The activate event cleans up old caches when a new service worker version takes control:
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(
        keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))
      )
    )
  );
  self.clients.claim();
});
Key behaviors:
  • Deletes all caches except the current version
  • Calls self.clients.claim() to take control of all open tabs immediately
  • Ensures users always have the latest cached assets

Fetch Event

The fetch event implements a cache-first strategy with network fallback:
self.addEventListener('fetch', (event) => {
  const req = event.request;
  if (req.method !== 'GET') return;
  
  event.respondWith(
    caches.match(req).then((hit) => {
      if (hit) return hit;
      return fetch(req)
        .then((res) => {
          const resClone = res.clone();
          caches.open(CACHE).then((cache) => cache.put(req, resClone)).catch(() => {});
          return res;
        })
        .catch(() => caches.match('./index.html'));
    })
  );
});
Strategy flow:
  1. Only intercepts GET requests - POST, PUT, DELETE requests bypass the service worker
  2. Check cache first - If resource is cached, return it immediately
  3. Network fallback - If not cached, fetch from network
  4. Cache on success - Clone and cache successful network responses
  5. Offline fallback - If network fails, serve index.html as fallback

Caching Strategy

Cache-First Approach

The service worker implements a cache-first strategy:
  • Cached resources load instantly without network requests
  • Perfect for offline functionality
  • New content requires cache version bump

Dynamic Caching

Resources not in the ASSETS array are cached dynamically:
  • First request fetches from network and caches response
  • Subsequent requests serve from cache
  • Applies to images, API responses, and other GET requests

Offline Fallback

When both cache and network fail, the service worker serves index.html. This ensures:
  • Users always see something instead of browser error pages
  • The application shell loads even when specific resources fail
  • Navigation remains functional offline

CORS Handling

The service worker explicitly avoids caching CDN resources due to CORS restrictions. External resources from different origins may not be cacheable without proper CORS headers.
Solution implemented:
  • All CSS is bundled locally in css/style.css
  • No external CDN dependencies in the ASSETS array
  • Individual cache failures are silently ignored during installation

Updating the Service Worker

Manual Cache Clear

To force update the cache version:
  1. Edit sw.js and increment the version:
    const CACHE = 'mini-pos-v3'; // was mini-pos-v2
    
  2. Deploy the updated file
  3. The service worker will:
    • Install the new version
    • Activate and delete old caches
    • Cache all assets with the new version name

Testing Updates

Browsers check for service worker updates:
  • On navigation to a page in scope
  • Every 24 hours (browser-dependent)
  • When navigator.serviceWorker.register() is called

Debugging Tips

Chrome DevTools

  1. View registered service workers:
    • Navigate to chrome://serviceworker-internals/
    • Or open DevTools → Application → Service Workers
  2. Clear cache:
    • DevTools → Application → Storage → Clear site data
    • Or Application → Cache Storage → Delete caches manually
  3. Test offline:
    • DevTools → Network → Check “Offline”
    • Or Application → Service Workers → “Offline” checkbox
  4. Force update:
    • Application → Service Workers → Click “Update”
    • Or check “Update on reload”

Firefox DevTools

  1. View service workers:
    • Navigate to about:debugging#/runtime/this-firefox
    • Or DevTools → Application
  2. Unregister:
    • Click “Unregister” next to the service worker
    • Reload the page to re-register

Common Issues

  • Increment the CACHE version in sw.js
  • Hard refresh the page (Ctrl+Shift+R or Cmd+Shift+R)
  • Check DevTools for service worker errors
  • Use “Update on reload” during development
  • Verify the asset path in the ASSETS array
  • Check that assets loaded successfully during install
  • Look for CORS errors in the console
  • Ensure the asset was visited/cached before going offline
  • The activate event should clear old caches automatically
  • Manually delete caches in DevTools → Application → Cache Storage
  • Verify the CACHE constant was changed
  • Check that caches.delete() is working in the activate handler
  • Remove external CDN resources from ASSETS array
  • Use local copies of third-party resources
  • Ensure Promise.allSettled() handles failures gracefully
  • External resources can still be fetched but won’t be cached

Best Practices

Development

  • Enable “Update on reload” in DevTools during development
  • Use “Bypass for network” to test without service worker interference
  • Clear cache frequently to avoid stale content
  • Test both online and offline scenarios

Production

  • Bump CACHE version with every deployment
  • Keep the ASSETS array updated with new files
  • Monitor service worker errors in production logs
  • Test the update flow before deploying
  • Consider cache size limits (browsers vary, typically 50MB+)

Performance

  • Cache only essential assets in the ASSETS array
  • Let dynamic caching handle additional resources
  • Clone responses before caching to avoid stream issues
  • Use cache-first strategy for static assets
  • Consider network-first for frequently updated API data

API Reference

Constants

ConstantTypeDescription
CACHEstringCache version identifier. Update to force cache refresh.
ASSETSstring[]Array of URLs to cache during service worker installation.

Event Handlers

EventHandlerPurpose
installself.addEventListener('install', ...)Caches initial assets when service worker installs
activateself.addEventListener('activate', ...)Cleans up old caches when new version activates
fetchself.addEventListener('fetch', ...)Intercepts network requests and serves from cache

Methods Used

MethodDescription
caches.open(name)Opens a cache by name, creating it if it doesn’t exist
caches.keys()Returns a promise resolving to an array of cache names
caches.delete(name)Deletes a cache by name
caches.match(request)Returns a promise resolving to a cached response or undefined
cache.add(url)Fetches a URL and adds it to the cache
cache.put(request, response)Stores a request/response pair in the cache
self.skipWaiting()Forces the waiting service worker to become active
self.clients.claim()Takes control of all clients immediately

Build docs developers (and LLMs) love