From 5e1ec3f213ab76d1e6cb3d4a74b402f99ce94378 Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Sun, 20 Oct 2024 10:56:57 -0400 Subject: [PATCH 1/5] Don't trim fundamental pages --- static/serviceworker.js | 44 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/static/serviceworker.js b/static/serviceworker.js index 0a934e2..04d5b15 100644 --- a/static/serviceworker.js +++ b/static/serviceworker.js @@ -6,7 +6,8 @@ let maxItems = 100; self.addEventListener('install', function (event) { // Cache offline fundamentals - event.waitUntil(caches.open(version + cacheName).then(function (cache) { + event.waitUntil( + caches.open(version + cacheName).then(function (cache) { return cache.addAll(offlineFundamentals); })); }); @@ -14,11 +15,21 @@ self.addEventListener('install', function (event) { function trimCache(name, maxItems) { caches.open(name).then(function(cache) { cache.keys().then(function(keys) { - if (keys.length > maxItems) { - cache.delete(keys[0]).then(trimCache(name, maxItems)); + // Make sure offlineFundamentals don't get deleted + possibleDelete = keys.filter(function(key) { + !offlineFundamentals.includes(key) + }); + + // Keep track of each delete + deleteInProgress = [] + for (let i = 0; i < keys.length - maxItems; i++) { + deleteInProgress.push(cache.delete(possibleDelete[i])); } - }); - }); + + // Return when everything is resolved + return Promise.all(deleteInProgress); + }) + }) } // Listens for trimCache command which occurs on page load @@ -28,17 +39,17 @@ self.addEventListener('message', event => { } }); - // If the version changes, invalidate the cache self.addEventListener('activate', function (event) { event.waitUntil( caches.keys() .then(function (keys) { - // Remove caches whose name is no longer valid return Promise.all(keys + // Find all caches that aren't the current version .filter(function (key) { - return key.indexOf(version) !== 0; + return !key.includes(version) }) + // Delete the cache .map(function (key) { return caches.delete(key); }) @@ -53,16 +64,19 @@ self.addEventListener('fetch', function (event) { // Always fetch non-GET requests from the network if (request.method !== 'GET') { - event.respondWith( - fetch(request) - .catch(function () { - return caches.match(offlinePage); - }) + event.respondWith(fetch(request) + .catch(function () { + return caches.match(offlinePage); + }) ); return; } - let isRequestType = function(name) { return request.headers.get('Accept').includes(name); } + let isRequestType = function(name) { + return request.headers + .get('Accept') + .includes(name); + } // Network-first Approach event.respondWith( @@ -71,7 +85,7 @@ self.addEventListener('fetch', function (event) { // If successful, create a copy of the response // and save it to the cache // Note: Ignore badges - if (request.url.includes("/badges")) { + if (!request.url.includes("/badges")) { let cacheCopy = response.clone(); event.waitUntil(caches.open(version + cacheName).then(function (cache) { return cache.put(request, cacheCopy); From b84102ea4bf1798d9a0879cc3a6b36b1bc24e2a8 Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Sun, 20 Oct 2024 12:05:59 -0400 Subject: [PATCH 2/5] Modernizing service worker --- static/serviceworker.js | 130 +++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/static/serviceworker.js b/static/serviceworker.js index 04d5b15..38ee813 100644 --- a/static/serviceworker.js +++ b/static/serviceworker.js @@ -4,34 +4,60 @@ let offlinePage = '/offline'; let offlineFundamentals = [offlinePage, '/']; let maxItems = 100; + +function addFundamentals() { + return caches.open(version + cacheName) + .then(cache => cache.addAll(offlineFundamentals)) +} + +// Cache the page(s) that initiate the service worker +function cacheClients() { + return clients.matchAll({ + includeUncontrolled: true + }) + .then(allClients => allClients.map(client => client.url)) + .then(pages => [pages, caches.open(cacheName)]) + .then(([pages, cache]) => cache.addAll(pages)) +} + +// Remove caches whose name is no longer valid +function clearInvalidatedCaches() { + return caches.keys() + .then( keys => { + return Promise.all(keys + .filter(key => !key.includes(version)) + .map(key => caches.delete(key)) + ); + }); +} + +function trimCache(name, maxItems) { + return caches.open(name) + .then(cache => [cache, cache.keys()]) + // Make sure offlineFundamentals don't get deleted + .then(([cache, keys]) => [cache, keys.filter(key => !offlineFundamentals.includes(key))]) + .then(([cache, possibleDelete]) => { + // Trim cache until we are of the right size + deleteInProgress = [] + for (let i = 0; i < keys.length - maxItems; i++) { + // Keep track of each delete + deleteInProgress.push(cache.delete(possibleDelete[i])); + } + + // Return when everything is resolved + return Promise.all(deleteInProgress); + }) +} + self.addEventListener('install', function (event) { // Cache offline fundamentals event.waitUntil( - caches.open(version + cacheName).then(function (cache) { - return cache.addAll(offlineFundamentals); - })); + addFundamentals() + .then(() => cacheClients()) + .then(() => skipWaiting()) + ) }); -function trimCache(name, maxItems) { - caches.open(name).then(function(cache) { - cache.keys().then(function(keys) { - // Make sure offlineFundamentals don't get deleted - possibleDelete = keys.filter(function(key) { - !offlineFundamentals.includes(key) - }); - - // Keep track of each delete - deleteInProgress = [] - for (let i = 0; i < keys.length - maxItems; i++) { - deleteInProgress.push(cache.delete(possibleDelete[i])); - } - - // Return when everything is resolved - return Promise.all(deleteInProgress); - }) - }) -} - // Listens for trimCache command which occurs on page load self.addEventListener('message', event => { if (event.data.command == 'trimCache') { @@ -42,32 +68,28 @@ self.addEventListener('message', event => { // If the version changes, invalidate the cache self.addEventListener('activate', function (event) { event.waitUntil( - caches.keys() - .then(function (keys) { - return Promise.all(keys - // Find all caches that aren't the current version - .filter(function (key) { - return !key.includes(version) - }) - // Delete the cache - .map(function (key) { - return caches.delete(key); - }) - ); - }) + clearInvalidatedCaches() + .then(() => clients.claim()) ); }); +if (registration.navigationPreload) { + addEventListener('activate', event => { + event.waitUntil( + registration.navigationPreload.enable() + ); + }); +} + // Listen for request events self.addEventListener('fetch', function (event) { - let request = event.request; + const request = event.request; // Always fetch non-GET requests from the network if (request.method !== 'GET') { - event.respondWith(fetch(request) - .catch(function () { - return caches.match(offlinePage); - }) + event.respondWith( + fetch(request) + .catch(() => caches.match(offlinePage)) ); return; } @@ -81,26 +103,26 @@ self.addEventListener('fetch', function (event) { // Network-first Approach event.respondWith( // Attepmt to grab the latest copy from the network - fetch(request).then(function (response) { + Promise.resolve(event.preloadResponse) + .then(preloadResponse => preloadResponse || fetch(request)) + .then(response => { // If successful, create a copy of the response // and save it to the cache // Note: Ignore badges if (!request.url.includes("/badges")) { let cacheCopy = response.clone(); - event.waitUntil(caches.open(version + cacheName).then(function (cache) { - return cache.put(request, cacheCopy); - })); + event.waitUntil( + caches.open(version + cacheName) + .then(cache => cache.put(request, cacheCopy)) + ); } return response; - }).catch(function (error) { - // Otherwise, check the cache. - return caches.match(request).then(function (response) { - // Show offline page if cache misses for HTML pages. - if (isRequestType('text/html')) { - return response || caches.match(offlinePage); - } - return response; - }); }) + // Check the cache + .catch(error => + caches.match(request) + // Show offline page for HTML pages if cache miss + .then(response => isRequestType('text/html')? response || caches.match(offlinePage) : response) + ) ); }); \ No newline at end of file From fd496d7cdd2e29cf9f16043a251cbc759d84770a Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Sun, 20 Oct 2024 12:13:26 -0400 Subject: [PATCH 3/5] Wait for promise resolution --- static/serviceworker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/serviceworker.js b/static/serviceworker.js index 38ee813..9b8e84e 100644 --- a/static/serviceworker.js +++ b/static/serviceworker.js @@ -16,7 +16,7 @@ function cacheClients() { includeUncontrolled: true }) .then(allClients => allClients.map(client => client.url)) - .then(pages => [pages, caches.open(cacheName)]) + .then(pages => Promise.all([pages, caches.open(cacheName)])) .then(([pages, cache]) => cache.addAll(pages)) } @@ -33,7 +33,7 @@ function clearInvalidatedCaches() { function trimCache(name, maxItems) { return caches.open(name) - .then(cache => [cache, cache.keys()]) + .then(cache => Promise.all([cache, cache.keys()])) // Make sure offlineFundamentals don't get deleted .then(([cache, keys]) => [cache, keys.filter(key => !offlineFundamentals.includes(key))]) .then(([cache, possibleDelete]) => { From 5ae3c6383442cc910bba09e6a657735625ea1db9 Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Sun, 20 Oct 2024 12:18:24 -0400 Subject: [PATCH 4/5] Fixing missing attribute error --- static/serviceworker.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/static/serviceworker.js b/static/serviceworker.js index 9b8e84e..c55e19e 100644 --- a/static/serviceworker.js +++ b/static/serviceworker.js @@ -35,11 +35,15 @@ function trimCache(name, maxItems) { return caches.open(name) .then(cache => Promise.all([cache, cache.keys()])) // Make sure offlineFundamentals don't get deleted - .then(([cache, keys]) => [cache, keys.filter(key => !offlineFundamentals.includes(key))]) - .then(([cache, possibleDelete]) => { + .then(([cache, keys]) => [ + cache, + keys.filter(key => !offlineFundamentals.includes(key)), + keys.length - maxItems + ]) + .then(([cache, possibleDelete, numToDelete]) => { // Trim cache until we are of the right size deleteInProgress = [] - for (let i = 0; i < keys.length - maxItems; i++) { + for (let i = 0; i < numToDelete; i++) { // Keep track of each delete deleteInProgress.push(cache.delete(possibleDelete[i])); } From 31dfeaaa0b5654a419b75c7f694aa99b0e36e0ae Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Sun, 20 Oct 2024 12:36:35 -0400 Subject: [PATCH 5/5] Offline page URL fix --- static/serviceworker.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/static/serviceworker.js b/static/serviceworker.js index c55e19e..66372a6 100644 --- a/static/serviceworker.js +++ b/static/serviceworker.js @@ -1,6 +1,6 @@ let version = 'v1::'; let cacheName = 'website'; -let offlinePage = '/offline'; +let offlinePage = '/offline/'; let offlineFundamentals = [offlinePage, '/']; let maxItems = 100; @@ -89,21 +89,25 @@ if (registration.navigationPreload) { self.addEventListener('fetch', function (event) { const request = event.request; - // Always fetch non-GET requests from the network - if (request.method !== 'GET') { - event.respondWith( - fetch(request) - .catch(() => caches.match(offlinePage)) - ); - return; - } - let isRequestType = function(name) { return request.headers .get('Accept') .includes(name); } + // Always fetch non-GET requests from the network + if (request.method !== 'GET') { + // Present offline page when failed to + // fetch a HTML page + if (isRequestType('text/html')) { + event.respondWith( + fetch(request) + .catch(() => caches.match(offlinePage)) + ); + } + return; + } + // Network-first Approach event.respondWith( // Attepmt to grab the latest copy from the network