Consider extracting functionality into service worker and window modules?
jcbhmr opened this issue ยท 4 comments
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!")
}
I guess it's more of a question of what you consider in-scope. Is this supposed to be:
- Just a
<script>
tag and that's it - 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
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
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