chemicaljs 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/baremux/baretypes.d.ts +22 -0
- package/dist/baremux/client.d.ts +73 -0
- package/dist/baremux/connection.d.ts +43 -0
- package/dist/baremux/index.d.ts +9 -0
- package/dist/baremux/index.js +542 -0
- package/dist/baremux/index.js.map +1 -0
- package/dist/baremux/index.mjs +525 -0
- package/dist/baremux/index.mjs.map +1 -0
- package/dist/baremux/snapshot.d.ts +44 -0
- package/dist/baremux/worker.d.ts +1 -0
- package/dist/baremux/worker.js +166 -0
- package/dist/baremux/worker.js.map +1 -0
- package/dist/baremux/workerHandlers.d.ts +5 -0
- package/dist/chemical.js +268 -0
- package/dist/chemical.sw.js +38 -0
- package/dist/epoxy/index.js +1615 -0
- package/dist/epoxy/index.mjs +1562 -0
- package/dist/libcurl/index.cjs +6634 -0
- package/dist/libcurl/index.js +6634 -0
- package/dist/libcurl/index.mjs +6589 -0
- package/dist/scramjet/scramjet.bundle.js +7 -0
- package/dist/scramjet/scramjet.bundle.js.map +7 -0
- package/dist/scramjet/scramjet.client.js +1 -0
- package/dist/scramjet/scramjet.client.js.map +7 -0
- package/dist/scramjet/scramjet.codecs.js +3 -0
- package/dist/scramjet/scramjet.codecs.js.map +7 -0
- package/dist/scramjet/scramjet.config.js +9 -0
- package/dist/scramjet/scramjet.config.js.map +7 -0
- package/dist/scramjet/scramjet.worker.js +42 -0
- package/dist/scramjet/scramjet.worker.js.map +7 -0
- package/dist/uv/sw.js +24 -0
- package/dist/uv/uv.bundle.js +8 -0
- package/dist/uv/uv.bundle.js.map +7 -0
- package/dist/uv/uv.client.js +5 -0
- package/dist/uv/uv.client.js.map +7 -0
- package/dist/uv/uv.config.js +10 -0
- package/dist/uv/uv.handler.js +2 -0
- package/dist/uv/uv.handler.js.map +7 -0
- package/dist/uv/uv.sw.js +45 -0
- package/dist/uv/uv.sw.js.map +7 -0
- package/examples/example/package-lock.json +4 -4
- package/examples/example/package.json +1 -1
- package/examples/{example build/build.js → example-build/build.js} +3 -1
- package/examples/{example build/package-lock.json → example-build/package-lock.json} +6 -6
- package/examples/{example build/package.json → example-build/package.json} +1 -1
- package/examples/example-components/package-lock.json +4 -4
- package/examples/example-components/package.json +1 -1
- package/examples/example-styled/package-lock.json +4 -4
- package/examples/example-styled/package.json +1 -1
- package/examples/example-vite/package-lock.json +12 -12
- package/examples/example-vite/package.json +3 -3
- package/index.js +13 -13
- package/package.json +1 -1
- /package/examples/{example build → example-build}/README.md +0 -0
- /package/examples/{example build → example-build}/index.js +0 -0
- /package/examples/{example build → example-build}/public/index.html +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type BareHeaders = Record<string, string | string[]>;
|
|
2
|
+
export type BareMeta = {};
|
|
3
|
+
export type TransferrableResponse = {
|
|
4
|
+
body: ReadableStream | ArrayBuffer | Blob | string;
|
|
5
|
+
headers: BareHeaders;
|
|
6
|
+
status: number;
|
|
7
|
+
statusText: string;
|
|
8
|
+
};
|
|
9
|
+
export interface BareTransport {
|
|
10
|
+
init: () => Promise<void>;
|
|
11
|
+
ready: boolean;
|
|
12
|
+
connect: (url: URL, origin: string, protocols: string[], requestHeaders: BareHeaders, onopen: (protocol: string) => void, onmessage: (data: Blob | ArrayBuffer | string) => void, onclose: (code: number, reason: string) => void, onerror: (error: string) => void) => [(data: Blob | ArrayBuffer | string) => void, (code: number, reason: string) => void];
|
|
13
|
+
request: (remote: URL, method: string, body: BodyInit | null, headers: BareHeaders, signal: AbortSignal | undefined) => Promise<TransferrableResponse>;
|
|
14
|
+
meta: () => BareMeta;
|
|
15
|
+
}
|
|
16
|
+
export interface BareWebSocketMeta {
|
|
17
|
+
protocol: string;
|
|
18
|
+
setCookies: string[];
|
|
19
|
+
}
|
|
20
|
+
export type BareHTTPProtocol = 'blob:' | 'http:' | 'https:' | string;
|
|
21
|
+
export type BareWSProtocol = 'ws:' | 'wss:' | string;
|
|
22
|
+
export declare const maxRedirects = 20;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { BareHeaders, BareTransport } from './baretypes';
|
|
2
|
+
import { WorkerConnection } from './connection';
|
|
3
|
+
export declare function validProtocol(protocol: string): boolean;
|
|
4
|
+
export type WebSocketImpl = {
|
|
5
|
+
new (...args: ConstructorParameters<typeof WebSocket>): WebSocket;
|
|
6
|
+
};
|
|
7
|
+
export declare namespace BareWebSocket {
|
|
8
|
+
type GetReadyStateCallback = () => number;
|
|
9
|
+
type GetSendErrorCallback = () => Error | undefined;
|
|
10
|
+
type GetProtocolCallback = () => string;
|
|
11
|
+
type HeadersType = BareHeaders | Headers | undefined;
|
|
12
|
+
type HeadersProvider = BareHeaders | (() => BareHeaders | Promise<BareHeaders>);
|
|
13
|
+
interface Options {
|
|
14
|
+
/**
|
|
15
|
+
* A provider of request headers to pass to the remote.
|
|
16
|
+
* Usually one of `User-Agent`, `Origin`, and `Cookie`
|
|
17
|
+
* Can be just the headers object or an synchronous/asynchronous function that returns the headers object
|
|
18
|
+
*/
|
|
19
|
+
headers?: BareWebSocket.HeadersProvider;
|
|
20
|
+
/**
|
|
21
|
+
* A hook executed by this function with helper arguments for hooking the readyState property. If a hook isn't provided, bare-client will hook the property on the instance. Hooking it on an instance basis is good for small projects, but ideally the class should be hooked by the user of bare-client.
|
|
22
|
+
*/
|
|
23
|
+
readyStateHook?: ((socket: WebSocket, getReadyState: BareWebSocket.GetReadyStateCallback) => void) | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* A hook executed by this function with helper arguments for determining if the send function should throw an error. If a hook isn't provided, bare-client will hook the function on the instance.
|
|
26
|
+
*/
|
|
27
|
+
sendErrorHook?: ((socket: WebSocket, getSendError: BareWebSocket.GetSendErrorCallback) => void) | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* A hook executed by this function with the URL. If a hook isn't provided, bare-client will hook the URL.
|
|
30
|
+
*/
|
|
31
|
+
urlHook?: ((socket: WebSocket, url: URL) => void) | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* A hook executed by this function with a helper for getting the current fake protocol. If a hook isn't provided, bare-client will hook the protocol.
|
|
34
|
+
*/
|
|
35
|
+
protocolHook?: ((socket: WebSocket, getProtocol: BareWebSocket.GetProtocolCallback) => void) | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* A callback executed by this function with an array of cookies. This is called once the metadata from the server is received.
|
|
38
|
+
*/
|
|
39
|
+
setCookiesCallback?: ((setCookies: string[]) => void) | undefined;
|
|
40
|
+
webSocketImpl?: WebSocket;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A Response with additional properties.
|
|
45
|
+
*/
|
|
46
|
+
export interface BareResponse extends Response {
|
|
47
|
+
rawResponse: Response;
|
|
48
|
+
rawHeaders: BareHeaders;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A BareResponse with additional properties.
|
|
52
|
+
*/
|
|
53
|
+
export interface BareResponseFetch extends BareResponse {
|
|
54
|
+
finalURL: string;
|
|
55
|
+
}
|
|
56
|
+
export declare class BareMuxConnection {
|
|
57
|
+
worker: WorkerConnection;
|
|
58
|
+
constructor(workerPath: string);
|
|
59
|
+
getTransport(): Promise<string>;
|
|
60
|
+
setTransport(path: string, options: any[], transferables?: Transferable[]): Promise<void>;
|
|
61
|
+
setManualTransport(functionBody: string, options: any[], transferables?: Transferable[]): Promise<void>;
|
|
62
|
+
setRemoteTransport(transport: BareTransport, name: string): Promise<void>;
|
|
63
|
+
getInnerPort(): MessagePort | Promise<MessagePort>;
|
|
64
|
+
}
|
|
65
|
+
export declare class BareClient {
|
|
66
|
+
worker: WorkerConnection;
|
|
67
|
+
/**
|
|
68
|
+
* Create a BareClient. Calls to fetch and connect will wait for an implementation to be ready.
|
|
69
|
+
*/
|
|
70
|
+
constructor(worker?: string | MessagePort);
|
|
71
|
+
createWebSocket(remote: string | URL, protocols?: string | string[] | undefined, webSocketImpl?: WebSocketImpl, requestHeaders?: BareHeaders, arrayBufferImpl?: ArrayBuffer): WebSocket;
|
|
72
|
+
fetch(url: string | URL, init?: RequestInit): Promise<BareResponseFetch>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BareHeaders, TransferrableResponse } from "./baretypes";
|
|
2
|
+
export type WorkerMessage = {
|
|
3
|
+
type: "fetch" | "websocket" | "set" | "get" | "ping";
|
|
4
|
+
fetch?: {
|
|
5
|
+
remote: string;
|
|
6
|
+
method: string;
|
|
7
|
+
headers: BareHeaders;
|
|
8
|
+
body: ReadableStream | ArrayBuffer | undefined;
|
|
9
|
+
};
|
|
10
|
+
websocket?: {
|
|
11
|
+
url: string;
|
|
12
|
+
origin: string;
|
|
13
|
+
protocols: string[];
|
|
14
|
+
requestHeaders: BareHeaders;
|
|
15
|
+
channel: MessagePort;
|
|
16
|
+
};
|
|
17
|
+
client?: {
|
|
18
|
+
function: string;
|
|
19
|
+
args: any[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export type WorkerRequest = {
|
|
23
|
+
message: WorkerMessage;
|
|
24
|
+
port: MessagePort;
|
|
25
|
+
};
|
|
26
|
+
export type WorkerResponse = {
|
|
27
|
+
type: "fetch" | "websocket" | "set" | "get" | "pong" | "error";
|
|
28
|
+
fetch?: TransferrableResponse;
|
|
29
|
+
name?: string;
|
|
30
|
+
error?: Error;
|
|
31
|
+
};
|
|
32
|
+
export type BroadcastMessage = {
|
|
33
|
+
type: "refreshPort";
|
|
34
|
+
};
|
|
35
|
+
export declare function browserSupportsTransferringStreams(): boolean;
|
|
36
|
+
export declare class WorkerConnection {
|
|
37
|
+
channel: BroadcastChannel;
|
|
38
|
+
port: MessagePort | Promise<MessagePort>;
|
|
39
|
+
workerPath: string;
|
|
40
|
+
constructor(worker?: string | MessagePort);
|
|
41
|
+
createChannel(workerPath?: string, inInit?: boolean): void;
|
|
42
|
+
sendMessage(message: WorkerMessage, transferable?: Transferable[]): Promise<WorkerResponse>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './baretypes';
|
|
2
|
+
export * from './client';
|
|
3
|
+
export * from './connection';
|
|
4
|
+
export { BareClient as default } from './client';
|
|
5
|
+
export { WebSocketFields } from "./snapshot";
|
|
6
|
+
export type * from './baretypes';
|
|
7
|
+
export type * from './client';
|
|
8
|
+
export type * from './connection';
|
|
9
|
+
export type * from "./snapshot";
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BareMux = {}));
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
6
|
+
|
|
7
|
+
const maxRedirects = 20;
|
|
8
|
+
|
|
9
|
+
// The user likely has overwritten all networking functions after importing bare-client
|
|
10
|
+
// It is our responsibility to make sure components of Bare-Client are using native networking functions
|
|
11
|
+
const fetch = globalThis.fetch;
|
|
12
|
+
const WebSocket = globalThis.WebSocket;
|
|
13
|
+
const Request = globalThis.Request;
|
|
14
|
+
const Response = globalThis.Response;
|
|
15
|
+
const SharedWorker = globalThis.SharedWorker;
|
|
16
|
+
const localStorage = globalThis.localStorage;
|
|
17
|
+
const serviceWorker = globalThis.navigator.serviceWorker;
|
|
18
|
+
const WebSocketFields = {
|
|
19
|
+
prototype: {
|
|
20
|
+
send: WebSocket.prototype.send,
|
|
21
|
+
},
|
|
22
|
+
CLOSED: WebSocket.CLOSED,
|
|
23
|
+
CLOSING: WebSocket.CLOSING,
|
|
24
|
+
CONNECTING: WebSocket.CONNECTING,
|
|
25
|
+
OPEN: WebSocket.OPEN,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async function searchForPort() {
|
|
29
|
+
// @ts-expect-error
|
|
30
|
+
const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
|
|
31
|
+
const promises = clients.map(async (x) => {
|
|
32
|
+
const port = await tryGetPort(x);
|
|
33
|
+
await testPort(port);
|
|
34
|
+
return port;
|
|
35
|
+
});
|
|
36
|
+
const promise = Promise.race([Promise.any(promises), new Promise((_, reject) => setTimeout(reject, 1000, new TypeError("timeout")))]);
|
|
37
|
+
try {
|
|
38
|
+
return await promise;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err instanceof AggregateError) {
|
|
42
|
+
console.error("bare-mux: failed to get a bare-mux SharedWorker MessagePort as all clients returned an invalid MessagePort.");
|
|
43
|
+
throw new Error("All clients returned an invalid MessagePort.");
|
|
44
|
+
}
|
|
45
|
+
console.warn("bare-mux: failed to get a bare-mux SharedWorker MessagePort within 1s, retrying");
|
|
46
|
+
return await searchForPort();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function tryGetPort(client) {
|
|
50
|
+
let channel = new MessageChannel();
|
|
51
|
+
return new Promise(resolve => {
|
|
52
|
+
client.postMessage({ type: "getPort", port: channel.port2 }, [channel.port2]);
|
|
53
|
+
channel.port1.onmessage = event => {
|
|
54
|
+
resolve(event.data);
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function testPort(port) {
|
|
59
|
+
const pingChannel = new MessageChannel();
|
|
60
|
+
const pingPromise = new Promise((resolve, reject) => {
|
|
61
|
+
pingChannel.port1.onmessage = event => {
|
|
62
|
+
if (event.data.type === "pong") {
|
|
63
|
+
resolve();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
setTimeout(reject, 1500);
|
|
67
|
+
});
|
|
68
|
+
port.postMessage({ message: { type: "ping" }, port: pingChannel.port2 }, [pingChannel.port2]);
|
|
69
|
+
return pingPromise;
|
|
70
|
+
}
|
|
71
|
+
function createPort(path, registerHandlers) {
|
|
72
|
+
const worker = new SharedWorker(path, "bare-mux-worker");
|
|
73
|
+
if (registerHandlers) {
|
|
74
|
+
// @ts-expect-error we are using snapshot.ts
|
|
75
|
+
serviceWorker.addEventListener("message", (event) => {
|
|
76
|
+
if (event.data.type === "getPort" && event.data.port) {
|
|
77
|
+
console.debug("bare-mux: recieved request for port from sw");
|
|
78
|
+
const newWorker = new SharedWorker(path, "bare-mux-worker");
|
|
79
|
+
event.data.port.postMessage(newWorker.port, [newWorker.port]);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return worker.port;
|
|
84
|
+
}
|
|
85
|
+
let browserSupportsTransferringStreamsCache = null;
|
|
86
|
+
function browserSupportsTransferringStreams() {
|
|
87
|
+
if (browserSupportsTransferringStreamsCache === null) {
|
|
88
|
+
const chan = new MessageChannel();
|
|
89
|
+
const stream = new ReadableStream();
|
|
90
|
+
let res;
|
|
91
|
+
try {
|
|
92
|
+
chan.port1.postMessage(stream, [stream]);
|
|
93
|
+
res = true;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
res = false;
|
|
97
|
+
}
|
|
98
|
+
browserSupportsTransferringStreamsCache = res;
|
|
99
|
+
return res;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return browserSupportsTransferringStreamsCache;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
class WorkerConnection {
|
|
106
|
+
constructor(worker) {
|
|
107
|
+
this.channel = new BroadcastChannel("bare-mux");
|
|
108
|
+
if (worker instanceof MessagePort) {
|
|
109
|
+
this.port = worker;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.createChannel(worker, true);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
createChannel(workerPath, inInit) {
|
|
116
|
+
// @ts-expect-error
|
|
117
|
+
if (self.clients) {
|
|
118
|
+
// running in a ServiceWorker
|
|
119
|
+
// ask a window for the worker port, register for refreshPort
|
|
120
|
+
this.port = searchForPort();
|
|
121
|
+
this.channel.onmessage = (event) => {
|
|
122
|
+
if (event.data.type === "refreshPort") {
|
|
123
|
+
this.port = searchForPort();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
else if (workerPath && SharedWorker) {
|
|
128
|
+
// running in a window, was passed a workerPath
|
|
129
|
+
// create the SharedWorker and help other bare-mux clients get the workerPath
|
|
130
|
+
if (!workerPath.startsWith("/") && !workerPath.includes("://"))
|
|
131
|
+
throw new Error("Invalid URL. Must be absolute or start at the root.");
|
|
132
|
+
this.port = createPort(workerPath, inInit);
|
|
133
|
+
console.debug("bare-mux: setting localStorage bare-mux-path to", workerPath);
|
|
134
|
+
localStorage["bare-mux-path"] = workerPath;
|
|
135
|
+
}
|
|
136
|
+
else if (SharedWorker) {
|
|
137
|
+
// running in a window, was not passed a workerPath
|
|
138
|
+
// use sessionStorage for the workerPath
|
|
139
|
+
const path = localStorage["bare-mux-path"];
|
|
140
|
+
console.debug("bare-mux: got localStorage bare-mux-path:", path);
|
|
141
|
+
if (!path)
|
|
142
|
+
throw new Error("Unable to get bare-mux workerPath from localStorage.");
|
|
143
|
+
this.port = createPort(path, inInit);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// SharedWorker does not exist
|
|
147
|
+
throw new Error("Unable to get a channel to the SharedWorker.");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async sendMessage(message, transferable) {
|
|
151
|
+
if (this.port instanceof Promise)
|
|
152
|
+
this.port = await this.port;
|
|
153
|
+
try {
|
|
154
|
+
await testPort(this.port);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
console.warn("bare-mux: Failed to get a ping response from the worker within 1.5s. Assuming port is dead.");
|
|
158
|
+
this.createChannel();
|
|
159
|
+
return await this.sendMessage(message, transferable);
|
|
160
|
+
}
|
|
161
|
+
const channel = new MessageChannel();
|
|
162
|
+
const toTransfer = [channel.port2, ...(transferable || [])];
|
|
163
|
+
const promise = new Promise((resolve, reject) => {
|
|
164
|
+
channel.port1.onmessage = event => {
|
|
165
|
+
const message = event.data;
|
|
166
|
+
if (message.type === "error") {
|
|
167
|
+
reject(message.error);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
resolve(message);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
this.port.postMessage({ message: message, port: channel.port2 }, toTransfer);
|
|
175
|
+
return await promise;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function sendError(port, err, name) {
|
|
180
|
+
console.error(`error while processing '${name}': `, err);
|
|
181
|
+
port.postMessage({ type: "error", error: err });
|
|
182
|
+
}
|
|
183
|
+
async function handleFetch(message, port, transport) {
|
|
184
|
+
const resp = await transport.request(new URL(message.fetch.remote), message.fetch.method, message.fetch.body, message.fetch.headers, null);
|
|
185
|
+
if (!browserSupportsTransferringStreams() && resp.body instanceof ReadableStream) {
|
|
186
|
+
const conversionResp = new Response(resp.body);
|
|
187
|
+
resp.body = await conversionResp.arrayBuffer();
|
|
188
|
+
}
|
|
189
|
+
if (resp.body instanceof ReadableStream || resp.body instanceof ArrayBuffer) {
|
|
190
|
+
port.postMessage({ type: "fetch", fetch: resp }, [resp.body]);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
port.postMessage({ type: "fetch", fetch: resp });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function handleWebsocket(message, port, transport) {
|
|
197
|
+
const onopen = (protocol) => {
|
|
198
|
+
message.websocket.channel.postMessage({ type: "open", args: [protocol] });
|
|
199
|
+
};
|
|
200
|
+
const onclose = (code, reason) => {
|
|
201
|
+
message.websocket.channel.postMessage({ type: "close", args: [code, reason] });
|
|
202
|
+
};
|
|
203
|
+
const onerror = (error) => {
|
|
204
|
+
message.websocket.channel.postMessage({ type: "error", args: [error] });
|
|
205
|
+
};
|
|
206
|
+
const onmessage = (data) => {
|
|
207
|
+
if (data instanceof ArrayBuffer) {
|
|
208
|
+
message.websocket.channel.postMessage({ type: "message", args: [data] }, [data]);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
message.websocket.channel.postMessage({ type: "message", args: [data] });
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const [data, close] = transport.connect(new URL(message.websocket.url), message.websocket.origin, message.websocket.protocols, message.websocket.requestHeaders, onopen, onmessage, onclose, onerror);
|
|
215
|
+
message.websocket.channel.onmessage = (event) => {
|
|
216
|
+
if (event.data.type === "data") {
|
|
217
|
+
data(event.data.data);
|
|
218
|
+
}
|
|
219
|
+
else if (event.data.type === "close") {
|
|
220
|
+
close(event.data.closeCode, event.data.closeReason);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
port.postMessage({ type: "websocket" });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const validChars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
|
|
227
|
+
function validProtocol(protocol) {
|
|
228
|
+
for (let i = 0; i < protocol.length; i++) {
|
|
229
|
+
const char = protocol[i];
|
|
230
|
+
if (!validChars.includes(char)) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
const wsProtocols = ['ws:', 'wss:'];
|
|
237
|
+
const statusEmpty = [101, 204, 205, 304];
|
|
238
|
+
const statusRedirect = [301, 302, 303, 307, 308];
|
|
239
|
+
class BareMuxConnection {
|
|
240
|
+
constructor(workerPath) {
|
|
241
|
+
this.worker = new WorkerConnection(workerPath);
|
|
242
|
+
}
|
|
243
|
+
async getTransport() {
|
|
244
|
+
return (await this.worker.sendMessage({ type: "get" })).name;
|
|
245
|
+
}
|
|
246
|
+
async setTransport(path, options, transferables) {
|
|
247
|
+
await this.setManualTransport(`
|
|
248
|
+
const { default: BareTransport } = await import("${path}");
|
|
249
|
+
return [BareTransport, "${path}"];
|
|
250
|
+
`, options, transferables);
|
|
251
|
+
}
|
|
252
|
+
async setManualTransport(functionBody, options, transferables) {
|
|
253
|
+
if (functionBody === "bare-mux-remote")
|
|
254
|
+
throw new Error("Use setRemoteTransport.");
|
|
255
|
+
await this.worker.sendMessage({
|
|
256
|
+
type: "set",
|
|
257
|
+
client: {
|
|
258
|
+
function: functionBody,
|
|
259
|
+
args: options,
|
|
260
|
+
},
|
|
261
|
+
}, transferables);
|
|
262
|
+
}
|
|
263
|
+
async setRemoteTransport(transport, name) {
|
|
264
|
+
const channel = new MessageChannel();
|
|
265
|
+
channel.port1.onmessage = async (event) => {
|
|
266
|
+
const port = event.data.port;
|
|
267
|
+
const message = event.data.message;
|
|
268
|
+
if (message.type === "fetch") {
|
|
269
|
+
try {
|
|
270
|
+
if (!transport.ready)
|
|
271
|
+
await transport.init();
|
|
272
|
+
await handleFetch(message, port, transport);
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
sendError(port, err, "fetch");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else if (message.type === "websocket") {
|
|
279
|
+
try {
|
|
280
|
+
if (!transport.ready)
|
|
281
|
+
await transport.init();
|
|
282
|
+
await handleWebsocket(message, port, transport);
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
sendError(port, err, "websocket");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
await this.worker.sendMessage({
|
|
290
|
+
type: "set",
|
|
291
|
+
client: {
|
|
292
|
+
function: "bare-mux-remote",
|
|
293
|
+
args: [channel.port2, name]
|
|
294
|
+
},
|
|
295
|
+
}, [channel.port2]);
|
|
296
|
+
}
|
|
297
|
+
getInnerPort() {
|
|
298
|
+
return this.worker.port;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
class BareClient {
|
|
302
|
+
/**
|
|
303
|
+
* Create a BareClient. Calls to fetch and connect will wait for an implementation to be ready.
|
|
304
|
+
*/
|
|
305
|
+
constructor(worker) {
|
|
306
|
+
this.worker = new WorkerConnection(worker);
|
|
307
|
+
}
|
|
308
|
+
createWebSocket(remote, protocols = [], webSocketImpl, requestHeaders, arrayBufferImpl) {
|
|
309
|
+
try {
|
|
310
|
+
remote = new URL(remote);
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
throw new DOMException(`Faiiled to construct 'WebSocket': The URL '${remote}' is invalid.`);
|
|
314
|
+
}
|
|
315
|
+
if (!wsProtocols.includes(remote.protocol))
|
|
316
|
+
throw new DOMException(`Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss'. '${remote.protocol}' is not allowed.`);
|
|
317
|
+
if (!Array.isArray(protocols))
|
|
318
|
+
protocols = [protocols];
|
|
319
|
+
protocols = protocols.map(String);
|
|
320
|
+
for (const proto of protocols)
|
|
321
|
+
if (!validProtocol(proto))
|
|
322
|
+
throw new DOMException(`Failed to construct 'WebSocket': The subprotocol '${proto}' is invalid.`);
|
|
323
|
+
let wsImpl = (webSocketImpl || WebSocket);
|
|
324
|
+
const socket = new wsImpl("ws://127.0.0.1:1", protocols);
|
|
325
|
+
let fakeProtocol = '';
|
|
326
|
+
let fakeReadyState = WebSocketFields.CONNECTING;
|
|
327
|
+
let initialErrorHappened = false;
|
|
328
|
+
socket.addEventListener("error", (e) => {
|
|
329
|
+
if (!initialErrorHappened) {
|
|
330
|
+
fakeReadyState = WebSocket.CONNECTING;
|
|
331
|
+
e.stopImmediatePropagation();
|
|
332
|
+
initialErrorHappened = true;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
let initialCloseHappened = false;
|
|
336
|
+
socket.addEventListener("close", (e) => {
|
|
337
|
+
if (!initialCloseHappened) {
|
|
338
|
+
e.stopImmediatePropagation();
|
|
339
|
+
initialCloseHappened = true;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
// TODO socket onerror will be broken
|
|
343
|
+
arrayBufferImpl = arrayBufferImpl || wsImpl.constructor.constructor("return ArrayBuffer")().prototype;
|
|
344
|
+
requestHeaders = requestHeaders || {};
|
|
345
|
+
requestHeaders['Host'] = (new URL(remote)).host;
|
|
346
|
+
// requestHeaders['Origin'] = origin;
|
|
347
|
+
requestHeaders['Pragma'] = 'no-cache';
|
|
348
|
+
requestHeaders['Cache-Control'] = 'no-cache';
|
|
349
|
+
requestHeaders['Upgrade'] = 'websocket';
|
|
350
|
+
// requestHeaders['User-Agent'] = navigator.userAgent;
|
|
351
|
+
requestHeaders['Connection'] = 'Upgrade';
|
|
352
|
+
const onopen = (protocol) => {
|
|
353
|
+
fakeReadyState = WebSocketFields.OPEN;
|
|
354
|
+
fakeProtocol = protocol;
|
|
355
|
+
socket.meta = {
|
|
356
|
+
headers: {
|
|
357
|
+
"sec-websocket-protocol": protocol,
|
|
358
|
+
}
|
|
359
|
+
}; // what the fuck is a meta
|
|
360
|
+
socket.dispatchEvent(new Event("open"));
|
|
361
|
+
};
|
|
362
|
+
const onmessage = async (payload) => {
|
|
363
|
+
if (typeof payload === "string") {
|
|
364
|
+
socket.dispatchEvent(new MessageEvent("message", { data: payload }));
|
|
365
|
+
}
|
|
366
|
+
else if ("byteLength" in payload) {
|
|
367
|
+
if (socket.binaryType === "blob") {
|
|
368
|
+
payload = new Blob([payload]);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
Object.setPrototypeOf(payload, arrayBufferImpl);
|
|
372
|
+
}
|
|
373
|
+
socket.dispatchEvent(new MessageEvent("message", { data: payload }));
|
|
374
|
+
}
|
|
375
|
+
else if ("arrayBuffer" in payload) {
|
|
376
|
+
if (socket.binaryType === "arraybuffer") {
|
|
377
|
+
payload = await payload.arrayBuffer();
|
|
378
|
+
Object.setPrototypeOf(payload, arrayBufferImpl);
|
|
379
|
+
}
|
|
380
|
+
socket.dispatchEvent(new MessageEvent("message", { data: payload }));
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const onclose = (code, reason) => {
|
|
384
|
+
fakeReadyState = WebSocketFields.CLOSED;
|
|
385
|
+
socket.dispatchEvent(new CloseEvent("close", { code, reason }));
|
|
386
|
+
};
|
|
387
|
+
const onerror = () => {
|
|
388
|
+
fakeReadyState = WebSocketFields.CLOSED;
|
|
389
|
+
socket.dispatchEvent(new Event("error"));
|
|
390
|
+
};
|
|
391
|
+
const channel = new MessageChannel();
|
|
392
|
+
channel.port1.onmessage = event => {
|
|
393
|
+
if (event.data.type === "open") {
|
|
394
|
+
onopen(event.data.args[0]);
|
|
395
|
+
}
|
|
396
|
+
else if (event.data.type === "message") {
|
|
397
|
+
onmessage(event.data.args[0]);
|
|
398
|
+
}
|
|
399
|
+
else if (event.data.type === "close") {
|
|
400
|
+
onclose(event.data.args[0], event.data.args[1]);
|
|
401
|
+
}
|
|
402
|
+
else if (event.data.type === "error") {
|
|
403
|
+
onerror( /* event.data.args[0] */);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
this.worker.sendMessage({
|
|
407
|
+
type: "websocket",
|
|
408
|
+
websocket: {
|
|
409
|
+
url: remote.toString(),
|
|
410
|
+
origin: origin,
|
|
411
|
+
protocols: protocols,
|
|
412
|
+
requestHeaders: requestHeaders,
|
|
413
|
+
channel: channel.port2,
|
|
414
|
+
},
|
|
415
|
+
}, [channel.port2]);
|
|
416
|
+
// protocol is always an empty before connecting
|
|
417
|
+
// updated when we receive the metadata
|
|
418
|
+
// this value doesn't change when it's CLOSING or CLOSED etc
|
|
419
|
+
const getReadyState = () => fakeReadyState;
|
|
420
|
+
// we have to hook .readyState ourselves
|
|
421
|
+
Object.defineProperty(socket, 'readyState', {
|
|
422
|
+
get: getReadyState,
|
|
423
|
+
configurable: true,
|
|
424
|
+
enumerable: true,
|
|
425
|
+
});
|
|
426
|
+
/**
|
|
427
|
+
* @returns The error that should be thrown if send() were to be called on this socket according to the fake readyState value
|
|
428
|
+
*/
|
|
429
|
+
const getSendError = () => {
|
|
430
|
+
const readyState = getReadyState();
|
|
431
|
+
if (readyState === WebSocketFields.CONNECTING)
|
|
432
|
+
return new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.");
|
|
433
|
+
};
|
|
434
|
+
// we have to hook .send ourselves
|
|
435
|
+
// use ...args to avoid giving the number of args a quantity
|
|
436
|
+
// no arguments will trip the following error: TypeError: Failed to execute 'send' on 'WebSocket': 1 argument required, but only 0 present.
|
|
437
|
+
socket.send = function (...args) {
|
|
438
|
+
const error = getSendError();
|
|
439
|
+
if (error)
|
|
440
|
+
throw error;
|
|
441
|
+
let data = args[0];
|
|
442
|
+
// @ts-expect-error idk why it errors?
|
|
443
|
+
if (data.buffer)
|
|
444
|
+
data = data.buffer;
|
|
445
|
+
channel.port1.postMessage({ type: "data", data: data }, data instanceof ArrayBuffer ? [data] : []);
|
|
446
|
+
};
|
|
447
|
+
socket.close = function (code, reason) {
|
|
448
|
+
channel.port1.postMessage({ type: "close", closeCode: code, closeReason: reason });
|
|
449
|
+
};
|
|
450
|
+
Object.defineProperty(socket, 'url', {
|
|
451
|
+
get: () => remote.toString(),
|
|
452
|
+
configurable: true,
|
|
453
|
+
enumerable: true,
|
|
454
|
+
});
|
|
455
|
+
const getProtocol = () => fakeProtocol;
|
|
456
|
+
Object.defineProperty(socket, 'protocol', {
|
|
457
|
+
get: getProtocol,
|
|
458
|
+
configurable: true,
|
|
459
|
+
enumerable: true,
|
|
460
|
+
});
|
|
461
|
+
return socket;
|
|
462
|
+
}
|
|
463
|
+
async fetch(url, init) {
|
|
464
|
+
// Only create an instance of Request to parse certain parameters of init such as method, headers, redirect
|
|
465
|
+
// But use init values whenever possible
|
|
466
|
+
const req = new Request(url, init);
|
|
467
|
+
// try to use init.headers because it may contain capitalized headers
|
|
468
|
+
// furthermore, important headers on the Request class are blocked...
|
|
469
|
+
// we should try to preserve the capitalization due to quirks with earlier servers
|
|
470
|
+
const inputHeaders = init?.headers || req.headers;
|
|
471
|
+
const headers = inputHeaders instanceof Headers
|
|
472
|
+
? Object.fromEntries(inputHeaders)
|
|
473
|
+
: inputHeaders;
|
|
474
|
+
const body = req.body;
|
|
475
|
+
let urlO = new URL(req.url);
|
|
476
|
+
if (urlO.protocol.startsWith('blob:')) {
|
|
477
|
+
const response = await fetch(urlO);
|
|
478
|
+
const result = new Response(response.body, response);
|
|
479
|
+
result.rawHeaders = Object.fromEntries(response.headers);
|
|
480
|
+
result.rawResponse = response;
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
for (let i = 0;; i++) {
|
|
484
|
+
if ('host' in headers)
|
|
485
|
+
headers.host = urlO.host;
|
|
486
|
+
else
|
|
487
|
+
headers.Host = urlO.host;
|
|
488
|
+
let resp = (await this.worker.sendMessage({
|
|
489
|
+
type: "fetch",
|
|
490
|
+
fetch: {
|
|
491
|
+
remote: urlO.toString(),
|
|
492
|
+
method: req.method,
|
|
493
|
+
headers: headers,
|
|
494
|
+
body: body || undefined,
|
|
495
|
+
},
|
|
496
|
+
}, body ? [body] : [])).fetch;
|
|
497
|
+
let responseobj = new Response(statusEmpty.includes(resp.status) ? undefined : resp.body, {
|
|
498
|
+
headers: new Headers(resp.headers),
|
|
499
|
+
status: resp.status,
|
|
500
|
+
statusText: resp.statusText,
|
|
501
|
+
});
|
|
502
|
+
responseobj.rawHeaders = resp.headers;
|
|
503
|
+
responseobj.rawResponse = new Response(resp.body);
|
|
504
|
+
responseobj.finalURL = urlO.toString();
|
|
505
|
+
const redirect = init?.redirect || req.redirect;
|
|
506
|
+
if (statusRedirect.includes(responseobj.status)) {
|
|
507
|
+
switch (redirect) {
|
|
508
|
+
case 'follow': {
|
|
509
|
+
const location = responseobj.headers.get('location');
|
|
510
|
+
if (maxRedirects > i && location !== null) {
|
|
511
|
+
urlO = new URL(location, urlO);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
else
|
|
515
|
+
throw new TypeError('Failed to fetch');
|
|
516
|
+
}
|
|
517
|
+
case 'error':
|
|
518
|
+
throw new TypeError('Failed to fetch');
|
|
519
|
+
case 'manual':
|
|
520
|
+
return responseobj;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
return responseobj;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
exports.BareClient = BareClient;
|
|
531
|
+
exports.BareMuxConnection = BareMuxConnection;
|
|
532
|
+
exports.WebSocketFields = WebSocketFields;
|
|
533
|
+
exports.WorkerConnection = WorkerConnection;
|
|
534
|
+
exports.browserSupportsTransferringStreams = browserSupportsTransferringStreams;
|
|
535
|
+
exports.default = BareClient;
|
|
536
|
+
exports.maxRedirects = maxRedirects;
|
|
537
|
+
exports.validProtocol = validProtocol;
|
|
538
|
+
|
|
539
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
540
|
+
|
|
541
|
+
}));
|
|
542
|
+
//# sourceMappingURL=index.js.map
|