@mercuryworkshop/bare-mux
Advanced tools
Comparing version 2.0.8 to 2.0.9
@@ -1,538 +0,2 @@ | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BareMux = {})); | ||
})(this, (function (exports) { 'use strict'; | ||
const maxRedirects = 20; | ||
// The user likely has overwritten all networking functions after importing bare-client | ||
// It is our responsibility to make sure components of Bare-Client are using native networking functions | ||
const fetch = globalThis.fetch; | ||
const WebSocket = globalThis.WebSocket; | ||
const Request = globalThis.Request; | ||
const Response = globalThis.Response; | ||
const SharedWorker = globalThis.SharedWorker; | ||
const localStorage = globalThis.localStorage; | ||
const serviceWorker = globalThis.navigator.serviceWorker; | ||
const WebSocketFields = { | ||
prototype: { | ||
send: WebSocket.prototype.send, | ||
}, | ||
CLOSED: WebSocket.CLOSED, | ||
CLOSING: WebSocket.CLOSING, | ||
CONNECTING: WebSocket.CONNECTING, | ||
OPEN: WebSocket.OPEN, | ||
}; | ||
async function searchForPort() { | ||
// @ts-expect-error | ||
const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true }); | ||
const promises = clients.map(async (x) => { | ||
const port = await tryGetPort(x); | ||
await testPort(port); | ||
return port; | ||
}); | ||
const promise = Promise.race([ | ||
Promise.any(promises), | ||
new Promise((_, reject) => setTimeout(reject, 1000, new TypeError("timeout"))) | ||
]); | ||
try { | ||
return await promise; | ||
} | ||
catch (err) { | ||
if (err instanceof AggregateError) { | ||
console.error("bare-mux: failed to get a bare-mux SharedWorker MessagePort as all clients returned an invalid MessagePort."); | ||
throw new Error("All clients returned an invalid MessagePort."); | ||
} | ||
console.warn("bare-mux: failed to get a bare-mux SharedWorker MessagePort within 1s, retrying"); | ||
return await searchForPort(); | ||
} | ||
} | ||
function tryGetPort(client) { | ||
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 testPort(port) { | ||
const pingChannel = new MessageChannel(); | ||
const pingPromise = new Promise((resolve, reject) => { | ||
pingChannel.port1.onmessage = event => { | ||
if (event.data.type === "pong") { | ||
resolve(); | ||
} | ||
}; | ||
setTimeout(reject, 1500); | ||
}); | ||
port.postMessage({ message: { type: "ping" }, port: pingChannel.port2 }, [pingChannel.port2]); | ||
return pingPromise; | ||
} | ||
function createPort(path, registerHandlers) { | ||
const worker = new SharedWorker(path, "bare-mux-worker"); | ||
if (registerHandlers) { | ||
// @ts-expect-error we are using snapshot.ts | ||
serviceWorker.addEventListener("message", (event) => { | ||
if (event.data.type === "getPort" && event.data.port) { | ||
console.debug("bare-mux: recieved request for port from sw"); | ||
const newWorker = new SharedWorker(path, "bare-mux-worker"); | ||
event.data.port.postMessage(newWorker.port, [newWorker.port]); | ||
} | ||
}); | ||
} | ||
return worker.port; | ||
} | ||
let browserSupportsTransferringStreamsCache = null; | ||
function browserSupportsTransferringStreams() { | ||
if (browserSupportsTransferringStreamsCache === null) { | ||
const chan = new MessageChannel(); | ||
const stream = new ReadableStream(); | ||
let res; | ||
try { | ||
chan.port1.postMessage(stream, [stream]); | ||
res = true; | ||
} | ||
catch (err) { | ||
res = false; | ||
} | ||
browserSupportsTransferringStreamsCache = res; | ||
return res; | ||
} | ||
else { | ||
return browserSupportsTransferringStreamsCache; | ||
} | ||
} | ||
class WorkerConnection { | ||
constructor(worker) { | ||
this.channel = new BroadcastChannel("bare-mux"); | ||
if (worker instanceof MessagePort || worker instanceof Promise) { | ||
this.port = worker; | ||
} | ||
else { | ||
this.createChannel(worker, true); | ||
} | ||
} | ||
createChannel(workerPath, inInit) { | ||
// @ts-expect-error | ||
if (self.clients) { | ||
// running in a ServiceWorker | ||
// ask a window for the worker port, register for refreshPort | ||
this.port = searchForPort(); | ||
this.channel.onmessage = (event) => { | ||
if (event.data.type === "refreshPort") { | ||
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, inInit); | ||
console.debug("bare-mux: setting localStorage bare-mux-path to", workerPath); | ||
localStorage["bare-mux-path"] = workerPath; | ||
} | ||
else if (SharedWorker) { | ||
// running in a window, was not passed a workerPath | ||
// use sessionStorage for the workerPath | ||
const path = localStorage["bare-mux-path"]; | ||
console.debug("bare-mux: got localStorage bare-mux-path:", path); | ||
if (!path) | ||
throw new Error("Unable to get bare-mux workerPath from localStorage."); | ||
this.port = createPort(path, inInit); | ||
} | ||
else { | ||
// SharedWorker does not exist | ||
throw new Error("Unable to get a channel to the SharedWorker."); | ||
} | ||
} | ||
async sendMessage(message, transferable) { | ||
if (this.port instanceof Promise) | ||
this.port = await this.port; | ||
try { | ||
await testPort(this.port); | ||
} | ||
catch { | ||
console.warn("bare-mux: Failed to get a ping response from the worker within 1.5s. Assuming port is dead."); | ||
this.createChannel(); | ||
return await this.sendMessage(message, transferable); | ||
} | ||
const channel = new MessageChannel(); | ||
const toTransfer = [channel.port2, ...(transferable || [])]; | ||
const promise = new Promise((resolve, reject) => { | ||
channel.port1.onmessage = event => { | ||
const message = event.data; | ||
if (message.type === "error") { | ||
reject(message.error); | ||
} | ||
else { | ||
resolve(message); | ||
} | ||
}; | ||
}); | ||
this.port.postMessage({ message: message, port: channel.port2 }, toTransfer); | ||
return await promise; | ||
} | ||
} | ||
class BareWebSocket extends EventTarget { | ||
constructor(remote, protocols = [], worker, requestHeaders, arrayBufferImpl) { | ||
super(); | ||
this.protocols = []; | ||
this.readyState = WebSocketFields.CONNECTING; | ||
this.binaryType = "blob"; | ||
//legacy event handlers | ||
this.onopen = (event) => { }; | ||
this.onerror = (event) => { }; | ||
this.onmessage = (event) => { }; | ||
this.onclose = (event) => { }; | ||
this.url = remote.toString(); | ||
this.protocols = protocols; | ||
const onopen = (protocol) => { | ||
this.readyState = WebSocketFields.OPEN; | ||
this.protocols = protocol; | ||
this.meta = { | ||
headers: { | ||
"sec-websocket-protocol": protocol, | ||
} | ||
}; | ||
const event = new Event("open"); | ||
this.dispatchEvent(event); | ||
if (this.onopen) { | ||
this.onopen(event); | ||
} | ||
}; | ||
const onmessage = async (payload) => { | ||
if (typeof payload === "string") ; | ||
else if ("byteLength" in payload) { | ||
if (this.binaryType === "blob") { | ||
payload = new Blob([payload]); | ||
} | ||
else { | ||
Object.setPrototypeOf(payload, arrayBufferImpl); | ||
} | ||
} | ||
else if ("arrayBuffer" in payload) { | ||
if (this.binaryType === "arraybuffer") { | ||
payload = await payload.arrayBuffer(); | ||
Object.setPrototypeOf(payload, arrayBufferImpl); | ||
} | ||
} | ||
const event = new MessageEvent("message", { data: payload }); | ||
this.dispatchEvent(event); | ||
if (this.onmessage) { | ||
this.onmessage(event); | ||
} | ||
}; | ||
const onclose = (code, reason) => { | ||
this.readyState = WebSocketFields.CLOSED; | ||
const event = new CloseEvent("close", { code, reason }); | ||
this.dispatchEvent(event); | ||
if (this.onclose) { | ||
this.onclose(event); | ||
} | ||
}; | ||
const onerror = () => { | ||
this.readyState = WebSocketFields.CLOSED; | ||
const event = new Event("error"); | ||
this.dispatchEvent(event); | ||
if (this.onerror) { | ||
this.onerror(event); | ||
} | ||
}; | ||
this.channel = new MessageChannel(); | ||
this.channel.port1.onmessage = event => { | ||
if (event.data.type === "open") { | ||
onopen(event.data.args[0]); | ||
} | ||
else if (event.data.type === "message") { | ||
onmessage(event.data.args[0]); | ||
} | ||
else if (event.data.type === "close") { | ||
onclose(event.data.args[0], event.data.args[1]); | ||
} | ||
else if (event.data.type === "error") { | ||
onerror( /* event.data.args[0] */); | ||
} | ||
}; | ||
worker.sendMessage({ | ||
type: "websocket", | ||
websocket: { | ||
url: remote.toString(), | ||
origin: origin, | ||
//@ts-expect-error | ||
protocols: protocols, | ||
requestHeaders: requestHeaders, | ||
channel: this.channel.port2, | ||
}, | ||
}, [this.channel.port2]); | ||
} | ||
send(...args) { | ||
if (this.readyState === WebSocketFields.CONNECTING) { | ||
throw new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state."); | ||
} | ||
let data = args[0]; | ||
if (data.buffer) | ||
data = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength); | ||
this.channel.port1.postMessage({ type: "data", data: data }, data instanceof ArrayBuffer ? [data] : []); | ||
} | ||
close(code, reason) { | ||
this.readyState = WebSocketFields.CLOSING; | ||
this.channel.port1.postMessage({ type: "close", closeCode: code, closeReason: reason }); | ||
} | ||
get bufferedAmount() { | ||
return 0; | ||
} | ||
get protocol() { | ||
if (Array.isArray(this.protocols)) { | ||
return this.protocols[0] || ""; | ||
} | ||
else { | ||
return this.protocols || ""; | ||
} | ||
} | ||
get extensions() { | ||
return ""; | ||
} | ||
} | ||
function sendError(port, err, name) { | ||
console.error(`error while processing '${name}': `, err); | ||
port.postMessage({ type: "error", error: err }); | ||
} | ||
async function handleFetch(message, port, transport) { | ||
const resp = await transport.request(new URL(message.fetch.remote), message.fetch.method, message.fetch.body, message.fetch.headers, null); | ||
if (!browserSupportsTransferringStreams() && resp.body instanceof ReadableStream) { | ||
const conversionResp = new Response(resp.body); | ||
resp.body = await conversionResp.arrayBuffer(); | ||
} | ||
if (resp.body instanceof ReadableStream || resp.body instanceof ArrayBuffer) { | ||
port.postMessage({ type: "fetch", fetch: resp }, [resp.body]); | ||
} | ||
else { | ||
port.postMessage({ type: "fetch", fetch: resp }); | ||
} | ||
} | ||
async function handleWebsocket(message, port, transport) { | ||
const onopen = (protocol) => { | ||
message.websocket.channel.postMessage({ type: "open", args: [protocol] }); | ||
}; | ||
const onclose = (code, reason) => { | ||
message.websocket.channel.postMessage({ type: "close", args: [code, reason] }); | ||
}; | ||
const onerror = (error) => { | ||
message.websocket.channel.postMessage({ type: "error", args: [error] }); | ||
}; | ||
const onmessage = (data) => { | ||
if (data instanceof ArrayBuffer) { | ||
message.websocket.channel.postMessage({ type: "message", args: [data] }, [data]); | ||
} | ||
else { | ||
message.websocket.channel.postMessage({ type: "message", args: [data] }); | ||
} | ||
}; | ||
const [data, close] = transport.connect(new URL(message.websocket.url), message.websocket.origin, message.websocket.protocols, message.websocket.requestHeaders, onopen, onmessage, onclose, onerror); | ||
message.websocket.channel.onmessage = (event) => { | ||
if (event.data.type === "data") { | ||
data(event.data.data); | ||
} | ||
else if (event.data.type === "close") { | ||
close(event.data.closeCode, event.data.closeReason); | ||
} | ||
}; | ||
port.postMessage({ type: "websocket" }); | ||
} | ||
const validChars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~"; | ||
function validProtocol(protocol) { | ||
for (let i = 0; i < protocol.length; i++) { | ||
const char = protocol[i]; | ||
if (!validChars.includes(char)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
const wsProtocols = ['ws:', 'wss:']; | ||
const statusEmpty = [101, 204, 205, 304]; | ||
const statusRedirect = [301, 302, 303, 307, 308]; | ||
class BareMuxConnection { | ||
constructor(worker) { | ||
this.worker = new WorkerConnection(worker); | ||
} | ||
async getTransport() { | ||
return (await this.worker.sendMessage({ type: "get" })).name; | ||
} | ||
async setTransport(path, options, transferables) { | ||
await this.setManualTransport(` | ||
const { default: BareTransport } = await import("${path}"); | ||
return [BareTransport, "${path}"]; | ||
`, options, transferables); | ||
} | ||
async setManualTransport(functionBody, options, transferables) { | ||
if (functionBody === "bare-mux-remote") | ||
throw new Error("Use setRemoteTransport."); | ||
await this.worker.sendMessage({ | ||
type: "set", | ||
client: { | ||
function: functionBody, | ||
args: options, | ||
}, | ||
}, transferables); | ||
} | ||
async setRemoteTransport(transport, name) { | ||
const channel = new MessageChannel(); | ||
channel.port1.onmessage = async (event) => { | ||
const port = event.data.port; | ||
const message = event.data.message; | ||
if (message.type === "fetch") { | ||
try { | ||
if (!transport.ready) | ||
await transport.init(); | ||
await handleFetch(message, port, transport); | ||
} | ||
catch (err) { | ||
sendError(port, err, "fetch"); | ||
} | ||
} | ||
else if (message.type === "websocket") { | ||
try { | ||
if (!transport.ready) | ||
await transport.init(); | ||
await handleWebsocket(message, port, transport); | ||
} | ||
catch (err) { | ||
sendError(port, err, "websocket"); | ||
} | ||
} | ||
}; | ||
await this.worker.sendMessage({ | ||
type: "set", | ||
client: { | ||
function: "bare-mux-remote", | ||
args: [channel.port2, name] | ||
}, | ||
}, [channel.port2]); | ||
} | ||
getInnerPort() { | ||
return this.worker.port; | ||
} | ||
} | ||
class BareClient { | ||
/** | ||
* Create a BareClient. Calls to fetch and connect will wait for an implementation to be ready. | ||
*/ | ||
constructor(worker) { | ||
this.worker = new WorkerConnection(worker); | ||
} | ||
createWebSocket(remote, protocols = [], webSocketImpl, requestHeaders, arrayBufferImpl) { | ||
try { | ||
remote = new URL(remote); | ||
} | ||
catch (err) { | ||
throw new DOMException(`Faiiled to construct 'WebSocket': The URL '${remote}' is invalid.`); | ||
} | ||
if (!wsProtocols.includes(remote.protocol)) | ||
throw new DOMException(`Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss'. '${remote.protocol}' is not allowed.`); | ||
if (!Array.isArray(protocols)) | ||
protocols = [protocols]; | ||
protocols = protocols.map(String); | ||
for (const proto of protocols) | ||
if (!validProtocol(proto)) | ||
throw new DOMException(`Failed to construct 'WebSocket': The subprotocol '${proto}' is invalid.`); | ||
arrayBufferImpl = arrayBufferImpl || (webSocketImpl || WebSocket).constructor.constructor("return ArrayBuffer")().prototype; | ||
requestHeaders = requestHeaders || {}; | ||
requestHeaders['Host'] = (new URL(remote)).host; | ||
// requestHeaders['Origin'] = origin; | ||
requestHeaders['Pragma'] = 'no-cache'; | ||
requestHeaders['Cache-Control'] = 'no-cache'; | ||
requestHeaders['Upgrade'] = 'websocket'; | ||
// requestHeaders['User-Agent'] = navigator.userAgent; | ||
requestHeaders['Connection'] = 'Upgrade'; | ||
const socket = new BareWebSocket(remote, protocols, this.worker, requestHeaders, arrayBufferImpl); | ||
return socket; | ||
} | ||
async fetch(url, init) { | ||
// Only create an instance of Request to parse certain parameters of init such as method, headers, redirect | ||
// But use init values whenever possible | ||
const req = new Request(url, init); | ||
// try to use init.headers because it may contain capitalized headers | ||
// furthermore, important headers on the Request class are blocked... | ||
// we should try to preserve the capitalization due to quirks with earlier servers | ||
const inputHeaders = init?.headers || req.headers; | ||
const headers = inputHeaders instanceof Headers | ||
? Object.fromEntries(inputHeaders) | ||
: inputHeaders; | ||
const body = req.body; | ||
let urlO = new URL(req.url); | ||
if (urlO.protocol.startsWith('blob:')) { | ||
const response = await fetch(urlO); | ||
const result = new Response(response.body, response); | ||
result.rawHeaders = Object.fromEntries(response.headers); | ||
result.rawResponse = response; | ||
return result; | ||
} | ||
for (let i = 0;; i++) { | ||
if ('host' in headers) | ||
headers.host = urlO.host; | ||
else | ||
headers.Host = urlO.host; | ||
let resp = (await this.worker.sendMessage({ | ||
type: "fetch", | ||
fetch: { | ||
remote: urlO.toString(), | ||
method: req.method, | ||
headers: headers, | ||
body: body || undefined, | ||
}, | ||
}, body ? [body] : [])).fetch; | ||
let responseobj = new Response(statusEmpty.includes(resp.status) ? undefined : resp.body, { | ||
headers: new Headers(resp.headers), | ||
status: resp.status, | ||
statusText: resp.statusText, | ||
}); | ||
responseobj.rawHeaders = resp.headers; | ||
responseobj.rawResponse = new Response(resp.body); | ||
responseobj.finalURL = urlO.toString(); | ||
const redirect = init?.redirect || req.redirect; | ||
if (statusRedirect.includes(responseobj.status)) { | ||
switch (redirect) { | ||
case 'follow': { | ||
const location = responseobj.headers.get('location'); | ||
if (maxRedirects > i && location !== null) { | ||
urlO = new URL(location, urlO); | ||
continue; | ||
} | ||
else | ||
throw new TypeError('Failed to fetch'); | ||
} | ||
case 'error': | ||
throw new TypeError('Failed to fetch'); | ||
case 'manual': | ||
return responseobj; | ||
} | ||
} | ||
else { | ||
return responseobj; | ||
} | ||
} | ||
} | ||
} | ||
exports.BareClient = BareClient; | ||
exports.BareMuxConnection = BareMuxConnection; | ||
exports.WebSocketFields = WebSocketFields; | ||
exports.WorkerConnection = WorkerConnection; | ||
exports.browserSupportsTransferringStreams = browserSupportsTransferringStreams; | ||
exports.default = BareClient; | ||
exports.maxRedirects = maxRedirects; | ||
exports.validProtocol = validProtocol; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).BareMux={})}(this,(function(e){"use strict";const t=globalThis.fetch,s=globalThis.WebSocket,o=globalThis.Request,r=globalThis.Response,a=globalThis.SharedWorker,n=globalThis.localStorage,i=globalThis.navigator.serviceWorker,c={prototype:{send:s.prototype.send},CLOSED:s.CLOSED,CLOSING:s.CLOSING,CONNECTING:s.CONNECTING,OPEN:s.OPEN};async function l(){const e=(await self.clients.matchAll({type:"window",includeUncontrolled:!0})).map((async e=>{const t=await function(e){let t=new MessageChannel;return new Promise((s=>{e.postMessage({type:"getPort",port:t.port2},[t.port2]),t.port1.onmessage=e=>{s(e.data)}}))}(e);return await h(t),t})),t=Promise.race([Promise.any(e),new Promise(((e,t)=>setTimeout(t,1e3,new TypeError("timeout"))))]);try{return await t}catch(e){if(e instanceof AggregateError)throw console.error("bare-mux: failed to get a bare-mux SharedWorker MessagePort as all clients returned an invalid MessagePort."),new Error("All clients returned an invalid MessagePort.");return console.warn("bare-mux: failed to get a bare-mux SharedWorker MessagePort within 1s, retrying"),await l()}}function h(e){const t=new MessageChannel,s=new Promise(((e,s)=>{t.port1.onmessage=t=>{"pong"===t.data.type&&e()},setTimeout(s,1500)}));return e.postMessage({message:{type:"ping"},port:t.port2},[t.port2]),s}function p(e,t){const s=new a(e,"bare-mux-worker");return t&&i.addEventListener("message",(t=>{if("getPort"===t.data.type&&t.data.port){console.debug("bare-mux: recieved request for port from sw");const s=new a(e,"bare-mux-worker");t.data.port.postMessage(s.port,[s.port])}})),s.port}let d=null;function u(){if(null===d){const e=new MessageChannel,t=new ReadableStream;let s;try{e.port1.postMessage(t,[t]),s=!0}catch(e){s=!1}return d=s,s}return d}class w{constructor(e){this.channel=new BroadcastChannel("bare-mux"),e instanceof MessagePort||e instanceof Promise?this.port=e:this.createChannel(e,!0)}createChannel(e,t){if(self.clients)this.port=l(),this.channel.onmessage=e=>{"refreshPort"===e.data.type&&(this.port=l())};else if(e&&a){if(!e.startsWith("/")&&!e.includes("://"))throw new Error("Invalid URL. Must be absolute or start at the root.");this.port=p(e,t),console.debug("bare-mux: setting localStorage bare-mux-path to",e),n["bare-mux-path"]=e}else{if(!a)throw new Error("Unable to get a channel to the SharedWorker.");{const e=n["bare-mux-path"];if(console.debug("bare-mux: got localStorage bare-mux-path:",e),!e)throw new Error("Unable to get bare-mux workerPath from localStorage.");this.port=p(e,t)}}}async sendMessage(e,t){this.port instanceof Promise&&(this.port=await this.port);try{await h(this.port)}catch{return console.warn("bare-mux: Failed to get a ping response from the worker within 1.5s. Assuming port is dead."),this.createChannel(),await this.sendMessage(e,t)}const s=new MessageChannel,o=[s.port2,...t||[]],r=new Promise(((e,t)=>{s.port1.onmessage=s=>{const o=s.data;"error"===o.type?t(o.error):e(o)}}));return this.port.postMessage({message:e,port:s.port2},o),await r}}class g extends EventTarget{constructor(e,t=[],s,o,r){super(),this.protocols=[],this.readyState=c.CONNECTING,this.binaryType="blob",this.onopen=null,this.onerror=null,this.onmessage=null,this.onclose=null,this.url=e.toString(),this.protocols=t;const a=e=>{this.readyState=c.OPEN,this.protocols=e,this.meta={headers:{"sec-websocket-protocol":e}};const t=new Event("open");this.dispatchEvent(t),this.onopen&&this.onopen(t)},n=async e=>{"string"==typeof e||("byteLength"in e?"blob"===this.binaryType?e=new Blob([e]):Object.setPrototypeOf(e,r):"arrayBuffer"in e&&"arraybuffer"===this.binaryType&&(e=await e.arrayBuffer(),Object.setPrototypeOf(e,r)));const t=new MessageEvent("message",{data:e});this.dispatchEvent(t),this.onmessage&&this.onmessage(t)},i=(e,t)=>{this.readyState=c.CLOSED;const s=new CloseEvent("close",{code:e,reason:t});this.dispatchEvent(s),this.onclose&&this.onclose(s)},l=()=>{this.readyState=c.CLOSED;const e=new Event("error");this.dispatchEvent(e),this.onerror&&this.onerror(e)};this.channel=new MessageChannel,this.channel.port1.onmessage=e=>{"open"===e.data.type?a(e.data.args[0]):"message"===e.data.type?n(e.data.args[0]):"close"===e.data.type?i(e.data.args[0],e.data.args[1]):"error"===e.data.type&&l()},s.sendMessage({type:"websocket",websocket:{url:e.toString(),origin:origin,protocols:t,requestHeaders:o,channel:this.channel.port2}},[this.channel.port2])}send(...e){if(this.readyState===c.CONNECTING)throw new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.");let t=e[0];t.buffer&&(t=t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)),this.channel.port1.postMessage({type:"data",data:t},t instanceof ArrayBuffer?[t]:[])}close(e,t){this.readyState=c.CLOSING,this.channel.port1.postMessage({type:"close",closeCode:e,closeReason:t})}get bufferedAmount(){return 0}get protocol(){return Array.isArray(this.protocols)?this.protocols[0]||"":this.protocols||""}get extensions(){return""}}function f(e,t,s){console.error(`error while processing '${s}': `,t),e.postMessage({type:"error",error:t})}g.prototype.CONNECTING=c.CONNECTING,g.prototype.OPEN=c.OPEN,g.prototype.CLOSING=c.CLOSING,g.prototype.CLOSED=c.CLOSED;function y(e){for(let t=0;t<e.length;t++){const s=e[t];if(!"!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~".includes(s))return!1}return!0}const b=["ws:","wss:"],m=[101,204,205,304],M=[301,302,303,307,308];class C{constructor(e){this.worker=new w(e)}createWebSocket(e,t=[],o,r,a){try{e=new URL(e)}catch(t){throw new DOMException(`Faiiled to construct 'WebSocket': The URL '${e}' is invalid.`)}if(!b.includes(e.protocol))throw new DOMException(`Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss'. '${e.protocol}' is not allowed.`);Array.isArray(t)||(t=[t]),t=t.map(String);for(const e of t)if(!y(e))throw new DOMException(`Failed to construct 'WebSocket': The subprotocol '${e}' is invalid.`);a=a||(o||s).constructor.constructor("return ArrayBuffer")().prototype,(r=r||{}).Host=new URL(e).host,r.Pragma="no-cache",r["Cache-Control"]="no-cache",r.Upgrade="websocket",r.Connection="Upgrade";return new g(e,t,this.worker,r,a)}async fetch(e,s){const a=new o(e,s),n=s?.headers||a.headers,i=n instanceof Headers?Object.fromEntries(n):n,c=a.body;let l=new URL(a.url);if(l.protocol.startsWith("blob:")){const e=await t(l),s=new r(e.body,e);return s.rawHeaders=Object.fromEntries(e.headers),s.rawResponse=e,s}for(let e=0;;e++){"host"in i?i.host=l.host:i.Host=l.host;let t=(await this.worker.sendMessage({type:"fetch",fetch:{remote:l.toString(),method:a.method,headers:i,body:c||void 0}},c?[c]:[])).fetch,o=new r(m.includes(t.status)?void 0:t.body,{headers:new Headers(t.headers),status:t.status,statusText:t.statusText});o.rawHeaders=t.headers,o.rawResponse=new r(t.body),o.finalURL=l.toString();const n=s?.redirect||a.redirect;if(!M.includes(o.status))return o;switch(n){case"follow":{const t=o.headers.get("location");if(20>e&&null!==t){l=new URL(t,l);continue}throw new TypeError("Failed to fetch")}case"error":throw new TypeError("Failed to fetch");case"manual":return o}}}}e.BareClient=C,e.BareMuxConnection=class{constructor(e){this.worker=new w(e)}async getTransport(){return(await this.worker.sendMessage({type:"get"})).name}async setTransport(e,t,s){await this.setManualTransport(`\n\t\t\tconst { default: BareTransport } = await import("${e}");\n\t\t\treturn [BareTransport, "${e}"];\n\t\t`,t,s)}async setManualTransport(e,t,s){if("bare-mux-remote"===e)throw new Error("Use setRemoteTransport.");await this.worker.sendMessage({type:"set",client:{function:e,args:t}},s)}async setRemoteTransport(e,t){const s=new MessageChannel;s.port1.onmessage=async t=>{const s=t.data.port,o=t.data.message;if("fetch"===o.type)try{e.ready||await e.init(),await async function(e,t,s){const o=await s.request(new URL(e.fetch.remote),e.fetch.method,e.fetch.body,e.fetch.headers,null);if(!u()&&o.body instanceof ReadableStream){const e=new r(o.body);o.body=await e.arrayBuffer()}o.body instanceof ReadableStream||o.body instanceof ArrayBuffer?t.postMessage({type:"fetch",fetch:o},[o.body]):t.postMessage({type:"fetch",fetch:o})}(o,s,e)}catch(e){f(s,e,"fetch")}else if("websocket"===o.type)try{e.ready||await e.init(),await async function(e,t,s){const[o,r]=s.connect(new URL(e.websocket.url),e.websocket.origin,e.websocket.protocols,e.websocket.requestHeaders,(t=>{e.websocket.channel.postMessage({type:"open",args:[t]})}),(t=>{t instanceof ArrayBuffer?e.websocket.channel.postMessage({type:"message",args:[t]},[t]):e.websocket.channel.postMessage({type:"message",args:[t]})}),((t,s)=>{e.websocket.channel.postMessage({type:"close",args:[t,s]})}),(t=>{e.websocket.channel.postMessage({type:"error",args:[t]})}));e.websocket.channel.onmessage=e=>{"data"===e.data.type?o(e.data.data):"close"===e.data.type&&r(e.data.closeCode,e.data.closeReason)},t.postMessage({type:"websocket"})}(o,s,e)}catch(e){f(s,e,"websocket")}},await this.worker.sendMessage({type:"set",client:{function:"bare-mux-remote",args:[s.port2,t]}},[s.port2])}getInnerPort(){return this.worker.port}},e.WebSocketFields=c,e.WorkerConnection=w,e.browserSupportsTransferringStreams=u,e.default=C,e.maxRedirects=20,e.validProtocol=y,Object.defineProperty(e,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=index.js.map |
@@ -8,6 +8,6 @@ import type { WorkerConnection } from "./connection"; | ||
binaryType: string; | ||
onopen: (event: Event) => void; | ||
onerror: (event: any) => void; | ||
onmessage: (event: MessageEvent) => void; | ||
onclose: (event: CloseEvent) => void; | ||
onopen: any; | ||
onerror: any; | ||
onmessage: any; | ||
onclose: any; | ||
channel: MessageChannel; | ||
@@ -14,0 +14,0 @@ constructor(remote: string | URL, protocols: string | string[] | undefined, worker: WorkerConnection, requestHeaders?: BareHeaders, arrayBufferImpl?: ArrayBuffer); |
@@ -1,166 +0,2 @@ | ||
(function () { | ||
'use strict'; | ||
// The user likely has overwritten all networking functions after importing bare-client | ||
// It is our responsibility to make sure components of Bare-Client are using native networking functions | ||
const Response = globalThis.Response; | ||
let browserSupportsTransferringStreamsCache = null; | ||
function browserSupportsTransferringStreams() { | ||
if (browserSupportsTransferringStreamsCache === null) { | ||
const chan = new MessageChannel(); | ||
const stream = new ReadableStream(); | ||
let res; | ||
try { | ||
chan.port1.postMessage(stream, [stream]); | ||
res = true; | ||
} | ||
catch (err) { | ||
res = false; | ||
} | ||
browserSupportsTransferringStreamsCache = res; | ||
return res; | ||
} | ||
else { | ||
return browserSupportsTransferringStreamsCache; | ||
} | ||
} | ||
function sendError(port, err, name) { | ||
console.error(`error while processing '${name}': `, err); | ||
port.postMessage({ type: "error", error: err }); | ||
} | ||
async function handleFetch(message, port, transport) { | ||
const resp = await transport.request(new URL(message.fetch.remote), message.fetch.method, message.fetch.body, message.fetch.headers, null); | ||
if (!browserSupportsTransferringStreams() && resp.body instanceof ReadableStream) { | ||
const conversionResp = new Response(resp.body); | ||
resp.body = await conversionResp.arrayBuffer(); | ||
} | ||
if (resp.body instanceof ReadableStream || resp.body instanceof ArrayBuffer) { | ||
port.postMessage({ type: "fetch", fetch: resp }, [resp.body]); | ||
} | ||
else { | ||
port.postMessage({ type: "fetch", fetch: resp }); | ||
} | ||
} | ||
async function handleWebsocket(message, port, transport) { | ||
const onopen = (protocol) => { | ||
message.websocket.channel.postMessage({ type: "open", args: [protocol] }); | ||
}; | ||
const onclose = (code, reason) => { | ||
message.websocket.channel.postMessage({ type: "close", args: [code, reason] }); | ||
}; | ||
const onerror = (error) => { | ||
message.websocket.channel.postMessage({ type: "error", args: [error] }); | ||
}; | ||
const onmessage = (data) => { | ||
if (data instanceof ArrayBuffer) { | ||
message.websocket.channel.postMessage({ type: "message", args: [data] }, [data]); | ||
} | ||
else { | ||
message.websocket.channel.postMessage({ type: "message", args: [data] }); | ||
} | ||
}; | ||
const [data, close] = transport.connect(new URL(message.websocket.url), message.websocket.origin, message.websocket.protocols, message.websocket.requestHeaders, onopen, onmessage, onclose, onerror); | ||
message.websocket.channel.onmessage = (event) => { | ||
if (event.data.type === "data") { | ||
data(event.data.data); | ||
} | ||
else if (event.data.type === "close") { | ||
close(event.data.closeCode, event.data.closeReason); | ||
} | ||
}; | ||
port.postMessage({ type: "websocket" }); | ||
} | ||
let currentTransport = null; | ||
let currentTransportName = ""; | ||
const channel = new BroadcastChannel("bare-mux"); | ||
channel.postMessage({ type: "refreshPort" }); | ||
function noClients() { | ||
// @ts-expect-error mdn error constructor: new Error(message, options) | ||
return new Error("there are no bare clients", { | ||
cause: "No BareTransport was set. Try creating a BareMuxConnection and calling setTransport() or setManualTransport() on it before using BareClient." | ||
}); | ||
} | ||
function handleRemoteClient(message, port) { | ||
const remote = currentTransport; | ||
let transferables = [port]; | ||
if (message.fetch?.body) | ||
transferables.push(message.fetch.body); | ||
if (message.websocket?.channel) | ||
transferables.push(message.websocket.channel); | ||
remote.postMessage({ message, port }, transferables); | ||
} | ||
function handleConnection(port) { | ||
port.onmessage = async (event) => { | ||
const port = event.data.port; | ||
const message = event.data.message; | ||
if (message.type === "ping") { | ||
port.postMessage({ type: "pong" }); | ||
} | ||
else if (message.type === "set") { | ||
try { | ||
const AsyncFunction = (async function () { }).constructor; | ||
if (message.client.function === "bare-mux-remote") { | ||
currentTransport = message.client.args[0]; | ||
currentTransportName = `bare-mux-remote (${message.client.args[1]})`; | ||
} | ||
else { | ||
// @ts-expect-error | ||
const func = new AsyncFunction(message.client.function); | ||
const [newTransport, name] = await func(); | ||
currentTransport = new newTransport(...message.client.args); | ||
currentTransportName = name; | ||
} | ||
console.log("set transport to ", currentTransport, currentTransportName); | ||
port.postMessage({ type: "set" }); | ||
} | ||
catch (err) { | ||
sendError(port, err, 'set'); | ||
} | ||
} | ||
else if (message.type === "get") { | ||
port.postMessage({ type: "get", name: currentTransportName }); | ||
} | ||
else if (message.type === "fetch") { | ||
try { | ||
if (!currentTransport) | ||
throw noClients(); | ||
if (currentTransport instanceof MessagePort) { | ||
handleRemoteClient(message, port); | ||
return; | ||
} | ||
if (!currentTransport.ready) | ||
await currentTransport.init(); | ||
await handleFetch(message, port, currentTransport); | ||
} | ||
catch (err) { | ||
sendError(port, err, 'fetch'); | ||
} | ||
} | ||
else if (message.type === "websocket") { | ||
try { | ||
if (!currentTransport) | ||
throw noClients(); | ||
if (currentTransport instanceof MessagePort) { | ||
handleRemoteClient(message, port); | ||
return; | ||
} | ||
if (!currentTransport.ready) | ||
await currentTransport.init(); | ||
await handleWebsocket(message, port, currentTransport); | ||
} | ||
catch (err) { | ||
sendError(port, err, 'websocket'); | ||
} | ||
} | ||
}; | ||
} | ||
// @ts-expect-error | ||
self.onconnect = (event) => { | ||
handleConnection(event.ports[0]); | ||
}; | ||
})(); | ||
!function(){"use strict";const e=globalThis.Response;let t=null;function s(e,t,s){console.error(`error while processing '${s}': `,t),e.postMessage({type:"error",error:t})}async function a(s,a,n){const o=await n.request(new URL(s.fetch.remote),s.fetch.method,s.fetch.body,s.fetch.headers,null);if(!function(){if(null===t){const e=new MessageChannel,s=new ReadableStream;let a;try{e.port1.postMessage(s,[s]),a=!0}catch(e){a=!1}return t=a,a}return t}()&&o.body instanceof ReadableStream){const t=new e(o.body);o.body=await t.arrayBuffer()}o.body instanceof ReadableStream||o.body instanceof ArrayBuffer?a.postMessage({type:"fetch",fetch:o},[o.body]):a.postMessage({type:"fetch",fetch:o})}let n=null,o="";function r(){return new Error("there are no bare clients",{cause:"No BareTransport was set. Try creating a BareMuxConnection and calling setTransport() or setManualTransport() on it before using BareClient."})}function c(e,t){const s=n;let a=[t];e.fetch?.body&&a.push(e.fetch.body),e.websocket?.channel&&a.push(e.websocket.channel),s.postMessage({message:e,port:t},a)}function i(e){e.onmessage=async e=>{const t=e.data.port,i=e.data.message;if("ping"===i.type)t.postMessage({type:"pong"});else if("set"===i.type)try{const e=async function(){}.constructor;if("bare-mux-remote"===i.client.function)n=i.client.args[0],o=`bare-mux-remote (${i.client.args[1]})`;else{const t=new e(i.client.function),[s,a]=await t();n=new s(...i.client.args),o=a}console.log("set transport to ",n,o),t.postMessage({type:"set"})}catch(e){s(t,e,"set")}else if("get"===i.type)t.postMessage({type:"get",name:o});else if("fetch"===i.type)try{if(!n)throw r();if(n instanceof MessagePort)return void c(i,t);n.ready||await n.init(),await a(i,t,n)}catch(e){s(t,e,"fetch")}else if("websocket"===i.type)try{if(!n)throw r();if(n instanceof MessagePort)return void c(i,t);n.ready||await n.init(),await async function(e,t,s){const[a,n]=s.connect(new URL(e.websocket.url),e.websocket.origin,e.websocket.protocols,e.websocket.requestHeaders,(t=>{e.websocket.channel.postMessage({type:"open",args:[t]})}),(t=>{t instanceof ArrayBuffer?e.websocket.channel.postMessage({type:"message",args:[t]},[t]):e.websocket.channel.postMessage({type:"message",args:[t]})}),((t,s)=>{e.websocket.channel.postMessage({type:"close",args:[t,s]})}),(t=>{e.websocket.channel.postMessage({type:"error",args:[t]})}));e.websocket.channel.onmessage=e=>{"data"===e.data.type?a(e.data.data):"close"===e.data.type&&n(e.data.closeCode,e.data.closeReason)},t.postMessage({type:"websocket"})}(i,t,n)}catch(e){s(t,e,"websocket")}}}new BroadcastChannel("bare-mux").postMessage({type:"refreshPort"}),self.onconnect=e=>{i(e.ports[0])}}(); | ||
//# sourceMappingURL=worker.js.map |
{ | ||
"name": "@mercuryworkshop/bare-mux", | ||
"version": "2.0.8", | ||
"version": "2.0.9", | ||
"description": "", | ||
@@ -24,2 +24,3 @@ "type": "module", | ||
"devDependencies": { | ||
"@rollup/plugin-terser": "^0.4.4", | ||
"@rollup/plugin-inject": "^5.0.5", | ||
@@ -26,0 +27,0 @@ "@rollup/plugin-replace": "^5.0.5", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
1
33601
5
270