You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

92 lines
2.6 KiB
JavaScript

/**
* Service Worker — Italiano Vivo
* Estratégia:
* - Shell (HTML/CSS/JS do app): cache-first
* - /content/* e /audio/*: cache-first com network fallback + runtime caching
* - /api/*: network-only (nunca cachear)
*
* Objetivo: modo resgate offline — frases + áudio já baixados funcionam no avião.
*/
const CACHE_NAME = "italiano-vivo-v1";
const SHELL_ASSETS = ["/", "/index.html"];
// ─── Install: pré-cache do shell ───
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(SHELL_ASSETS)),
);
self.skipWaiting();
});
// ─── Activate: limpar caches antigos ───
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys
.filter((k) => k !== CACHE_NAME)
.map((k) => caches.delete(k)),
),
),
);
self.clients.claim();
});
// ─── Fetch: roteamento por padrão de URL ───
self.addEventListener("fetch", (event) => {
const url = new URL(event.request.url);
const pathname = url.pathname;
// API: sempre network-only
if (pathname.startsWith("/api/")) {
return; // deixa o browser fazer o fetch normal
}
// Content e Audio: cache-first com network fallback + runtime caching
if (pathname.startsWith("/content/") || pathname.startsWith("/audio/")) {
event.respondWith(cacheFirstWithRuntimeCache(event.request));
return;
}
// Shell e demais assets: cache-first
event.respondWith(cacheFirst(event.request));
});
/**
* Cache-first: tenta cache, se não achar vai na rede (sem salvar no cache).
* Usado para shell/assets que são pré-cachados no install.
*/
async function cacheFirst(request) {
const cached = await caches.match(request);
if (cached) return cached;
return fetch(request);
}
/**
* Cache-first com runtime caching: tenta cache, se não achar vai na rede
* e salva a resposta no cache para uso offline futuro.
* Usado para /content/* e /audio/* que são baixados sob demanda.
*/
async function cacheFirstWithRuntimeCache(request) {
const cached = await caches.match(request);
if (cached) return cached;
try {
const response = await fetch(request);
// Só cachear respostas OK (não cachear 404/500)
if (response.ok) {
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
}
return response;
} catch (_err) {
// Offline e sem cache: retorna erro genérico
return new Response("Offline — Italiano Vivo", {
status: 503,
statusText: "Service Unavailable",
headers: { "Content-Type": "text/plain" },
});
}
}