import { core, internals, primordials } from "ext:core/mod.js";
const {
isAnyArrayBuffer,
isArrayBuffer,
internalRidSymbol,
} = core;
import {
op_ws_check_permission_and_cancel_handle,
op_ws_close,
op_ws_create,
op_ws_get_buffer,
op_ws_get_buffer_as_string,
op_ws_get_buffered_amount,
op_ws_get_error,
op_ws_next_event,
op_ws_send_binary,
op_ws_send_binary_ab,
op_ws_send_ping,
op_ws_send_text,
} from "ext:core/ops";
const {
ArrayBufferIsView,
ArrayIsArray,
ArrayPrototypeJoin,
ArrayPrototypeMap,
ArrayPrototypePush,
ArrayPrototypeShift,
ArrayPrototypeSome,
DateNow,
decodeURIComponent,
Error,
ErrorPrototypeToString,
ObjectDefineProperties,
ObjectPrototypeIsPrototypeOf,
PromisePrototypeCatch,
PromisePrototypeThen,
RegExpPrototypeExec,
SafeSet,
SetPrototypeGetSize,
String,
StringPrototypeEndsWith,
StringPrototypeToLowerCase,
Symbol,
SymbolFor,
SymbolIterator,
TypedArrayPrototypeGetByteLength,
TypeError,
} = primordials;
const { URL } = core.loadExtScript("ext:deno_web/00_url.js");
const webidl = core.loadExtScript("ext:deno_webidl/00_webidl.js");
const { createFilteredInspectProxy } = core.loadExtScript(
"ext:deno_web/01_console.js",
);
const { HTTP_TOKEN_CODE_POINT_RE, byteLowerCase, forgivingBase64Encode } = core
.loadExtScript(
"ext:deno_web/00_infra.js",
);
const { DOMException } = core.loadExtScript("ext:deno_web/01_dom_exception.js");
const {
CloseEvent,
defineEventHandler,
dispatch,
ErrorEvent,
Event,
EventTarget,
MessageEvent,
setIsTrusted,
} = core.loadExtScript("ext:deno_web/02_event.js");
const { Blob, BlobPrototype } = core.loadExtScript("ext:deno_web/09_file.js");
const { getLocationHref } = core.loadExtScript("ext:deno_web/12_location.js");
const {
fillHeaders,
headerListFromHeaders,
headersFromHeaderList,
} = core.loadExtScript("ext:deno_fetch/20_headers.js");
const { HttpClientPrototype } = core.loadExtScript(
"ext:deno_fetch/22_http_client.js",
);
const kNodeUndiciDispatcherOptions = SymbolFor(
"Deno.internal.node.undici.dispatcherOptions",
);
const kNodeUndiciGlobalDispatcher = SymbolFor(
"Deno.internal.node.undici.globalDispatcher",
);
webidl.converters["WebSocketInit"] = webidl.createDictionaryConverter(
"WebSocketInit",
[
{
key: "headers",
converter: webidl.converters["HeadersInit"],
},
{
key: "protocols",
converter: webidl.converters["sequence<DOMString>"],
},
{ key: "client", converter: webidl.converters.any },
{ key: "dispatcher", converter: webidl.converters.any },
],
);
webidl.converters["WebSocketInit or sequence<DOMString> or DOMString"] = (
V,
prefix,
context,
opts,
) => {
if (V === null || V === undefined) {
return webidl.converters["WebSocketInit"](V, prefix, context, opts);
}
if (webidl.type(V) === "Object" && V !== null) {
if (V[SymbolIterator] !== undefined) {
return webidl.converters["sequence<DOMString>"](V, prefix, context, opts);
}
return webidl.converters["WebSocketInit"](V, prefix, context, opts);
}
return webidl.converters.DOMString(V, prefix, context, opts);
};
webidl.converters["WebSocketSend"] = (V, prefix, context, opts) => {
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
return webidl.converters["Blob"](V, prefix, context, opts);
}
if (typeof V === "object") {
if (isAnyArrayBuffer(V)) {
return webidl.converters["ArrayBuffer"](V, prefix, context, opts);
}
if (ArrayBufferIsView(V)) {
return webidl.converters["ArrayBufferView"](V, prefix, context, opts);
}
}
return webidl.converters["USVString"](V, prefix, context, opts);
};
const SERVER = 0;
const CLIENT = 1;
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;
const _readyState = Symbol("[[readyState]]");
const _url = Symbol("[[url]]");
const _rid = Symbol("[[rid]]");
const _role = Symbol("[[role]]");
const _extensions = Symbol("[[extensions]]");
const _protocol = Symbol("[[protocol]]");
const _binaryType = Symbol("[[binaryType]]");
const _eventLoop = Symbol("[[eventLoop]]");
const _sendQueue = Symbol("[[sendQueue]]");
const _queueSend = Symbol("[[queueSend]]");
const _cancelHandle = Symbol("[[cancelHandle]]");
const _idleTimeoutDuration = Symbol("[[idleTimeout]]");
const _idleTimeoutTimeout = Symbol("[[idleTimeoutTimeout]]");
const _serverHandleIdleTimeout = Symbol("[[serverHandleIdleTimeout]]");
const _inspectorRequestId = Symbol("[[inspectorRequestId]]");
function getInspectorNetwork() {
const ins = internals.__inspectorNetwork;
if (ins && ins.isEnabled()) return ins;
return null;
}
function emitWebSocketClosed(ws) {
const ins = getInspectorNetwork();
const requestId = ws[_inspectorRequestId];
if (ins === null || requestId === undefined) return;
try {
ins.webSocketClosed({
requestId,
timestamp: DateNow() / 1000,
});
} catch {
}
ws[_inspectorRequestId] = undefined;
}
function emitWebSocketFrame(ws, eventName, opcode, payloadData) {
const ins = getInspectorNetwork();
const requestId = ws[_inspectorRequestId];
if (ins === null || requestId === undefined) return;
const sent = eventName === "frameSent";
const mask = ws[_role] === SERVER ? !sent : sent;
try {
const params = {
requestId,
timestamp: DateNow() / 1000,
response: { opcode, mask, payloadData },
};
if (sent) ins.webSocketFrameSent(params);
else ins.webSocketFrameReceived(params);
} catch {
}
}
function installServerInspector(ws, url) {
const ins = getInspectorNetwork();
if (ins === null) return;
const requestId = ins.nextRequestId();
ws[_inspectorRequestId] = requestId;
try {
ins.webSocketCreated({ requestId, url });
} catch {
}
}
function emitWebSocketFrameError(ws, errorMessage) {
const ins = getInspectorNetwork();
const requestId = ws[_inspectorRequestId];
if (ins === null || requestId === undefined) return;
try {
ins.webSocketFrameError({
requestId,
timestamp: DateNow() / 1000,
errorMessage,
});
} catch {
}
}
class WebSocket extends EventTarget {
constructor(url, initOrProtocols) {
super();
this[webidl.brand] = webidl.brand;
this[_rid] = undefined;
this[_role] = undefined;
this[_readyState] = CONNECTING;
this[_extensions] = "";
this[_protocol] = "";
this[_url] = "";
this[_binaryType] = "blob";
this[_idleTimeoutDuration] = 0;
this[_idleTimeoutTimeout] = undefined;
this[_sendQueue] = [];
this[_cancelHandle] = undefined;
const prefix = "Failed to construct 'WebSocket'";
webidl.requiredArguments(arguments.length, 1, prefix);
url = webidl.converters.USVString(url, prefix, "Argument 1");
initOrProtocols = webidl.converters
["WebSocketInit or sequence<DOMString> or DOMString"](
initOrProtocols,
prefix,
"Argument 2",
);
let wsURL;
try {
wsURL = new URL(url, getLocationHref());
} catch (e) {
throw new DOMException(e.message, "SyntaxError");
}
if (wsURL.protocol === "http:") {
wsURL.protocol = "ws:";
} else if (wsURL.protocol === "https:") {
wsURL.protocol = "wss:";
}
if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
throw new DOMException(
`Only ws & wss schemes are allowed in a WebSocket URL: received ${wsURL.protocol}`,
"SyntaxError",
);
}
if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
throw new DOMException(
"Fragments are not allowed in a WebSocket URL",
"SyntaxError",
);
}
let urlCredentials = null;
if (wsURL.username !== "" || wsURL.password !== "") {
urlCredentials = forgivingBase64Encode(
core.encode(
`${decodeURIComponent(wsURL.username)}:${
decodeURIComponent(wsURL.password)
}`,
),
);
wsURL.username = "";
wsURL.password = "";
}
this[_url] = wsURL.href;
this[_role] = CLIENT;
let protocols;
let headers = null;
let clientRid = null;
let caCerts = null;
let unsafelyIgnoreCertificateErrors = false;
if (typeof initOrProtocols === "string") {
protocols = [initOrProtocols];
} else if (ArrayIsArray(initOrProtocols)) {
protocols = initOrProtocols;
} else {
protocols = initOrProtocols.protocols || [];
if (initOrProtocols.headers !== undefined) {
headers = headersFromHeaderList([], "request");
fillHeaders(headers, initOrProtocols.headers);
}
if (initOrProtocols.client !== undefined) {
if (
initOrProtocols.client !== null &&
!ObjectPrototypeIsPrototypeOf(
HttpClientPrototype,
initOrProtocols.client,
)
) {
throw webidl.makeException(
TypeError,
"`client` must be a Deno.HttpClient",
prefix,
"Argument 2",
);
}
clientRid = initOrProtocols.client?.[internalRidSymbol] ?? null;
}
let dispatcher = initOrProtocols.dispatcher;
if (
dispatcher === undefined &&
internals[kNodeUndiciGlobalDispatcher] !== undefined
) {
dispatcher = internals[kNodeUndiciGlobalDispatcher];
}
if (clientRid === null && dispatcher !== undefined) {
clientRid = dispatcher?.client?.[internalRidSymbol] ?? null;
const dispatcherOptions = dispatcher?.[kNodeUndiciDispatcherOptions];
if (dispatcherOptions !== undefined) {
caCerts = dispatcherOptions.caCerts ?? null;
unsafelyIgnoreCertificateErrors =
dispatcherOptions.unsafelyIgnoreCertificateErrors === true;
}
}
}
if (urlCredentials !== null) {
if (headers === null) {
headers = headersFromHeaderList([], "request");
}
if (headers.get("Authorization") === null) {
headers.set("Authorization", `Basic ${urlCredentials}`);
}
}
if (
protocols.length !==
SetPrototypeGetSize(
new SafeSet(
ArrayPrototypeMap(protocols, (p) => StringPrototypeToLowerCase(p)),
),
)
) {
throw new DOMException(
"Cannot supply the same protocol multiple times",
"SyntaxError",
);
}
if (
ArrayPrototypeSome(
protocols,
(protocol) =>
RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, protocol) === null,
)
) {
throw new DOMException(
"Invalid protocol value",
"SyntaxError",
);
}
const cancelRid = op_ws_check_permission_and_cancel_handle(
"WebSocket.abort()",
this[_url],
true,
);
this[_cancelHandle] = cancelRid;
const inspectorNetwork = getInspectorNetwork();
if (inspectorNetwork !== null) {
const requestId = inspectorNetwork.nextRequestId();
this[_inspectorRequestId] = requestId;
try {
inspectorNetwork.webSocketCreated({
requestId,
url: this[_url],
});
} catch {
}
const requestHeaders = { __proto__: null };
if (headers !== null) {
const list = headerListFromHeaders(headers);
for (let i = 0; i < list.length; i++) {
const name = byteLowerCase(list[i][0]);
const value = list[i][1];
if (requestHeaders[name] === undefined) {
requestHeaders[name] = value;
} else {
requestHeaders[name] = requestHeaders[name] + ", " + value;
}
}
}
if (protocols.length > 0) {
requestHeaders["sec-websocket-protocol"] = ArrayPrototypeJoin(
protocols,
", ",
);
}
try {
inspectorNetwork.webSocketWillSendHandshakeRequest({
requestId,
timestamp: DateNow() / 1000,
wallTime: DateNow() / 1000,
request: {
headers: requestHeaders,
},
});
} catch {
}
}
PromisePrototypeThen(
op_ws_create(
"new WebSocket()",
wsURL.href,
ArrayPrototypeJoin(protocols, ", "),
cancelRid,
headers ? headerListFromHeaders(headers) : null,
caCerts,
unsafelyIgnoreCertificateErrors,
clientRid,
),
(create) => {
this[_rid] = create.rid;
this[_extensions] = create.extensions;
this[_protocol] = create.protocol;
const reqId = this[_inspectorRequestId];
if (reqId !== undefined) {
const ins = getInspectorNetwork();
if (ins !== null) {
const responseHeaders = { __proto__: null };
for (let i = 0; i < create.headers.length; i++) {
const name = byteLowerCase(create.headers[i][0]);
const value = create.headers[i][1];
if (responseHeaders[name] === undefined) {
responseHeaders[name] = value;
} else {
responseHeaders[name] = responseHeaders[name] + ", " + value;
}
}
try {
ins.webSocketHandshakeResponseReceived({
requestId: reqId,
timestamp: DateNow() / 1000,
response: {
status: create.status,
statusText: create.statusText,
headers: responseHeaders,
},
});
} catch {
}
}
}
if (this[_readyState] === CLOSING) {
PromisePrototypeThen(
op_ws_close(this[_rid]),
() => {
this[_readyState] = CLOSED;
const errEvent = new ErrorEvent("error");
this.dispatchEvent(errEvent);
emitWebSocketClosed(this);
const event = new CloseEvent("close");
this.dispatchEvent(event);
core.tryClose(this[_rid]);
},
);
} else {
this[_readyState] = OPEN;
const event = new Event("open");
this.dispatchEvent(event);
this[_eventLoop]();
}
},
(err) => {
this[_readyState] = CLOSED;
const errorEv = new ErrorEvent(
"error",
{ error: err, message: ErrorPrototypeToString(err) },
);
this.dispatchEvent(errorEv);
if (this[_cancelHandle]) {
core.tryClose(this[_cancelHandle]);
this[_cancelHandle] = undefined;
}
emitWebSocketClosed(this);
const closeEv = new CloseEvent("close");
this.dispatchEvent(closeEv);
},
);
}
get readyState() {
webidl.assertBranded(this, WebSocketPrototype);
return this[_readyState];
}
get CONNECTING() {
webidl.assertBranded(this, WebSocketPrototype);
return CONNECTING;
}
get OPEN() {
webidl.assertBranded(this, WebSocketPrototype);
return OPEN;
}
get CLOSING() {
webidl.assertBranded(this, WebSocketPrototype);
return CLOSING;
}
get CLOSED() {
webidl.assertBranded(this, WebSocketPrototype);
return CLOSED;
}
get extensions() {
webidl.assertBranded(this, WebSocketPrototype);
return this[_extensions];
}
get protocol() {
webidl.assertBranded(this, WebSocketPrototype);
return this[_protocol];
}
get url() {
webidl.assertBranded(this, WebSocketPrototype);
return this[_url];
}
get binaryType() {
webidl.assertBranded(this, WebSocketPrototype);
return this[_binaryType];
}
set binaryType(value) {
webidl.assertBranded(this, WebSocketPrototype);
value = webidl.converters.DOMString(
value,
"Failed to set 'binaryType' on 'WebSocket'",
);
if (value === "blob" || value === "arraybuffer") {
this[_binaryType] = value;
}
}
get bufferedAmount() {
webidl.assertBranded(this, WebSocketPrototype);
if (this[_readyState] === OPEN) {
return op_ws_get_buffered_amount(this[_rid]);
} else {
return 0;
}
}
send(data) {
webidl.assertBranded(this, WebSocketPrototype);
const prefix = "Failed to execute 'send' on 'WebSocket'";
webidl.requiredArguments(arguments.length, 1, prefix);
data = webidl.converters.WebSocketSend(data, prefix, "Argument 1");
if (this[_readyState] === CONNECTING) {
throw new DOMException("'readyState' not OPEN", "InvalidStateError");
}
if (this[_readyState] !== OPEN) {
return;
}
if (this[_sendQueue].length === 0) {
if (ArrayBufferIsView(data)) {
op_ws_send_binary(this[_rid], data);
emitWebSocketFrame(this, "frameSent", 2, data);
} else if (isArrayBuffer(data)) {
op_ws_send_binary_ab(this[_rid], data);
emitWebSocketFrame(this, "frameSent", 2, data);
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, data)) {
this[_queueSend](data);
} else {
const string = String(data);
op_ws_send_text(
this[_rid],
string,
);
emitWebSocketFrame(this, "frameSent", 1, string);
}
} else {
this[_queueSend](data);
}
}
close(code = undefined, reason = undefined) {
webidl.assertBranded(this, WebSocketPrototype);
const prefix = "Failed to execute 'close' on 'WebSocket'";
if (code !== undefined) {
code = webidl.converters["unsigned short"](code, prefix, "Argument 1", {
clamp: true,
});
}
if (reason !== undefined) {
reason = webidl.converters.USVString(reason, prefix, "Argument 2");
}
if (this[_role] === CLIENT) {
if (
code !== undefined &&
!(code === 1000 || (3000 <= code && code < 5000))
) {
throw new DOMException(
`The close code must be either 1000 or in the range of 3000 to 4999: received ${code}`,
"InvalidAccessError",
);
}
}
if (
reason !== undefined &&
TypedArrayPrototypeGetByteLength(core.encode(reason)) > 123
) {
throw new DOMException(
"The close reason may not be longer than 123 bytes",
"SyntaxError",
);
}
if (this[_cancelHandle]) {
core.tryClose(this[_cancelHandle]);
this[_cancelHandle] = undefined;
}
if (this[_readyState] === CONNECTING) {
this[_readyState] = CLOSING;
} else if (this[_readyState] === OPEN) {
this[_readyState] = CLOSING;
PromisePrototypeCatch(
op_ws_close(
this[_rid],
code,
reason,
),
(err) => {
this[_readyState] = CLOSED;
const errorEv = new ErrorEvent("error", {
error: err,
message: ErrorPrototypeToString(err),
});
this.dispatchEvent(errorEv);
emitWebSocketClosed(this);
const closeEv = new CloseEvent("close");
this.dispatchEvent(closeEv);
core.tryClose(this[_rid]);
},
);
}
}
async [_eventLoop]() {
const rid = this[_rid];
while (this[_readyState] !== CLOSED) {
const kind = await op_ws_next_event(rid);
if (
(this[_readyState] == CLOSING) &&
kind <= 3 && this[_role] !== CLIENT
) {
this[_readyState] = CLOSED;
emitWebSocketClosed(this);
const event = new CloseEvent("close");
this.dispatchEvent(event);
core.tryClose(rid);
break;
}
switch (kind) {
case 0: {
const data = op_ws_get_buffer_as_string(rid);
if (data === undefined) {
break;
}
this[_serverHandleIdleTimeout]();
emitWebSocketFrame(this, "frameReceived", 1, data);
const event = new MessageEvent("message", {
data,
origin: this[_url],
});
setIsTrusted(event, true);
dispatch(this, event);
break;
}
case 1: {
const d = op_ws_get_buffer(rid);
if (d == undefined) {
break;
}
this[_serverHandleIdleTimeout]();
const buffer = d.buffer;
emitWebSocketFrame(this, "frameReceived", 2, buffer);
let data;
if (this.binaryType === "blob") {
data = new Blob([buffer]);
} else {
data = buffer;
}
const event = new MessageEvent("message", {
data,
origin: this[_url],
});
setIsTrusted(event, true);
dispatch(this, event);
break;
}
case 2: {
this[_serverHandleIdleTimeout]();
break;
}
case 3: {
this[_readyState] = CLOSED;
const message = op_ws_get_error(rid);
emitWebSocketFrameError(this, message);
const error = new Error(message);
const errorEv = new ErrorEvent("error", {
error,
message,
});
this.dispatchEvent(errorEv);
emitWebSocketClosed(this);
const closeEv = new CloseEvent("close");
this.dispatchEvent(closeEv);
core.tryClose(rid);
break;
}
default: {
const code = kind;
const reason = code == 1005 ? "" : op_ws_get_error(rid);
const prevState = this[_readyState];
this[_readyState] = CLOSED;
if (this[_idleTimeoutTimeout]) {
core.cancelTimer(this[_idleTimeoutTimeout]);
}
if (prevState === OPEN) {
try {
await op_ws_close(
rid,
code,
reason,
);
} catch {
}
}
emitWebSocketClosed(this);
const event = new CloseEvent("close", {
wasClean: true,
code: code,
reason,
});
this.dispatchEvent(event);
core.tryClose(rid);
break;
}
}
}
}
async [_queueSend](data) {
const queue = this[_sendQueue];
ArrayPrototypePush(queue, data);
if (queue.length > 1) {
return;
}
while (queue.length > 0) {
const data = queue[0];
if (ArrayBufferIsView(data)) {
op_ws_send_binary(this[_rid], data);
emitWebSocketFrame(this, "frameSent", 2, data);
} else if (isArrayBuffer(data)) {
op_ws_send_binary_ab(this[_rid], data);
emitWebSocketFrame(this, "frameSent", 2, data);
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, data)) {
const ab = await data.slice().arrayBuffer();
op_ws_send_binary_ab(this[_rid], ab);
emitWebSocketFrame(this, "frameSent", 2, ab);
} else {
const string = String(data);
op_ws_send_text(
this[_rid],
string,
);
emitWebSocketFrame(this, "frameSent", 1, string);
}
ArrayPrototypeShift(queue);
}
}
[_serverHandleIdleTimeout]() {
if (this[_idleTimeoutDuration]) {
if (this[_idleTimeoutTimeout]) {
core.cancelTimer(this[_idleTimeoutTimeout]);
}
this[_idleTimeoutTimeout] = core.createSystemTimer(
async () => {
if (this[_readyState] === OPEN) {
await PromisePrototypeCatch(op_ws_send_ping(this[_rid]), () => {});
this[_idleTimeoutTimeout] = core.createSystemTimer(
async () => {
if (this[_readyState] === OPEN) {
this[_readyState] = CLOSING;
const reason = "No response from ping frame.";
await PromisePrototypeCatch(
op_ws_close(this[_rid], 1001, reason),
() => {},
);
this[_readyState] = CLOSED;
const errEvent = new ErrorEvent("error", {
message: reason,
});
this.dispatchEvent(errEvent);
const event = new CloseEvent("close", {
wasClean: false,
code: 1001,
reason,
});
this.dispatchEvent(event);
core.tryClose(this[_rid]);
} else {
if (this[_idleTimeoutTimeout]) {
core.cancelTimer(this[_idleTimeoutTimeout]);
}
}
},
(this[_idleTimeoutDuration] / 2) * 1000,
true,
);
} else {
if (this[_idleTimeoutTimeout]) {
core.cancelTimer(this[_idleTimeoutTimeout]);
}
}
},
(this[_idleTimeoutDuration] / 2) * 1000,
true,
);
}
}
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(WebSocketPrototype, this),
keys: [
"url",
"readyState",
"extensions",
"protocol",
"binaryType",
"bufferedAmount",
"onmessage",
"onerror",
"onclose",
"onopen",
],
}),
inspectOptions,
);
}
}
ObjectDefineProperties(WebSocket, {
CONNECTING: {
__proto__: null,
value: 0,
},
OPEN: {
__proto__: null,
value: 1,
},
CLOSING: {
__proto__: null,
value: 2,
},
CLOSED: {
__proto__: null,
value: 3,
},
});
defineEventHandler(WebSocket.prototype, "message");
defineEventHandler(WebSocket.prototype, "error");
defineEventHandler(WebSocket.prototype, "close");
defineEventHandler(WebSocket.prototype, "open");
webidl.configureInterface(WebSocket);
const WebSocketPrototype = WebSocket.prototype;
function createWebSocketBranded() {
const socket = webidl.createBranded(WebSocket);
socket[_rid] = undefined;
socket[_role] = undefined;
socket[_readyState] = CONNECTING;
socket[_extensions] = "";
socket[_protocol] = "";
socket[_url] = "";
socket[_binaryType] = "arraybuffer";
socket[_idleTimeoutDuration] = 0;
socket[_idleTimeoutTimeout] = undefined;
socket[_sendQueue] = [];
return socket;
}
export {
_eventLoop,
_idleTimeoutDuration,
_idleTimeoutTimeout,
_protocol,
_readyState,
_rid,
_role,
_serverHandleIdleTimeout,
CLIENT,
createWebSocketBranded,
emitWebSocketClosed,
installServerInspector,
SERVER,
WebSocket,
};