diff --git a/package.json b/package.json index e8466ff..7b2a263 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "build": "rollup -c", + "watch": "rollup -cw", "prepare": "npm run build" }, "author": "", diff --git a/rollup.config.js b/rollup.config.js index 2f98fcd..f2cdd83 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -44,7 +44,7 @@ const configs = [ format: 'umd', name: 'BareMux', sourcemap: true, - exports: 'auto', + exports: 'named', }, plugins: commonPlugins(), }, diff --git a/src/client.ts b/src/client.ts index 5c07e21..89a6ebc 100644 --- a/src/client.ts +++ b/src/client.ts @@ -107,10 +107,14 @@ export class BareMuxConnection { this.worker = new WorkerConnection(workerPath); } + async getTransport(): Promise { + return (await this.worker.sendMessage({ type: "get" })).name; + } + async setTransport(path: string, options: any[]) { await this.setManualTransport(` const { default: BareTransport } = await import("${path}"); - return new BareTransport(${options.map(x => JSON.stringify(x)).join(", ")}); + return [new BareTransport(${options.map(x => JSON.stringify(x)).join(", ")}), "${path}"]; `); } diff --git a/src/connection.ts b/src/connection.ts index b007d22..37167f5 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -2,18 +2,8 @@ import { BareHeaders, TransferrableResponse } from "./baretypes"; type SWClient = { postMessage: typeof MessagePort.prototype.postMessage }; -function tryGetPort(client: SWClient): Promise { - let channel = new MessageChannel(); - return new Promise(resolve => { - client.postMessage({ type: "getPort", port: channel.port2 }, [channel.port2]); - channel.port1.onmessage = event => { - resolve(event.data) - } - }); -} - export type WorkerMessage = { - type: "fetch" | "websocket" | "set", + type: "fetch" | "websocket" | "set" | "get", fetch?: { remote: string, method: string, @@ -36,8 +26,9 @@ export type WorkerRequest = { } export type WorkerResponse = { - type: "fetch" | "websocket" | "set" | "error", + type: "fetch" | "websocket" | "set" | "get" | "error", fetch?: TransferrableResponse, + name?: string, error?: Error, } @@ -46,6 +37,28 @@ type BroadcastMessage = { path?: string, } +async function searchForPort(): Promise { + // @ts-expect-error + const clients: SWClient[] = await self.clients.matchAll({ type: "window", includeUncontrolled: true }); + const promise: Promise = Promise.race([...clients.map((x: SWClient) => tryGetPort(x)), new Promise((_, reject) => setTimeout(reject, 1000, new Error("")))]) as Promise; + try { + return await promise; + } catch { + console.warn("bare-mux: failed to get a bare-mux SharedWorker MessagePort within 1s, retrying"); + return await searchForPort(); + } +} + +function tryGetPort(client: SWClient): Promise { + let channel = new MessageChannel(); + return new Promise(resolve => { + client.postMessage({ type: "getPort", port: channel.port2 }, [channel.port2]); + channel.port1.onmessage = event => { + resolve(event.data) + } + }); +} + function createPort(path: string, channel: BroadcastChannel): MessagePort { const worker = new SharedWorker(path, "bare-mux-worker"); // uv removes navigator.serviceWorker so this errors @@ -77,12 +90,12 @@ export class WorkerConnection { if (self.clients) { // running in a ServiceWorker // ask a window for the worker port - // @ts-expect-error - const clients: Promise = self.clients.matchAll({ type: "window", includeUncontrolled: true }); - this.port = clients.then(clients => Promise.any(clients.map((x: SWClient) => tryGetPort(x)))); + this.port = searchForPort(); } else if (workerPath && SharedWorker) { // running in a window, was passed a workerPath // create the SharedWorker and help other bare-mux clients get the workerPath + + if (!workerPath.startsWith("/") && !workerPath.includes("://")) throw new Error("Invalid URL. Must be absolute or start at the root."); this.port = createPort(workerPath, this.channel); } else if (SharedWorker) { // running in a window, was not passed a workerPath diff --git a/src/snapshot.ts b/src/snapshot.ts index b466625..0cee3cd 100644 --- a/src/snapshot.ts +++ b/src/snapshot.ts @@ -6,6 +6,7 @@ export const WebSocket = globalThis.WebSocket; export const Request = globalThis.Request; export const Response = globalThis.Response; export const XMLHttpRequest = globalThis.XMLHttpRequest; +export const SharedWorker = globalThis.SharedWorker; export const WebSocketFields = { prototype: { diff --git a/src/worker.ts b/src/worker.ts index 501b8be..e62e415 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -2,6 +2,7 @@ import { BareTransport } from "./baretypes"; import { WorkerMessage, WorkerResponse } from "./connection" let currentTransport: BareTransport | null = null; +let currentTransportName: string = ""; function noClients(): Error { // @ts-expect-error mdn error constructor: new Error(message, options) @@ -21,13 +22,17 @@ function handleConnection(port: MessagePort) { // @ts-expect-error const func = new AsyncFunction(message.client); - currentTransport = await func(); - console.log("set transport to ", currentTransport); + const [newTransport, name] = await func(); + currentTransport = newTransport; + currentTransportName = name; + console.log("set transport to ", currentTransport, name); port.postMessage({ type: "set" }); } catch (err) { port.postMessage({ type: "error", error: err }); } + } else if (message.type === "get") { + port.postMessage({ type: "get", name: currentTransportName }); } else if (message.type === "fetch") { try { if (!currentTransport) throw noClients();