From ae66d48a0725d49414646ccc6f8b49daedf56eb8 Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Thu, 22 Dec 2022 20:22:17 +0530 Subject: [PATCH 01/12] Add module service worker --- src/serviceWorker/cachingStrategy.js | 69 ++++++++++++++++++++++++++++ src/serviceWorker/policies.js | 35 ++++++++++++++ src/serviceWorker/service_worker.js | 17 +++++++ 3 files changed, 121 insertions(+) create mode 100644 src/serviceWorker/cachingStrategy.js create mode 100644 src/serviceWorker/policies.js create mode 100644 src/serviceWorker/service_worker.js diff --git a/src/serviceWorker/cachingStrategy.js b/src/serviceWorker/cachingStrategy.js new file mode 100644 index 00000000..2dfd2292 --- /dev/null +++ b/src/serviceWorker/cachingStrategy.js @@ -0,0 +1,69 @@ +// Caching strategies for service workers +// For more details refer +// https://web.dev/learn/pwa/caching/ + +export const cacheFirst = async function (cacheName, event) { + let cacheResponse = await caches.match(event.request); + let request = event.request; + if (cacheResponse !== undefined) { + // cache hit + return cacheResponse; + } + // miss + else { + return fetch(request).then(response => { + let responseClone = response.clone(); + caches.open(cacheName).then(cache => + cache.put(request, responseClone) + ); + return response; + }); + } +}; + +export const networkFirst = async function (cacheName, event) { + let request = event.request; + try { + return fetch(request).then( + response => { + let responseClone = response.clone(); + caches.open(cacheName).then(cache => + cache.put(request, responseClone) + ); + return response; + }); + } + catch (err) { + return caches.match(event.request); + } +}; + +export const networkOnly = async function (cacheName, event) { + return fetch(event.request); +}; + +export const raceCacheNetwork = async function (cacheName, event) { + let request = event.request; + + let cachePromise = caches.match(request).then( + response => { + if (response) { + return response; + } + else + throw Error('cache miss'); + } + ); + + let networkPromise = fetch(request).then( + response => { + let responseClone = response.clone(); + caches.open(cacheName).then(cache => + cache.put(request, responseClone) + ); + return response; + } + ); + + return Promise.any([cachePromise, networkPromise]); +}; diff --git a/src/serviceWorker/policies.js b/src/serviceWorker/policies.js new file mode 100644 index 00000000..d79dc032 --- /dev/null +++ b/src/serviceWorker/policies.js @@ -0,0 +1,35 @@ +import { cacheFirst, networkFirst, networkOnly } from './cachingStrategy'; + +// the various network policies for service workers +export const policies = [ + { + // images + url: /^(ftp|https?):.*\.(jpe?g|png|gif|svg)($|\?.*)/i, + handle: cacheFirst + }, + { + // audios & videos + url: /^(ftp|https?):.*\.(mp\d|webm|ogg|wav|flac)($|\?.*)/i, + handle: cacheFirst + }, + { + // fonts + url: /^(ftp|https?):.*\.(ttf|otf|woff\d?)($|\?.*)/i, + handle: cacheFirst + }, + { + // web content + url: /^(ftp|https?):.*\.([jt]sp?|css|html?)($|\?.*)/i, + handle: networkFirst + }, + { + // web data + url: /^(ftp|https?):.*\.(csv|json|txt|xml)($|\?.*)/i, + handle: networkFirst + }, + // fallback + { + url: /.+/i, + handle: networkOnly + } +]; diff --git a/src/serviceWorker/service_worker.js b/src/serviceWorker/service_worker.js new file mode 100644 index 00000000..017c74aa --- /dev/null +++ b/src/serviceWorker/service_worker.js @@ -0,0 +1,17 @@ +import { policies } from './policies'; +import {APP_NAME} from '../constant'; + +self.addEventListener('install', event => { // register + console.log('serviceWorker installed'); + event.waitUntil(async () => { + await caches.open(APP_NAME); + console.log('serviceWorker installed...'); + }); +}); + +self.addEventListener('fetch', event => { // intercept + let policy = policies.find( + pattern => pattern.url.test(event.request.url) + ); + event.respondWith(policy.handle(APP_NAME, event)); +}); From 8670701cf1bdc227876b833f9db62dd9f4ab4073 Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Thu, 22 Dec 2022 20:23:39 +0530 Subject: [PATCH 02/12] Register service worker on load --- src/initialBackground.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/initialBackground.js b/src/initialBackground.js index 6d52f422..482e69ee 100644 --- a/src/initialBackground.js +++ b/src/initialBackground.js @@ -13,3 +13,13 @@ if (localStorage.getItem('nightTabStyle')) { } document.querySelector('head').appendChild(style); } + +// check if service worker is available +if ('serviceWorker' in navigator) { + // register service worker + navigator.serviceWorker.register('./service_worker.js').then(() => { + console.log('serviceWorker registered'); + }).catch(error => { + console.log('serviceWorker registration failed', error); + }); +} From f90ce7c1b1e22ac816ec2b56e706056d69914fc2 Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Thu, 22 Dec 2022 20:25:13 +0530 Subject: [PATCH 03/12] Wepack instruction to build service worker --- webpack.common.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/webpack.common.js b/webpack.common.js index b98656d1..eb51346d 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -4,10 +4,11 @@ const CopyPlugin = require('copy-webpack-plugin'); module.exports = { entry: { - index: path.resolve(__dirname, 'src', 'index.js') + index: path.resolve(__dirname, 'src', 'index.js'), + service_worker: path.resolve(__dirname, 'src', 'serviceWorker', 'service_worker.js'), }, output: { - filename: '[name].[contenthash].js', + filename: '[name].js', path: path.resolve(__dirname, 'dist/web'), clean: true }, @@ -43,7 +44,8 @@ module.exports = { }, plugins: [ new HtmlWebpackPlugin({ - template: './src/index.html' + template: './src/index.html', + chunks : ['index'] }), new CopyPlugin({ patterns: [{ From ceab8bb76dae42f3982fc5c61c0eb3d7da0d45fa Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Sun, 16 Apr 2023 20:37:31 +0530 Subject: [PATCH 04/12] Use cache first as fallback policy --- src/serviceWorker/policies.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serviceWorker/policies.js b/src/serviceWorker/policies.js index d79dc032..f94119c6 100644 --- a/src/serviceWorker/policies.js +++ b/src/serviceWorker/policies.js @@ -1,4 +1,4 @@ -import { cacheFirst, networkFirst, networkOnly } from './cachingStrategy'; +import { cacheFirst, networkFirst } from './cachingStrategy'; // the various network policies for service workers export const policies = [ @@ -30,6 +30,6 @@ export const policies = [ // fallback { url: /.+/i, - handle: networkOnly - } + handle: cacheFirst + }, ]; From 23f00c116988b6323908b74e18263326b08dffc2 Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Sun, 16 Apr 2023 20:38:15 +0530 Subject: [PATCH 05/12] Cache `index.html` on page first load --- src/serviceWorker/service_worker.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/serviceWorker/service_worker.js b/src/serviceWorker/service_worker.js index 017c74aa..e1a10c53 100644 --- a/src/serviceWorker/service_worker.js +++ b/src/serviceWorker/service_worker.js @@ -4,7 +4,12 @@ import {APP_NAME} from '../constant'; self.addEventListener('install', event => { // register console.log('serviceWorker installed'); event.waitUntil(async () => { - await caches.open(APP_NAME); + await caches.open(APP_NAME).then( + cache => cache.addAll([ + '/', + '/index.html', + ]) + ); console.log('serviceWorker installed...'); }); }); From b921493704587a79386f6a1a0cf0ed8d05aa438d Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Sun, 16 Apr 2023 21:29:39 +0530 Subject: [PATCH 06/12] add policy for `chrome-extensions://*` url --- src/serviceWorker/policies.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/serviceWorker/policies.js b/src/serviceWorker/policies.js index f94119c6..365a8f89 100644 --- a/src/serviceWorker/policies.js +++ b/src/serviceWorker/policies.js @@ -2,6 +2,11 @@ import { cacheFirst, networkFirst } from './cachingStrategy'; // the various network policies for service workers export const policies = [ + { + // chrome extension + url: /^chrome-extension:.*$/i, + handle: networkOnly + }, { // images url: /^(ftp|https?):.*\.(jpe?g|png|gif|svg)($|\?.*)/i, From e7470f087d3db0d198bc439454a76d3fa5cadf7c Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Sun, 16 Apr 2023 22:45:46 +0530 Subject: [PATCH 07/12] prevent caching of page when running as chrome extension --- src/serviceWorker/policies.js | 8 ++++++-- src/serviceWorker/service_worker.js | 27 ++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/serviceWorker/policies.js b/src/serviceWorker/policies.js index 365a8f89..84a2e846 100644 --- a/src/serviceWorker/policies.js +++ b/src/serviceWorker/policies.js @@ -1,6 +1,10 @@ -import { cacheFirst, networkFirst } from './cachingStrategy'; +import { cacheFirst, networkFirst, networkOnly } from './cachingStrategy'; -// the various network policies for service workers +/* the various network policies for service workers +these are used to determine how to handle a request +based on the request's url +determine which policy to select is done based on the +first matching pattern */ export const policies = [ { // chrome extension diff --git a/src/serviceWorker/service_worker.js b/src/serviceWorker/service_worker.js index e1a10c53..587ab7e9 100644 --- a/src/serviceWorker/service_worker.js +++ b/src/serviceWorker/service_worker.js @@ -2,21 +2,34 @@ import { policies } from './policies'; import {APP_NAME} from '../constant'; self.addEventListener('install', event => { // register - console.log('serviceWorker installed'); + console.log('serviceWorker installing...'); event.waitUntil(async () => { - await caches.open(APP_NAME).then( - cache => cache.addAll([ - '/', - '/index.html', - ]) - ); + + await caches.delete(APP_NAME); // clear old cache + await caches.open(APP_NAME).then( cache => { + // check if running in chrome extension + if(chrome?.extension) { + console.log('running in chrome extension, nothing to cache'); + } + else + cache.addAll([ + '/', + '/index.html', + ]); + }); console.log('serviceWorker installed...'); }); }); self.addEventListener('fetch', event => { // intercept + + if(event.request.method !== 'GET') + event.respondWith(fetch(event.request)); + + // only cache GET requests let policy = policies.find( pattern => pattern.url.test(event.request.url) ); + console.log('fetch event ', event.request.url, ' with ', policy.handle.name); event.respondWith(policy.handle(APP_NAME, event)); }); From 98d26e78ae00e76e8351f7a4dcac2d2a740bf1dd Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Sun, 16 Apr 2023 22:50:43 +0530 Subject: [PATCH 08/12] simplify cache strategy code --- src/serviceWorker/cachingStrategy.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/serviceWorker/cachingStrategy.js b/src/serviceWorker/cachingStrategy.js index 2dfd2292..d159f628 100644 --- a/src/serviceWorker/cachingStrategy.js +++ b/src/serviceWorker/cachingStrategy.js @@ -11,27 +11,23 @@ export const cacheFirst = async function (cacheName, event) { } // miss else { - return fetch(request).then(response => { - let responseClone = response.clone(); - caches.open(cacheName).then(cache => - cache.put(request, responseClone) - ); - return response; - }); + const fetchResponse = await fetch(request); + const fetchResponseClone = fetchResponse.clone(); + const cache = await caches.open(cacheName); + await cache.put(request, fetchResponseClone); + return fetchResponse; } }; + export const networkFirst = async function (cacheName, event) { let request = event.request; try { - return fetch(request).then( - response => { - let responseClone = response.clone(); - caches.open(cacheName).then(cache => - cache.put(request, responseClone) - ); - return response; - }); + const fetchResponse = await fetch(request); + const fetchResponseClone = fetchResponse.clone(); + const cache = await caches.open(cacheName); + await cache.put(request, fetchResponseClone); + return fetchResponse; } catch (err) { return caches.match(event.request); From c673580060d2d5688ff5df2327d5303fea62ff01 Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Sun, 16 Apr 2023 22:51:32 +0530 Subject: [PATCH 09/12] policy for github pages --- src/serviceWorker/policies.js | 5 +++++ src/serviceWorker/service_worker.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/serviceWorker/policies.js b/src/serviceWorker/policies.js index 84a2e846..10ea404c 100644 --- a/src/serviceWorker/policies.js +++ b/src/serviceWorker/policies.js @@ -11,6 +11,11 @@ export const policies = [ url: /^chrome-extension:.*$/i, handle: networkOnly }, + { + // github pages + url: /^https:\/\/\w*\.github\.io\/nightTab\/.*$/i, + handle: cacheFirst + }, { // images url: /^(ftp|https?):.*\.(jpe?g|png|gif|svg)($|\?.*)/i, diff --git a/src/serviceWorker/service_worker.js b/src/serviceWorker/service_worker.js index 587ab7e9..bcf1df5f 100644 --- a/src/serviceWorker/service_worker.js +++ b/src/serviceWorker/service_worker.js @@ -13,7 +13,7 @@ self.addEventListener('install', event => { // register } else cache.addAll([ - '/', + '/', // alias for '/index.html' '/index.html', ]); }); From 56f2b55ec1311b82ef08e0da9b12f441a02949ba Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Mon, 17 Apr 2023 00:59:20 +0530 Subject: [PATCH 10/12] remove fetch event logging --- src/serviceWorker/service_worker.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serviceWorker/service_worker.js b/src/serviceWorker/service_worker.js index bcf1df5f..9bf1d5ae 100644 --- a/src/serviceWorker/service_worker.js +++ b/src/serviceWorker/service_worker.js @@ -30,6 +30,5 @@ self.addEventListener('fetch', event => { // intercept let policy = policies.find( pattern => pattern.url.test(event.request.url) ); - console.log('fetch event ', event.request.url, ' with ', policy.handle.name); event.respondWith(policy.handle(APP_NAME, event)); }); From 91bc21aa48861273bbd2f9c076e4ade1b81b965a Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Mon, 17 Apr 2023 01:19:03 +0530 Subject: [PATCH 11/12] add service worker versioning --- src/component/version/index.js | 46 ++++++++++++++--------------- src/serviceWorker/service_worker.js | 31 +++++++++++++++++-- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/component/version/index.js b/src/component/version/index.js index b77a3d1e..24a2cd09 100644 --- a/src/component/version/index.js +++ b/src/component/version/index.js @@ -1,38 +1,36 @@ -export const version = {}; +export const version = { + number: '7.5.0', + name: 'Delightful Komodo Dragon', + compare: (a, b) => { -version.number = '7.5.0'; + let pa = a.split('.'); -version.name = 'Delightful Komodo Dragon'; + let pb = b.split('.'); -version.compare = (a, b) => { + for (let i = 0; i < 3; i++) { - let pa = a.split('.'); + let na = Number(pa[i]); - let pb = b.split('.'); + let nb = Number(pb[i]); - for (let i = 0; i < 3; i++) { + if (na > nb) { + return 1; + } - let na = Number(pa[i]); + if (nb > na) { + return -1; + } - let nb = Number(pb[i]); + if (!isNaN(na) && isNaN(nb)) { + return 1; + } - if (na > nb) { - return 1; - } - - if (nb > na) { - return -1; - } + if (isNaN(na) && !isNaN(nb)) { + return -1; + } - if (!isNaN(na) && isNaN(nb)) { - return 1; - } - - if (isNaN(na) && !isNaN(nb)) { - return -1; } + return 0; } - - return 0; }; diff --git a/src/serviceWorker/service_worker.js b/src/serviceWorker/service_worker.js index 9bf1d5ae..2653ef02 100644 --- a/src/serviceWorker/service_worker.js +++ b/src/serviceWorker/service_worker.js @@ -1,12 +1,15 @@ import { policies } from './policies'; import {APP_NAME} from '../constant'; +import { version } from '../component/version'; + +const CACHE_NAME = `${APP_NAME}-${version.number}`; + self.addEventListener('install', event => { // register console.log('serviceWorker installing...'); event.waitUntil(async () => { - await caches.delete(APP_NAME); // clear old cache - await caches.open(APP_NAME).then( cache => { + await caches.open(CACHE_NAME).then( cache => { // check if running in chrome extension if(chrome?.extension) { console.log('running in chrome extension, nothing to cache'); @@ -30,5 +33,27 @@ self.addEventListener('fetch', event => { // intercept let policy = policies.find( pattern => pattern.url.test(event.request.url) ); - event.respondWith(policy.handle(APP_NAME, event)); + event.respondWith(policy.handle(CACHE_NAME, event)); +}); + +// Delete outdated caches +self.addEventListener('activate', function (e) { + e.waitUntil( + caches.keys().then(function (keyList) { + // filter out all caches that are not part of this app + let cacheWhitelist = keyList.filter(function (key) { + return key.indexOf(APP_NAME); + }); + // add current latest cache to whitelist + cacheWhitelist.push(CACHE_NAME); + + return Promise.all(keyList.map(function (key, i) { + if (cacheWhitelist.indexOf(key) === -1) { + console.log('deleting cache : ' + keyList[i]); + return caches.delete(keyList[i]); + } + })); + }) + ); + console.log('serviceWorker version ' + version.number + ' activated'); }); From 7f0a302bcd260b2c01b38759122d1c6606dc7faa Mon Sep 17 00:00:00 2001 From: sansmoraxz <31374920+sansmoraxz@users.noreply.github.com> Date: Sun, 21 May 2023 13:06:07 +0530 Subject: [PATCH 12/12] Clear cache and service worker on reload --- src/component/data/index.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/component/data/index.js b/src/component/data/index.js index 760e485e..9ffa42f0 100644 --- a/src/component/data/index.js +++ b/src/component/data/index.js @@ -240,6 +240,26 @@ data.remove = (key) => { window.localStorage.removeItem(key); }; +data.clear_serviceWorker = () => { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations().then((registrations) => { + registrations.forEach((registration) => { + registration.unregister(); + }); + }); + } +}; + +data.clear_cache = () => { + if ('caches' in window) { + caches.keys().then((cacheNames) => { + cacheNames.forEach((cacheName) => { + caches.delete(cacheName); + }); + }); + } +}; + data.backup = (dataToBackup) => { if (dataToBackup) { data.set(APP_NAME + 'Backup', JSON.stringify(dataToBackup)); @@ -315,11 +335,16 @@ data.load = () => { data.wipe = { all: () => { + data.clear_serviceWorker(); + data.clear_cache(); + data.remove(APP_NAME); data.reload.render(); }, partial: () => { + data.clear_serviceWorker(); + data.clear_cache(); bookmark.reset(); data.set(APP_NAME, JSON.stringify({