@ricsam/isolate-fetch 0.1.13 → 0.1.15
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 +488 -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 +488 -14
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/index.d.ts +33 -3
- package/dist/types/isolate.d.ts +233 -0
- package/package.json +1 -1
package/dist/mjs/index.mjs
CHANGED
|
@@ -80,25 +80,32 @@ var headersCode = `
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
forEach(callback, thisArg) {
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
84
|
+
for (const key of sortedKeys) {
|
|
85
|
+
const [, values] = this.#headers.get(key);
|
|
86
|
+
callback.call(thisArg, values.join(', '), key, this);
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
*entries() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
92
|
+
for (const key of sortedKeys) {
|
|
93
|
+
const [, values] = this.#headers.get(key);
|
|
94
|
+
yield [key, values.join(', ')];
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
*keys() {
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
100
|
+
for (const key of sortedKeys) {
|
|
101
|
+
yield key;
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
*values() {
|
|
101
|
-
|
|
106
|
+
const sortedKeys = [...this.#headers.keys()].sort();
|
|
107
|
+
for (const key of sortedKeys) {
|
|
108
|
+
const [, values] = this.#headers.get(key);
|
|
102
109
|
yield values.join(', ');
|
|
103
110
|
}
|
|
104
111
|
}
|
|
@@ -1445,14 +1452,20 @@ function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
|
1445
1452
|
fetchAbortControllers.set(fetchId, hostController);
|
|
1446
1453
|
const headers = JSON.parse(headersJson);
|
|
1447
1454
|
const bodyBytes = bodyJson ? JSON.parse(bodyJson) : null;
|
|
1448
|
-
const
|
|
1449
|
-
const
|
|
1455
|
+
const rawBody = bodyBytes ? new Uint8Array(bodyBytes) : null;
|
|
1456
|
+
const init = {
|
|
1450
1457
|
method,
|
|
1451
1458
|
headers,
|
|
1452
|
-
|
|
1459
|
+
rawBody,
|
|
1460
|
+
body: rawBody,
|
|
1453
1461
|
signal: hostController.signal
|
|
1454
|
-
}
|
|
1455
|
-
const onFetch = options?.onFetch ?? fetch
|
|
1462
|
+
};
|
|
1463
|
+
const onFetch = options?.onFetch ?? ((url2, init2) => fetch(url2, {
|
|
1464
|
+
method: init2.method,
|
|
1465
|
+
headers: init2.headers,
|
|
1466
|
+
body: init2.body,
|
|
1467
|
+
signal: init2.signal
|
|
1468
|
+
}));
|
|
1456
1469
|
try {
|
|
1457
1470
|
let cleanupAbort;
|
|
1458
1471
|
const abortPromise = new Promise((_, reject) => {
|
|
@@ -1467,7 +1480,7 @@ function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
|
1467
1480
|
cleanupAbort = () => hostController.signal.removeEventListener("abort", onAbort);
|
|
1468
1481
|
});
|
|
1469
1482
|
abortPromise.catch(() => {});
|
|
1470
|
-
const nativeResponse = await Promise.race([onFetch(
|
|
1483
|
+
const nativeResponse = await Promise.race([onFetch(url, init), abortPromise]);
|
|
1471
1484
|
cleanupAbort?.();
|
|
1472
1485
|
const status = nativeResponse.status;
|
|
1473
1486
|
const isNullBody = status === 204 || status === 304 || method.toUpperCase() === "HEAD";
|
|
@@ -1687,6 +1700,373 @@ function setupServerWebSocket(context, wsCommandCallbacks) {
|
|
|
1687
1700
|
})();
|
|
1688
1701
|
`);
|
|
1689
1702
|
}
|
|
1703
|
+
function setupClientWebSocket(context, clientWsCommandCallbacks) {
|
|
1704
|
+
const global = context.global;
|
|
1705
|
+
global.setSync("__WebSocket_connect", new ivm.Callback((socketId, url, protocols) => {
|
|
1706
|
+
const cmd = {
|
|
1707
|
+
type: "connect",
|
|
1708
|
+
socketId,
|
|
1709
|
+
url,
|
|
1710
|
+
protocols
|
|
1711
|
+
};
|
|
1712
|
+
for (const cb of clientWsCommandCallbacks)
|
|
1713
|
+
cb(cmd);
|
|
1714
|
+
}));
|
|
1715
|
+
global.setSync("__WebSocket_send", new ivm.Callback((socketId, data) => {
|
|
1716
|
+
const cmd = { type: "send", socketId, data };
|
|
1717
|
+
for (const cb of clientWsCommandCallbacks)
|
|
1718
|
+
cb(cmd);
|
|
1719
|
+
}));
|
|
1720
|
+
global.setSync("__WebSocket_close", new ivm.Callback((socketId, code, reason) => {
|
|
1721
|
+
const cmd = { type: "close", socketId, code, reason };
|
|
1722
|
+
for (const cb of clientWsCommandCallbacks)
|
|
1723
|
+
cb(cmd);
|
|
1724
|
+
}));
|
|
1725
|
+
context.evalSync(`
|
|
1726
|
+
(function() {
|
|
1727
|
+
// Socket ID counter
|
|
1728
|
+
let __nextSocketId = 1;
|
|
1729
|
+
|
|
1730
|
+
// Active sockets registry
|
|
1731
|
+
const __clientWebSockets = new Map();
|
|
1732
|
+
|
|
1733
|
+
// Simple Event class (if not defined globally)
|
|
1734
|
+
const _Event = globalThis.Event || class Event {
|
|
1735
|
+
constructor(type, options = {}) {
|
|
1736
|
+
this.type = type;
|
|
1737
|
+
this.bubbles = options.bubbles || false;
|
|
1738
|
+
this.cancelable = options.cancelable || false;
|
|
1739
|
+
this.defaultPrevented = false;
|
|
1740
|
+
this.timeStamp = Date.now();
|
|
1741
|
+
this.target = null;
|
|
1742
|
+
this.currentTarget = null;
|
|
1743
|
+
}
|
|
1744
|
+
preventDefault() {
|
|
1745
|
+
if (this.cancelable) this.defaultPrevented = true;
|
|
1746
|
+
}
|
|
1747
|
+
stopPropagation() {}
|
|
1748
|
+
stopImmediatePropagation() {}
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
// MessageEvent class for WebSocket messages
|
|
1752
|
+
const _MessageEvent = globalThis.MessageEvent || class MessageEvent extends _Event {
|
|
1753
|
+
constructor(type, options = {}) {
|
|
1754
|
+
super(type, options);
|
|
1755
|
+
this.data = options.data !== undefined ? options.data : null;
|
|
1756
|
+
this.origin = options.origin || '';
|
|
1757
|
+
this.lastEventId = options.lastEventId || '';
|
|
1758
|
+
this.source = options.source || null;
|
|
1759
|
+
this.ports = options.ports || [];
|
|
1760
|
+
}
|
|
1761
|
+
};
|
|
1762
|
+
|
|
1763
|
+
// CloseEvent class for WebSocket close
|
|
1764
|
+
const _CloseEvent = globalThis.CloseEvent || class CloseEvent extends _Event {
|
|
1765
|
+
constructor(type, options = {}) {
|
|
1766
|
+
super(type, options);
|
|
1767
|
+
this.code = options.code !== undefined ? options.code : 0;
|
|
1768
|
+
this.reason = options.reason !== undefined ? options.reason : '';
|
|
1769
|
+
this.wasClean = options.wasClean !== undefined ? options.wasClean : false;
|
|
1770
|
+
}
|
|
1771
|
+
};
|
|
1772
|
+
|
|
1773
|
+
// Helper to dispatch events
|
|
1774
|
+
function dispatchEvent(ws, event) {
|
|
1775
|
+
const listeners = ws._listeners.get(event.type) || [];
|
|
1776
|
+
for (const listener of listeners) {
|
|
1777
|
+
try {
|
|
1778
|
+
listener.call(ws, event);
|
|
1779
|
+
} catch (e) {
|
|
1780
|
+
console.error('WebSocket event listener error:', e);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
// Also call on* handler if set
|
|
1784
|
+
const handler = ws['on' + event.type];
|
|
1785
|
+
if (typeof handler === 'function') {
|
|
1786
|
+
try {
|
|
1787
|
+
handler.call(ws, event);
|
|
1788
|
+
} catch (e) {
|
|
1789
|
+
console.error('WebSocket handler error:', e);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
class WebSocket {
|
|
1795
|
+
static CONNECTING = 0;
|
|
1796
|
+
static OPEN = 1;
|
|
1797
|
+
static CLOSING = 2;
|
|
1798
|
+
static CLOSED = 3;
|
|
1799
|
+
|
|
1800
|
+
#socketId;
|
|
1801
|
+
#url;
|
|
1802
|
+
#readyState = WebSocket.CONNECTING;
|
|
1803
|
+
#bufferedAmount = 0;
|
|
1804
|
+
#extensions = '';
|
|
1805
|
+
#protocol = '';
|
|
1806
|
+
#binaryType = 'blob';
|
|
1807
|
+
_listeners = new Map();
|
|
1808
|
+
|
|
1809
|
+
// Event handlers
|
|
1810
|
+
onopen = null;
|
|
1811
|
+
onmessage = null;
|
|
1812
|
+
onerror = null;
|
|
1813
|
+
onclose = null;
|
|
1814
|
+
|
|
1815
|
+
constructor(url, protocols) {
|
|
1816
|
+
// Validate URL
|
|
1817
|
+
let parsedUrl;
|
|
1818
|
+
try {
|
|
1819
|
+
parsedUrl = new URL(url);
|
|
1820
|
+
} catch (e) {
|
|
1821
|
+
throw new DOMException("Failed to construct 'WebSocket': The URL '" + url + "' is invalid.", 'SyntaxError');
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
if (parsedUrl.protocol !== 'ws:' && parsedUrl.protocol !== 'wss:') {
|
|
1825
|
+
throw new DOMException("Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss'.", 'SyntaxError');
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// Per WHATWG spec, fragments must be stripped from WebSocket URLs
|
|
1829
|
+
parsedUrl.hash = '';
|
|
1830
|
+
this.#url = parsedUrl.href;
|
|
1831
|
+
this.#socketId = String(__nextSocketId++);
|
|
1832
|
+
|
|
1833
|
+
// Normalize protocols to array
|
|
1834
|
+
let protocolArray = [];
|
|
1835
|
+
if (protocols !== undefined) {
|
|
1836
|
+
if (typeof protocols === 'string') {
|
|
1837
|
+
protocolArray = [protocols];
|
|
1838
|
+
} else if (Array.isArray(protocols)) {
|
|
1839
|
+
protocolArray = protocols;
|
|
1840
|
+
} else {
|
|
1841
|
+
protocolArray = [String(protocols)];
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
// Check for duplicate protocols
|
|
1846
|
+
const seen = new Set();
|
|
1847
|
+
for (const p of protocolArray) {
|
|
1848
|
+
if (seen.has(p)) {
|
|
1849
|
+
throw new DOMException("Failed to construct 'WebSocket': The subprotocol '" + p + "' is duplicated.", 'SyntaxError');
|
|
1850
|
+
}
|
|
1851
|
+
seen.add(p);
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// Register socket
|
|
1855
|
+
__clientWebSockets.set(this.#socketId, this);
|
|
1856
|
+
|
|
1857
|
+
// Call host to create connection
|
|
1858
|
+
__WebSocket_connect(this.#socketId, this.#url, protocolArray);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
get url() {
|
|
1862
|
+
return this.#url;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
get readyState() {
|
|
1866
|
+
return this.#readyState;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
get bufferedAmount() {
|
|
1870
|
+
return this.#bufferedAmount;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
get extensions() {
|
|
1874
|
+
return this.#extensions;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
get protocol() {
|
|
1878
|
+
return this.#protocol;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
get binaryType() {
|
|
1882
|
+
return this.#binaryType;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
set binaryType(value) {
|
|
1886
|
+
if (value !== 'blob' && value !== 'arraybuffer') {
|
|
1887
|
+
throw new DOMException("Failed to set the 'binaryType' property: '" + value + "' is not a valid value.", 'SyntaxError');
|
|
1888
|
+
}
|
|
1889
|
+
this.#binaryType = value;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// ReadyState constants
|
|
1893
|
+
get CONNECTING() { return WebSocket.CONNECTING; }
|
|
1894
|
+
get OPEN() { return WebSocket.OPEN; }
|
|
1895
|
+
get CLOSING() { return WebSocket.CLOSING; }
|
|
1896
|
+
get CLOSED() { return WebSocket.CLOSED; }
|
|
1897
|
+
|
|
1898
|
+
send(data) {
|
|
1899
|
+
if (this.#readyState === WebSocket.CONNECTING) {
|
|
1900
|
+
throw new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.", 'InvalidStateError');
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
if (this.#readyState !== WebSocket.OPEN) {
|
|
1904
|
+
// Silently discard if not open (per spec)
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
// Convert data to string for transfer
|
|
1909
|
+
let dataStr;
|
|
1910
|
+
if (typeof data === 'string') {
|
|
1911
|
+
dataStr = data;
|
|
1912
|
+
} else if (data instanceof ArrayBuffer) {
|
|
1913
|
+
// Convert ArrayBuffer to base64 for transfer
|
|
1914
|
+
const bytes = new Uint8Array(data);
|
|
1915
|
+
let binary = '';
|
|
1916
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
1917
|
+
binary += String.fromCharCode(bytes[i]);
|
|
1918
|
+
}
|
|
1919
|
+
dataStr = '__BINARY__' + btoa(binary);
|
|
1920
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
1921
|
+
const bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
1922
|
+
let binary = '';
|
|
1923
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
1924
|
+
binary += String.fromCharCode(bytes[i]);
|
|
1925
|
+
}
|
|
1926
|
+
dataStr = '__BINARY__' + btoa(binary);
|
|
1927
|
+
} else if (data instanceof Blob) {
|
|
1928
|
+
// Blob.arrayBuffer() is async, but send() is sync
|
|
1929
|
+
// For now, throw - this is a limitation
|
|
1930
|
+
throw new DOMException("Failed to execute 'send' on 'WebSocket': Blob data is not supported in this environment.", 'NotSupportedError');
|
|
1931
|
+
} else {
|
|
1932
|
+
dataStr = String(data);
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
__WebSocket_send(this.#socketId, dataStr);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
close(code, reason) {
|
|
1939
|
+
if (code !== undefined) {
|
|
1940
|
+
if (typeof code !== 'number' || code !== Math.floor(code)) {
|
|
1941
|
+
throw new DOMException("Failed to execute 'close' on 'WebSocket': The code must be an integer.", 'InvalidAccessError');
|
|
1942
|
+
}
|
|
1943
|
+
if (code !== 1000 && (code < 3000 || code > 4999)) {
|
|
1944
|
+
throw new DOMException("Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999.", 'InvalidAccessError');
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
if (reason !== undefined) {
|
|
1949
|
+
const encoder = new TextEncoder();
|
|
1950
|
+
if (encoder.encode(reason).byteLength > 123) {
|
|
1951
|
+
throw new DOMException("Failed to execute 'close' on 'WebSocket': The message must not be greater than 123 bytes.", 'SyntaxError');
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
if (this.#readyState === WebSocket.CLOSING || this.#readyState === WebSocket.CLOSED) {
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
this.#readyState = WebSocket.CLOSING;
|
|
1960
|
+
__WebSocket_close(this.#socketId, code ?? 1000, reason ?? '');
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
// EventTarget interface
|
|
1964
|
+
addEventListener(type, listener, options) {
|
|
1965
|
+
if (typeof listener !== 'function') return;
|
|
1966
|
+
let listeners = this._listeners.get(type);
|
|
1967
|
+
if (!listeners) {
|
|
1968
|
+
listeners = [];
|
|
1969
|
+
this._listeners.set(type, listeners);
|
|
1970
|
+
}
|
|
1971
|
+
if (!listeners.includes(listener)) {
|
|
1972
|
+
listeners.push(listener);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
removeEventListener(type, listener, options) {
|
|
1977
|
+
const listeners = this._listeners.get(type);
|
|
1978
|
+
if (!listeners) return;
|
|
1979
|
+
const index = listeners.indexOf(listener);
|
|
1980
|
+
if (index !== -1) {
|
|
1981
|
+
listeners.splice(index, 1);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
dispatchEvent(event) {
|
|
1986
|
+
dispatchEvent(this, event);
|
|
1987
|
+
return !event.defaultPrevented;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// Internal methods called from host
|
|
1991
|
+
_setProtocol(protocol) {
|
|
1992
|
+
this.#protocol = protocol;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
_setExtensions(extensions) {
|
|
1996
|
+
this.#extensions = extensions;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
_setReadyState(state) {
|
|
2000
|
+
this.#readyState = state;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
_dispatchOpen() {
|
|
2004
|
+
this.#readyState = WebSocket.OPEN;
|
|
2005
|
+
const event = new _Event('open');
|
|
2006
|
+
dispatchEvent(this, event);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
_dispatchMessage(data) {
|
|
2010
|
+
// Handle binary data
|
|
2011
|
+
let messageData = data;
|
|
2012
|
+
if (typeof data === 'string' && data.startsWith('__BINARY__')) {
|
|
2013
|
+
const base64 = data.slice(10);
|
|
2014
|
+
const binary = atob(base64);
|
|
2015
|
+
const bytes = new Uint8Array(binary.length);
|
|
2016
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2017
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2018
|
+
}
|
|
2019
|
+
if (this.#binaryType === 'arraybuffer') {
|
|
2020
|
+
messageData = bytes.buffer;
|
|
2021
|
+
} else {
|
|
2022
|
+
messageData = new Blob([bytes]);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
const event = new _MessageEvent('message', { data: messageData });
|
|
2027
|
+
dispatchEvent(this, event);
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
_dispatchError() {
|
|
2031
|
+
const event = new _Event('error');
|
|
2032
|
+
dispatchEvent(this, event);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
_dispatchClose(code, reason, wasClean) {
|
|
2036
|
+
this.#readyState = WebSocket.CLOSED;
|
|
2037
|
+
const event = new _CloseEvent('close', { code, reason, wasClean });
|
|
2038
|
+
dispatchEvent(this, event);
|
|
2039
|
+
__clientWebSockets.delete(this.#socketId);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// Helper to dispatch events from host to a socket by ID
|
|
2044
|
+
globalThis.__dispatchClientWebSocketEvent = function(socketId, eventType, data) {
|
|
2045
|
+
const ws = __clientWebSockets.get(socketId);
|
|
2046
|
+
if (!ws) return;
|
|
2047
|
+
|
|
2048
|
+
switch (eventType) {
|
|
2049
|
+
case 'open':
|
|
2050
|
+
ws._setProtocol(data.protocol || '');
|
|
2051
|
+
ws._setExtensions(data.extensions || '');
|
|
2052
|
+
ws._dispatchOpen();
|
|
2053
|
+
break;
|
|
2054
|
+
case 'message':
|
|
2055
|
+
ws._dispatchMessage(data.data);
|
|
2056
|
+
break;
|
|
2057
|
+
case 'error':
|
|
2058
|
+
ws._dispatchError();
|
|
2059
|
+
break;
|
|
2060
|
+
case 'close':
|
|
2061
|
+
ws._dispatchClose(data.code, data.reason, data.wasClean);
|
|
2062
|
+
break;
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
globalThis.WebSocket = WebSocket;
|
|
2067
|
+
})();
|
|
2068
|
+
`);
|
|
2069
|
+
}
|
|
1690
2070
|
function setupServe(context) {
|
|
1691
2071
|
context.evalSync(`
|
|
1692
2072
|
(function() {
|
|
@@ -1717,9 +2097,46 @@ async function setupFetch(context, options) {
|
|
|
1717
2097
|
activeConnections: new Map
|
|
1718
2098
|
};
|
|
1719
2099
|
const wsCommandCallbacks = new Set;
|
|
2100
|
+
const clientWsCommandCallbacks = new Set;
|
|
1720
2101
|
setupServer(context, serveState);
|
|
1721
2102
|
setupServerWebSocket(context, wsCommandCallbacks);
|
|
1722
2103
|
setupServe(context);
|
|
2104
|
+
setupClientWebSocket(context, clientWsCommandCallbacks);
|
|
2105
|
+
const eventCallbacks = new Set;
|
|
2106
|
+
context.global.setSync("__emit", new ivm.Callback((eventName, payloadJson) => {
|
|
2107
|
+
const payload = JSON.parse(payloadJson);
|
|
2108
|
+
for (const cb of eventCallbacks)
|
|
2109
|
+
cb(eventName, payload);
|
|
2110
|
+
}));
|
|
2111
|
+
context.evalSync(`
|
|
2112
|
+
(function() {
|
|
2113
|
+
const __eventListeners = new Map();
|
|
2114
|
+
|
|
2115
|
+
globalThis.__on = function(event, callback) {
|
|
2116
|
+
let listeners = __eventListeners.get(event);
|
|
2117
|
+
if (!listeners) {
|
|
2118
|
+
listeners = new Set();
|
|
2119
|
+
__eventListeners.set(event, listeners);
|
|
2120
|
+
}
|
|
2121
|
+
listeners.add(callback);
|
|
2122
|
+
return function() {
|
|
2123
|
+
listeners.delete(callback);
|
|
2124
|
+
if (listeners.size === 0) {
|
|
2125
|
+
__eventListeners.delete(event);
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
};
|
|
2129
|
+
|
|
2130
|
+
globalThis.__dispatchExternalEvent = function(event, payloadJson) {
|
|
2131
|
+
const listeners = __eventListeners.get(event);
|
|
2132
|
+
if (!listeners) return;
|
|
2133
|
+
const payload = JSON.parse(payloadJson);
|
|
2134
|
+
for (const cb of listeners) {
|
|
2135
|
+
try { cb(payload); } catch (e) { console.error('Event listener error:', e); }
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
})();
|
|
2139
|
+
`);
|
|
1723
2140
|
return {
|
|
1724
2141
|
dispose() {
|
|
1725
2142
|
stateMap.clear();
|
|
@@ -1977,6 +2394,63 @@ async function setupFetch(context, options) {
|
|
|
1977
2394
|
},
|
|
1978
2395
|
hasActiveConnections() {
|
|
1979
2396
|
return serveState.activeConnections.size > 0;
|
|
2397
|
+
},
|
|
2398
|
+
dispatchClientWebSocketOpen(socketId, protocol, extensions) {
|
|
2399
|
+
const safeProtocol = protocol.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
2400
|
+
const safeExtensions = extensions.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
2401
|
+
context.evalSync(`
|
|
2402
|
+
__dispatchClientWebSocketEvent("${socketId}", "open", {
|
|
2403
|
+
protocol: "${safeProtocol}",
|
|
2404
|
+
extensions: "${safeExtensions}"
|
|
2405
|
+
});
|
|
2406
|
+
`);
|
|
2407
|
+
},
|
|
2408
|
+
dispatchClientWebSocketMessage(socketId, data) {
|
|
2409
|
+
if (typeof data === "string") {
|
|
2410
|
+
const safeData = data.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
2411
|
+
context.evalSync(`
|
|
2412
|
+
__dispatchClientWebSocketEvent("${socketId}", "message", { data: "${safeData}" });
|
|
2413
|
+
`);
|
|
2414
|
+
} else {
|
|
2415
|
+
const bytes = new Uint8Array(data);
|
|
2416
|
+
let binary = "";
|
|
2417
|
+
for (let i = 0;i < bytes.byteLength; i++) {
|
|
2418
|
+
binary += String.fromCharCode(bytes[i]);
|
|
2419
|
+
}
|
|
2420
|
+
const base64 = Buffer.from(binary, "binary").toString("base64");
|
|
2421
|
+
context.evalSync(`
|
|
2422
|
+
__dispatchClientWebSocketEvent("${socketId}", "message", { data: "__BINARY__${base64}" });
|
|
2423
|
+
`);
|
|
2424
|
+
}
|
|
2425
|
+
},
|
|
2426
|
+
dispatchClientWebSocketClose(socketId, code, reason, wasClean) {
|
|
2427
|
+
const safeReason = reason.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
|
|
2428
|
+
context.evalIgnored(`
|
|
2429
|
+
__dispatchClientWebSocketEvent("${socketId}", "close", {
|
|
2430
|
+
code: ${code},
|
|
2431
|
+
reason: "${safeReason}",
|
|
2432
|
+
wasClean: ${wasClean}
|
|
2433
|
+
});
|
|
2434
|
+
`);
|
|
2435
|
+
},
|
|
2436
|
+
dispatchClientWebSocketError(socketId) {
|
|
2437
|
+
context.evalIgnored(`
|
|
2438
|
+
__dispatchClientWebSocketEvent("${socketId}", "error", {});
|
|
2439
|
+
`);
|
|
2440
|
+
},
|
|
2441
|
+
onClientWebSocketCommand(callback) {
|
|
2442
|
+
clientWsCommandCallbacks.add(callback);
|
|
2443
|
+
return () => clientWsCommandCallbacks.delete(callback);
|
|
2444
|
+
},
|
|
2445
|
+
onEvent(callback) {
|
|
2446
|
+
eventCallbacks.add(callback);
|
|
2447
|
+
return () => eventCallbacks.delete(callback);
|
|
2448
|
+
},
|
|
2449
|
+
dispatchEvent(event, payload) {
|
|
2450
|
+
const json = JSON.stringify(payload);
|
|
2451
|
+
const safeEvent = JSON.stringify(event);
|
|
2452
|
+
const safeJson = JSON.stringify(json);
|
|
2453
|
+
context.evalSync(`__dispatchExternalEvent(${safeEvent}, ${safeJson});`);
|
|
1980
2454
|
}
|
|
1981
2455
|
};
|
|
1982
2456
|
}
|
|
@@ -1985,4 +2459,4 @@ export {
|
|
|
1985
2459
|
clearAllInstanceState
|
|
1986
2460
|
};
|
|
1987
2461
|
|
|
1988
|
-
//# debugId=
|
|
2462
|
+
//# debugId=98B5825CD3CA576464756E2164756E21
|