@ricsam/isolate-fetch 0.1.13 → 0.1.14
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/README.md +131 -4
- package/dist/cjs/consistency/origins.cjs +2 -2
- package/dist/cjs/consistency/origins.cjs.map +3 -3
- package/dist/cjs/index.cjs +443 -14
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/consistency/origins.mjs +2 -2
- package/dist/mjs/consistency/origins.mjs.map +3 -3
- package/dist/mjs/index.mjs +443 -14
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/index.d.ts +29 -3
- package/dist/types/isolate.d.ts +233 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -124,25 +124,32 @@ var headersCode = `
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
forEach(callback, thisArg) {
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
128
|
+
for (const key of sortedKeys) {
|
|
129
|
+
const [, values] = this.#headers.get(key);
|
|
130
|
+
callback.call(thisArg, values.join(', '), key, this);
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
*entries() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
136
|
+
for (const key of sortedKeys) {
|
|
137
|
+
const [, values] = this.#headers.get(key);
|
|
138
|
+
yield [key, values.join(', ')];
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
*keys() {
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
144
|
+
for (const key of sortedKeys) {
|
|
145
|
+
yield key;
|
|
141
146
|
}
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
*values() {
|
|
145
|
-
|
|
150
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
151
|
+
for (const key of sortedKeys) {
|
|
152
|
+
const [, values] = this.#headers.get(key);
|
|
146
153
|
yield values.join(', ');
|
|
147
154
|
}
|
|
148
155
|
}
|
|
@@ -1489,14 +1496,20 @@ function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
|
1489
1496
|
fetchAbortControllers.set(fetchId, hostController);
|
|
1490
1497
|
const headers = JSON.parse(headersJson);
|
|
1491
1498
|
const bodyBytes = bodyJson ? JSON.parse(bodyJson) : null;
|
|
1492
|
-
const
|
|
1493
|
-
const
|
|
1499
|
+
const rawBody = bodyBytes ? new Uint8Array(bodyBytes) : null;
|
|
1500
|
+
const init = {
|
|
1494
1501
|
method,
|
|
1495
1502
|
headers,
|
|
1496
|
-
|
|
1503
|
+
rawBody,
|
|
1504
|
+
body: rawBody,
|
|
1497
1505
|
signal: hostController.signal
|
|
1498
|
-
}
|
|
1499
|
-
const onFetch = options?.onFetch ?? fetch
|
|
1506
|
+
};
|
|
1507
|
+
const onFetch = options?.onFetch ?? ((url2, init2) => fetch(url2, {
|
|
1508
|
+
method: init2.method,
|
|
1509
|
+
headers: init2.headers,
|
|
1510
|
+
body: init2.body,
|
|
1511
|
+
signal: init2.signal
|
|
1512
|
+
}));
|
|
1500
1513
|
try {
|
|
1501
1514
|
let cleanupAbort;
|
|
1502
1515
|
const abortPromise = new Promise((_, reject) => {
|
|
@@ -1511,7 +1524,7 @@ function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
|
1511
1524
|
cleanupAbort = () => hostController.signal.removeEventListener("abort", onAbort);
|
|
1512
1525
|
});
|
|
1513
1526
|
abortPromise.catch(() => {});
|
|
1514
|
-
const nativeResponse = await Promise.race([onFetch(
|
|
1527
|
+
const nativeResponse = await Promise.race([onFetch(url, init), abortPromise]);
|
|
1515
1528
|
cleanupAbort?.();
|
|
1516
1529
|
const status = nativeResponse.status;
|
|
1517
1530
|
const isNullBody = status === 204 || status === 304 || method.toUpperCase() === "HEAD";
|
|
@@ -1731,6 +1744,373 @@ function setupServerWebSocket(context, wsCommandCallbacks) {
|
|
|
1731
1744
|
})();
|
|
1732
1745
|
`);
|
|
1733
1746
|
}
|
|
1747
|
+
function setupClientWebSocket(context, clientWsCommandCallbacks) {
|
|
1748
|
+
const global = context.global;
|
|
1749
|
+
global.setSync("__WebSocket_connect", new import_isolated_vm.default.Callback((socketId, url, protocols) => {
|
|
1750
|
+
const cmd = {
|
|
1751
|
+
type: "connect",
|
|
1752
|
+
socketId,
|
|
1753
|
+
url,
|
|
1754
|
+
protocols
|
|
1755
|
+
};
|
|
1756
|
+
for (const cb of clientWsCommandCallbacks)
|
|
1757
|
+
cb(cmd);
|
|
1758
|
+
}));
|
|
1759
|
+
global.setSync("__WebSocket_send", new import_isolated_vm.default.Callback((socketId, data) => {
|
|
1760
|
+
const cmd = { type: "send", socketId, data };
|
|
1761
|
+
for (const cb of clientWsCommandCallbacks)
|
|
1762
|
+
cb(cmd);
|
|
1763
|
+
}));
|
|
1764
|
+
global.setSync("__WebSocket_close", new import_isolated_vm.default.Callback((socketId, code, reason) => {
|
|
1765
|
+
const cmd = { type: "close", socketId, code, reason };
|
|
1766
|
+
for (const cb of clientWsCommandCallbacks)
|
|
1767
|
+
cb(cmd);
|
|
1768
|
+
}));
|
|
1769
|
+
context.evalSync(`
|
|
1770
|
+
(function() {
|
|
1771
|
+
// Socket ID counter
|
|
1772
|
+
let __nextSocketId = 1;
|
|
1773
|
+
|
|
1774
|
+
// Active sockets registry
|
|
1775
|
+
const __clientWebSockets = new Map();
|
|
1776
|
+
|
|
1777
|
+
// Simple Event class (if not defined globally)
|
|
1778
|
+
const _Event = globalThis.Event || class Event {
|
|
1779
|
+
constructor(type, options = {}) {
|
|
1780
|
+
this.type = type;
|
|
1781
|
+
this.bubbles = options.bubbles || false;
|
|
1782
|
+
this.cancelable = options.cancelable || false;
|
|
1783
|
+
this.defaultPrevented = false;
|
|
1784
|
+
this.timeStamp = Date.now();
|
|
1785
|
+
this.target = null;
|
|
1786
|
+
this.currentTarget = null;
|
|
1787
|
+
}
|
|
1788
|
+
preventDefault() {
|
|
1789
|
+
if (this.cancelable) this.defaultPrevented = true;
|
|
1790
|
+
}
|
|
1791
|
+
stopPropagation() {}
|
|
1792
|
+
stopImmediatePropagation() {}
|
|
1793
|
+
};
|
|
1794
|
+
|
|
1795
|
+
// MessageEvent class for WebSocket messages
|
|
1796
|
+
const _MessageEvent = globalThis.MessageEvent || class MessageEvent extends _Event {
|
|
1797
|
+
constructor(type, options = {}) {
|
|
1798
|
+
super(type, options);
|
|
1799
|
+
this.data = options.data !== undefined ? options.data : null;
|
|
1800
|
+
this.origin = options.origin || '';
|
|
1801
|
+
this.lastEventId = options.lastEventId || '';
|
|
1802
|
+
this.source = options.source || null;
|
|
1803
|
+
this.ports = options.ports || [];
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
|
|
1807
|
+
// CloseEvent class for WebSocket close
|
|
1808
|
+
const _CloseEvent = globalThis.CloseEvent || class CloseEvent extends _Event {
|
|
1809
|
+
constructor(type, options = {}) {
|
|
1810
|
+
super(type, options);
|
|
1811
|
+
this.code = options.code !== undefined ? options.code : 0;
|
|
1812
|
+
this.reason = options.reason !== undefined ? options.reason : '';
|
|
1813
|
+
this.wasClean = options.wasClean !== undefined ? options.wasClean : false;
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
|
|
1817
|
+
// Helper to dispatch events
|
|
1818
|
+
function dispatchEvent(ws, event) {
|
|
1819
|
+
const listeners = ws._listeners.get(event.type) || [];
|
|
1820
|
+
for (const listener of listeners) {
|
|
1821
|
+
try {
|
|
1822
|
+
listener.call(ws, event);
|
|
1823
|
+
} catch (e) {
|
|
1824
|
+
console.error('WebSocket event listener error:', e);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
// Also call on* handler if set
|
|
1828
|
+
const handler = ws['on' + event.type];
|
|
1829
|
+
if (typeof handler === 'function') {
|
|
1830
|
+
try {
|
|
1831
|
+
handler.call(ws, event);
|
|
1832
|
+
} catch (e) {
|
|
1833
|
+
console.error('WebSocket handler error:', e);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
class WebSocket {
|
|
1839
|
+
static CONNECTING = 0;
|
|
1840
|
+
static OPEN = 1;
|
|
1841
|
+
static CLOSING = 2;
|
|
1842
|
+
static CLOSED = 3;
|
|
1843
|
+
|
|
1844
|
+
#socketId;
|
|
1845
|
+
#url;
|
|
1846
|
+
#readyState = WebSocket.CONNECTING;
|
|
1847
|
+
#bufferedAmount = 0;
|
|
1848
|
+
#extensions = '';
|
|
1849
|
+
#protocol = '';
|
|
1850
|
+
#binaryType = 'blob';
|
|
1851
|
+
_listeners = new Map();
|
|
1852
|
+
|
|
1853
|
+
// Event handlers
|
|
1854
|
+
onopen = null;
|
|
1855
|
+
onmessage = null;
|
|
1856
|
+
onerror = null;
|
|
1857
|
+
onclose = null;
|
|
1858
|
+
|
|
1859
|
+
constructor(url, protocols) {
|
|
1860
|
+
// Validate URL
|
|
1861
|
+
let parsedUrl;
|
|
1862
|
+
try {
|
|
1863
|
+
parsedUrl = new URL(url);
|
|
1864
|
+
} catch (e) {
|
|
1865
|
+
throw new DOMException("Failed to construct 'WebSocket': The URL '" + url + "' is invalid.", 'SyntaxError');
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
if (parsedUrl.protocol !== 'ws:' && parsedUrl.protocol !== 'wss:') {
|
|
1869
|
+
throw new DOMException("Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss'.", 'SyntaxError');
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// Per WHATWG spec, fragments must be stripped from WebSocket URLs
|
|
1873
|
+
parsedUrl.hash = '';
|
|
1874
|
+
this.#url = parsedUrl.href;
|
|
1875
|
+
this.#socketId = String(__nextSocketId++);
|
|
1876
|
+
|
|
1877
|
+
// Normalize protocols to array
|
|
1878
|
+
let protocolArray = [];
|
|
1879
|
+
if (protocols !== undefined) {
|
|
1880
|
+
if (typeof protocols === 'string') {
|
|
1881
|
+
protocolArray = [protocols];
|
|
1882
|
+
} else if (Array.isArray(protocols)) {
|
|
1883
|
+
protocolArray = protocols;
|
|
1884
|
+
} else {
|
|
1885
|
+
protocolArray = [String(protocols)];
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// Check for duplicate protocols
|
|
1890
|
+
const seen = new Set();
|
|
1891
|
+
for (const p of protocolArray) {
|
|
1892
|
+
if (seen.has(p)) {
|
|
1893
|
+
throw new DOMException("Failed to construct 'WebSocket': The subprotocol '" + p + "' is duplicated.", 'SyntaxError');
|
|
1894
|
+
}
|
|
1895
|
+
seen.add(p);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// Register socket
|
|
1899
|
+
__clientWebSockets.set(this.#socketId, this);
|
|
1900
|
+
|
|
1901
|
+
// Call host to create connection
|
|
1902
|
+
__WebSocket_connect(this.#socketId, this.#url, protocolArray);
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
get url() {
|
|
1906
|
+
return this.#url;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
get readyState() {
|
|
1910
|
+
return this.#readyState;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
get bufferedAmount() {
|
|
1914
|
+
return this.#bufferedAmount;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
get extensions() {
|
|
1918
|
+
return this.#extensions;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
get protocol() {
|
|
1922
|
+
return this.#protocol;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
get binaryType() {
|
|
1926
|
+
return this.#binaryType;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
set binaryType(value) {
|
|
1930
|
+
if (value !== 'blob' && value !== 'arraybuffer') {
|
|
1931
|
+
throw new DOMException("Failed to set the 'binaryType' property: '" + value + "' is not a valid value.", 'SyntaxError');
|
|
1932
|
+
}
|
|
1933
|
+
this.#binaryType = value;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// ReadyState constants
|
|
1937
|
+
get CONNECTING() { return WebSocket.CONNECTING; }
|
|
1938
|
+
get OPEN() { return WebSocket.OPEN; }
|
|
1939
|
+
get CLOSING() { return WebSocket.CLOSING; }
|
|
1940
|
+
get CLOSED() { return WebSocket.CLOSED; }
|
|
1941
|
+
|
|
1942
|
+
send(data) {
|
|
1943
|
+
if (this.#readyState === WebSocket.CONNECTING) {
|
|
1944
|
+
throw new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.", 'InvalidStateError');
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
if (this.#readyState !== WebSocket.OPEN) {
|
|
1948
|
+
// Silently discard if not open (per spec)
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// Convert data to string for transfer
|
|
1953
|
+
let dataStr;
|
|
1954
|
+
if (typeof data === 'string') {
|
|
1955
|
+
dataStr = data;
|
|
1956
|
+
} else if (data instanceof ArrayBuffer) {
|
|
1957
|
+
// Convert ArrayBuffer to base64 for transfer
|
|
1958
|
+
const bytes = new Uint8Array(data);
|
|
1959
|
+
let binary = '';
|
|
1960
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
1961
|
+
binary += String.fromCharCode(bytes[i]);
|
|
1962
|
+
}
|
|
1963
|
+
dataStr = '__BINARY__' + btoa(binary);
|
|
1964
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
1965
|
+
const bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
1966
|
+
let binary = '';
|
|
1967
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
1968
|
+
binary += String.fromCharCode(bytes[i]);
|
|
1969
|
+
}
|
|
1970
|
+
dataStr = '__BINARY__' + btoa(binary);
|
|
1971
|
+
} else if (data instanceof Blob) {
|
|
1972
|
+
// Blob.arrayBuffer() is async, but send() is sync
|
|
1973
|
+
// For now, throw - this is a limitation
|
|
1974
|
+
throw new DOMException("Failed to execute 'send' on 'WebSocket': Blob data is not supported in this environment.", 'NotSupportedError');
|
|
1975
|
+
} else {
|
|
1976
|
+
dataStr = String(data);
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
__WebSocket_send(this.#socketId, dataStr);
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
close(code, reason) {
|
|
1983
|
+
if (code !== undefined) {
|
|
1984
|
+
if (typeof code !== 'number' || code !== Math.floor(code)) {
|
|
1985
|
+
throw new DOMException("Failed to execute 'close' on 'WebSocket': The code must be an integer.", 'InvalidAccessError');
|
|
1986
|
+
}
|
|
1987
|
+
if (code !== 1000 && (code < 3000 || code > 4999)) {
|
|
1988
|
+
throw new DOMException("Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999.", 'InvalidAccessError');
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
if (reason !== undefined) {
|
|
1993
|
+
const encoder = new TextEncoder();
|
|
1994
|
+
if (encoder.encode(reason).byteLength > 123) {
|
|
1995
|
+
throw new DOMException("Failed to execute 'close' on 'WebSocket': The message must not be greater than 123 bytes.", 'SyntaxError');
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
if (this.#readyState === WebSocket.CLOSING || this.#readyState === WebSocket.CLOSED) {
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
this.#readyState = WebSocket.CLOSING;
|
|
2004
|
+
__WebSocket_close(this.#socketId, code ?? 1000, reason ?? '');
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
// EventTarget interface
|
|
2008
|
+
addEventListener(type, listener, options) {
|
|
2009
|
+
if (typeof listener !== 'function') return;
|
|
2010
|
+
let listeners = this._listeners.get(type);
|
|
2011
|
+
if (!listeners) {
|
|
2012
|
+
listeners = [];
|
|
2013
|
+
this._listeners.set(type, listeners);
|
|
2014
|
+
}
|
|
2015
|
+
if (!listeners.includes(listener)) {
|
|
2016
|
+
listeners.push(listener);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
removeEventListener(type, listener, options) {
|
|
2021
|
+
const listeners = this._listeners.get(type);
|
|
2022
|
+
if (!listeners) return;
|
|
2023
|
+
const index = listeners.indexOf(listener);
|
|
2024
|
+
if (index !== -1) {
|
|
2025
|
+
listeners.splice(index, 1);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
dispatchEvent(event) {
|
|
2030
|
+
dispatchEvent(this, event);
|
|
2031
|
+
return !event.defaultPrevented;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
// Internal methods called from host
|
|
2035
|
+
_setProtocol(protocol) {
|
|
2036
|
+
this.#protocol = protocol;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
_setExtensions(extensions) {
|
|
2040
|
+
this.#extensions = extensions;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
_setReadyState(state) {
|
|
2044
|
+
this.#readyState = state;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
_dispatchOpen() {
|
|
2048
|
+
this.#readyState = WebSocket.OPEN;
|
|
2049
|
+
const event = new _Event('open');
|
|
2050
|
+
dispatchEvent(this, event);
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
_dispatchMessage(data) {
|
|
2054
|
+
// Handle binary data
|
|
2055
|
+
let messageData = data;
|
|
2056
|
+
if (typeof data === 'string' && data.startsWith('__BINARY__')) {
|
|
2057
|
+
const base64 = data.slice(10);
|
|
2058
|
+
const binary = atob(base64);
|
|
2059
|
+
const bytes = new Uint8Array(binary.length);
|
|
2060
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2061
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2062
|
+
}
|
|
2063
|
+
if (this.#binaryType === 'arraybuffer') {
|
|
2064
|
+
messageData = bytes.buffer;
|
|
2065
|
+
} else {
|
|
2066
|
+
messageData = new Blob([bytes]);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
const event = new _MessageEvent('message', { data: messageData });
|
|
2071
|
+
dispatchEvent(this, event);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
_dispatchError() {
|
|
2075
|
+
const event = new _Event('error');
|
|
2076
|
+
dispatchEvent(this, event);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
_dispatchClose(code, reason, wasClean) {
|
|
2080
|
+
this.#readyState = WebSocket.CLOSED;
|
|
2081
|
+
const event = new _CloseEvent('close', { code, reason, wasClean });
|
|
2082
|
+
dispatchEvent(this, event);
|
|
2083
|
+
__clientWebSockets.delete(this.#socketId);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// Helper to dispatch events from host to a socket by ID
|
|
2088
|
+
globalThis.__dispatchClientWebSocketEvent = function(socketId, eventType, data) {
|
|
2089
|
+
const ws = __clientWebSockets.get(socketId);
|
|
2090
|
+
if (!ws) return;
|
|
2091
|
+
|
|
2092
|
+
switch (eventType) {
|
|
2093
|
+
case 'open':
|
|
2094
|
+
ws._setProtocol(data.protocol || '');
|
|
2095
|
+
ws._setExtensions(data.extensions || '');
|
|
2096
|
+
ws._dispatchOpen();
|
|
2097
|
+
break;
|
|
2098
|
+
case 'message':
|
|
2099
|
+
ws._dispatchMessage(data.data);
|
|
2100
|
+
break;
|
|
2101
|
+
case 'error':
|
|
2102
|
+
ws._dispatchError();
|
|
2103
|
+
break;
|
|
2104
|
+
case 'close':
|
|
2105
|
+
ws._dispatchClose(data.code, data.reason, data.wasClean);
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
|
|
2110
|
+
globalThis.WebSocket = WebSocket;
|
|
2111
|
+
})();
|
|
2112
|
+
`);
|
|
2113
|
+
}
|
|
1734
2114
|
function setupServe(context) {
|
|
1735
2115
|
context.evalSync(`
|
|
1736
2116
|
(function() {
|
|
@@ -1761,9 +2141,11 @@ async function setupFetch(context, options) {
|
|
|
1761
2141
|
activeConnections: new Map
|
|
1762
2142
|
};
|
|
1763
2143
|
const wsCommandCallbacks = new Set;
|
|
2144
|
+
const clientWsCommandCallbacks = new Set;
|
|
1764
2145
|
setupServer(context, serveState);
|
|
1765
2146
|
setupServerWebSocket(context, wsCommandCallbacks);
|
|
1766
2147
|
setupServe(context);
|
|
2148
|
+
setupClientWebSocket(context, clientWsCommandCallbacks);
|
|
1767
2149
|
return {
|
|
1768
2150
|
dispose() {
|
|
1769
2151
|
stateMap.clear();
|
|
@@ -2021,8 +2403,55 @@ async function setupFetch(context, options) {
|
|
|
2021
2403
|
},
|
|
2022
2404
|
hasActiveConnections() {
|
|
2023
2405
|
return serveState.activeConnections.size > 0;
|
|
2406
|
+
},
|
|
2407
|
+
dispatchClientWebSocketOpen(socketId, protocol, extensions) {
|
|
2408
|
+
const safeProtocol = protocol.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
2409
|
+
const safeExtensions = extensions.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
2410
|
+
context.evalSync(`
|
|
2411
|
+
__dispatchClientWebSocketEvent("${socketId}", "open", {
|
|
2412
|
+
protocol: "${safeProtocol}",
|
|
2413
|
+
extensions: "${safeExtensions}"
|
|
2414
|
+
});
|
|
2415
|
+
`);
|
|
2416
|
+
},
|
|
2417
|
+
dispatchClientWebSocketMessage(socketId, data) {
|
|
2418
|
+
if (typeof data === "string") {
|
|
2419
|
+
const safeData = data.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
2420
|
+
context.evalSync(`
|
|
2421
|
+
__dispatchClientWebSocketEvent("${socketId}", "message", { data: "${safeData}" });
|
|
2422
|
+
`);
|
|
2423
|
+
} else {
|
|
2424
|
+
const bytes = new Uint8Array(data);
|
|
2425
|
+
let binary = "";
|
|
2426
|
+
for (let i = 0;i < bytes.byteLength; i++) {
|
|
2427
|
+
binary += String.fromCharCode(bytes[i]);
|
|
2428
|
+
}
|
|
2429
|
+
const base64 = Buffer.from(binary, "binary").toString("base64");
|
|
2430
|
+
context.evalSync(`
|
|
2431
|
+
__dispatchClientWebSocketEvent("${socketId}", "message", { data: "__BINARY__${base64}" });
|
|
2432
|
+
`);
|
|
2433
|
+
}
|
|
2434
|
+
},
|
|
2435
|
+
dispatchClientWebSocketClose(socketId, code, reason, wasClean) {
|
|
2436
|
+
const safeReason = reason.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
|
|
2437
|
+
context.evalIgnored(`
|
|
2438
|
+
__dispatchClientWebSocketEvent("${socketId}", "close", {
|
|
2439
|
+
code: ${code},
|
|
2440
|
+
reason: "${safeReason}",
|
|
2441
|
+
wasClean: ${wasClean}
|
|
2442
|
+
});
|
|
2443
|
+
`);
|
|
2444
|
+
},
|
|
2445
|
+
dispatchClientWebSocketError(socketId) {
|
|
2446
|
+
context.evalIgnored(`
|
|
2447
|
+
__dispatchClientWebSocketEvent("${socketId}", "error", {});
|
|
2448
|
+
`);
|
|
2449
|
+
},
|
|
2450
|
+
onClientWebSocketCommand(callback) {
|
|
2451
|
+
clientWsCommandCallbacks.add(callback);
|
|
2452
|
+
return () => clientWsCommandCallbacks.delete(callback);
|
|
2024
2453
|
}
|
|
2025
2454
|
};
|
|
2026
2455
|
}
|
|
2027
2456
|
|
|
2028
|
-
//# debugId=
|
|
2457
|
+
//# debugId=7837D23CACB6E1A364756E2164756E21
|