DataScience / service-worker.js
AashishAIHub's picture
feat: Update all modules with latest UI enhancements and content
13d9acc
/**
* DataScience Masterclass - Service Worker
* Provides offline support and caching
* Version: 2.0.0
*/
const CACHE_NAME = 'ds-masterclass-v2.0.0';
const OFFLINE_URL = '/offline.html';
// Assets to cache immediately on install
const PRECACHE_ASSETS = [
'/',
'/index.html',
'/shared/css/design-system.css',
'/shared/css/components.css',
'/shared/js/search.js',
'/shared/js/progress.js',
'/shared/js/theme.js',
'/manifest.json'
];
// Module pages to cache on first visit
const MODULE_PAGES = [
'/DeepLearning/index.html',
'/ml_complete-all-topics/index.html',
'/complete-statistics/index.html',
'/math-ds-complete/index.html',
'/feature-engineering/index.html',
'/Visualization/index.html',
'/prompt-engineering-guide/index.html'
];
/**
* Install event - cache core assets
*/
self.addEventListener('install', (event) => {
console.log('[SW] Installing service worker...');
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('[SW] Caching core assets');
return cache.addAll(PRECACHE_ASSETS);
})
.then(() => {
console.log('[SW] Core assets cached successfully');
return self.skipWaiting();
})
.catch((error) => {
console.error('[SW] Failed to cache core assets:', error);
})
);
});
/**
* Activate event - clean up old caches
*/
self.addEventListener('activate', (event) => {
console.log('[SW] Activating service worker...');
event.waitUntil(
caches.keys()
.then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => {
console.log('[SW] Deleting old cache:', name);
return caches.delete(name);
})
);
})
.then(() => {
console.log('[SW] Service worker activated');
return self.clients.claim();
})
);
});
/**
* Fetch event - serve from cache or network
*/
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Skip non-GET requests
if (request.method !== 'GET') {
return;
}
// Skip cross-origin requests (except CDN assets)
if (url.origin !== location.origin && !isTrustedCDN(url)) {
return;
}
// Handle navigation requests
if (request.mode === 'navigate') {
event.respondWith(networkFirst(request));
return;
}
// Handle static assets
if (isStaticAsset(url)) {
event.respondWith(cacheFirst(request));
return;
}
// Default: network first with cache fallback
event.respondWith(networkFirst(request));
});
/**
* Cache-first strategy (for static assets)
*/
async function cacheFirst(request) {
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(request);
if (cachedResponse) {
// Update cache in background
fetchAndCache(request, cache);
return cachedResponse;
}
try {
const networkResponse = await fetch(request);
if (networkResponse.ok) {
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
console.error('[SW] Failed to fetch:', request.url);
throw error;
}
}
/**
* Network-first strategy (for HTML pages)
*/
async function networkFirst(request) {
const cache = await caches.open(CACHE_NAME);
try {
const networkResponse = await fetch(request);
if (networkResponse.ok) {
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
console.log('[SW] Network failed, trying cache:', request.url);
const cachedResponse = await cache.match(request);
if (cachedResponse) {
return cachedResponse;
}
// Return offline page for navigation requests
if (request.mode === 'navigate') {
const offlinePage = await cache.match(OFFLINE_URL);
if (offlinePage) {
return offlinePage;
}
}
throw error;
}
}
/**
* Fetch and cache in background
*/
async function fetchAndCache(request, cache) {
try {
const response = await fetch(request);
if (response.ok) {
cache.put(request, response);
}
} catch (error) {
// Silently fail for background updates
}
}
/**
* Check if URL is a static asset
*/
function isStaticAsset(url) {
const staticExtensions = ['.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2'];
return staticExtensions.some(ext => url.pathname.endsWith(ext));
}
/**
* Check if URL is from trusted CDN
*/
function isTrustedCDN(url) {
const trustedHosts = [
'cdn.jsdelivr.net',
'cdnjs.cloudflare.com',
'unpkg.com',
'fonts.googleapis.com',
'fonts.gstatic.com'
];
return trustedHosts.includes(url.host);
}
/**
* Handle messages from main thread
*/
self.addEventListener('message', (event) => {
if (event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
if (event.data.type === 'CACHE_MODULE') {
const moduleUrl = event.data.url;
caches.open(CACHE_NAME).then(cache => {
cache.add(moduleUrl).then(() => {
console.log('[SW] Cached module:', moduleUrl);
});
});
}
if (event.data.type === 'GET_CACHE_STATUS') {
getCacheStatus().then(status => {
event.ports[0].postMessage(status);
});
}
});
/**
* Get cache status
*/
async function getCacheStatus() {
const cache = await caches.open(CACHE_NAME);
const keys = await cache.keys();
const cachedModules = MODULE_PAGES.filter(page =>
keys.some(key => key.url.includes(page))
);
return {
totalCached: keys.length,
cachedModules: cachedModules.length,
totalModules: MODULE_PAGES.length,
cacheVersion: CACHE_NAME
};
}
/**
* Periodic background sync (if supported)
*/
self.addEventListener('periodicsync', (event) => {
if (event.tag === 'update-content') {
event.waitUntil(updateCachedContent());
}
});
/**
* Update cached content
*/
async function updateCachedContent() {
const cache = await caches.open(CACHE_NAME);
for (const asset of PRECACHE_ASSETS) {
try {
const response = await fetch(asset);
if (response.ok) {
await cache.put(asset, response);
}
} catch (error) {
console.log('[SW] Failed to update:', asset);
}
}
console.log('[SW] Cache updated');
}