diff --git a/src/client/swruntime.ts b/src/client/swruntime.ts new file mode 100644 index 0000000..28fda91 --- /dev/null +++ b/src/client/swruntime.ts @@ -0,0 +1,21 @@ +import { encodeUrl } from "../shared/rewriters/url"; + +class ScramjetServiceWorkerRuntime { + constructor() { + addEventListener("message", (event) => { + if ("scramjet$type" in event.data) { + event.stopImmediatePropagation(); + + return; + } + }); + } +} + +declare global { + interface Window { + ScramjetServiceWorkerRuntime: typeof ScramjetServiceWorkerRuntime; + } +} + +self.ScramjetServiceWorkerRuntime = ScramjetServiceWorkerRuntime; diff --git a/src/client/window.ts b/src/client/window.ts index dab327f..92ea9e9 100644 --- a/src/client/window.ts +++ b/src/client/window.ts @@ -45,6 +45,14 @@ export const windowProxy = new Proxy(self, { return Reflect.set(target, prop, newValue); }, + defineProperty(target, property, attributes) { + if (!attributes.get && !attributes.set) { + attributes.writable = true; + } + attributes.configurable = true; + + return Reflect.defineProperty(target, property, attributes); + }, }); export const documentProxy = new Proxy(self.document || {}, { diff --git a/src/client/worker.ts b/src/client/worker.ts index 031d5af..78d9c08 100644 --- a/src/client/worker.ts +++ b/src/client/worker.ts @@ -25,8 +25,50 @@ if ("window" in self) { return Reflect.apply(target, thisArg, argArray); }, }); - //@ts-expect-error temporary until nested sw support - delete window.Navigator.prototype.serviceWorker; + + client.Proxy("navigator.serviceWorker.register", { + apply(ctx) { + if (ctx.args[0] instanceof URL) ctx.args[0] = ctx.args[0].href; + let url = encodeUrl(ctx.args[0]) + "?dest=serviceworker"; + if (ctx.args[1] && ctx.args[1].type === "module") { + url += "&type=module"; + } + let worker = new SharedWorker(url); + + let handle = worker.port; + + navigator.serviceWorker.controller.postMessage({ + scramjet$type: "registerServiceWorker", + port: handle, + }); + + const fakeRegistration = new Proxy( + { + __proto__: ServiceWorkerRegistration.prototype, + }, + { + get(target, prop) { + if (prop === "installing") { + return null; + } + if (prop === "waiting") { + return null; + } + if (prop === "active") { + return handle; + } + if (prop === "scope") { + return ctx.args[0]; + } + + return Reflect.get(target, prop); + }, + } + ); + + ctx.return(new Promise((resolve) => resolve(fakeRegistration))); + }, + }); } client.Proxy("importScripts", { diff --git a/src/worker/fakesw.ts b/src/worker/fakesw.ts new file mode 100644 index 0000000..2cd08c2 --- /dev/null +++ b/src/worker/fakesw.ts @@ -0,0 +1,3 @@ +export class FakeServiceWorker { + constructor(public handle: MessagePort) {} +} diff --git a/src/worker/index.ts b/src/worker/index.ts index 9218f8a..7387372 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -1,3 +1,4 @@ +import { FakeServiceWorker } from "./fakesw"; import { swfetch } from "./fetch"; import { ScramjetThreadpool } from "./threadpool"; @@ -15,6 +16,8 @@ export class ScramjetServiceWorker { syncPool: Record void> = {}; synctoken = 0; + serviceWorkers: FakeServiceWorker[] = []; + constructor(config = self.$scramjet.config) { this.client = new self.$scramjet.shared.util.BareClient(); if (!config.prefix) config.prefix = "/scramjet/"; @@ -23,6 +26,14 @@ export class ScramjetServiceWorker { this.threadpool = new ScramjetThreadpool(); addEventListener("message", ({ data }) => { + if (!("scramjet$type" in data)) return; + + if (data.scramjet$type === "registerServiceWorker") { + this.serviceWorkers.push(new FakeServiceWorker(data.port)); + + return; + } + if (!("scramjet$token" in data)) return; const resolve = this.syncPool[data.scramjet$token];