pcb icon indicating copy to clipboard operation
pcb copied to clipboard

API design for cacheMaps and dynamicCaches

Open NekR opened this issue 9 years ago • 26 comments

Possible API:

new OfflinePlugin({
  cacheMaps: [
    {
      matchPath: [/^\/\w+\.html$/],
      to: '/app-shell.html',
      // possible values 'navigate', 'cross-origin', 'normal'
      // special case: `requestType: 'any'`?
      requestType: ['navigate']
    },
    {
      matchUrl: function(url) {
        if (url !== '/something') {
          return '/other'; // rewrite
        }

        // Should return be implicit, i.e. use `url` if return is undefined?
        return url;
      },
    }
  ],
})

new OfflinePlugin({
  dynamicCaches: [
    {
      // possible values 'navigate', 'cross-origin', 'normal'
      // special case: `requestType: 'any'`?
      requestType: ['cross-origin'],
      matchUrl: [/\w+fonts\.googleapis\.com\/\w+/],
      responseStrategy: 'cache-first'
    },
    {
      responseStrategy: 'network-first',
      matchUrl: function(url) {
        if (url.startsWith('https://example.com')) {
          return true;
        }

        return false;
      }
    }
  ]
})

NekR avatar Oct 15 '16 17:10 NekR

The way to use this looks good to me, but what is exactly cacheMap & dynamicCache?

MoOx avatar Oct 15 '16 18:10 MoOx

Well, the name should say on its own. Cache maps map how caches are returned, e.g. it matches one cache request and returns another cache instead.

So imagine /about.html is being request. With the cacheMaps service worker would first try get /app-shell.html from the cache and respond with it, if that fails it will request /about.html from the network.

That is the case when /about.html's responseStrategy is cache-first. With network-first it will try to fetch /about.html first and then try get /app-shell.html from the cache.

Dynamic caches means that caches are resolved dynamically and aren't pre-cached, some one may call it runtime caches. So dynamic caches put responses into a cache when it matches some request. Also, probably, dynamicCaches could be used to change responseStrategy individually per files.

Another thing what I thought is to allow dynamicCaches to have its own sub caches, like this:

{
  // possible values 'navigate', 'cross-origin', 'normal'
  // special case: `requestType: 'any'`?
  requestType: ['cross-origin'],
  matchUrl: [/\w+fonts\.googleapis\.com\/\w+/],
  responseStrategy: 'cache-first',
  cacheName: 'fonts'
},

but I'm not sure yet which benefits it provides.

P.S. I removed responseStrategy from cacheMaps example because it was mistakenly put there. cachesMaps respects url's/route's responseStrategy option.

NekR avatar Oct 15 '16 19:10 NekR

For cacheMaps, is there an option for a function for matching? For Gatsby, I'm planning on having a AppShell per layout. To match routes to layout AppShells, I'd need to load into the service worker a mapping between the two and then perform the mapping in a function.

KyleAMathews avatar Oct 15 '16 19:10 KyleAMathews

@KyleAMathews see this (it's in the example too):

{
  matchUrl: function(url) {
    if (url !== '/something') {
      return '/other'; // rewrite
    }

    // Should return be implicit, i.e. use `url` if return is undefined?
    return url;
  },
}

Would this work? Is this enough? Maybe you need some more info there?

The important thing about that function is that it isn't run on build or in node.js, it's being called in SW and simply stringified to be put there. So that must be a pure function.

NekR avatar Oct 15 '16 19:10 NekR

Maybe it should be just function instead of object with matchUrl prop?

NekR avatar Oct 15 '16 19:10 NekR

Oh... I thought that was some sort of redirect thing :-)

Does it have the same requestType option?

To keep things simple, why not let matchPath take a function as well as an array of regexes?

KyleAMathews avatar Oct 15 '16 19:10 KyleAMathews

Yeah, it should. I mean, matchPath is there for simplicity, e.g. when you match for your domain your don't want write whole regexp for your domain. So it's either matchPath or matchUrl, or maybe even something else. So all match* should accepts the same params. And yeah, I agree it must be a array with possible regexp/string/function.

Should it be always array or it should be this too: matchUrl: 'myfile.html'?

Does it have the same requestType option?

Usage of match* doesn't exclude any other option, no matter what you pass there, function or regexp, or string.

NekR avatar Oct 15 '16 19:10 NekR

Should it be always array or it should be this too: matchUrl: 'myfile.html'?

I find these types of APIs intuitive.

Perhaps instead of matchPath use matchOwnDomain?

KyleAMathews avatar Oct 17 '16 17:10 KyleAMathews

Yeah, I agree, matchPath is confusing. Maybe it would be good to use just match and for own/current website it would start from path and for others be absolute?

NekR avatar Oct 17 '16 18:10 NekR

