@enplug/scripts
Version:
Enplug scripts
145 lines (124 loc) • 4.91 kB
JavaScript
/**
* Service worker responsible for providing offline support to the WebPlayer
* @author Michal Drewniak (michal@enplug.com)
*/
;
/**
* Offline support configuration.
* @type {Object}
* @property {string} cacheVersion - Version of the cache. This should be updated any time we want
* the previous version of the cache removed.
* @property {string} cacheName - Name of the cache. This is usually the same as the application
* name or ID.
* @property {Array.<string>} = A list of static URLs which should be cached.
* @property {Object.<string, number>} - A mapping of URLs (or their parts) to time in minutes.
* The URL denotes for which locations cached responses should be refreshed. Time denotes
* after which time (in minutes), given response should be refreshed. Time set to 0 means that
* a given response should never be cached.
*/
var config = <% config %>;
// End of config. There shouldn't be any need to edit code below.
/**
* Installs service worker and adds static resources to cache.
*/
self.addEventListener('install', function (event) {
event.waitUntil(caches.open(config.cacheVersion).then((cache) => {
return cache.addAll(config.staticResources)
.then(() => {
console.log(`[<% app_name %>] Offline support service worker v${config.cacheVersion} installed.`);
self.skipWaiting();
})
.catch((error) => {
console.warn(`[<% app_name %>] Error adding static resources to cache ${config.cacheVersion}.`, error);
console.warn(`[<% app_name %>] Static resources: ${JSON.stringify(config.staticResources)}`, config.staticResources);
})
}));
});
/**
* Activate phase. Clears all non-whitelisted cache.
*/
self.addEventListener('activate', (event) => {
var cacheWhitelist = [config.cacheVersion];
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
console.log(`[<% app_name %>] Deleting cache ${cacheName}`);
return caches.delete(cacheName);
}
})
).then(() => self.clients.claim());
})
);
});
/**
* Captures requests and either exectures request to the server or returns a cached version.
*/
self.addEventListener("fetch", (event) => {
function cacheResponse(response) {
// Fetching new data resulted in a valid response. Cache it.
var responseToCache = response.clone();
caches.open(config.cacheVersion).then(
(cache) => {
// Cache response and update the time of caching this particular request.
if (response && response.ok) {
cache.put(cacheUrl, responseToCache);
} else {
return caches.match(cacheUrl);
}
},
(error) => {
console.warn(`[player-web Unable to cache ${cacheUrl}`, error)
}
);
// Return the response.
return response;
}
// Ignore POST requests
if (event.request.method === 'POST') {
return false;
}
// Get rid of the app token to generalize URL for caching. Having unique URLs for each request
// will only make the cache get bigger over time.
var cacheUrl = event.request.url.replace(/\?apptoken=[^&]*/g, '');
if (cacheUrl.startsWith('blob:') || cacheUrl.endsWith('.mp4')) {
console.log(`[player-web|offline] Caching prevented: URL starts with blob: or ends with .mp4.`);
return false;
}
for (const noCacheUrl of config.noCacheUrls) {
if (event.request.url.indexOf(noCacheUrl) >= 0) {
return false;
}
}
const originalRequest = event.request;
// We need to explicitly change the reqest to CORS request so that cross-domain resources
// get properly cached.
var fetchRequest = new Request(event.request.url, {
headers: originalRequest.headers,
mode: 'cors',
credentials: 'omit',
referrer: 'no-referrer',
});
event.respondWith(fetch(fetchRequest)
.then((fetchResponse) => {
if (!fetchResponse || fetchResponse.status !== 200) {
throw Error('CORS request failed.');
}
// Propagate correct response down the Promise chain.
return fetchResponse;
})
// CORS request has failed. We need attempt to refetch the request as no-cors (which is still preferable)
// then a failed response. We want to propagate down the Promise chain the response to our no-CORS request.
.catch((err) => {
console.log(`${TAG} CORS request failed. Attempting fetch original request.`);
return fetch(originalRequest);
})
.then((response) => cacheResponse(response))
.catch(() => fetch(originalRequest))
.then((response) => cacheResponse(response))
.catch(() => {
console.log(`[player-web Unable to fetch ${cacheUrl}. Attempting to return it from cache.`);
return caches.match(cacheUrl);
}));
});