@reactgjs/react-gtk
Version:
## Getting Started
177 lines (176 loc) • 5.71 kB
JavaScript
// src/polyfills/websocket.ts
import GLib from "gi://GLib?version=2.0";
import Soup from "gi://Soup?version=2.4";
import { Event, EventEmitter } from "./shared/event-emitter.mjs";
import { registerPolyfills } from "./shared/polyfill-global.mjs";
import { _async } from "./shared/utils.mjs";
registerPolyfills("WebSocket")(() => {
let WebSocketEventType;
((WebSocketEventType2) => {
WebSocketEventType2["CLOSE"] = "close";
WebSocketEventType2["ERROR"] = "error";
WebSocketEventType2["MESSAGE"] = "message";
WebSocketEventType2["OPEN"] = "open";
})(WebSocketEventType || (WebSocketEventType = {}));
class WebSocketConnectionError extends Error {
constructor(statusCode, statusText, responseBody, err) {
super("WebSocket connection failed.");
this.statusCode = statusCode;
this.statusText = statusText;
this.responseBody = responseBody;
this.err = err;
this.name = "WebSocketConnectionError";
}
}
class WebSocket {
static CONNECTING = 0;
static OPEN = 1;
static CLOSING = 2;
static CLOSED = 3;
CONNECTING = WebSocket.CONNECTING;
OPEN = WebSocket.OPEN;
CLOSING = WebSocket.CLOSING;
CLOSED = WebSocket.CLOSED;
#emitter = new EventEmitter();
#connection;
#bufferedAmount = 0;
#extensions = "";
#protocol = "";
#readyState = WebSocket.CONNECTING;
#uri;
binaryType = "arraybuffer";
get bufferedAmount() {
return this.#bufferedAmount;
}
get extensions() {
return this.#extensions;
}
get protocol() {
return this.#protocol;
}
get readyState() {
return this.#readyState;
}
get url() {
return this.#uri;
}
onclose = null;
onerror = null;
onmessage = null;
onopen = null;
constructor(url, protocols = []) {
this.#emitter.add("close" /* CLOSE */, (e) => this.onclose?.(e));
this.#emitter.add("error" /* ERROR */, (e) => this.onerror?.(e));
this.#emitter.add("message" /* MESSAGE */, (e) => this.onmessage?.(e));
this.#emitter.add("open" /* OPEN */, (e) => this.onopen?.(e));
this.#uri = url.toString();
this.#startConnection(Array.isArray(protocols) ? protocols : [protocols]);
}
#cleanup() {
this.#emitter.clear();
}
#setupConnection() {
this.#protocol = this.#connection.get_protocol();
this.#connection.connect("closing", () => {
this.#readyState = WebSocket.CLOSING;
});
this.#connection.connect("closed", () => {
this.#readyState = WebSocket.CLOSED;
this.#emitter.emit("close" /* CLOSE */, Event.create({}));
this.#cleanup();
});
this.#connection.connect("error", (_, error) => {
this.#emitter.emit("error" /* ERROR */, Event.create({ error }));
});
this.#connection.connect("message", (message, type, data) => {
const origin = message.get_origin();
if (type === Soup.WebsocketDataType.TEXT) {
const text = new TextDecoder().decode(data.toArray());
this.#emitter.emit(
"message" /* MESSAGE */,
Event.create({ origin, data: text })
);
} else {
this.#emitter.emit(
"message" /* MESSAGE */,
Event.create({ origin, data: data.toArray() })
);
}
});
}
async #startConnection(protocols) {
try {
const uri = new Soup.URI(this.#uri);
const session = new Soup.Session();
const message = new Soup.Message({
method: "GET",
uri
});
const connection = await _async((p) => {
session.websocket_connect_async(
message,
"origin",
protocols,
null,
(_, response) => {
try {
const connection2 = session.websocket_connect_finish(response);
p.resolve(connection2);
} catch (e) {
p.reject(
new WebSocketConnectionError(
message.status_code,
message.reason_phrase ?? "",
message.response_body.data,
e
)
);
}
}
);
});
this.#connection = connection;
this.#setupConnection();
this.#readyState = WebSocket.OPEN;
this.#emitter.emit("open" /* OPEN */, Event.create({}));
} catch (error) {
this.#readyState = WebSocket.CLOSED;
this.#emitter.emit("error" /* ERROR */, Event.create({ error }));
}
}
async send(data) {
let bytes;
let type;
if (typeof data === "string") {
bytes = new GLib.Bytes(new TextEncoder().encode(data));
type = Soup.WebsocketDataType.TEXT;
} else if (data instanceof Uint8Array) {
bytes = new GLib.Bytes(data);
type = Soup.WebsocketDataType.BINARY;
} else if (data instanceof ArrayBuffer) {
bytes = new GLib.Bytes(new Uint8Array(data));
type = Soup.WebsocketDataType.BINARY;
} else {
const array = await data.arrayBuffer();
bytes = new GLib.Bytes(new Uint8Array(array));
type = Soup.WebsocketDataType.BINARY;
}
this.#connection.send_message(type, bytes);
}
close(code, reason) {
this.#connection.close(
code ?? Soup.WebsocketCloseCode.NORMAL,
reason ?? null
);
}
addEventListener(type, listener, options) {
this.#emitter.add(type, listener, options);
}
removeEventListener(type, listener) {
this.#emitter.remove(type, listener);
}
}
return {
WebSocket
};
});