Spaces:
Running
Running
| const CACHE_NAME = 'jarvis-real-ar-v5' | |
| const BASE_PATH = new URL(self.registration.scope).pathname.replace(/\/?$/, '/') | |
| const shellURL = path => new URL(path, self.registration.scope).href | |
| const START_URL = shellURL('./?device=POCO-M5&mode=vision_pro&source=manifest') | |
| const APP_SHELL = [ | |
| shellURL('./'), | |
| shellURL('./index.html'), | |
| shellURL('./manifest.json'), | |
| START_URL, | |
| shellURL('./ui/panels.css'), | |
| shellURL('./ui/mobile.css'), | |
| shellURL('./ui/hud.css'), | |
| shellURL('./ui/animations.css'), | |
| ] | |
| function isSameOriginAsset(url) { | |
| return url.origin === self.location.origin | |
| } | |
| function discoverAppAssets(html) { | |
| if (!html) return [] | |
| const urls = new Set() | |
| const re = /<(?:script|link)\b[^>]*(?:\ssrc|\shref)=["']([^"']+)["'][^>]*>/gi | |
| for (const match of html.matchAll(re)) { | |
| try { | |
| const url = new URL(match[1], shellURL('./index.html')) | |
| if (isSameOriginAsset(url)) urls.add(url.href) | |
| } catch { | |
| // Ignore malformed asset URLs. | |
| } | |
| } | |
| return [...urls] | |
| } | |
| function discoverDynamicImports(js, baseUrl) { | |
| if (!js) return [] | |
| const urls = new Set() | |
| const re = /import\(\s*["']([^"']+)["']\s*\)/g | |
| for (const match of js.matchAll(re)) { | |
| try { | |
| const url = new URL(match[1], baseUrl) | |
| if (isSameOriginAsset(url)) urls.add(url.href) | |
| } catch { | |
| // Ignore malformed dynamic import specifiers. | |
| } | |
| } | |
| return [...urls] | |
| } | |
| async function cacheAppShell() { | |
| const cache = await caches.open(CACHE_NAME) | |
| const urls = new Set(APP_SHELL) | |
| const seen = new Set() | |
| const queue = [...APP_SHELL] | |
| try { | |
| const response = await fetch(shellURL('./index.html'), { cache: 'reload' }) | |
| if (response.ok) { | |
| const html = await response.text() | |
| discoverAppAssets(html).forEach(url => { | |
| urls.add(url) | |
| queue.push(url) | |
| }) | |
| await cache.put(shellURL('./index.html'), new Response(html, { | |
| status: response.status, | |
| statusText: response.statusText, | |
| headers: response.headers, | |
| })) | |
| } | |
| } catch { | |
| // Shell HTML is best-effort during install. | |
| } | |
| while (queue.length) { | |
| const url = queue.shift() | |
| if (!url || seen.has(url)) continue | |
| seen.add(url) | |
| try { | |
| const response = await fetch(url, { cache: 'reload' }) | |
| const contentType = response.headers.get('content-type') || '' | |
| const expectedCSS = url.endsWith('.css') | |
| const expectedJS = url.endsWith('.js') | |
| if (response.ok && (!expectedCSS || contentType.includes('text/css'))) { | |
| await cache.put(url, response.clone()) | |
| if (expectedJS) { | |
| const js = await response.text() | |
| discoverDynamicImports(js, url).forEach(nextUrl => { | |
| if (!seen.has(nextUrl)) queue.push(nextUrl) | |
| }) | |
| } | |
| } | |
| } catch { | |
| await Promise.resolve() | |
| } | |
| } | |
| } | |
| self.addEventListener('install', event => { | |
| event.waitUntil(cacheAppShell().then(() => self.skipWaiting())) | |
| }) | |
| self.addEventListener('activate', event => { | |
| event.waitUntil( | |
| caches.keys() | |
| .then(keys => Promise.all(keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key)))) | |
| .then(() => self.clients.claim()) | |
| ) | |
| }) | |
| self.addEventListener('fetch', event => { | |
| const request = event.request | |
| if (request.method !== 'GET') return | |
| const url = new URL(request.url) | |
| if (url.origin !== self.location.origin) return | |
| const relativePath = url.pathname.startsWith(BASE_PATH) | |
| ? url.pathname.slice(BASE_PATH.length - 1) | |
| : url.pathname | |
| if (relativePath.startsWith('/api/') || relativePath.startsWith('/scene-ws')) return | |
| event.respondWith( | |
| fetch(request) | |
| .then(response => { | |
| if (response.ok) { | |
| const copy = response.clone() | |
| caches.open(CACHE_NAME).then(cache => cache.put(request, copy)).catch(() => undefined) | |
| } | |
| return response | |
| }) | |
| .catch(() => caches.match(request).then(cached => { | |
| if (cached) return cached | |
| if (request.mode === 'navigate') { | |
| return caches.match(shellURL('./')).then(shell => shell || caches.match(shellURL('./index.html'))) | |
| } | |
| return new Response('Offline: this AR asset is not cached.', { status: 503, headers: { 'content-type': 'text/plain' } }) | |
| })) | |
| ) | |
| }) | |