gzuidhof/coi-serviceworker

Consider extracting functionality into service worker and window modules?

jcbhmr opened this issue ยท 4 comments

jcbhmr commented

This would make it easier to compose with an existing application and service worker.

example idea that I had

// main.js
import doThing from "your-package/window.js";

// THIS WILL EXECUTE TWICE since the page is reloaded
console.log("before", crossOriginIsolated);
//=> false (first time)
//=> true (second time)

// It will hang here and reload the page (think process.exit()-like)
await doThing(navigator.serviceWorker?.register("sw.js", { type: "module" }));

// THIS WILL EXECUTE ONCE the second time
console.log("after", crossOriginIsolated);
//=> true
// sw.js
// This auto-registers the 'fetch' event listener, but don't worry! You can
// still add your own listeners! It's just a fallback for any you don't handle.
import "your-package/sw.js";

// REQUIRED(?)
globalThis.addEventListener("install", () => skipWaiting());
globalThis.addEventListener("activate", (e) => e.waitUntil(clients.claim()));

globalThis.addEventListener("fetch", (e) => {
  // We also add a 'fetch()' wrapper polyfill to add the COOP and COEP headers
  // to any fetch requests you make that would have been handled by our 'fetch'
  // event handler. This way, ๐Ÿ‘‡ this still works! If we didn't override the
  // 'fetch()' function, this wouldn't get the right headers.
  e.respondWith(fetch(e.request));
});

cc @gzuidhof

I'd propose to avoid top level await wherever you can, but that doesn't mean we couldn't somehow expose a Promise that can be awaited if someone does wish to use such a top level await.

I imagine that to support this we would either

  • Put something in global scope.. Kind of ugly (e.g. putting a variable called coi on window).
  • Provide an ESM module build that exports some goodies.

As for splitting up the main.js and sw.js, at that point I would wonder why one would use coi-serviceworker at all? It's quite a bit more involved - perhaps this makes sense as a blogpost that people can copy paste recipes from, instead of a library.

Other than providing a Promise, why would one not be able to put their own if/else on crossOriginIsolated, and depending on that show content or not? Relevant issue: #14 - Could you help me understand why a top level await that blocks forever is better than

if (!window.crossOriginIsolated) {
  throw new Error("Oh no!")
}
jcbhmr commented

I guess it's more of a question of what you consider in-scope. Is this supposed to be:

  1. Just a <script> tag and that's it
  2. A collection of helpful COI utils for simple apps (with an AiO <script> tag)

For the second option, here's some ideas of other utils that could be included:

  • A Vite plugin to auto-insert the AiO <script> tag
  • A quick CLI or something to auto-inject the <script> tag into a single file or folder -- this could be useful for projects that generate HTML output like TypeDoc
  • A service worker plugin that you can call or something (or that auto-injects; idk) to compose these COI headers with the rest of your cache logic stuff
  • A whiteout frozen "loading" until the service worker gets setup
  • An ESM js script that you can import and expect it to "lock until loaded"

All of those things ๐Ÿ‘† could also just be examples since the code to do them is so minimal. i.e.

  • A quick demo of the Vite https://vitejs.dev/config/server-options.html#server-headers option
  • A quick copy-paste curl + sed command to inject the AiO script
  • A quick <details> or something on how to take the simple logic and apply it in your own SW
  • An example/copy-paste sample or something to demo "don't show content until COI"
  • A demo of location.reload(); await new Promise(() => {})

you're right, it's very easy to copy-paste the relevant bits into your service worker.

If you want to go with that I'd suggest putting at least an excerpt of whatever blog post in the readme. like > excerpt or something idk

jcbhmr commented

Also I think TLA is available in all spots that COI is. i.e. there's no place you can get COI without also having TLA (EXCEPT for FF 79-89). Am I reading that right? ๐Ÿค”

https://caniuse.com/mdn-javascript_operators_await_top_level
https://caniuse.com/sharedarraybuffer
image
image

Aha ok, thank you for helping me understand a bit better, maybe something like the following could do the job for displaying something without needing any additional errors or never-resolving-awaits:

if (!window.crossOriginIsolated) {
 const e = document.createElement("div");
 e.innerText = "Please wait while the page refreshes..."
 const s = e.style;
 s.position = "absolute"
 s.zIndex = "10000"
 s.width = "100%"
 s.height = "100%"
 s.backgroundColor = "#FFF"
 
 document.body.appendChild(e);
}

I like the idea of having a set of examples / recipes a lot - it is great if people can copy paste snippets of code into their projects without them needing to add a dependency. For those who want a more involved integration, we can of course also link to the tinycoi library where it makes sense