Le piege que CLAUDE.md signale : en Blazor WebAssembly le code tourne dans le navigateur alors que SQLite vit sur le serveur, et le service worker ne met en cache que les assets. Sans travail explicite, l'application demarre hors-ligne et affiche une bibliotheque vide. - js/cache-hors-ligne.js : instantanes JSON dans IndexedDB, et surveillance des bascules online/offline. Aucune logique metier. - CacheHorsLigne / EtatReseau : lecture-ecriture des instantanes, et etat reseau combinant navigator.onLine (fiable seulement par sa negation) avec le sort reel des appels HTTP. - ServiceLivresApi : les lectures retombent sur le cache, les ecritures sont refusees. Le catalogue entier est memorise, pas les reponses filtrees : c'est ce qui rend la recherche hors-ligne possible sur tout le fonds. - FiltreLivresLocal : pendant navigateur de FiltreLivres, avec un test qui confronte les deux implementations sur les memes donnees. - Interface : pastille et bandeau d'etat avec la date de synchronisation, et actions d'ecriture desactivees avec leur raison plutot que boutons morts. - js/mise-a-jour.js : les empreintes WASM etant desactivees, le service worker est le seul cache-busting du projet. L'enregistrement journalise desormais ses echecs, et un bandeau propose la nouvelle version sans attendre la fermeture de tous les onglets. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
66 lines
3.2 KiB
JavaScript
66 lines
3.2 KiB
JavaScript
// Caution! Be sure you understand the caveats before publishing an application with
|
|
// offline support. See https://aka.ms/blazor-offline-considerations
|
|
|
|
self.importScripts('./service-worker-assets.js');
|
|
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
|
|
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
|
|
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
|
|
|
|
// Mise à jour à la demande. Sans cela, un nouveau worker attend que TOUS les onglets de
|
|
// l'application soient fermés — un onglet oublié fige indéfiniment l'utilisateur sur l'ancienne
|
|
// version. C'est d'autant plus important ici que les empreintes des assets WASM sont désactivées
|
|
// (voir CLAUDE.md) : ce worker est le SEUL mécanisme de cache-busting du projet.
|
|
// Le message ne vient que de js/mise-a-jour.js, après un clic explicite : jamais tout seul, pour
|
|
// ne pas mélanger deux versions au milieu d'une session.
|
|
self.addEventListener('message', event => {
|
|
if (event.data && event.data.type === 'SKIP_WAITING') self.skipWaiting();
|
|
});
|
|
|
|
const cacheNamePrefix = 'offline-cache-';
|
|
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
|
|
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/, /\.webmanifest$/ ];
|
|
const offlineAssetsExclude = [ /^service-worker\.js$/ ];
|
|
|
|
// Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'.
|
|
const base = "/";
|
|
const baseUrl = new URL(base, self.origin);
|
|
const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href);
|
|
|
|
async function onInstall(event) {
|
|
console.info('Service worker: Install');
|
|
|
|
// Fetch and cache all matching items from the assets manifest
|
|
const assetsRequests = self.assetsManifest.assets
|
|
.filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
|
|
.filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
|
|
.map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
|
|
await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
|
|
}
|
|
|
|
async function onActivate(event) {
|
|
console.info('Service worker: Activate');
|
|
|
|
// Delete unused caches
|
|
const cacheKeys = await caches.keys();
|
|
await Promise.all(cacheKeys
|
|
.filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
|
|
.map(key => caches.delete(key)));
|
|
}
|
|
|
|
async function onFetch(event) {
|
|
let cachedResponse = null;
|
|
if (event.request.method === 'GET') {
|
|
// For all navigation requests, try to serve index.html from cache,
|
|
// unless that request is for an offline resource.
|
|
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
|
|
const shouldServeIndexHtml = event.request.mode === 'navigate'
|
|
&& !manifestUrlList.some(url => url === event.request.url);
|
|
|
|
const request = shouldServeIndexHtml ? 'index.html' : event.request;
|
|
const cache = await caches.open(cacheName);
|
|
cachedResponse = await cache.match(request);
|
|
}
|
|
|
|
return cachedResponse || fetch(event.request);
|
|
}
|