Rename service worker runtime and scope route persistence
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Browser worker/runtime helpers.
|
||||
* Keep the top-level module compact, then expose focused namespaces that we
|
||||
* can later split into separate files without changing the import surface.
|
||||
*/
|
||||
|
||||
import { isElectronHost, isTauriHost } from './host.js';
|
||||
import { getConfig, isDevelopment, CONFIG_KEYS } from './env.js';
|
||||
|
||||
const SW_PATH = '/sw.js';
|
||||
const SW_SCOPE = '/';
|
||||
const DEV_SW_RESET_KEY = '__bface_dev_sw_reset__';
|
||||
|
||||
async function clearAllCaches() {
|
||||
if ('caches' in window) {
|
||||
try {
|
||||
const cacheNames = await caches.keys();
|
||||
await Promise.all(cacheNames.map((name) => {
|
||||
console.log('[SW] Clearing cache:', name);
|
||||
return caches.delete(name);
|
||||
}));
|
||||
console.log('[SW] All caches cleared');
|
||||
return cacheNames.length;
|
||||
} catch (error) {
|
||||
console.error('[SW] Failed to clear caches:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function unregisterAllServiceWorkers() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
try {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
await Promise.all(registrations.map((reg) => {
|
||||
console.log('[SW] Unregistering service worker:', reg.scope);
|
||||
return reg.unregister();
|
||||
}));
|
||||
console.log(`[SW] Unregistered ${registrations.length} service worker(s)`);
|
||||
return registrations.length;
|
||||
} catch (error) {
|
||||
console.error('[SW] Failed to unregister service workers:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function clearAllStorage() {
|
||||
try {
|
||||
localStorage.clear();
|
||||
console.log('[Storage] localStorage cleared');
|
||||
|
||||
sessionStorage.clear();
|
||||
console.log('[Storage] sessionStorage cleared');
|
||||
|
||||
if ('indexedDB' in window) {
|
||||
const databases = await indexedDB.databases();
|
||||
await Promise.all(databases.map((db) => {
|
||||
if (db.name) {
|
||||
return new Promise((resolve) => {
|
||||
const deleteReq = indexedDB.deleteDatabase(db.name);
|
||||
deleteReq.onsuccess = () => {
|
||||
console.log(`[Storage] IndexedDB database "${db.name}" deleted`);
|
||||
resolve();
|
||||
};
|
||||
deleteReq.onerror = () => {
|
||||
console.warn(`[Storage] Failed to delete IndexedDB database "${db.name}"`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
|
||||
console.log('[Storage] All storage cleared');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[Storage] Failed to clear storage:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function clearPWACache() {
|
||||
console.log('Clearing all PWA caches and storage...');
|
||||
|
||||
try {
|
||||
const swCount = await unregisterAllServiceWorkers();
|
||||
const cacheCount = await clearAllCaches();
|
||||
await clearAllStorage();
|
||||
|
||||
console.log(`PWA cache cleared: ${swCount} service worker(s), ${cacheCount} cache(s), and all storage`);
|
||||
console.log('Reload the page to re-register service workers');
|
||||
|
||||
return {
|
||||
serviceWorkers: swCount,
|
||||
caches: cacheCount,
|
||||
storage: true
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to clear PWA cache:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function registerServiceWorker() {
|
||||
if (isElectronHost() || isTauriHost()) {
|
||||
await unregisterAllServiceWorkers();
|
||||
console.log('[SW] Skipping service worker registration in desktop host');
|
||||
return null;
|
||||
}
|
||||
|
||||
const devHost = await getConfig(CONFIG_KEYS.DEV_HOST, isDevelopment());
|
||||
if (devHost) {
|
||||
console.log('[SW] Skipping service worker registration in development');
|
||||
return null;
|
||||
}
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.register(SW_PATH, {
|
||||
scope: SW_SCOPE,
|
||||
updateViaCache: 'none'
|
||||
});
|
||||
|
||||
console.log('Service Worker registered:', registration);
|
||||
await registration.update();
|
||||
|
||||
registration.addEventListener('updatefound', () => {
|
||||
const newWorker = registration.installing;
|
||||
if (newWorker) {
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
if (newWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
console.log('[SW] New service worker available, reloading...');
|
||||
window.location.reload();
|
||||
} else {
|
||||
console.log('[SW] Service worker installed for the first time');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return registration;
|
||||
} catch (error) {
|
||||
console.error('Service Worker registration failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn('Service Workers are not supported');
|
||||
return null;
|
||||
}
|
||||
|
||||
async function resetServiceWorkers() {
|
||||
if (isElectronHost() || isTauriHost()) {
|
||||
await unregisterAllServiceWorkers();
|
||||
return false;
|
||||
}
|
||||
|
||||
const devHost = await getConfig(CONFIG_KEYS.DEV_HOST, isDevelopment());
|
||||
if (!devHost || typeof window === 'undefined' || !('serviceWorker' in navigator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasController = Boolean(navigator.serviceWorker.controller);
|
||||
const alreadyReset = sessionStorage.getItem(DEV_SW_RESET_KEY) === '1';
|
||||
|
||||
if (!hasController || alreadyReset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await unregisterAllServiceWorkers();
|
||||
await clearAllCaches();
|
||||
} catch (error) {
|
||||
console.warn('[SW] Failed to reset dev service workers:', error);
|
||||
}
|
||||
|
||||
sessionStorage.setItem(DEV_SW_RESET_KEY, '1');
|
||||
window.location.reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getServiceWorkerStatus() {
|
||||
if (isElectronHost() || isTauriHost()) {
|
||||
return 'Desktop Disabled';
|
||||
}
|
||||
|
||||
if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) {
|
||||
return 'Not Supported';
|
||||
}
|
||||
|
||||
const devHost = await getConfig(CONFIG_KEYS.DEV_HOST, isDevelopment());
|
||||
try {
|
||||
if (devHost) {
|
||||
const registration = await navigator.serviceWorker.getRegistration();
|
||||
if (registration) {
|
||||
registration.update();
|
||||
return 'Active';
|
||||
}
|
||||
return 'Development Disabled';
|
||||
}
|
||||
|
||||
await navigator.serviceWorker.ready;
|
||||
return 'Active';
|
||||
} catch (error) {
|
||||
console.warn('[SW] Failed to resolve service worker status:', error);
|
||||
return 'Not Supported';
|
||||
}
|
||||
}
|
||||
|
||||
async function unregisterServiceWorker() {
|
||||
if (isElectronHost() || isTauriHost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registration = await navigator.serviceWorker.getRegistration();
|
||||
if (registration) {
|
||||
await registration.unregister();
|
||||
console.log('Service Worker unregistered');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const sw = {
|
||||
clearAllCaches,
|
||||
unregisterAllServiceWorkers,
|
||||
clearAllStorage,
|
||||
clearPWACache,
|
||||
registerServiceWorker,
|
||||
resetServiceWorkers,
|
||||
getServiceWorkerStatus,
|
||||
unregisterServiceWorker
|
||||
};
|
||||
Reference in New Issue
Block a user