Using multiple javascript service workers at the same domain but different folders
There's two things to keep in mind here:
It's possible to register an arbitrary number of service workers for a given origin, as long as each service worker has its own unique scope. It sounds like you're doing this already, registering both a top-level service worker with a scope of
/
and then additional service workers scoped to the paths from which each of your independent web apps run.The Cache Storage API (and other storage mechanisms, like IndexedDB) are shared throughout the entire origin, and by default there's no "sharding" or namespace segregation.
Taking those two facts together, my guess is that what's happening is that you have some cache cleanup code in the activate
handler of one of the service workers that's scoped to a specific web app, and that code retrieves a list of current cache names and deletes any caches that it thinks is out of date. That's a common thing to do in a service worker, but you can run into trouble if you don't take into account the fact that caches.keys()
will return a list of all the caches in your origin, even the caches that were created by other service worker instances.
Assuming that's what's going on here, a model that I'd recommend following is to include the value of registration.scope
as part of the cache name when you create your caches. When it comes time to delete old cache entries in your activate
handler, it's easy to make sure that you're only cleaning up caches that your specific service worker is supposed to manage by filtering out those that don't match registration.scope
.
E.g., something like this:
const CACHE_VERSION = 'v2';
const CACHE_NAME = `${registration.scope}!${CACHE_VERSION}`;
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
// Add entries to the cache...
})
);
});
self.addEventListener('activate', (event) => {
const cleanup = async () => {
const cacheNames = await caches.keys();
const cachesToDelete = cachesNames.filter(
(cacheName) => cacheName.startsWith(`${registration.scope}!`) &&
cacheName !== CACHE_NAME);
await Promise.all(cachesToDelete.map(
(cacheName) => caches.delete(cacheName)));
};
event.waitUntil(cleanup());
});