scratch-gui / src /playground /service-worker.js
soiz1's picture
Upload folder using huggingface_hub
8fd7a1d verified
// Enhanced service worker for improved caching and performance
const CACHE_NAME = 'mistwarp-cache-v1';
const RUNTIME_CACHE = 'mistwarp-runtime';
// Assets to cache immediately
const PRECACHE_URLS = [
'/',
'/static/blocks-media/default/backdrop1.svg',
'/static/blocks-media/default/costume1.svg',
'/static/assets/icon-96x96.png'
];
// Install event - cache core assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Precaching core assets');
return cache.addAll(PRECACHE_URLS.filter(url => url !== '/'));
})
.then(() => self.skipWaiting())
.catch(err => {
console.log('Precache failed, continuing anyway:', err);
self.skipWaiting();
})
);
});
// Activate event - clean old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME && cacheName !== RUNTIME_CACHE) {
console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
}).then(() => self.clients.claim())
);
});
// Fetch event - implement caching strategies
self.addEventListener('fetch', event => {
const { request } = event;
const url = new URL(request.url);
// Skip non-GET requests
if (request.method !== 'GET') return;
// Skip Chrome extension requests
if (url.protocol === 'chrome-extension:') return;
// Handle different types of requests with appropriate strategies
if (request.destination === 'script' || request.destination === 'style') {
// Cache first for JS/CSS files
event.respondWith(cacheFirst(request));
} else if (request.destination === 'image') {
// Cache first for images
event.respondWith(cacheFirst(request));
} else if (url.pathname.includes('/api/') || url.pathname.includes('/internalapi/')) {
// Network first for API calls
event.respondWith(networkFirst(request));
} else if (url.pathname.endsWith('.sb3') || url.pathname.includes('projects')) {
// Network first for project files, but cache for offline
event.respondWith(networkFirst(request));
} else {
// Stale while revalidate for everything else
event.respondWith(staleWhileRevalidate(request));
}
});
// Cache first strategy - good for static assets
async function cacheFirst(request) {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
if (cached) {
return cached;
}
try {
const response = await fetch(request);
if (response.status === 200) {
cache.put(request, response.clone());
}
return response;
} catch (error) {
console.log('Cache first failed for:', request.url);
throw error;
}
}
// Network first strategy - good for dynamic content
async function networkFirst(request) {
const cache = await caches.open(RUNTIME_CACHE);
try {
const response = await fetch(request);
if (response.status === 200) {
cache.put(request, response.clone());
}
return response;
} catch (error) {
const cached = await cache.match(request);
if (cached) {
return cached;
}
throw error;
}
}
// Stale while revalidate - good for frequently updated content
async function staleWhileRevalidate(request) {
const cache = await caches.open(RUNTIME_CACHE);
const cached = await cache.match(request);
const fetchPromise = fetch(request).then(response => {
if (response.status === 200) {
cache.put(request, response.clone());
}
return response;
}).catch(() => cached);
return cached || fetchPromise;
}
// Handle periodic cache cleanup
self.addEventListener('message', event => {
if (event.data && event.data.type === 'CLEANUP_CACHE') {
cleanupCache();
}
});
async function cleanupCache() {
const cache = await caches.open(RUNTIME_CACHE);
const requests = await cache.keys();
// Remove old entries (keep last 100)
if (requests.length > 100) {
const toDelete = requests.slice(0, requests.length - 100);
await Promise.all(toDelete.map(request => cache.delete(request)));
}
}
// Cleanup cache every hour
setInterval(cleanupCache, 60 * 60 * 1000);