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

Future of Service Worker Tooling [Updated] #61

Closed
jeffposnick opened this issue Oct 31, 2016 · 3 comments
Closed

Future of Service Worker Tooling [Updated] #61

jeffposnick opened this issue Oct 31, 2016 · 3 comments
Labels
New Project Idea Ideas for a new, standalone module.

Comments

@jeffposnick
Copy link
Contributor

jeffposnick commented Oct 31, 2016

This is a continuation of the discussion from #44, moved into a new issue with some concrete goals, plans, and samples.

Here are some of the high-level goals for the new libraries:

  • Produce libraries that fit together as a "framework", but also expose interfaces that would allow third-party developers to pick and choose useful bits, e.g. just the cache expiration or runtime caching strategies.
  • Design with the future in mind, to account for use cases and enhancements that we don't initially envision. Focus on a flexible set of "behaviors" that provide generic hooks into the framework via cache/request lifecycle events, e.g. cacheDidUpdate or requestDidFail.
  • Avoid global state for cleaner, easier-to-test, easier-to-repurpose code.
  • Provide a great developer experience. Take steps to avoid silent failures due to misconfiguration. Validate all parameters and opt for using classes rather than anonymous objects for configuration.

The following is an low-level example of how we see the pieces—"behaviors", routing, and runtime caching strategies—fitting together, for those who want to use the full framework.

Not covered in this example, but something that's planned, are helpers for smart precaching based on an asset manifest. It will be covered by new build time tooling, equivalent to the current sw-precache.

This code will result in a service worker that will use a stale-while-revalidate strategy for any requests for URLs ending in .txt, and automatically post a message using the Broadcast Channel API whenever the previously cached entry is updated. It uses a default strategy of network-first for all other URLs.

import {Router, RegExpRoute} from 'routing';
import {RequestWrapper, NetworkFirst, StaleWhileRevalidate} from 'runtime-caching';
import {Behavior as BroadcastCacheUpdateBehavior} from 'broadcast-cache-update';

const requestWrapper = new RequestWrapper({
  cacheName: 'text-files',
  behaviors: [
    new BroadcastCacheUpdateBehavior({channelName: 'cache-updates'})
  ]
});

const route = new RegExpRoute({
  regExp: /\.txt$/,
  handler: new StaleWhileRevalidate({requestWrapper})
});

const router = new Router();
router.registerRoute({route});
router.setDefaultHandler({handler: new NetworkFirst()});
@jeffposnick jeffposnick added the New Project Idea Ideas for a new, standalone module. label Oct 31, 2016
@gauntface
Copy link

cc @TalAter @samertm @NekR @felipenmoura as an FYI in case anyone had any thoughts / comments or questions.

Very early stages so feedback would be greatly appreciated.

@TalAter
Copy link

TalAter commented Nov 3, 2016

Thanks for the cc.

I will try to provide the third party developer perspective on this, so I will be looking at this from the perspective of two projects I have been working on (both WIP).


First, my ongoing experiments with progressive web app UX.

It is often necessary to communicate different events from the service worker to the user interface.

For example:

  • When we have successfully cached an app and it is ready to be used offline, we might want to assure our user that she can visit the app even when she is offline.
  • When we are doing network falling back to cache, we might want to alert the user that the content she is looking at may be stale.

I have been working on a project to ease this communication, called Progressive UI KITT.

importScripts('progressive-ui-kitt-sw-helper.js');

self.addEventListener('install', function(event) {
  // cacheStuff.then()
  ProgressiveKITT.addMessage('Caching complete! Future visits will work offline.', {hideAfter: 2000});
});

self.addEventListener('fetch', function(event) {
  // fetch.catch()
  ProgressiveKITT.addConfirm('Offline. These tweets are from 7 hours ago.', 'OK, I understand');
});

I like the idea proposed above of having a set of "behaviors" that provide generic hooks into the framework. My two examples could be implemented using the cacheDidUpdate and requestServedFromCache hooks.

Takeaway:

The two examples above show how these hooks would need to be configurable per route, as well as for the entire service worker.

For example, the caching complete message would need to be attached to the entire service worker (to be shown when all caching is complete), while in the second example I might only want to attach the "content is stale" message to the tweets route hook, and not the avatars route hook.


I have also been working on a replacement for cache.addAll() which I call cache.adderall().

cache.adderall() aims to speed up service worker updates by looking for the URLs you want to cache in existing caches, before trying to get them from the network. Any response found in an existing cache is simply copied to the new cache, and anything not found is then handled by the native cache.addAll().

How would we allow something like this? Well, the idea of cache.adderall() first doing its thing and then deferring to the native cache.addAll() got me thinking of middleware.

I am thinking specifically about redux (and others) style middleware, where the middleware can act before and after the thing it is applied on.

cache.adderall() could be implemented as middleware that first attempts to find the URLs in existing caches, and then defers to the native cache.addAll() for any URLs it did not find.

Takeaway:

The specific example above might seem like taking customization a bit too far, and may be beyond the scope, but I think it's a good way to see how far middleware could take us.

Some other examples:

  • we could easily create a middleware that would replace requests for .png with .webp.
  • offline analytics middleware
  • error logging
  • url rewriting

In fact, it could even solve the same problems the hooks above are trying to solve (e.g. a caching middleware could easily handle both cacheWillUpdate and cacheDidUpdate).

@addyosmani
Copy link
Member

With a few quarters of work on Workbox behind us and many of the ideas here having been iterated on in master, I'm going to close this issue up. @jeffposnick if there's anything remaining here we would like to keep exploring, could you file separate issues for them? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
New Project Idea Ideas for a new, standalone module.
Projects
None yet
Development

No branches or pull requests

4 participants