@ccheever/exact-ibex-runtime 0.1.0
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/package.json +63 -0
- package/src/abort/AbortController.ts +23 -0
- package/src/abort/AbortSignal.ts +152 -0
- package/src/abort/index.ts +2 -0
- package/src/accessibility.ts +12 -0
- package/src/arraybuffer-detach.ts +109 -0
- package/src/base64/base64.ts +168 -0
- package/src/base64/index.ts +1 -0
- package/src/blob/Blob.ts +259 -0
- package/src/blob/File.ts +59 -0
- package/src/blob/FormData.ts +323 -0
- package/src/blob/index.ts +3 -0
- package/src/bootstrap.ts +1946 -0
- package/src/broadcast/BroadcastChannel.ts +280 -0
- package/src/broadcast/index.ts +5 -0
- package/src/cache/Cache.ts +349 -0
- package/src/cache/CacheStorage.ts +89 -0
- package/src/cache/index.ts +27 -0
- package/src/camera/index.ts +6202 -0
- package/src/camera/processor.worker.ts +194 -0
- package/src/camera/scene.ts +195 -0
- package/src/clipboard/Clipboard.ts +129 -0
- package/src/clipboard/ClipboardItem.ts +97 -0
- package/src/clipboard/index.ts +6 -0
- package/src/clone/index.ts +1 -0
- package/src/clone/structuredClone.ts +389 -0
- package/src/clone/transferableSymbols.ts +2 -0
- package/src/compression/CompressionStream.ts +146 -0
- package/src/compression/DecompressionStream.ts +342 -0
- package/src/compression/index.ts +4 -0
- package/src/console/Console.ts +341 -0
- package/src/console/index.ts +2 -0
- package/src/core/accessibility-state.ts +263 -0
- package/src/core/accessibility.ts +184 -0
- package/src/core/agent-state.ts +37 -0
- package/src/core/diagnostics-logs.ts +144 -0
- package/src/core/host-call-bridge.ts +16 -0
- package/src/core/i18n-helpers.ts +189 -0
- package/src/core/locale-state.ts +253 -0
- package/src/core/locale.ts +95 -0
- package/src/crypto/Crypto.ts +2743 -0
- package/src/crypto/index.ts +1 -0
- package/src/diagnostics/logs.ts +7 -0
- package/src/encoding/TextDecoder.ts +1181 -0
- package/src/encoding/TextDecoderStream.ts +58 -0
- package/src/encoding/TextEncoder.ts +180 -0
- package/src/encoding/TextEncoderStream.ts +39 -0
- package/src/encoding/index.ts +8 -0
- package/src/events/CloseEvent.ts +91 -0
- package/src/events/DOMException.ts +409 -0
- package/src/events/ErrorEvent.ts +39 -0
- package/src/events/Event.ts +151 -0
- package/src/events/EventTarget.ts +280 -0
- package/src/events/FocusEvent.ts +27 -0
- package/src/events/KeyboardEvent.ts +46 -0
- package/src/events/MessageEvent.ts +61 -0
- package/src/events/ProgressEvent.ts +33 -0
- package/src/events/PromiseRejectionEvent.ts +31 -0
- package/src/events/index.ts +52 -0
- package/src/eventsource/EventSource.ts +371 -0
- package/src/eventsource/index.ts +2 -0
- package/src/fetch/Headers.ts +642 -0
- package/src/fetch/Request.ts +760 -0
- package/src/fetch/Response.ts +543 -0
- package/src/fetch/body.ts +1256 -0
- package/src/fetch/cookie-jar.ts +566 -0
- package/src/fetch/demo.ts +207 -0
- package/src/fetch/errors.ts +101 -0
- package/src/fetch/fetch.ts +2610 -0
- package/src/fetch/index.ts +101 -0
- package/src/fetch/native-bridge.ts +65 -0
- package/src/fetch/types.ts +258 -0
- package/src/filereader/FileReader.ts +236 -0
- package/src/filereader/index.ts +1 -0
- package/src/fs/Dirent.ts +39 -0
- package/src/fs/ExactFile.ts +450 -0
- package/src/fs/Stats.ts +80 -0
- package/src/fs/index.ts +944 -0
- package/src/fs/promises.ts +386 -0
- package/src/fs/shared.ts +328 -0
- package/src/http-server/index.js +697 -0
- package/src/http-server/index.ts +27 -0
- package/src/identity.generated.ts +14 -0
- package/src/index.ts +283 -0
- package/src/indexeddb/IDBCursor.ts +188 -0
- package/src/indexeddb/IDBDatabase.ts +343 -0
- package/src/indexeddb/IDBFactory.ts +269 -0
- package/src/indexeddb/IDBIndex.ts +194 -0
- package/src/indexeddb/IDBKeyRange.ts +109 -0
- package/src/indexeddb/IDBObjectStore.ts +468 -0
- package/src/indexeddb/IDBRequest.ts +163 -0
- package/src/indexeddb/IDBTransaction.ts +207 -0
- package/src/indexeddb/index.ts +34 -0
- package/src/indexeddb/utils.ts +52 -0
- package/src/inspect/index.ts +1 -0
- package/src/inspect/inspect.ts +465 -0
- package/src/internal/detect.ts +104 -0
- package/src/locale.ts +10 -0
- package/src/location/index.ts +1059 -0
- package/src/locks/LockManager.ts +460 -0
- package/src/locks/index.ts +12 -0
- package/src/media/VideoFrame.ts +58 -0
- package/src/messaging/MessageChannel.ts +31 -0
- package/src/messaging/MessagePort.ts +180 -0
- package/src/messaging/index.ts +2 -0
- package/src/messaging.ts +247 -0
- package/src/native/NativeModules.ts +354 -0
- package/src/native/index.ts +1 -0
- package/src/navigator/Navigator.ts +351 -0
- package/src/navigator/index.ts +1 -0
- package/src/node/Buffer.ts +1786 -0
- package/src/node/index.ts +4 -0
- package/src/node/path.ts +495 -0
- package/src/node/process.ts +2528 -0
- package/src/performance/Performance.ts +532 -0
- package/src/performance/index.ts +21 -0
- package/src/polyfills/array.ts +236 -0
- package/src/polyfills/arraybuffer.ts +172 -0
- package/src/polyfills/groupby.ts +85 -0
- package/src/polyfills/index.ts +85 -0
- package/src/polyfills/intl.ts +1956 -0
- package/src/polyfills/iterator.ts +479 -0
- package/src/polyfills/promise.ts +37 -0
- package/src/polyfills/set.ts +245 -0
- package/src/polyfills/string.ts +85 -0
- package/src/polyfills/typedarray.ts +110 -0
- package/src/promise-rejection-tracking.ts +464 -0
- package/src/react-native/index.ts +388 -0
- package/src/runtime-entry.ts +55 -0
- package/src/scheduling/AnimationFrame.ts +105 -0
- package/src/scheduling/IdleCallback.ts +167 -0
- package/src/scheduling/index.ts +13 -0
- package/src/security/Capabilities.ts +1146 -0
- package/src/security/Permissions.ts +392 -0
- package/src/security/capability-bits.generated.ts +63 -0
- package/src/security/index.ts +16 -0
- package/src/sqlite/Database.ts +456 -0
- package/src/sqlite/Statement.ts +206 -0
- package/src/sqlite/constants.ts +79 -0
- package/src/sqlite/errors.ts +25 -0
- package/src/sqlite/index.ts +34 -0
- package/src/sqlite/module.js +438 -0
- package/src/storage/Storage.ts +291 -0
- package/src/storage/StorageManager.ts +91 -0
- package/src/storage/index.ts +3 -0
- package/src/stream-compat.ts +47 -0
- package/src/streams/ReadableStream.ts +4131 -0
- package/src/streams/TransformStream.ts +375 -0
- package/src/streams/WritableStream.ts +866 -0
- package/src/streams/index.ts +41 -0
- package/src/timers/Timers.ts +296 -0
- package/src/timers/index.ts +11 -0
- package/src/url/URL.ts +656 -0
- package/src/url/URLPattern.ts +850 -0
- package/src/url/URLSearchParams.ts +244 -0
- package/src/url/index.ts +9 -0
- package/src/websocket/WebSocket.ts +770 -0
- package/src/websocket/WebSocketError.ts +52 -0
- package/src/websocket/WebSocketStream.ts +628 -0
- package/src/websocket/index.ts +7 -0
- package/src/window/index.ts +872 -0
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* WebSocket implementation for Ibex runtime
|
|
4
|
+
*
|
|
5
|
+
* Implements the WHATWG WebSocket API.
|
|
6
|
+
* @see https://websockets.spec.whatwg.org/
|
|
7
|
+
*
|
|
8
|
+
* NOTE: Uses _prefix private state for compatibility with the embedded runtimes
|
|
9
|
+
* we support, including Hermes-based builds.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EventTarget } from '../events/EventTarget';
|
|
13
|
+
import { Event } from '../events/Event';
|
|
14
|
+
import { MessageEvent } from '../events/MessageEvent';
|
|
15
|
+
import { CloseEvent } from '../events/CloseEvent';
|
|
16
|
+
import { ErrorEvent } from '../events/ErrorEvent';
|
|
17
|
+
import { getNativeModule } from '../native/NativeModules';
|
|
18
|
+
import { Blob } from '../blob/Blob';
|
|
19
|
+
import { requireCapability, Capabilities } from '../security/Capabilities';
|
|
20
|
+
|
|
21
|
+
// WebSocket ready states
|
|
22
|
+
const CONNECTING = 0;
|
|
23
|
+
const OPEN = 1;
|
|
24
|
+
const CLOSING = 2;
|
|
25
|
+
const CLOSED = 3;
|
|
26
|
+
|
|
27
|
+
// Standard WebSocket close codes
|
|
28
|
+
// @see https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
|
|
29
|
+
const CLOSE_NORMAL = 1000;
|
|
30
|
+
const CLOSE_GOING_AWAY = 1001;
|
|
31
|
+
const CLOSE_PROTOCOL_ERROR = 1002;
|
|
32
|
+
const CLOSE_UNSUPPORTED_DATA = 1003;
|
|
33
|
+
// 1004 is reserved
|
|
34
|
+
const CLOSE_NO_STATUS = 1005; // Synthetic - must not be sent on wire
|
|
35
|
+
const CLOSE_ABNORMAL = 1006; // Synthetic - must not be sent on wire
|
|
36
|
+
const CLOSE_INVALID_PAYLOAD = 1007;
|
|
37
|
+
const CLOSE_POLICY_VIOLATION = 1008;
|
|
38
|
+
const CLOSE_TOO_BIG = 1009;
|
|
39
|
+
const CLOSE_MANDATORY_EXT = 1010;
|
|
40
|
+
const CLOSE_INTERNAL_ERROR = 1011;
|
|
41
|
+
// 1012-1014 are reserved
|
|
42
|
+
const CLOSE_TLS_HANDSHAKE = 1015; // Synthetic - must not be sent on wire
|
|
43
|
+
|
|
44
|
+
function getBaseUrl(): string | undefined {
|
|
45
|
+
const currentLocation = (globalThis as any).location;
|
|
46
|
+
if (currentLocation && typeof currentLocation.href === 'string') {
|
|
47
|
+
return currentLocation.href;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isSecureContextForWebSocket(): boolean {
|
|
53
|
+
const currentLocation = (globalThis as any).location;
|
|
54
|
+
return !!currentLocation && currentLocation.protocol === 'https:';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isBlobLike(value: unknown): value is Blob {
|
|
58
|
+
return !!value &&
|
|
59
|
+
typeof value === 'object' &&
|
|
60
|
+
typeof (value as any).arrayBuffer === 'function' &&
|
|
61
|
+
typeof (value as any).size === 'number';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isValidWebSocketSubprotocol(value: string): boolean {
|
|
65
|
+
return /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function assertWebSocketBrand(value: unknown): WebSocket {
|
|
69
|
+
if (!(value instanceof WebSocket)) {
|
|
70
|
+
throw new TypeError('Illegal invocation');
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* WebSocket provides full-duplex communication over a single TCP connection.
|
|
77
|
+
*/
|
|
78
|
+
export class WebSocket extends EventTarget {
|
|
79
|
+
// Ready state constants
|
|
80
|
+
static readonly CONNECTING = CONNECTING;
|
|
81
|
+
static readonly OPEN = OPEN;
|
|
82
|
+
static readonly CLOSING = CLOSING;
|
|
83
|
+
static readonly CLOSED = CLOSED;
|
|
84
|
+
|
|
85
|
+
// Internal state (using _prefix for Hermes compatibility - no #private fields)
|
|
86
|
+
_url: string = '';
|
|
87
|
+
_protocol: string = '';
|
|
88
|
+
_extensions: string = '';
|
|
89
|
+
_readyState: number = CONNECTING;
|
|
90
|
+
_bufferedAmount: number = 0;
|
|
91
|
+
_binaryType: BinaryType = 'blob';
|
|
92
|
+
_socketId: number = -1;
|
|
93
|
+
_closeEventPending: boolean = false;
|
|
94
|
+
_incomingPaused: boolean = false;
|
|
95
|
+
_incomingFlowControlled: boolean = false;
|
|
96
|
+
|
|
97
|
+
// Send queue to maintain ordering for native sends and async Blob conversion.
|
|
98
|
+
_sendQueue: Array<() => Promise<void>> = [];
|
|
99
|
+
_isSendingQueue = false;
|
|
100
|
+
_pendingSendAcks: Array<{
|
|
101
|
+
remaining: number;
|
|
102
|
+
resolve: () => void;
|
|
103
|
+
}> = [];
|
|
104
|
+
_eventQueue: Array<() => void> = [];
|
|
105
|
+
_eventQueueScheduled = false;
|
|
106
|
+
|
|
107
|
+
// Event handlers
|
|
108
|
+
_onopen: ((this: WebSocket, ev: Event) => any) | null = null;
|
|
109
|
+
_onmessage: ((this: WebSocket, ev: MessageEvent) => any) | null = null;
|
|
110
|
+
_onerror: ((this: WebSocket, ev: Event) => any) | null = null;
|
|
111
|
+
_onclose: ((this: WebSocket, ev: CloseEvent) => any) | null = null;
|
|
112
|
+
|
|
113
|
+
constructor(url: string | URL, protocols?: string | string[]) {
|
|
114
|
+
super();
|
|
115
|
+
|
|
116
|
+
// Check capability before proceeding
|
|
117
|
+
// This throws NotAllowedError if the capability is not granted
|
|
118
|
+
requireCapability(Capabilities.NETWORK_CONNECT);
|
|
119
|
+
|
|
120
|
+
// Normalize URL
|
|
121
|
+
const urlString = url instanceof URL ? url.href : String(url);
|
|
122
|
+
|
|
123
|
+
if (urlString.includes('#')) {
|
|
124
|
+
throw new DOMException(
|
|
125
|
+
"The URL contains a fragment identifier, which is not allowed in WebSocket URLs.",
|
|
126
|
+
'SyntaxError'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Validate URL
|
|
131
|
+
let parsedUrl: URL;
|
|
132
|
+
try {
|
|
133
|
+
parsedUrl = new URL(urlString, getBaseUrl());
|
|
134
|
+
} catch {
|
|
135
|
+
throw new DOMException('Invalid URL', 'SyntaxError');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (parsedUrl.protocol === 'http:') {
|
|
139
|
+
parsedUrl.protocol = 'ws:';
|
|
140
|
+
} else if (parsedUrl.protocol === 'https:') {
|
|
141
|
+
parsedUrl.protocol = 'wss:';
|
|
142
|
+
} else if (parsedUrl.protocol !== 'ws:' && parsedUrl.protocol !== 'wss:') {
|
|
143
|
+
throw new DOMException(
|
|
144
|
+
`The URL's scheme must be either 'ws' or 'wss'. '${parsedUrl.protocol}' is not allowed.`,
|
|
145
|
+
'SyntaxError'
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (isSecureContextForWebSocket() && parsedUrl.protocol === 'ws:') {
|
|
150
|
+
throw new DOMException(
|
|
151
|
+
'Cannot construct an insecure WebSocket from a secure context.',
|
|
152
|
+
'SecurityError'
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Normalize protocols
|
|
157
|
+
const protocolList = protocols !== undefined
|
|
158
|
+
? Array.isArray(protocols) ? protocols : [protocols]
|
|
159
|
+
: [];
|
|
160
|
+
|
|
161
|
+
// Validate protocols
|
|
162
|
+
const seen = new Set<string>();
|
|
163
|
+
for (const protocol of protocolList) {
|
|
164
|
+
if (protocol === '') {
|
|
165
|
+
throw new DOMException('Invalid protocol: empty string', 'SyntaxError');
|
|
166
|
+
}
|
|
167
|
+
const normalizedProtocol = String(protocol);
|
|
168
|
+
const seenKey = normalizedProtocol.toLowerCase();
|
|
169
|
+
if (seen.has(seenKey)) {
|
|
170
|
+
throw new DOMException(`Invalid protocol: duplicate '${protocol}'`, 'SyntaxError');
|
|
171
|
+
}
|
|
172
|
+
if (!isValidWebSocketSubprotocol(normalizedProtocol)) {
|
|
173
|
+
throw new DOMException(`Invalid protocol: '${protocol}'`, 'SyntaxError');
|
|
174
|
+
}
|
|
175
|
+
seen.add(seenKey);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this._url = parsedUrl.href;
|
|
179
|
+
|
|
180
|
+
// Try to connect via native module
|
|
181
|
+
const nativeWebSocket = getNativeModule('websocket');
|
|
182
|
+
if (nativeWebSocket) {
|
|
183
|
+
this._connectNative(nativeWebSocket, protocolList);
|
|
184
|
+
} else {
|
|
185
|
+
// Fallback: simulate connection failure after a delay
|
|
186
|
+
this._simulateConnection();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
get readyState(): number {
|
|
191
|
+
return assertWebSocketBrand(this)._readyState;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
get url(): string {
|
|
195
|
+
return assertWebSocketBrand(this)._url;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
get protocol(): string {
|
|
199
|
+
return assertWebSocketBrand(this)._protocol;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
get extensions(): string {
|
|
203
|
+
return assertWebSocketBrand(this)._extensions;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get bufferedAmount(): number {
|
|
207
|
+
return assertWebSocketBrand(this)._bufferedAmount;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
get binaryType(): BinaryType {
|
|
211
|
+
return assertWebSocketBrand(this)._binaryType;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
set binaryType(value: BinaryType) {
|
|
215
|
+
const socket = assertWebSocketBrand(this);
|
|
216
|
+
if (value !== 'blob' && value !== 'arraybuffer') {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
socket._binaryType = value;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
get onopen(): ((this: WebSocket, ev: Event) => any) | null {
|
|
223
|
+
return assertWebSocketBrand(this)._onopen;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
set onopen(value: ((this: WebSocket, ev: Event) => any) | null) {
|
|
227
|
+
assertWebSocketBrand(this)._onopen = typeof value === 'function' ? value : null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
get onmessage(): ((this: WebSocket, ev: MessageEvent) => any) | null {
|
|
231
|
+
return assertWebSocketBrand(this)._onmessage;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
set onmessage(value: ((this: WebSocket, ev: MessageEvent) => any) | null) {
|
|
235
|
+
assertWebSocketBrand(this)._onmessage = typeof value === 'function' ? value : null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
get onerror(): ((this: WebSocket, ev: Event) => any) | null {
|
|
239
|
+
return assertWebSocketBrand(this)._onerror;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
set onerror(value: ((this: WebSocket, ev: Event) => any) | null) {
|
|
243
|
+
assertWebSocketBrand(this)._onerror = typeof value === 'function' ? value : null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
get onclose(): ((this: WebSocket, ev: CloseEvent) => any) | null {
|
|
247
|
+
return assertWebSocketBrand(this)._onclose;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
set onclose(value: ((this: WebSocket, ev: CloseEvent) => any) | null) {
|
|
251
|
+
assertWebSocketBrand(this)._onclose = typeof value === 'function' ? value : null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_enqueueEventTask(task: () => void): void {
|
|
255
|
+
this._eventQueue.push(task);
|
|
256
|
+
|
|
257
|
+
if (this._eventQueueScheduled) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
this._eventQueueScheduled = true;
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
this._eventQueueScheduled = false;
|
|
264
|
+
while (this._eventQueue.length > 0) {
|
|
265
|
+
const next = this._eventQueue.shift();
|
|
266
|
+
if (next) {
|
|
267
|
+
next();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}, 0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Send data through the WebSocket connection.
|
|
275
|
+
*/
|
|
276
|
+
send(data: string | ArrayBuffer | ArrayBufferView | Blob): void {
|
|
277
|
+
const socket = assertWebSocketBrand(this);
|
|
278
|
+
if (arguments.length === 0) {
|
|
279
|
+
throw new TypeError(
|
|
280
|
+
"Failed to execute 'send' on 'WebSocket': 1 argument required, but only 0 present."
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (socket._readyState === CONNECTING) {
|
|
285
|
+
throw new DOMException(
|
|
286
|
+
'WebSocket is still in CONNECTING state',
|
|
287
|
+
'InvalidStateError'
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (socket._readyState !== OPEN) {
|
|
292
|
+
// Silently fail if not open (per spec)
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const nativeWebSocket = getNativeModule('websocket');
|
|
297
|
+
if (!nativeWebSocket || socket._socketId === -1) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Handle Blob specially - queue it to maintain ordering
|
|
302
|
+
if (isBlobLike(data)) {
|
|
303
|
+
const byteSize = data.size;
|
|
304
|
+
socket._bufferedAmount += byteSize;
|
|
305
|
+
socket._queueSend(async () => {
|
|
306
|
+
if (socket._readyState !== OPEN) return;
|
|
307
|
+
const buffer = await data.arrayBuffer();
|
|
308
|
+
if (socket._readyState !== OPEN) return;
|
|
309
|
+
await socket._sendNative(new Uint8Array(buffer), byteSize);
|
|
310
|
+
});
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Convert data to sendable format
|
|
315
|
+
let sendData: string | Uint8Array;
|
|
316
|
+
let byteSize: number;
|
|
317
|
+
|
|
318
|
+
if (typeof data === 'string') {
|
|
319
|
+
sendData = data;
|
|
320
|
+
byteSize = new TextEncoder().encode(data).byteLength;
|
|
321
|
+
socket._bufferedAmount += byteSize;
|
|
322
|
+
} else if (data instanceof ArrayBuffer) {
|
|
323
|
+
sendData = new Uint8Array(data);
|
|
324
|
+
byteSize = data.byteLength;
|
|
325
|
+
socket._bufferedAmount += byteSize;
|
|
326
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
327
|
+
sendData = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
328
|
+
byteSize = data.byteLength;
|
|
329
|
+
socket._bufferedAmount += byteSize;
|
|
330
|
+
} else {
|
|
331
|
+
sendData = String(data);
|
|
332
|
+
byteSize = new TextEncoder().encode(sendData).byteLength;
|
|
333
|
+
socket._bufferedAmount += byteSize;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
socket._queueSend(async () => {
|
|
337
|
+
if (socket._readyState !== OPEN) return;
|
|
338
|
+
await socket._sendNative(sendData, byteSize);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Queue a send operation to maintain ordering.
|
|
344
|
+
*/
|
|
345
|
+
async _queueSend(sendFn: () => Promise<void>): Promise<void> {
|
|
346
|
+
this._sendQueue.push(sendFn);
|
|
347
|
+
|
|
348
|
+
if (this._isSendingQueue) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this._isSendingQueue = true;
|
|
353
|
+
|
|
354
|
+
while (this._sendQueue.length > 0) {
|
|
355
|
+
const fn = this._sendQueue.shift()!;
|
|
356
|
+
try {
|
|
357
|
+
await fn();
|
|
358
|
+
} catch (e) {
|
|
359
|
+
// Silently ignore send errors per WebSocket spec
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
this._isSendingQueue = false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
_sendNative(data: string | Uint8Array, byteSize: number): Promise<void> {
|
|
367
|
+
const nativeWebSocket = getNativeModule('websocket');
|
|
368
|
+
if (!nativeWebSocket || this._socketId === -1 || this._readyState !== OPEN) {
|
|
369
|
+
return Promise.resolve();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return new Promise<void>((resolve) => {
|
|
373
|
+
if (byteSize === 0) {
|
|
374
|
+
try {
|
|
375
|
+
nativeWebSocket.send(this._socketId, data);
|
|
376
|
+
} catch (_sendZeroErr) {}
|
|
377
|
+
resolve();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
this._pendingSendAcks.push({
|
|
382
|
+
remaining: byteSize,
|
|
383
|
+
resolve,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
nativeWebSocket.send(this._socketId, data);
|
|
388
|
+
} catch (_sendErr) {
|
|
389
|
+
this._bufferedAmount = Math.max(0, this._bufferedAmount - byteSize);
|
|
390
|
+
const ack = this._pendingSendAcks.pop();
|
|
391
|
+
ack?.resolve();
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
_resolvePendingSendAcks(): void {
|
|
397
|
+
while (this._pendingSendAcks.length > 0) {
|
|
398
|
+
const ack = this._pendingSendAcks.shift();
|
|
399
|
+
ack?.resolve();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
_pauseIncoming(): void {
|
|
404
|
+
if (this._incomingPaused || this._socketId === -1) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const nativeWebSocket = getNativeModule('websocket');
|
|
408
|
+
nativeWebSocket?.pause?.(this._socketId);
|
|
409
|
+
this._incomingPaused = true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
_resumeIncoming(): void {
|
|
413
|
+
if (!this._incomingPaused || this._socketId === -1) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const nativeWebSocket = getNativeModule('websocket');
|
|
417
|
+
nativeWebSocket?.resume?.(this._socketId);
|
|
418
|
+
this._incomingPaused = false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
_setIncomingFlowControl(enabled: boolean): void {
|
|
422
|
+
const nextEnabled = !!enabled;
|
|
423
|
+
this._incomingFlowControlled = nextEnabled;
|
|
424
|
+
if (this._socketId === -1) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const nativeWebSocket = getNativeModule('websocket');
|
|
428
|
+
nativeWebSocket?.setFlowControlled?.(this._socketId, nextEnabled);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Close the WebSocket connection.
|
|
433
|
+
*/
|
|
434
|
+
close(code?: number, reason?: string): void {
|
|
435
|
+
const socket = assertWebSocketBrand(this);
|
|
436
|
+
if (reason !== undefined && code === undefined) {
|
|
437
|
+
throw new DOMException(
|
|
438
|
+
'Close reason must not be provided without a close code.',
|
|
439
|
+
'InvalidAccessError'
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (code !== undefined) {
|
|
444
|
+
if (typeof code !== 'number' || !Number.isInteger(code)) {
|
|
445
|
+
throw new DOMException(
|
|
446
|
+
`Invalid close code: ${String(code)}. Must be an integer.`,
|
|
447
|
+
'InvalidAccessError'
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
if (code !== 1000 && (code < 3000 || code > 4999)) {
|
|
451
|
+
throw new DOMException(
|
|
452
|
+
`Invalid close code: ${code}. Must be 1000 or in range 3000-4999.`,
|
|
453
|
+
'InvalidAccessError'
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (reason !== undefined) {
|
|
459
|
+
// Reason must be valid UTF-8 and <= 123 bytes
|
|
460
|
+
const encoded = new TextEncoder().encode(reason);
|
|
461
|
+
if (encoded.byteLength > 123) {
|
|
462
|
+
throw new DOMException(
|
|
463
|
+
'Close reason is too long (max 123 bytes)',
|
|
464
|
+
'SyntaxError'
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (socket._readyState === CLOSING || socket._readyState === CLOSED) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
socket._readyState = CLOSING;
|
|
474
|
+
|
|
475
|
+
const nativeWebSocket = getNativeModule('websocket');
|
|
476
|
+
if (nativeWebSocket && socket._socketId !== -1) {
|
|
477
|
+
nativeWebSocket.close(socket._socketId, code ?? CLOSE_NO_STATUS, reason ?? '');
|
|
478
|
+
} else {
|
|
479
|
+
// Simulate close asynchronously (native would also be async)
|
|
480
|
+
queueMicrotask(() => {
|
|
481
|
+
socket._handleCloseInternal(code ?? CLOSE_NO_STATUS, reason ?? '', true);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Connect using native WebSocket module.
|
|
488
|
+
*/
|
|
489
|
+
_connectNative(nativeWebSocket: any, protocols: string[]): void {
|
|
490
|
+
// The C++ bridge calls methods via JSI fn.call() which doesn't bind `this`,
|
|
491
|
+
// so we pass a wrapper with arrow functions that capture `this` correctly.
|
|
492
|
+
const self = this;
|
|
493
|
+
const bridge = {
|
|
494
|
+
_handleOpen: (protocol: string, extensions: string) => self._handleOpen(protocol, extensions),
|
|
495
|
+
_handleMessage: (data: string | ArrayBuffer) => self._handleMessage(data),
|
|
496
|
+
_handleClose: (code: number, reason: string, wasClean: boolean) => self._handleClose(code, reason, wasClean),
|
|
497
|
+
_handleError: (message: string) => self._handleError(message),
|
|
498
|
+
_handleBytesSent: (bytesSent: number) => self._handleBytesSent(bytesSent),
|
|
499
|
+
};
|
|
500
|
+
this._socketId = nativeWebSocket.create(this.url, protocols, bridge);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Simulate connection for testing without native module.
|
|
505
|
+
*/
|
|
506
|
+
_simulateConnection(): void {
|
|
507
|
+
// Simulate failed connection after a short delay
|
|
508
|
+
queueMicrotask(() => {
|
|
509
|
+
setTimeout(() => {
|
|
510
|
+
this._handleErrorInternal('WebSocket connection failed: no native module');
|
|
511
|
+
this._handleCloseInternal(CLOSE_ABNORMAL, '', false);
|
|
512
|
+
}, 100);
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Handle incoming message (called by native bridge).
|
|
518
|
+
*/
|
|
519
|
+
_handleMessage(data: string | ArrayBuffer): void {
|
|
520
|
+
if (this._readyState === CLOSED) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (this._incomingFlowControlled) {
|
|
525
|
+
this._incomingPaused = true;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
this._enqueueEventTask(() => {
|
|
529
|
+
if (this._readyState === CLOSED) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
let messageData: string | ArrayBuffer | Blob;
|
|
534
|
+
|
|
535
|
+
if (typeof data === 'string') {
|
|
536
|
+
messageData = data;
|
|
537
|
+
} else if (this._binaryType === 'arraybuffer') {
|
|
538
|
+
messageData = data;
|
|
539
|
+
} else {
|
|
540
|
+
const BlobCtor = ((globalThis as any).Blob || Blob) as typeof Blob;
|
|
541
|
+
messageData = new BlobCtor([data]);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const event = new MessageEvent('message', {
|
|
545
|
+
data: messageData,
|
|
546
|
+
origin: new URL(this.url).origin,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
if (this._onmessage) {
|
|
550
|
+
this._onmessage.call(this, event);
|
|
551
|
+
}
|
|
552
|
+
this.dispatchEvent(event);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Handle connection open (called by native bridge).
|
|
558
|
+
*/
|
|
559
|
+
_handleOpen(protocol: string, extensions: string): void {
|
|
560
|
+
if (this._readyState !== CONNECTING) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
this._readyState = OPEN;
|
|
565
|
+
this._protocol = protocol || '';
|
|
566
|
+
this._extensions = extensions || '';
|
|
567
|
+
this._enqueueEventTask(() => {
|
|
568
|
+
const event = new Event('open');
|
|
569
|
+
|
|
570
|
+
if (this._onopen) {
|
|
571
|
+
this._onopen.call(this, event);
|
|
572
|
+
}
|
|
573
|
+
this.dispatchEvent(event);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Handle error (internal).
|
|
579
|
+
*/
|
|
580
|
+
_handleErrorInternal(message: string): void {
|
|
581
|
+
this._enqueueEventTask(() => {
|
|
582
|
+
const event = new ErrorEvent('error', {
|
|
583
|
+
message,
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
if (this._onerror) {
|
|
587
|
+
this._onerror.call(this, event);
|
|
588
|
+
}
|
|
589
|
+
this.dispatchEvent(event);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Handle connection close (internal).
|
|
595
|
+
*/
|
|
596
|
+
_handleCloseInternal(code: number, reason: string, wasClean: boolean): void {
|
|
597
|
+
if (this._readyState === CLOSED || this._closeEventPending) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
this._readyState = CLOSING;
|
|
602
|
+
this._closeEventPending = true;
|
|
603
|
+
|
|
604
|
+
setTimeout(() => {
|
|
605
|
+
this._enqueueEventTask(() => {
|
|
606
|
+
if (this._readyState === CLOSED) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
this._readyState = CLOSED;
|
|
611
|
+
this._closeEventPending = false;
|
|
612
|
+
this._bufferedAmount = 0;
|
|
613
|
+
this._incomingPaused = false;
|
|
614
|
+
this._incomingFlowControlled = false;
|
|
615
|
+
this._resolvePendingSendAcks();
|
|
616
|
+
|
|
617
|
+
const event = new CloseEvent('close', {
|
|
618
|
+
code,
|
|
619
|
+
reason,
|
|
620
|
+
wasClean,
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
if (this._onclose) {
|
|
624
|
+
this._onclose.call(this, event);
|
|
625
|
+
}
|
|
626
|
+
this.dispatchEvent(event);
|
|
627
|
+
});
|
|
628
|
+
}, 0);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Public method for native bridge to call on close.
|
|
633
|
+
*/
|
|
634
|
+
_handleClose(code: number, reason: string, wasClean: boolean): void {
|
|
635
|
+
this._handleCloseInternal(code, reason, wasClean);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Public method for native bridge to call on error.
|
|
640
|
+
*/
|
|
641
|
+
_handleError(message: string): void {
|
|
642
|
+
this._handleErrorInternal(message);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Public method for native bridge to call when data has been sent.
|
|
647
|
+
* This allows bufferedAmount to decrease as data is transmitted.
|
|
648
|
+
* @param bytesSent Number of bytes that have been sent to the network
|
|
649
|
+
*/
|
|
650
|
+
_handleBytesSent(bytesSent: number): void {
|
|
651
|
+
this._bufferedAmount = Math.max(0, this._bufferedAmount - bytesSent);
|
|
652
|
+
|
|
653
|
+
let remainingBytes = bytesSent;
|
|
654
|
+
while (remainingBytes > 0 && this._pendingSendAcks.length > 0) {
|
|
655
|
+
const current = this._pendingSendAcks[0];
|
|
656
|
+
if (remainingBytes >= current.remaining) {
|
|
657
|
+
remainingBytes -= current.remaining;
|
|
658
|
+
this._pendingSendAcks.shift();
|
|
659
|
+
current.resolve();
|
|
660
|
+
} else {
|
|
661
|
+
current.remaining -= remainingBytes;
|
|
662
|
+
remainingBytes = 0;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
get [Symbol.toStringTag](): string {
|
|
668
|
+
return 'WebSocket';
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
Object.defineProperty(WebSocket, 'length', {
|
|
673
|
+
value: 1,
|
|
674
|
+
configurable: true,
|
|
675
|
+
});
|
|
676
|
+
Object.defineProperty(WebSocket.prototype.close, 'length', {
|
|
677
|
+
value: 0,
|
|
678
|
+
configurable: true,
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
function nameAccessor(
|
|
682
|
+
descriptor: PropertyDescriptor | undefined,
|
|
683
|
+
propertyName: string
|
|
684
|
+
): PropertyDescriptor | undefined {
|
|
685
|
+
if (!descriptor) {
|
|
686
|
+
return descriptor;
|
|
687
|
+
}
|
|
688
|
+
if (typeof descriptor.get === 'function') {
|
|
689
|
+
Object.defineProperty(descriptor.get, 'name', {
|
|
690
|
+
value: `get ${propertyName}`,
|
|
691
|
+
configurable: true,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
if (typeof descriptor.set === 'function') {
|
|
695
|
+
Object.defineProperty(descriptor.set, 'name', {
|
|
696
|
+
value: `set ${propertyName}`,
|
|
697
|
+
configurable: true,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
return descriptor;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const urlDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'url'), 'url')!;
|
|
704
|
+
const readyStateDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'readyState'), 'readyState')!;
|
|
705
|
+
const bufferedAmountDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'bufferedAmount'), 'bufferedAmount')!;
|
|
706
|
+
const onopenDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'onopen'), 'onopen')!;
|
|
707
|
+
const onerrorDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'onerror'), 'onerror')!;
|
|
708
|
+
const oncloseDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'onclose'), 'onclose')!;
|
|
709
|
+
const extensionsDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'extensions'), 'extensions')!;
|
|
710
|
+
const protocolDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'protocol'), 'protocol')!;
|
|
711
|
+
const onmessageDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'onmessage'), 'onmessage')!;
|
|
712
|
+
const binaryTypeDescriptor = nameAccessor(Object.getOwnPropertyDescriptor(WebSocket.prototype, 'binaryType'), 'binaryType')!;
|
|
713
|
+
|
|
714
|
+
Object.defineProperties(WebSocket, {
|
|
715
|
+
CONNECTING: { value: CONNECTING, writable: false, enumerable: true, configurable: false },
|
|
716
|
+
OPEN: { value: OPEN, writable: false, enumerable: true, configurable: false },
|
|
717
|
+
CLOSING: { value: CLOSING, writable: false, enumerable: true, configurable: false },
|
|
718
|
+
CLOSED: { value: CLOSED, writable: false, enumerable: true, configurable: false },
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
Object.defineProperties(WebSocket.prototype, {
|
|
722
|
+
CONNECTING: { value: CONNECTING, writable: false, enumerable: true, configurable: false },
|
|
723
|
+
OPEN: { value: OPEN, writable: false, enumerable: true, configurable: false },
|
|
724
|
+
CLOSING: { value: CLOSING, writable: false, enumerable: true, configurable: false },
|
|
725
|
+
CLOSED: { value: CLOSED, writable: false, enumerable: true, configurable: false },
|
|
726
|
+
url: { get: urlDescriptor.get, enumerable: true, configurable: true },
|
|
727
|
+
readyState: { get: readyStateDescriptor.get, enumerable: true, configurable: true },
|
|
728
|
+
bufferedAmount: { get: bufferedAmountDescriptor.get, enumerable: true, configurable: true },
|
|
729
|
+
onopen: {
|
|
730
|
+
get: onopenDescriptor.get,
|
|
731
|
+
set: onopenDescriptor.set,
|
|
732
|
+
enumerable: true,
|
|
733
|
+
configurable: true,
|
|
734
|
+
},
|
|
735
|
+
onerror: {
|
|
736
|
+
get: onerrorDescriptor.get,
|
|
737
|
+
set: onerrorDescriptor.set,
|
|
738
|
+
enumerable: true,
|
|
739
|
+
configurable: true,
|
|
740
|
+
},
|
|
741
|
+
onclose: {
|
|
742
|
+
get: oncloseDescriptor.get,
|
|
743
|
+
set: oncloseDescriptor.set,
|
|
744
|
+
enumerable: true,
|
|
745
|
+
configurable: true,
|
|
746
|
+
},
|
|
747
|
+
extensions: { get: extensionsDescriptor.get, enumerable: true, configurable: true },
|
|
748
|
+
protocol: { get: protocolDescriptor.get, enumerable: true, configurable: true },
|
|
749
|
+
close: { value: WebSocket.prototype.close, writable: true, enumerable: true, configurable: true },
|
|
750
|
+
onmessage: {
|
|
751
|
+
get: onmessageDescriptor.get,
|
|
752
|
+
set: onmessageDescriptor.set,
|
|
753
|
+
enumerable: true,
|
|
754
|
+
configurable: true,
|
|
755
|
+
},
|
|
756
|
+
binaryType: {
|
|
757
|
+
get: binaryTypeDescriptor.get,
|
|
758
|
+
set: binaryTypeDescriptor.set,
|
|
759
|
+
enumerable: true,
|
|
760
|
+
configurable: true,
|
|
761
|
+
},
|
|
762
|
+
send: { value: WebSocket.prototype.send, writable: true, enumerable: true, configurable: true },
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
Object.setPrototypeOf(WebSocket.prototype, EventTarget.prototype);
|
|
766
|
+
Object.setPrototypeOf(WebSocket, EventTarget);
|
|
767
|
+
|
|
768
|
+
export type BinaryType = 'blob' | 'arraybuffer';
|
|
769
|
+
|
|
770
|
+
export default WebSocket;
|