simple-pwa/sw.js
2023-06-15 16:37:25 +02:00

136 lines
3.4 KiB
JavaScript

const appCacheName = 'simple-pwa';
const appContentToCache = [
'/',
'index.html',
'app.js',
'favicon.ico',
'manifest.webmanifest',
'icons/icon-512.png',
];
const dataCacheName = `${appCacheName}-data`;
// Communication channel with clients
const channel = new BroadcastChannel('sw-messages');
/**
* First of all, the service worker will react to an 'install' event (triggered automatically)
* We'll put the application content into cache.
*/
self.addEventListener('install', (event) => {
event.waitUntil(install());
});
/**
* Then take immediate control on the page that triggered the installation
*/
self.addEventListener('activate', () => {
self.clients.claim();
channel.postMessage({ ready: true });
});
/**
* Then, the service worker will be involved every time a request is made
*/
self.addEventListener('fetch', (e) => {
e.respondWith(fetch_resource(e.request));
});
/**
* handle installation request:
* - fetch application resources and store them in appCache
*/
async function install() {
return new Promise(async (resolve, reject) => {
try {
await caches.delete(appCacheName);
const cache = await caches.open(appCacheName);
await cache.addAll(appContentToCache);
resolve();
} catch (e) {
reject(e);
}
});
}
/**
* fetch a resource:
* - if resource is in app cache, return in
* - if resource can be obtained from remote server, fetch it
* - if resource is in data cache, return it
* - otherwise return HTTP-408 response
*/
async function fetch_resource(resource) {
response = await get_from_cache(resource, appCacheName);
if (response) {
return response;
} else {
channel.postMessage({ loading: true });
try {
response = await fetch(resource);
await put_into_cache(resource, response);
channel.postMessage({ loading: false });
channel.postMessage({ offline: false });
return response;
} catch (error) {
// TODO: check if error is because we're offline
response = await get_from_cache(resource);
channel.postMessage({ loading: false });
channel.postMessage({ offline: true });
if (response) {
// resource was found in data cache
return response;
} else {
return new Response('offline', {
status: 408,
headers: { 'Content-Type': 'text/plain' },
});
}
}
}
}
/**
* reset app cache that contains application code to be cached for offline use
*/
async function reset_app_cache() {
log('resetting app cache');
await caches.delete(appCacheName);
const cache = await caches.open(appCacheName);
await cache.addAll(appContentToCache);
}
/**
* query cache for resource
*/
async function get_from_cache(resource, cacheName = dataCacheName) {
try {
const cache = await caches.open(cacheName);
const response = await cache.match(resource);
if (response) {
log(
`FROM ${cacheName === appCacheName ? 'APP' : 'DATA'} CACHE: ${
resource.url
}`,
);
}
return response;
} catch (error) {
return;
}
}
/**
* put resource into cache
*/
async function put_into_cache(request, response, cacheName = dataCacheName) {
const cache = await caches.open(cacheName);
await cache.put(request, response.clone());
}
/**
* Log a message to console
*/
function log(message) {
console.log(`[Service Worker] ${message}`);
}