Javascript: service worker

Date: 2018-06-20
/* eslint-disable no-restricted-globals */
const worker = self;
const uniqueName = new Date().toISOString();
const preloadCache = 'preloadCache-' + uniqueName;
const cacheJsonPath = '/assets/cache.json';

const getRandom = () => String(Math.random()).slice(2);
const getRandomQuery = () => "?version=" + getRandom();

const getLatestCacheJson = async () => {
    const response = await fetch(cacheJsonPath + getRandomQuery());
    return response.json();
};

const getCacheJsonFromCache = async () => {
    const cache = await caches.open(preloadCache);
    const response = await cache.match(cacheJsonPath);
    if (!response) return null;
    return response.json();
};

worker.addEventListener('message', event => {
    if (event.data && event.data.action !== "check-for-updates") return;
    console.log("service-worker: check-for-updates");
    checkForUpdates();
});

async function checkForUpdates() {
    const [oldJson, newJson] = await Promise.all([getCacheJsonFromCache(), getLatestCacheJson()]);
    if (oldJson && oldJson.id !== newJson.id) {
        console.log('old version', oldJson.id, 'new version', newJson.id, "updating...");
        await addUrlsToTheCache(newJson);
    }
}

function sendUpdatedMessageToClients() {
    worker.clients.matchAll().then(clients => {
        clients.forEach(client => client.postMessage({ "serviceWorkerUpdated": true }));
    });
}

worker.addEventListener('activate', event => {
    console.log('service-worker', 'activate');
    event.waitUntil(checkForUpdates());
});

worker.addEventListener('install', event => {
    console.log('service-worker', 'install');
    event.waitUntil(
        getLatestCacheJson()
            .then(addUrlsToTheCache)
            .then(() => {
                console.log('service-worker', 'install: complete');
            })
    );
});

let installing = false;

async function addUrlsToTheCache(cacheJsonContent) {
    installing = true;
    const urlsToCache = cacheJsonContent.preload;
    urlsToCache.push(cacheJsonPath);
    const names = await caches.keys();
    const hadPreviousCache = names.length > 0;
    await Promise.all(names.map(name => caches.delete(name)));
    const cache = await caches.open(preloadCache);
    await addAllToCache(cache, urlsToCache);
    await worker.skipWaiting();
    console.log('service-worker', 'install: added caches');
    installing = false;

    if (hadPreviousCache) {
        sendUpdatedMessageToClients();
    }
}

async function addAllToCache(cache, urls) {
    await Promise.all(urls.map(async url => {
        const response = await fetch(url + getRandomQuery());
        if (!response.ok) {
            throw new TypeError('bad response status');
        }
        return cache.put(url, response);
    }));
}

worker.addEventListener('fetch', event => {
    const url = event.request.url;
    if (!installing && url.startsWith(worker.location.origin)) {
        event.respondWith(
            caches.match(event.request, {
                ignoreSearch: true,
                ignoreVary: true
            }).then(cachedResponse => {
                if (cachedResponse) {
                    return cachedResponse;
                }
                return fetch(event.request);
            })
        );
    }
});
if ('serviceWorker' in navigator) {
    const serviceWorker = navigator.serviceWorker;
    serviceWorker.getRegistrations().then(registrations => {
        for (const registration of registrations) {
            if (registration.active) {
                registration.active.postMessage({ action: "check-for-updates" });
            }
        }
    });
    serviceWorker.register('/serviceWorker.js', { scope: '/' });
    serviceWorker.addEventListener('message', e => {
        if (e.data && e.data.serviceWorkerUpdated) {
            window.alert('Updating app to the latest version');
            window.location.reload(true);
        }
    });
}

empty /assets/cache.json (generated by script)

{
    "id": "",
    "preload": []
}
11430cookie-checkJavascript: service worker