@ryinner/web-socket-manager
Version:
simple ws manager
126 lines • 4.65 kB
JavaScript
import parseQueryParams from './parseQueryParams';
class WebSocketManager {
get ws() {
if (!this.isWebSocket(this.webSocketInstance) || this.isClose()) {
const openHandler = this.onOpenHandler.bind(this);
const closeHandler = this.onCloseHandler.bind(this);
const messageHandler = this.onMessageHandler.bind(this);
const errorHandler = this.onErrorHandler.bind(this);
this.webSocketInstance = new WebSocket(this.wss);
this.webSocketInstance.onclose = closeHandler;
this.webSocketInstance.onopen = openHandler;
this.webSocketInstance.onmessage = messageHandler;
this.webSocketInstance.onerror = errorHandler;
}
return this.webSocketInstance;
}
constructor(settings) {
this.operations = new Map();
this.isTesting = false;
this.wss = settings.url + parseQueryParams(settings.additionalQueryParams);
this.defaultInterval = settings.interval ?? DEFAULT_SOCKET_INTERVAL;
}
open() {
this.ws;
}
close() {
if (!this.isClose() && !this.isClosing()) {
this.ws.close();
clearInterval(this.reconnectInterval);
}
}
addOperation(operationSetting) {
const operation = this.findOperation(operationSetting.method);
if (operation !== undefined && Array.isArray(operationSetting.handlers)) {
this.addHandlers(operation, operationSetting.handlers);
}
else {
const callback = () => {
this.ws.send(JSON.stringify({ method: operationSetting.method, ...operationSetting.request() }));
};
this.operations.set(operationSetting.method, { ...operationSetting, callback });
}
this.open();
}
removeOperation(method) {
this.operations.delete(method);
}
removeHandler(method, handler) {
const operation = this.findOperation(method);
if (operation !== undefined && Array.isArray(operation?.handlers)) {
const handlerIndex = operation.handlers.findIndex(existHandler => existHandler === handler);
operation.handlers.splice(handlerIndex, 1);
}
}
findOperation(method) {
return this.operations.get(method);
}
addHandlers(operation, handlers) {
if (!Array.isArray(operation.handlers)) {
operation.handlers = [];
}
operation.handlers.push(...handlers);
}
onCloseHandler(event) {
if (!event.wasClean) {
this.reconnectInterval = setInterval(this.open, this.defaultInterval);
}
for (const operation of this.operations.values()) {
if (this.isIntervaledOperation(operation)) {
clearInterval(operation._interval);
}
}
}
onOpenHandler() {
clearInterval(this.reconnectInterval);
for (const operation of this.operations.values()) {
this.pickOperationStrategy(operation);
}
}
onMessageHandler(event) {
const answer = JSON.parse(event.data);
const operation = this.findOperation(answer.method);
if (this.isValidWebSocketAnswer(answer) && operation !== undefined) {
const { handlers } = operation;
if (Array.isArray(handlers)) {
handlers.forEach(handler => {
handler(answer.data);
});
}
}
}
onErrorHandler(error) {
this.close();
console.log(error);
}
pickOperationStrategy(operation) {
if (this.isIntervaledOperation(operation)) {
const interval = typeof operation.interval !== 'number' ? this.defaultInterval : operation.interval;
operation._interval = setInterval(operation.callback, interval);
}
else {
operation.callback();
}
}
isWebSocket(instance) {
return instance instanceof WebSocket || this.isTesting;
}
isClosing() {
return this.webSocketInstance?.readyState === WebSocket.CLOSING;
}
isClose() {
return this.webSocketInstance?.readyState === WebSocket.CLOSED;
}
isOpen() {
return this.webSocketInstance?.readyState === WebSocket.OPEN;
}
isIntervaledOperation(operation) {
return 'interval' in operation;
}
isValidWebSocketAnswer(answer) {
return typeof answer === 'object' && answer !== null && 'method' in answer;
}
}
export default WebSocketManager;
export const DEFAULT_SOCKET_INTERVAL = 3000;
//# sourceMappingURL=websocket.js.map