'use strict'; // Serve app files and individually selected route snapshots independently (v2). importScripts('/js/texts-de.js', '/js/texts-en.js', '/js/i18n.js', '/js/offline-storage.js'); globalThis.addEventListener('install', event => event.waitUntil(globalThis.skipWaiting())); globalThis.addEventListener('activate', event => event.waitUntil(globalThis.clients.claim())); /** Audio seeks need partial responses even when the entire file is stored locally. */ async function rangedResponse(response, range) { const bytes = await response.arrayBuffer(); const match = /^bytes=(\d*)-(\d*)$/.exec(range); if (!match || (!match[1] && !match[2])) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } }); const start = match[1] ? Number(match[1]) : Math.max(0, bytes.byteLength - Number(match[2])); let end = bytes.byteLength - 1; if (match[1] && match[2]) end = Math.min(Number(match[2]), end); if (start > end || start >= bytes.byteLength) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } }); const headers = new Headers(response.headers); headers.set('Content-Range', `bytes ${start}-${end}/${bytes.byteLength}`); headers.set('Content-Length', String(end - start + 1)); headers.set('Accept-Ranges', 'bytes'); return new Response(bytes.slice(start, end + 1), { status: 206, headers }); } globalThis.addEventListener('fetch', event => { const request = event.request; const url = new URL(request.url); if (request.method !== 'GET' || url.origin !== globalThis.location.origin || request.headers.has('X-Wegwichtel-Offline-Download') || url.pathname === '/api/offline-manifest') return; event.respondWith((async () => { // A complete snapshot is cache-first, avoiding any wait for a failing mobile network. const cached = await globalThis.Wegwichtel.OfflineStore.match(request.url).catch(() => null); if (cached) { const range = request.headers.get('Range'); return range ? rangedResponse(cached, range) : cached; } return fetch(request); })()); });