WICG / service-worker-static-routing-api

A repository for the ServiceWorker static routing API.
Other
26 stars 6 forks source link

Certain URLPatterns don't work; missing 'not' condition #21

Closed screenspan closed 3 weeks ago

screenspan commented 9 months ago

SHORT DESCRIPTION

We'd like to be able to

CURRENT BEHAVIOR

Google Version 123.0.6289.0 (Developer Build) (arm64) on Mac OS 14.3.1 Date: 2024-02-16

The URL Pattern API does allow for alternates using |, e.g. image

But using alternates doesn't currently work in the Service Worker Static Routing API. The following code results in an error:

self.addEventListener('install', event => {
  if (event.addRoutes) {
    event.addRoutes([
      {
        condition: {
          urlPattern: new URLPattern({ pathname: "/(collections|search|621532|account|pages|cart|checkout)/*" })
        },
        source: "network"
      },
    ]);
  }
});

image

We therefore have to resort to a verbose way of declaring multiple patterns that does let us bypass the Service Worker:

self.addEventListener('install', event => {
  if (event.addRoutes) {
    event.addRoutes([
      {
        condition: {
          urlPattern: new URLPattern({pathname: "/collections*"})
        },
        source: "network"
      },

      {
        condition: {
          urlPattern: new URLPattern({pathname: "/search*"})
        },
        source: "network"
      },
      {
        condition: {
          urlPattern: new URLPattern({pathname: "/621532*"})
        },
        source: "network"
      },
      {
        condition: {
          urlPattern: new URLPattern({pathname: "/account*"})
        },
        source: "network"
      },
      {
        condition: {
          urlPattern: new URLPattern({pathname: "/pages*"})
        },
        source: "network"
      },
      {
        condition: {
          urlPattern: new URLPattern({pathname: "/cart*"})
        },
        source: "network"
      },
      {
        condition: {
          urlPattern: new URLPattern({pathname: "/checkouts*"})
        },
        source: "network"
      },
      {
        condition: {
          urlPattern: new URLPattern({pathname: "/"})
        },
        source: "network"
      }]);
  }
});

Result: A matched URL is fetched from the network as per Service Worker Static Routing API. image

DESIRED BEHAVIOR

Pattern alternatives

Use pattern alternatives separated by a pipe character, e.g.

self.addEventListener('install', event => {
  if (event.addRoutes) {
    event.addRoutes([
      {
        condition: {
          urlPattern: new URLPattern({ pathname: "/(collections|search|621532|account|pages|cart|checkout)/*" })
        },
        source: "network"
      },
    ]);
  }
});

Exclude URL patterns using 'not' condition

Add all routes except for certain patterns by using a not condition, as the URL Pattern API apparently doesn't have a way to exclude patterns.

self.addEventListener('install', event => {
  if (event.addRoutes) {
    event.addRoutes([
      {
        condition: {
          not: [{ // This would be helpful!
            urlPattern: new URLPattern({ pathname: "/(collections|search|621532|account|pages|cart|checkout)/*" })
          }]
        },
        source: "fetch-event"
      },
    ]);
  }
});

Show 'Source' in Network Panel

Currently routed via Service Worker Static Routing API are annotated by (ServiceWorker router) in the Network panel. image

Ideally, the source would also be displayed, e.g. (ServiceWorker: routed to network)

sisidovski commented 9 months ago

Thank you @screenspan for reporting this!

Currently the API intentionally prohibits the URLPattern input with having regular expressions because from security concerns. Although it's not merged yet, we have a mention in the proposed spec. https://github.com/w3c/ServiceWorker/pull/1701/files#diff-7bdebf23eab87aaf2676a41586076394031b6b98fde24e35d1672aa65a1cc0feR3373-R3375

You can check if your URLPattern input has a regexp or not, by using hasRegExpGroups.

new URLPattern({pathname: '/(collections|search|621532|account|pages|cart|checkout)/*'}).hasRegExpGroups
=> true

We therefore have to resort to a verbose way of declaring multiple patterns that does let us bypass the Service Worker:

Agree, that's not a smart solution, but currently having multiple patterns is the right approach.

Show 'Source' in Network Panel

I understand this is important for the debug, but this is more like a chrome DevTools feature. If possible, could you file a bug to https://crbug.com/ ?

yoshisatoyanagisawa commented 9 months ago

Thank you for the report,

As @sisidovski mentioned, using regexp is intentionally prohibited. hasRegExpGroups has been introduced for checking that.

For the pattern you wrote, I suppose you can also use the or condition.

self.addEventListener('install', event => {
  if (event.addRoutes) {
    event.addRoutes([
      {
        condition: {
          or: [
            {urlPattern: new URLPattern({pathname: "/collections*"})},
            {urlPattern: new URLPattern({pathname: "/search*"})},
            {urlPattern: new URLPattern({pathname: "/621532*"})},
            {urlPattern: new URLPattern({pathname: "/account*"})},
            {urlPattern: new URLPattern({pathname: "/pages*"})},
            {urlPattern: new URLPattern({pathname: "/cart*"})},
            {urlPattern: new URLPattern({pathname: "/checkouts*"})},
            {urlPattern: new URLPattern({pathname: "/"})}
         ]
        },
        source: "network"
      }]);
  }
});

Unfortunately, Chromium only implements the or condition, and other conjunctive conditions are not implemented yet.

When you hover the network panel entry, router ID will be shown. You can find an associated source by checking the ID in the routes section shown in the Application panel. However, I understand it is not so convenient.

As @sisidovski mentioned, please feel free to file a crbug.

screenspan commented 9 months ago

Hi @sisidovski and @yoshisatoyanagisawa , thanks for the additional information.

Using the or condition, as @yoshisatoyanagisawa suggested, did work for me.

If I understand the proposal for the final form of the API, a not condition will be added:

dictionary RouterNotCondition : RouterCondition {
  RouterCondition not;
};

I think that'll be helpful once implemented.

Are there any other restrictions on using the URL Pattern API with Static Routing besides custom RegExp groups?

I've filed a crbug for the DevTools feature request, so feel free to close this issue if you want.

yoshisatoyanagisawa commented 9 months ago

Are there any other restrictions on using the URL Pattern API with Static Routing besides custom RegExp groups?

Except for prohibiting use of a regular expression (or, what URLPattern think as a regular expression), there should not be any restrictions.

One thing you may need an attention to use URLPattern is that an implicit base URL configuration for short-hands. However, since you use the URLPattern object, you should not be affected. It is discussed in https://github.com/whatwg/urlpattern/issues/182, and the static routing API follows: https://urlpattern.spec.whatwg.org/#other-specs-javascript

I do not come up with anything else.

yoshisatoyanagisawa commented 8 months ago

FYI, the "not" condition implementation is tracked in https://issues.chromium.org/issues/328565554.

yoshisatoyanagisawa commented 7 months ago

Note that the not condition has been implemented behind the flag (ServiceWorkerStaticRouterNotConditionEnabled).

yoshisatoyanagisawa commented 6 months ago

FYI, I2S has been sent. https://groups.google.com/a/chromium.org/g/blink-dev/c/1HxS284iKCE

yoshisatoyanagisawa commented 3 weeks ago

Let me mark this resolved.