From 52593858a0115669f69bdb6c9ff4bd99a28459dd Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Sat, 25 Apr 2026 20:44:17 +0300 Subject: [PATCH] fix: guard service worker registration --- packages/web/src/main.tsx | 83 +++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx index df3d28b3..f1db2d33 100644 --- a/packages/web/src/main.tsx +++ b/packages/web/src/main.tsx @@ -13,16 +13,75 @@ declare global { window.__OPENCHAMBER_RUNTIME_APIS__ = createWebAPIs(); -if (import.meta.env.PROD) { - registerSW({ - onRegisterError(error: unknown) { - console.warn('[PWA] service worker registration failed:', error); - }, - }); -} else if ('serviceWorker' in navigator) { - void navigator.serviceWorker.getRegistrations() - .then((registrations) => Promise.all(registrations.map((registration) => registration.unregister()))) - .catch(() => {}); -} +type PrerenderingDocument = Document & { + prerendering?: boolean; +}; -import('@openchamber/ui/main'); +const canUseServiceWorker = (): boolean => { + if (!('serviceWorker' in navigator)) return false; + if (!window.isSecureContext) return false; + if (window.location.protocol !== 'http:' && window.location.protocol !== 'https:') return false; + + const documentState = document as PrerenderingDocument; + if (documentState.prerendering || String(document.visibilityState) === 'prerender') { + return false; + } + + return true; +}; + +const runWhenDocumentCanRegisterServiceWorker = (task: () => void): void => { + let completed = false; + const run = () => { + if (completed) return; + if (canUseServiceWorker()) { + completed = true; + task(); + } + }; + + const afterLoad = () => { + setTimeout(run, 0); + }; + + if (document.readyState === 'complete') { + afterLoad(); + } else { + window.addEventListener('load', afterLoad, { once: true }); + } + + const documentState = document as PrerenderingDocument; + if (documentState.prerendering || String(document.visibilityState) === 'prerender') { + document.addEventListener('visibilitychange', run, { once: true }); + } +}; + +const registerPwaServiceWorker = (): void => { + runWhenDocumentCanRegisterServiceWorker(() => { + try { + registerSW({ + onRegisterError(error: unknown) { + console.warn('[PWA] service worker registration skipped:', error); + }, + }); + } catch (error) { + console.warn('[PWA] service worker registration skipped:', error); + } + }); +}; + +const unregisterDevelopmentServiceWorkers = (): void => { + runWhenDocumentCanRegisterServiceWorker(() => { + void navigator.serviceWorker.getRegistrations() + .then((registrations) => Promise.all(registrations.map((registration) => registration.unregister()))) + .catch(() => {}); + }); +}; + +void import('@openchamber/ui/main'); + +if (import.meta.env.PROD) { + registerPwaServiceWorker(); +} else { + unregisterDevelopmentServiceWorkers(); +}