Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is workbox BroadcastCacheUpdate supported? #4

Closed
goofblob opened this issue Feb 4, 2018 · 17 comments
Closed

Is workbox BroadcastCacheUpdate supported? #4

goofblob opened this issue Feb 4, 2018 · 17 comments

Comments

@goofblob
Copy link

goofblob commented Feb 4, 2018

If so, how is it implemented in package.json, and how would a page listen to the notification?

@mischnic
Copy link
Owner

mischnic commented Feb 4, 2018

You could inject you own code with importScripts: ['push-notifications.js']
However, as the documtation says:

When NOT to use generateSW

You want to use other Service Worker features (i.e. Web Push).
You want to import additional scripts or add additional logic.

So you would be better off using injectManifest (see example).

Here is the documentation from workbox (last is a demo, look at the code in devtools)

@goofblob
Copy link
Author

goofblob commented Feb 4, 2018

The inject approach would indeed work. But the API that comes with this plugin actually goes quite a long way.

So for example:
"runtimeCaching": [ { "urlPattern": [ "https://path.to.api" ], "handler": "staleWhileRevalidate", "options": { "cacheName": "app-data", "plugins": [ { "broadcastUpdate": { "channelName": "api-updates" } } ] } }]

This does in fact build successfully. And the output to sw.js is non-breaking. But unfortunately it passes the broadcastUpdate object as is.

So this then would be a feature request, if this is doable. Take this object:

{ "broadcastUpdate": { "channelName": "api-updates" } }

... and produce:

new workbox.broadcastUpdate.Plugin({ channelName: 'api-updates', })

...in the output sw.js as per the documentation you have mentioned.

I think this would be very much in the spirit of this plugin, in that it is producing a fully functional SW out of just the json config in package.json

But you know... only if it's not too much bother :-)

@mischnic
Copy link
Owner

mischnic commented Feb 4, 2018

This plugin is only an interface to workbox-build, so it basically only calls generateSW or injectManifest. I don't really want to change the serviceworker "manually" after the generation, it seems like this could cause some bugs. If workbox-build doesn't offer enough options for you, please open an issue at https://github.com/GoogleChrome/workbox/issues.

