Workbox precaching: do not fail if some URLs fail - service-worker

I'm trying to generate SW using the next code with workbox and gulp
gulp.task('generate-service-worker', () => {
return workbox.generateSW({
globDirectory: root,
globPatterns: [
'**/!(*.map|*.html|*.txt|*.orig)'
],
swDest: `${root}/javascripts/sw/sw.js`,
clientsClaim: true,
skipWaiting: true
}).then(({warnings, count, size}) => {
//In case there are any warnings from workbox-build, log them.
for (const warning of warnings) {
console.warn(warning);
}
console.info(`Service worker generation completed.Count: ${count}, size: ${size}`);
}).catch((error) => {
console.warn('Service worker generation failed:', error);
});
});
The service worker is generated. However, if during install one of the URLs fails then the whole install process fails and the new service worker is not activated. Is it possible to ignore fetch failures and just continue with the precache of rest URLs?

Related

Socket rejoins again and again which causes Live View to emit events itself periodically. when using with Alpine js

Issue:
Socket rejoins again and again which causes Live View to emit events itself periodically.
Scenario: I have index page which contains phx-change events. When I left the page idle for sometime, the events started to triggered automatically periodically.
It is not limited to single page but it happen on each live view page, I googled the issue but couldn’t find any solution.
1 reason I noticed that sometimes heartbeat stops for more than 1 minute(timeout limit is 1 minute), in this case socket rejoined again.
I increased timeout at client side as well at server side in Endpoint to check if this is the only issue but it didn’t work and socket behaved the same.
Did anyone face same issue before, what could be a possible reasons and how to avoid this issue, any suggestion?
Stack: PETAL
Elixir: 1.11
Erlang: 23.0
phoenix: 1.5.3
phoenix_live_view: 0.15.7
alpinejs “^2.8.2”
Browsers: chrome, safari
I think issue is with Alpine Js but couldn’t figure out any solution.
Here is my app.js code
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let liveSocket = new LiveSocket("/live", Socket, {
uploaders: Uploaders,
hooks: Hooks,
params: { _csrf_token: csrfToken },
timeout: 60000,
dom: {
onBeforeElUpdated(from, to) {
if (from.__x) {
Alpine.clone(from.__x, to);
}
flatpickr(".date-picker", {
wrap: true,
disableMobile: true,
dateFormat: "Ymd H:i:S.ssss"
});
flatpickr(".datetime-picker", {
enableTime: true,
wrap: true,
disableMobile: true,
"plugins": [new confirmDatePlugin({ confirmIcon: "", confirmText: "Confirm" })],
time_24hr: true,
dateFormat: "Ymd H:i:S.ssss"
});
}
},
heartbeatIntervalMs: 10000
});
Endpoint Configuration:
socket "/socket", PrintSimpleWeb.UserSocket,
websocket: [
timeout: :infinity,
],
longpoll: false
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: #session_options]]
it was fixed by simple updating Elixir dependencies and npm packages.
Here are the steps which I followed:
> remove _build, node_modules, deps folder
RUN:
> mix deps.get
> mix compile
> npm install (inside /assets)
> iex -S mix phx.server

When I switched my web application from App Cache to Service Workers some browsers require clear cache

I had App Cache working correctly for offline capability for our web app but I was tasked with switching to service workers because several major browsers have threatened that app cache support may be dropped at any time. I was able to get service workers to work fine but the problem comes when an end-user device using the app with app cache does not load the new service worker without having to clear the browser cache manually (in settings).
The service worker version works great for any device that never used the app with the app cache version or had cache manually cleared.
Is there something that I should be doing when setting up the service worker to force a change for the app cache version?
The app is using ASP.NET and the service worker code is in the Global.master which is included in every page.
Global.master
--------------------------------------------------
if ('serviceWorker' in navigator)
{
navigator.serviceWorker
.register('/Service_Worker.js')
.then(function (registration)
{
console.log("Service Worker Registered");
})
.catch(function (err)
{
console.log("Service Worker registration failed");
});
}
else
{
console.log('service worker not supported.');
}
Service_Worker.js
--------------------------------------------------
self.addEventListener('install', function (e)
{
self.skipWaiting();
e.waitUntil(
caches.open(cacheName).then(function (cache)
{
return cache.addAll(appShellFiles);
})
);
});
// Fetching content using Service Worker
self.addEventListener('fetch', function (e)
{
e.respondWith(
caches.match(e.request).then(function (r)
{
return r || fetch(e.request).then(function (response)
{
return caches.open(cacheName).then(function (cache)
{
cache.put(e.request, response.clone());
return response;
});
});
})
);
});
self.addEventListener('activate', function (e)
{
e.waitUntil(
caches.keys().then(function (keyList)
{
return Promise.all(keyList.map(function (key)
{
if (cacheName.indexOf(key) === -1)
{
return caches.delete(key);
}
}));
})
);
});
Expected Results: devices running application cache should work with service workers after the app is updated
Actual Results: Until the device previously running application cache version has cache cleared it continues running client-side code from the previous version's cache.
most likely the user is still fetching from appCache since that is registered. You need to bypass the cached asset(s) to trigger a trip to your server to get the updated code.

