/ concept-collection / jupyterlite-numbl-kernel
Sign in
concept-collection / jupyterlite-numbl-kernel
jupyterlite-numbl-kernel / demo / coi-serviceworker.js
73 lines · 2.7 KBCodeBlameHistory
2 * Cross-Origin Isolation Service Worker
3 *
4 * Adds COOP/COEP headers to responses so that SharedArrayBuffer is available
5 * on hosts that don't allow custom response headers (e.g. GitHub Pages). numbl
6 * uses SharedArrayBuffer for cooperative cell interruption; without cross-origin
7 * isolation the Stop button can't stop a running cell.
8 *
9 * Based on https://github.com/niccokunzmann/coi-serviceworker (MIT).
10 */
12/* global self, caches, fetch, Response, clients */
14if (typeof window === "undefined") {
15 // --- Service Worker scope ---
16 self.addEventListener("install", () => self.skipWaiting());
17 self.addEventListener("activate", event =>
18 event.waitUntil(self.clients.claim())
19 );
21 self.addEventListener("fetch", event => {
22 const request = event.request;
23 if (request.cache === "only-if-cached" && request.mode !== "same-origin") {
24 return; // Chrome bug workaround
25 }
27 // Only add isolation headers to same-origin responses.
28 // Wrapping cross-origin responses in a new Response strips CORS
29 // internal flags, which breaks cross-origin fetch requests (e.g. numbl
30 // downloading the mip package manager from its GitHub release).
31 if (new URL(request.url).origin !== self.location.origin) {
32 return; // let the browser handle cross-origin requests normally
33 }
35 event.respondWith(
36 fetch(request).then(response => {
37 if (response.status === 0) return response; // opaque response
39 const headers = new Headers(response.headers);
40 headers.set("Cross-Origin-Embedder-Policy", "credentialless");
41 headers.set("Cross-Origin-Opener-Policy", "same-origin");
43 return new Response(response.body, {
44 status: response.status,
45 statusText: response.statusText,
46 headers,
47 });
48 })
49 );
50 });
51} else {
52 // --- Window scope (registration) ---
54 // Capture currentScript synchronously — it becomes null after script runs.
55 const scriptUrl = document.currentScript && document.currentScript.src;
57 if (!window.crossOriginIsolated && navigator.serviceWorker) {
58 navigator.serviceWorker.register(scriptUrl || "/coi-serviceworker.js").then(
59 reg => {
60 if (reg.installing || reg.waiting) {
61 const sw = reg.installing || reg.waiting;
62 sw.addEventListener("statechange", () => {
63 if (sw.state === "activated") window.location.reload();
64 });
65 } else if (reg.active && !navigator.serviceWorker.controller) {
66 // Active but not yet controlling — reload to let it intercept.
67 window.location.reload();
68 }
69 },
70 err => console.error("COI service worker registration failed:", err)
71 );
72 }
moveopenescclose