Files
travelplanning/frontend/public/sw.js
T

117 lines
3.9 KiB
JavaScript

/**
* sw.js — YoTrip Service Worker
* ─────────────────────────────────────────────────────────────────────────────
* Strategy: Network-First with Cache-API fallback.
*
* Caches:
* 1. App Shell (static assets): cached at install time
* 2. OpenStreetMap tiles: cached dynamically on first fetch, served from
* cache when offline — so previously visited map areas remain navigable
*
* Cache names are versioned so old caches get evicted on SW update.
*/
const SHELL_CACHE = 'yotrip-shell-v1';
const MAP_CACHE = 'yotrip-map-tiles-v1';
// App shell files to pre-cache at install
const SHELL_URLS = [
'/',
'/index.html',
'/manifest.json',
'/favicon.ico',
];
// URL patterns for map tile providers
const MAP_TILE_ORIGINS = [
'tile.openstreetmap.org',
'a.tile.openstreetmap.org',
'b.tile.openstreetmap.org',
'c.tile.openstreetmap.org',
'tiles.stadiamaps.com',
'server.arcgisonline.com',
];
// ─── Install ──────────────────────────────────────────────────────────────
self.addEventListener('install', (event) => {
console.log('[SW] Installing yotrip service worker…');
event.waitUntil(
caches.open(SHELL_CACHE).then((cache) => {
return cache.addAll(SHELL_URLS).catch((err) => {
// Non-fatal: some shell files may not exist in dev mode
console.warn('[SW] Shell pre-cache partial failure (non-fatal):', err);
});
})
);
// Activate immediately without waiting for old tabs to close
self.skipWaiting();
});
// ─── Activate ─────────────────────────────────────────────────────────────
self.addEventListener('activate', (event) => {
const CURRENT_CACHES = [SHELL_CACHE, MAP_CACHE];
event.waitUntil(
caches.keys().then((cacheNames) =>
Promise.all(
cacheNames
.filter((name) => !CURRENT_CACHES.includes(name))
.map((name) => {
console.log('[SW] Evicting stale cache:', name);
return caches.delete(name);
})
)
)
);
// Take control of all open clients immediately
self.clients.claim();
});
// ─── Fetch ────────────────────────────────────────────────────────────────
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
// 1. Map tile requests — Network-First, fall back to cache
const isMapTile = MAP_TILE_ORIGINS.some((origin) => url.hostname.includes(origin));
if (isMapTile) {
event.respondWith(
fetch(event.request)
.then((response) => {
if (response && response.status === 200) {
const clone = response.clone();
caches.open(MAP_CACHE).then((cache) => {
cache.put(event.request, clone);
});
}
return response;
})
.catch(() => caches.match(event.request))
);
return;
}
// 2. API calls — Network-Only (never cache API responses)
if (url.pathname.startsWith('/api/')) {
return; // Let the browser handle normally
}
// 3. App shell navigation — Cache-First for HTML, then network
if (event.request.mode === 'navigate') {
event.respondWith(
caches.match('/index.html').then((cached) => {
return cached || fetch(event.request);
})
);
return;
}
// 4. Static assets (JS/CSS/images) — Cache-First
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request);
})
);
});