However, I think that this is already in the workbox-build configuration: (from https://developers.google.com/web/tools/workbox/modules/workbox-build#full_generatesw_config)

runtimeCaching: [{
    urlPattern: /api/,
    handler: 'networkFirst',
    options: {
      networkTimeoutSeconds: 10,
      cacheName: 'my-api-cache',
      // Configure the broadcast cache update plugin.
      broadcastUpdate: {
        channelName: 'my-update-channel',
      },
      // Add in any additional plugin logic you need.
      plugins: [
        {cacheDidUpdate: () => /* custom plugin code */}
      ],
    },
  }]

If this code doesn't work, open an issue with workbox and tell them that either the docs are wrong or that something isn't working properly.

@goofblob
Copy link
Author

goofblob commented Feb 5, 2018

What I am saying is that some options passed to generateSW work properly, while others do not.

So this works:

    "runtimeCaching": [
      {
        "urlPattern": [
          "https://api"
        ],
        "handler": "staleWhileRevalidate",
        "options": {
          "cacheName": "api"
        }
      }

But this does not:

    "runtimeCaching": [
      {
        "urlPattern": [
          "https://api"
        ],
        "handler": "staleWhileRevalidate",
        "options": {
          "cacheName": "api",
          "broadcastUpdate": {
            "channelName": "my-update-channel",
           }
        }
      }

So the plug-in does not interface with that aspect of generateSW. If given the latter config, the sw.js is not produced in ./dist/

@mischnic
Copy link
Owner

mischnic commented Feb 5, 2018

I will look into this.

@mischnic
Copy link
Owner

mischnic commented Feb 9, 2018

Seems to be a problem with the documentation and workbox itself: GoogleChrome/workbox/issues/1289

@mischnic
Copy link
Owner

mischnic commented Feb 10, 2018

This will get fixed in the next workbox release.
The docs are correct, so your code should work then (couldn't test it yet):

    "runtimeCaching": [
      {
        "urlPattern": [
          "https://api"
        ],
        "handler": "staleWhileRevalidate",
        "options": {
          "cacheName": "api",
          "broadcastUpdate": {
            "channelName": "my-update-channel",
           }
        }
      }

@goofblob
Copy link
Author

Ah... I see.

I will leave this issue open until we hear from upstream and we can test it.

Thanks!

@mischnic
Copy link
Owner

mischnic commented Feb 27, 2018

There is another issue with workbox 😞 (GoogleChrome/workbox#1334).


@goofblob The fix is in workbox beta.1. Could you please test it?
I get (in the browser):
Uncaught channel-name-required: You must provide a channelName to construct a BroadcastCacheUpdate instance.

"cache": {
		"inDev": true,
		"runtimeCaching": [
			{
				"urlPattern": "https://raw.githubusercontent.com/parcel-bundler/website/01a1f7dd/src/assets/parcel@3x.png",
				"handler": "cacheFirst",
				"options": {
					"broadcastUpdate": {
						"channelName": "my-update-channel"
					}
				}
			}
		]
	}

@goofblob
Copy link
Author

Hmm... I do not get that specific log error, but:

  1. I get Unhandled promise rejection: TypeError: Cannot read property 'error' of undefined at workbox.generateSW.then.catch (~path/node_modules/parcel-plugin-sw-cache/index.js:114:12)

  2. The event does not seem to trigger - I've tested both locally and in a test deploy environment. I can't quite figure out why this happens, because I am not getting any useful error messages, but it might relate to the error message you are getting.

The reason I believe this might be the case is the following. The Workbox spec says that the broadast channel should be registered with the following syntax:

workbox.strategies.staleWhileRevalidate({
    plugins: [
      new workbox.broadcastUpdate.Plugin({
        channelName: 'api-updates',
      })
    ]
  })

But the plugin generates:

workbox.strategies.staleWhileRevalidate({
    plugins: [
      new workbox.broadcastUpdate.Plugin({
        "channelName": "api-updates",
      })
    ]
  })

The difference is "channelName". Ideally that difference in syntax should not make a difference, but given how goofy and finnecky these things can be...

For the sake of completeness, I listed to the broadcast within an onLoad listener on the index page as follows:

            const updatesChannel = new BroadcastChannel('api-updates');
            updatesChannel.addEventListener('message', (event) => {
                const { cacheName, updatedUrl } = event.data.payload;
                console.log("Master List Updated \n");
                console.log(event.data.payload);
            });

This is more or less taken as is from the Workbox documentation.

@mischnic
Copy link
Owner

That error of undefined is caused by a change in parcel, so the actual workbox error doesn't get logged.

@mischnic
Copy link
Owner

mischnic commented Mar 6, 2018

The service worker generation works with

{
	"urlPattern": "...",
	"handler": "cacheFirst",
	"options": {
		"broadcastUpdate": {
			"channelName": "api-updates"
		},
		"cacheableResponse": {
			"statuses": [
				0
			]
		}
	}
}

and workbox-3-beta1.

I however can't test the actual functionality.


The logger is fixed in version 0.2.0

@goofblob
Copy link
Author

goofblob commented Mar 7, 2018

Hmm...

In order to test it, we need to listen to the broadcastUpdate event:

const updatesChannel = new BroadcastChannel('api-updates');
updatesChannel.addEventListener('message', (event) => {
        console.log(event.data.payload);
});

Problem is that if I put this in index.html the Parcel bundler complains that it doesn't know what BroadcastChannel is. If I put it lower down in one of my components, it bundles it successfully. But then it does not seem to trigger.

@mischnic
Copy link
Owner

mischnic commented Mar 7, 2018

It worked for me:

config:

"cache": {
  "runtimeCaching": [
    {
      "urlPattern": "http://localhost:1234/cache.jpg",
      "handler": "staleWhileRevalidate",
      "options": {
        "broadcastUpdate": {
          "channelName": "api-updates"
        }
      }
    }
  ]
}

index.js

const updatesChannel = new BroadcastChannel('api-updates');
updatesChannel.addEventListener('message', event => {
        console.log(event);
});

index.html contained <img src="http://localhost:1234/cache.jpg">.
Changing out cache.jpg fires the eventlistener.

Did you use staleWhileRevalidate?

@goofblob
Copy link
Author

goofblob commented Mar 8, 2018

Hmm... Still not working for me.

Yes, I did use staleWhileRevalidate.

In package.json I have:

"runtimeCaching": [
      {
        "urlPattern": [
          "https://link.to/api.json"
        ],
        "handler": "staleWhileRevalidate",
        "options": {
          "cacheName": "api",
          "broadcastUpdate": {
            "channelName": "api-update"
          }
        }
      }
]

And in index.js I have:

// LISTEN FOR API UPDATES
    const updatesChannel = new BroadcastChannel('api-update');
    updatesChannel.addEventListener('message', (event) => {
        console.log("API Updated.");
        console.log(event.data.payload);
    });

But when I make changes to the API, and go through the reloads of the site nothing happens. So I get served the stale .json from the Service Worker cache just fine on the first go, and then the refreshed one on the second go. But I do not get the "API Updated." console message on the first go when the .json has been successfully refreshed in the background. Or the payload.

If it works in your tests, then that must mean that the functionality does work. There is something else about my setup that interferes. So if you can see nothing wrong with my code here, I think you can close the issue. I'll just have to bash my head against my code a bit more until it works.

Thanks again for your work on the plugin!

@mischnic
Copy link
Owner

mischnic commented Mar 8, 2018

Workbox checks the headers to determine whether the file updated, by default ['content-length', 'etag', 'last-modified']; if these headers don't change, the event won't fire.

https://developers.google.com/web/tools/workbox/reference-docs/prerelease/workbox.broadcastUpdate.Plugin

With my example: The event fires on the first reload (after getting the old response) and the new response gets returned on the second reload.

@goofblob
Copy link
Author

goofblob commented Mar 9, 2018

Right. That's how it is intended to work.

What happens in my use case is that the update does happen in the background, and that is reflected in the second reload. But the updatesChannel event listener does not get triggered to give me the console output it should on the first load.

My intended use for this is that I want to serve stale data from the service worker cache on the first load, and then automatically update that data without reload when/if the background service worker process finds fresh API data. But if the event does not get triggered, I cannot do this live API data update.

It's not mission critical, but that is pretty much the standard use case for staleWhileRevalidate, and it's bugging me that I have not yet managed to get it working.

But as I say, if the event triggers in your example, there is something I'm doing wrong on my end, so this would not be an issue with the plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants