PHP Classes

File: public_html/sw.js

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   public_html/sw.js   Download  
File: public_html/sw.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 8,270 bytes
 

Contents

Class file image Download
const CACHE_NAME = 'binkcache-v908'; // Static assets to precache const staticAssets = [ '/favicon.svg', '/js/user-storage.js', '/js/app.js', '/js/media-player.js', '/js/retro-audio-player.js', '/js/binkplayer/binkplayer.js', '/vendor/plyr-3.8.4/plyr.min.js', '/vendor/plyr-3.8.4/plyr.min.css', '/vendor/chiptune3/chiptune3.js', '/vendor/chiptune3/chiptune3.worklet.js', '/vendor/chiptune3/libopenmpt.worklet.js', '/css/binkplayer/binkplayer.css', '/js/interest-picker.js', '/js/netmail.js', '/js/echomail.js', '/js/message-list-context-menu.js', '/js/chat-page.js', '/js/notifier.js', '/js/binkstream-client.js', '/js/binkstream-worker-v2.js', '/js/ansisys.js', '/js/ansi-editor.js', '/js/pcboard.js', '/css/ansisys.css', '/css/chat-page.css', // Theme stylesheets '/css/style.css', '/css/amber.css', '/css/dark.css', '/css/greenterm.css', '/css/cyberpunk.css', // Vendor libraries '/vendor/toastui-editor-3.2.2/toastui-editor-all.min.js', '/vendor/toastui-editor-3.2.2/toastui-editor.css', '/vendor/toastui-editor-3.2.2/toastui-editor-dark.css', '/js/markdown-editor.js', '/vendor/bootstrap-5.3.0/css/bootstrap.min.css', '/vendor/bootstrap-5.3.0/js/bootstrap.bundle.min.js', '/vendor/jquery-3.7.1/jquery-3.7.1.min.js', '/vendor/fontawesome-6.4.0/css/all.min.css', '/vendor/fontawesome-6.4.0/webfonts/fa-solid-900.woff2', '/vendor/fontawesome-6.4.0/webfonts/fa-regular-400.woff2', '/vendor/riptermjs/BGI.js', '/vendor/riptermjs/ripterm.js', '/vendor/marked.min.js', // Notification sounds '/sounds/notify1.mp3', '/sounds/notify2.mp3', '/sounds/notify3.mp3', '/sounds/notify4.mp3', '/sounds/notify5.mp3' ]; // Keep a reference to the open cache to avoid re-opening on every fetch let _cache = null; function getCache() { if (_cache) return Promise.resolve(_cache); // Race against a timeout ? Cache Storage can deadlock in Edge when another // browser process holds the cache lock (e.g. PWA + browser window both open). const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('cache-open-timeout')), 3000) ); return Promise.race([ caches.open(CACHE_NAME).then((c) => { _cache = c; return c; }), timeout ]); } async function precacheStaticAssets(cache) { for (const asset of staticAssets) { const request = new Request(asset, { cache: 'reload' }); const response = await fetch(request); if (!response.ok) { throw new Error(`precache-failed:${asset}:${response.status}`); } await cache.put(request, response); } } // Install event - cache static assets and activate immediately. // Always delete and re-create the cache on install so that a version bump // guarantees fresh assets even if the same cache name was previously populated // with older content (e.g. a redeployment without a CACHE_NAME bump, or // DevTools "Update on reload" forcing a reinstall of the same version). self.addEventListener('install', (event) => { event.waitUntil( caches.delete(CACHE_NAME) .then(() => caches.open(CACHE_NAME)) .then(async (cache) => { _cache = cache; console.log('[SW] Caching', staticAssets.length, 'static assets'); await precacheStaticAssets(cache); }) .then(() => { console.log('[SW] New version installed, skipping wait'); return self.skipWaiting(); }) ); }); // Activate event - purge old caches self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { const oldCaches = cacheNames.filter(name => name !== CACHE_NAME); return Promise.all( oldCaches.map((cacheName) => { console.log('[SW] Deleting old cache:', cacheName); return caches.delete(cacheName); }) ).then(() => oldCaches.length > 0); // true only when replacing an older version }).then((replacedOld) => { console.log('[SW] New version activated'); return self.clients.claim().then(() => replacedOld); }).then((replacedOld) => { // Only notify pages when we actually replaced an older cache version. // Skipping on first install and on re-activations of the same version // prevents the "Update available" prompt from looping after a reload. if (!replacedOld) return; return self.clients.matchAll({ type: 'window' }).then(clients => { clients.forEach(client => { client.postMessage({ type: 'UPDATE_AVAILABLE', version: CACHE_NAME }); }); }); }) ); }); // Fetch event - serve from cache, update in background self.addEventListener('fetch', (event) => { const request = event.request; const url = new URL(request.url); // Only handle same-origin requests if (url.origin !== self.location.origin) { return; } // Hard refresh (Ctrl+Shift+R) sets cache mode to 'reload' ? bypass SW cache // entirely so the browser always gets fresh files on a forced reload. if (request.cache === 'reload') { return; } // Don't cache API calls or HTML pages (except i18n catalog which is static per deployment) if (url.pathname.startsWith('/api/') && url.pathname !== '/api/i18n/catalog') { return; } if (request.headers.get('accept')?.includes('text/html')) { return; } // Admin-only files are excluded from caching ? they change frequently during // development and are only loaded for admins, so per-user caching isn't worth it. const adminPaths = ['/js/admin-terminal.js', '/js/xterm.js', '/js/xterm-addon-fit.js', '/css/xterm.css']; if (adminPaths.includes(url.pathname)) { return; } // Handle CSS/JS/font files with cache-first strategy. // Version bumps to CACHE_NAME purge and repopulate the cache at install time, // so there is no need to re-fetch on every request. if (url.pathname.match(/\.(css|js|woff2?|ttf|eot|svg|mp3|ogg)$/)) { event.respondWith( getCache().then((cache) => { return cache.match(request).then((cachedResponse) => { if (cachedResponse) { return cachedResponse; } // Not in cache yet ? fetch, store, and return. // Do not cache partial responses (206) ? the Cache API // rejects them and audio range requests can trigger this. return fetch(request).then((networkResponse) => { if (networkResponse.ok && networkResponse.status === 200) { cache.put(request, networkResponse.clone()); } return networkResponse; }); }); }).catch(() => { // Cache unavailable (lock timeout or error) ? fall back to network // so the page loads rather than hanging with a white screen. return fetch(request); }) ); } // Cache i18n catalog with cache-first strategy (same as CSS/JS) if (url.pathname === '/api/i18n/catalog') { event.respondWith( getCache().then((cache) => { return cache.match(request).then((cachedResponse) => { if (cachedResponse) { return cachedResponse; } return fetch(request).then((networkResponse) => { if (networkResponse.ok && networkResponse.status === 200) { cache.put(request, networkResponse.clone()); } return networkResponse; }); }); }).catch(() => { return fetch(request); }) ); } });