@renegade-fi/core
Version:
VanillaJS library for Renegade
185 lines • 6.25 kB
JavaScript
import { SIG_EXPIRATION_BUFFER_MS } from "../constants.js";
import { SocketClosedError, WebSocketConnectionError, WebSocketRequestError, } from "../errors/websocket.js";
import { addExpiringAuthToHeaders } from "./http.js";
export var AuthType;
(function (AuthType) {
AuthType["None"] = "None";
AuthType["Wallet"] = "Wallet";
AuthType["Admin"] = "Admin";
})(AuthType || (AuthType = {}));
export class RelayerWebsocket {
constructor(params) {
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "topic", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "authType", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "onmessage", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "onopenCallback", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "oncloseCallback", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "onerrorCallback", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "ws", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "handleOpen", {
enumerable: true,
configurable: true,
writable: true,
value: (event) => {
if (!this.ws)
return;
const message = this.buildSubscriptionMessage();
this.request(message);
return this.onopenCallback?.call(this.ws, event);
}
});
Object.defineProperty(this, "handleClose", {
enumerable: true,
configurable: true,
writable: true,
value: (event) => {
this.cleanup();
return this.oncloseCallback?.call(this.ws, event);
}
});
Object.defineProperty(this, "handleError", {
enumerable: true,
configurable: true,
writable: true,
value: (event) => {
this.cleanup();
return this.onerrorCallback?.call(this.ws, event);
}
});
this.config = params.config;
this.topic = params.topic;
this.authType = params.authType;
this.onmessage = params.onmessage;
this.onopenCallback = params.onopenCallback ?? null;
this.oncloseCallback = params.oncloseCallback ?? null;
this.onerrorCallback = params.onerrorCallback ?? null;
}
// --------------
// | Public API |
// --------------
async connect() {
if (this.ws) {
throw new Error("WebSocket connection attempt aborted: already connected.");
}
const WebSocket = await import("isows").then((module) => module.WebSocket);
const url = this.config.getWebsocketBaseUrl();
this.ws = new WebSocket(url);
this.ws.addEventListener("open", this.handleOpen);
this.ws.addEventListener("message", this.onmessage);
this.ws.addEventListener("close", this.handleClose);
this.ws.addEventListener("error", this.handleError);
// Wait for the socket to open.
if (this.ws?.readyState === WebSocket.CONNECTING) {
await new Promise((resolve, reject) => {
if (!this.ws)
return;
this.ws.onopen = (event) => resolve(event);
this.ws.onerror = (error) => reject(new WebSocketConnectionError({
url,
cause: error,
}));
});
}
}
close() {
if (!this.ws) {
return;
}
const message = this.buildUnsubscriptionMessage();
this.request(message);
this.ws.close();
}
// ---------------
// | Private API |
// ---------------
request(message) {
if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
throw new WebSocketRequestError({
body: message,
url: this.ws?.url || "",
cause: new SocketClosedError({ url: this.ws?.url }),
});
}
this.ws?.send(JSON.stringify(message));
}
buildSubscriptionMessage() {
const body = {
method: "subscribe",
topic: this.topic,
};
if (this.authType === AuthType.None) {
return { body };
}
const headers = this.buildAuthHeaders(body);
return {
headers,
body,
};
}
buildUnsubscriptionMessage() {
return {
body: {
method: "unsubscribe",
topic: this.topic,
},
};
}
buildAuthHeaders(body) {
const symmetricKey = this.config.getSymmetricKey(this.authType);
return addExpiringAuthToHeaders(this.config, body.topic, {}, // Headers
JSON.stringify(body), symmetricKey, SIG_EXPIRATION_BUFFER_MS);
}
cleanup() {
// Remove all event listeners before nullifying the reference
if (this.ws) {
this.ws.removeEventListener("open", this.handleOpen);
this.ws.removeEventListener("message", this.onmessage);
this.ws.removeEventListener("close", this.handleClose);
this.ws.removeEventListener("error", this.handleError);
}
// Nullify the WebSocket instance
this.ws = null;
}
}
//# sourceMappingURL=websocket.js.map