Workbox precache putting items inside -temp cache, and not using it later [duplicate]

To enable my app running offline. During installation the service worker should:
fetch a list of URLs from an async API
reformat the response
add all URLs in the response to the precache
For this task I use Googles Workbox in combination with Webpack.
The problem: While the service worker successfully caches all the Webpack assets (which tells me that the workbox basically does what it should), it does not wait for the async API call to cache the additional remote assets. They are simply ignored and neither cached nor ever fetched in the network.
Here is my service worker code:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');
workbox.skipWaiting();
workbox.clientsClaim();
self.addEventListener('install', (event) => {
const precacheController = new
workbox.precaching.PrecacheController();
const preInstallUrl = 'https://www.myapiurl/Assets';
event.waitUntil(fetch(preInstallUrl)
.then(response => response.json()
.then((Assets) => {
Object.keys(Assets.data.Assets).forEach((key) => {
precacheController.addToCacheList([Assets.data.Assets[key]]);
});
})
);
});
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');
And this is my webpack configuration for the workbox:
new InjectManifest({
swDest: 'sw.js',
swSrc: './src/sw.js',
globPatterns: ['dist/*.{js,png,html,css,gif,GIF,PNG,JPG,jpeg,woff,woff2,ttf,svg,eot}'],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
})
It looks like you're creating your own PrecacheController instance and also using the precacheAndRoute(), which aren't actually intended to be used together (not super well explained in the docs, it's only mentioned in this one place).
The problem is the helper methods on workbox.precaching.* actually create their own PrecacheController instance under the hood. Since you're creating your own PrecacheController instance and also calling workbox.precaching.precacheAndRoute([...]), you'll end up with two PrecacheController instances that aren't working together.
From your code sample, it looks like you're creating a PrecacheController instance because you want to load your list of files to precache at runtime. That's fine, but if you're going to do that, there are a few things to be aware of:
Your SW might not update
Service worker updates are usually triggered when you call navigator.serviceWorker.register() and the browser detects that the service worker file has changed. That means if you change what /Assets returns but the contents of your service worker files haven't change, your service worker won't update. This is why most people hard-code their precache list in their service worker (since any changes to those files will trigger a new service worker installation).
You'll have to manually add your own routes
I mentioned before that workbox.precaching.precacheAndRoute([...]) creates its own PrecacheController instance under the hood. It also adds its own fetch listener manually to respond to requests. That means if you're not using precacheAndRoute(), you'll have to create your own router and define your own routes. Here are the docs on how to create routes: https://developers.google.com/web/tools/workbox/modules/workbox-routing.
I realised my mistake. I hope this helps others as well. The problem was that I did not call precacheController.install() manually. While this function will be executed automatically it will not wait for additional precache files that are inserted asynchronously. This is why the function needs to be called after all the precaching happened. Here is the working code:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');
workbox.skipWaiting();
workbox.clientsClaim();
const precacheController = new workbox.precaching.PrecacheController();
// Hook into install event
self.addEventListener('install', (event) => {
// Get API URL passed as query parameter to service worker
const preInstallUrl = new URL(location).searchParams.get('preInstallUrl');
// Fetch precaching URLs and attach them to the cache list
const assetsLoaded = fetch(preInstallUrl)
.then(response => response.json())
.then((values) => {
Object.keys(values.data.Assets).forEach((key) => {
precacheController.addToCacheList([values.data.Assets[key]]);
});
})
.then(() => {
// After all assets are added install them
precacheController.install();
});
event.waitUntil(assetsLoaded);
});
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');

Service worker installs via localhost, but fails when deployed to GitHub pages

I am working on a PWA and I have installed and activated a service worker on my site. It works perfectly well while testing on local server but when I ship my code live, it fails.
This is my SW:
const cacheName = 'v1';
const cacheFiles = [
'/',
'/css/styles.css',
'/images/test1.png',
'/images/test2.png',
'/js/app.js',
'/js/sw-registration.js'
]
// Install event
self.addEventListener('install', function(event) {
console.log("SW installed");
event.waitUntil(
caches.open(cacheName)
.then(function(cache){
console.log('SW caching cachefiles');
return cache.addAll(cacheFiles);
})
)
});
// Activate event
self.addEventListener('activate', function(event) {
console.log("SW activated");
event.waitUntil(
caches.keys()
.then(function(cacheNames){
return Promise.all(cacheNames.map(function(thisCacheName){
if(thisCacheName !== cacheName){
console.log('SW Removing cached files from', thisCacheName);
return caches.delete(thisCacheName);
}
}))
})
)
});
// Fetch event
self.addEventListener('fetch', function(event) {
console.log("SW fetching", event.request.url);
event.respondWith(
caches.match(event.request)
.then(function(response){
console.log('Fetching new files');
return response || fetch(event.request);
})
);
});
This is the error I'm getting:
Uncaught (in promise) TypeError: Request failed (sw.js:1)
I don't understand why it fails to cache my files online (github pages) when it works locally. Can someone help me understand?
Thank you.
EDIT: I tried to deploy the site via Netlify and it works there. So it has to be something to do with Github pages. I would still like to know what it is, if anyone can shed any light.
As mentioned in Service Worker caches locally but fails online, when deploying to gh-pages, your web app's content will normally be accessed from a subpath, not in the top-level path, for the domain.
For instance, if your files are in the gh-pages branch of https://github.com/<user>/<repo>, then your web content can be accessed from https://<user>.github.io/<repo>/.
All of the URLs in your cacheFiles array are prefixed with /, which isn't what you want, given that all of your content is accessible under /<repo>/. For instance, / is interpreted as https://<user>.github.io/, which is different from https://<user>.github.io/<repo>/.
The solution to your problem, which will lead to a configuration that works regardless of what the base URL is for your hosting environment, is to prepend each of your URLs with ./ rather than /. For instance:
const cacheFiles = [
'./',
'./css/styles.css',
// etc.
];
The ./ means that the URL is relative, with the location of the service worker file used as the base. Your service worker file will be deployed under https://<user>.github.io/<repo>/, so that will end up being the correct base URL to use for the rest of your content as well.

Manifest start_url is not cached by a Service Worker

I'm using Lighthouse to audit my webapp. I'm working through the failures, but I'm stuck on this one:
Failures: Manifest start_url is not cached by a Service Worker.
In my manifest.json I have
"start_url": "index.html",
In my worker.js I am caching the following:
let CACHE_NAME = 'my-site-cache-v1';
let urlsToCache = [
'/',
'/scripts/app.js',
'/index.html'
];
Which lines up with what I see in the Application tab in Chrome Dev tools:
So... why is it telling me start_url is not cached?
Here is my full worker.js file:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/worker.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
let CACHE_NAME = 'my-site-cache-v1.1';
let urlsToCache = [
'/',
'/scripts/app.js',
'/index.html'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
Let's look at Lighthouse's source code
static assessOfflineStartUrl(artifacts, result) {
const hasOfflineStartUrl = artifacts.StartUrl.statusCode === 200;
if (!hasOfflineStartUrl) {
result.failures.push('Manifest start_url is not cached by a service worker');
}
}
We can notice, that it's not checking your cache, but response of the entry point. The reason for that must be that your service worker is not sending proper Response on fetch.
You'll know that it's working, if in DevTools, in your first request, there'll be (from ServiceWorker) in size column:
There're two problems with the code you've provided:
First one is that you're messing service worker code with service worker registration code. Service worker registration code should be the code executed on your webpage.
That code should be included on your page:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/worker.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
and the rest of what you've pasted should be your worker.js code. However service worker get installed, because you've files in cache, so I suspect you just pasted this incorrectly.
The second (real) problem is that service worker is not returning this cached files. As I've proved earlier, that error from lighthouse means that service worker is not returning start_url entry file.
The most basic code to achieve that is:
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request));
});
Service worker is event-driven, so when your page wants to get some resource, service worker reacts, and serves the one from cache. In real world, you really don't want to use it like that, because you need some kind of fallback. I strongly recommend reading section Serving files from the cache here
Edit: I've created pull request in Lighthouse source code to clarify that error message
It seems to be that Chrome lighthouse (chrome v62) performs a generic fetch(). See discussion on https://github.com/GoogleChrome/lighthouse/issues/2688#issuecomment-315394447
In my case, an offline.html is served after an "if (event.request.mode === 'navigate'){".
Due to the use of lighthouse´s generic fetch(), lighthouse will not get served this offline.html, and shows the "Manifest start_url is not cached by a Service Worker" error.
I solved this problem by replacing:
if (event.request.mode === 'navigate'){
with
if (event.request.method === 'GET' ){

Resources