The automated cache update in e0e4b5f accidentally reverted service worker
cache version from 0.1.2 back to 0.1.1. Restoring to 0.1.2 to ensure homepage
cultural DNA updates are served to visitors.
210 lines
6 KiB
JavaScript
210 lines
6 KiB
JavaScript
/**
|
|
* Tractatus Service Worker
|
|
* - Version management and update notifications
|
|
* - Cache management for offline support
|
|
* - PWA functionality
|
|
*/
|
|
|
|
const CACHE_VERSION = '0.1.2';
|
|
const CACHE_NAME = `tractatus-v${CACHE_VERSION}`;
|
|
const VERSION_CHECK_INTERVAL = 3600000; // 1 hour in milliseconds
|
|
|
|
// Paths that should NEVER be cached (always fetch fresh from network)
|
|
const NEVER_CACHE_PATHS = [
|
|
'/js/admin/', // Admin JavaScript - always fresh
|
|
'/api/', // API calls
|
|
'/admin/', // Admin pages
|
|
'/locales/' // Translation files - always fresh
|
|
];
|
|
|
|
// Assets to cache immediately on install
|
|
const CRITICAL_ASSETS = [
|
|
'/',
|
|
'/index.html',
|
|
'/css/tailwind.css',
|
|
'/js/components/navbar.js',
|
|
'/images/tractatus-icon.svg',
|
|
'/favicon.svg'
|
|
];
|
|
|
|
// Install event - cache critical assets
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
console.log('[Service Worker] Caching critical assets');
|
|
return cache.addAll(CRITICAL_ASSETS);
|
|
}).then(() => {
|
|
// Force activation of new service worker
|
|
return self.skipWaiting();
|
|
})
|
|
);
|
|
});
|
|
|
|
// Activate event - clean up old caches
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) => {
|
|
return Promise.all(
|
|
// Delete ALL caches (including current) to force fresh fetch
|
|
cacheNames.map((name) => {
|
|
console.log('[Service Worker] Deleting cache:', name);
|
|
return caches.delete(name);
|
|
})
|
|
);
|
|
}).then(() => {
|
|
// Take control of all clients immediately
|
|
return self.clients.claim();
|
|
})
|
|
);
|
|
});
|
|
|
|
// Fetch event - network-first strategy with cache fallback
|
|
self.addEventListener('fetch', (event) => {
|
|
const { request } = event;
|
|
const url = new URL(request.url);
|
|
|
|
// Skip chrome-extension and other non-http requests
|
|
if (!url.protocol.startsWith('http')) {
|
|
return;
|
|
}
|
|
|
|
// NEVER CACHE: Admin files, API calls, translations - bypass service worker entirely
|
|
if (NEVER_CACHE_PATHS.some(path => url.pathname.startsWith(path))) {
|
|
// Don't intercept at all - let browser handle it directly
|
|
return;
|
|
}
|
|
|
|
// HTML files: Network-ONLY (never cache, always fetch fresh)
|
|
// This ensures users always get the latest content without cache refresh
|
|
if (request.destination === 'document' || url.pathname.endsWith('.html')) {
|
|
event.respondWith(
|
|
fetch(request)
|
|
.catch(() => {
|
|
// Only for offline fallback: serve cached index.html
|
|
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
return caches.match('/index.html');
|
|
}
|
|
// All other HTML: network only, fail if offline
|
|
throw new Error('Network required for HTML pages');
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Static assets (CSS, JS, images): Network-first for versioned URLs, cache-first for others
|
|
if (
|
|
request.destination === 'style' ||
|
|
request.destination === 'script' ||
|
|
request.destination === 'image' ||
|
|
request.destination === 'font'
|
|
) {
|
|
// If URL has version parameter, always fetch fresh (network-first)
|
|
const hasVersionParam = url.searchParams.has('v');
|
|
|
|
if (hasVersionParam) {
|
|
// Network-first for versioned assets (ensures cache-busting works)
|
|
event.respondWith(
|
|
fetch(request).then((response) => {
|
|
// Cache the response for offline use
|
|
const responseClone = response.clone();
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
cache.put(request, responseClone);
|
|
});
|
|
return response;
|
|
}).catch(() => {
|
|
// Fallback to cache if offline
|
|
return caches.match(request);
|
|
})
|
|
);
|
|
} else {
|
|
// Cache-first for non-versioned assets
|
|
event.respondWith(
|
|
caches.match(request).then((cachedResponse) => {
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
return fetch(request).then((response) => {
|
|
const responseClone = response.clone();
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
cache.put(request, responseClone);
|
|
});
|
|
return response;
|
|
});
|
|
})
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// API calls and other requests: Network-first
|
|
event.respondWith(
|
|
fetch(request)
|
|
.then((response) => {
|
|
return response;
|
|
})
|
|
.catch(() => {
|
|
return caches.match(request);
|
|
})
|
|
);
|
|
});
|
|
|
|
// Message event - handle version checks from clients
|
|
self.addEventListener('message', (event) => {
|
|
if (event.data.type === 'CHECK_VERSION') {
|
|
checkVersion().then((versionInfo) => {
|
|
event.ports[0].postMessage({
|
|
type: 'VERSION_INFO',
|
|
...versionInfo
|
|
});
|
|
});
|
|
}
|
|
|
|
if (event.data.type === 'SKIP_WAITING') {
|
|
self.skipWaiting();
|
|
}
|
|
});
|
|
|
|
// Check for version updates
|
|
async function checkVersion() {
|
|
try {
|
|
const response = await fetch('/version.json', { cache: 'no-store' });
|
|
const serverVersion = await response.json();
|
|
|
|
return {
|
|
currentVersion: CACHE_VERSION,
|
|
serverVersion: serverVersion.version,
|
|
updateAvailable: CACHE_VERSION !== serverVersion.version,
|
|
forceUpdate: serverVersion.forceUpdate,
|
|
changelog: serverVersion.changelog
|
|
};
|
|
} catch (error) {
|
|
console.error('[Service Worker] Version check failed:', error);
|
|
return {
|
|
currentVersion: CACHE_VERSION,
|
|
serverVersion: null,
|
|
updateAvailable: false,
|
|
error: true
|
|
};
|
|
}
|
|
}
|
|
|
|
// Periodic background sync for version checks (if supported)
|
|
self.addEventListener('periodicsync', (event) => {
|
|
if (event.tag === 'version-check') {
|
|
event.waitUntil(
|
|
checkVersion().then((versionInfo) => {
|
|
if (versionInfo.updateAvailable) {
|
|
// Notify all clients about update
|
|
self.clients.matchAll().then((clients) => {
|
|
clients.forEach((client) => {
|
|
client.postMessage({
|
|
type: 'UPDATE_AVAILABLE',
|
|
...versionInfo
|
|
});
|
|
});
|
|
});
|
|
}
|
|
})
|
|
);
|
|
}
|
|
});
|