Yeah that sounds better — would love to see some examples of this fleshed out!

KyleAMathews avatar Oct 17 '16 19:10 KyleAMathews

@KyleAMathews Yeah, I'm getting into it :-)

Btw, what about using some pattern to match urls instead of RegExp? The only problem which I see is that plugin already uses minimatch for matching against fs paths and having open more pattern syntax for network stuff may confuse the things.

NekR avatar Oct 17 '16 21:10 NekR

There's also the path-to-exp library that many are familiar with. That's what sw-toolbox uses.

KyleAMathews avatar Oct 17 '16 21:10 KyleAMathews

So, do you mean to use it for both fs and network? sw-toolbox indeed uses that for route paths and also uses an option origin to match different domains. See here for example: https://github.com/insin/react-hn/blob/master/public/runtime-caching.js#L12

I'm not sure if I like that way. It would this in our case:

new OfflinePlugin({
  dynamicCaches: [
    {
      match: '/(.*)',
      origin: /\.(?:googleapis|gstatic|firebaseio|appspot)\.com$/,
      responseStrategy: 'fastest'
    },
    {
      match: '/(.+)',
      origin: 'https://hacker-news.firebaseio.com',
      responseStrategy: 'fastest'
    },
    {
      match: '/(.+)',
      origin: 'https://s-usc1c-nss-136.firebaseio.com',
      responseStrategy: 'fastest'
    },
    {
      match: '/*',
      responseStrategy: 'fastest'
    },
  ]
})

I don't like such syntax by some reason.

NekR avatar Oct 17 '16 21:10 NekR

The API as described should address my needs nicely! Basically declarative cache URL rewrites and external whitelisting. Happy to see regexes as the matchers. Regarding path-to-regexp, i don't know if that works with full URLs, I've only ever seen it used with paths. Maybe support path patterns via that lib but under a separate property in config like path?

developit avatar Oct 17 '16 21:10 developit

FWIW, we (the sw-toolbox maintainers) don't like that syntax either 😄

@NekR has already been CC:ed on the GitHub issue, but we're thinking about a rewrite of a lot of the sw-precache/sw-toolbox/sw-helpers project right now, and some of our thoughts at outlined at https://github.com/GoogleChrome/sw-helpers/issues/44

I don't want to hijack your own rewrite, or imply that you need to follow our lead, but I did want to chime in and suggest that the whole origin thing in particular is not something that we think is worthy of emulation.

jeffposnick avatar Oct 17 '16 21:10 jeffposnick

@jeffposnick thanks for coming here, every feedback is welcome for sure! Unfortunately that issue is too long to read for me right now, but I remember about it. Still trying to wrap my mind around this features :-)

I don't want to hijack your own rewrite, or imply that you need to follow our lead,

I don't have any of this API implemented yet, I just image here how it could look and gather feedback before I start any work on it (which want to do for v4 anyway though).

Thanks for suggestions again, I think I need to read that issue to understand your concerns about your current API. Maybe together we can find some better way to handle it.

NekR avatar Oct 17 '16 21:10 NekR

@jeffposnick @KyleAMathews @developit

At some point I though about using Chrome Match Pattern which I like because I was developing Chrome extensions for a long time already. It's very simple, even my non-programmer clients understand it. So maybe that's the way to go.

With some improvements, probably.

NekR avatar Oct 17 '16 21:10 NekR

@developit

Maybe support path patterns via that lib but under a separate property in config like path?

Yeah, I feel like adding two props pattern and match could make sense. Still thinking.

NekR avatar Oct 17 '16 22:10 NekR

Okay, so roughly this is the only issue holding v4 release 🎉😱

NekR avatar Oct 22 '16 13:10 NekR

Chrome Match Pattern looks good, though I don't see anything about being able to use the values match by wildcards in the resulting URL?

developit avatar Oct 23 '16 14:10 developit

@developit yeah, not possible with plain Chrome Match Pattern. Could be extended though. Probably it's right just to ship it with RegExp/Functions first and add patterns later.

NekR avatar Oct 23 '16 20:10 NekR

Agreed!

developit avatar Oct 23 '16 20:10 developit

Okay, cacheMaps are added and v4 is in master now. Here are some docs for cacheMaps (hint: API is hard and docs too :-(): https://github.com/NekR/offline-plugin/blob/master/docs/cache-maps.md

NekR avatar Nov 29 '16 02:11 NekR

What is the status of runtime dynamic caching?

alecmerdler avatar Jan 09 '18 03:01 alecmerdler

any word on dynamic caching?

kodeine avatar Oct 16 '18 21:10 kodeine

Is it possible to add a configuration in responseStrategy: 'cache-first', not to cache all the first time, but cache at the time of the request

Zane0816 avatar Apr 03 '19 03:04 Zane0816