Standart Error in Utils

This commit is contained in:
Dariusz L
2025-07-27 20:30:06 +02:00
parent 64ee2c6abb
commit 058a1c4d67
20 changed files with 770 additions and 492 deletions

View File

@@ -1,4 +1,5 @@
import {createModuleLogger} from "./LoggerUtils.js";
import { withErrorHandling, createValidationError, createNetworkError } from "../ErrorHandler.js";
import type { WebSocketMessage, AckCallbacks } from "../types.js";
const log = createModuleLogger('WebSocketManager');
@@ -26,7 +27,7 @@ class WebSocketManager {
this.connect();
}
connect() {
connect = withErrorHandling(() => {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
log.debug("WebSocket is already open.");
return;
@@ -37,58 +38,56 @@ class WebSocketManager {
return;
}
if (!this.url) {
throw createValidationError("WebSocket URL is required", { url: this.url });
}
this.isConnecting = true;
log.info(`Connecting to WebSocket at ${this.url}...`);
try {
this.socket = new WebSocket(this.url);
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
this.isConnecting = false;
this.reconnectAttempts = 0;
log.info("WebSocket connection established.");
this.flushMessageQueue();
};
this.socket.onmessage = (event: MessageEvent) => {
try {
const data: WebSocketMessage = JSON.parse(event.data);
log.debug("Received message:", data);
if (data.type === 'ack' && data.nodeId) {
const callback = this.ackCallbacks.get(data.nodeId);
if (callback) {
log.debug(`ACK received for nodeId: ${data.nodeId}, resolving promise.`);
callback.resolve(data);
this.ackCallbacks.delete(data.nodeId);
}
}
} catch (error) {
log.error("Error parsing incoming WebSocket message:", error);
}
};
this.socket.onclose = (event: CloseEvent) => {
this.isConnecting = false;
if (event.wasClean) {
log.info(`WebSocket closed cleanly, code=${event.code}, reason=${event.reason}`);
} else {
log.warn("WebSocket connection died. Attempting to reconnect...");
this.handleReconnect();
}
};
this.socket.onerror = (error: Event) => {
this.isConnecting = false;
log.error("WebSocket error:", error);
};
} catch (error) {
this.socket.onopen = () => {
this.isConnecting = false;
log.error("Failed to create WebSocket connection:", error);
this.handleReconnect();
}
}
this.reconnectAttempts = 0;
log.info("WebSocket connection established.");
this.flushMessageQueue();
};
this.socket.onmessage = (event: MessageEvent) => {
try {
const data: WebSocketMessage = JSON.parse(event.data);
log.debug("Received message:", data);
if (data.type === 'ack' && data.nodeId) {
const callback = this.ackCallbacks.get(data.nodeId);
if (callback) {
log.debug(`ACK received for nodeId: ${data.nodeId}, resolving promise.`);
callback.resolve(data);
this.ackCallbacks.delete(data.nodeId);
}
}
} catch (error) {
log.error("Error parsing incoming WebSocket message:", error);
}
};
this.socket.onclose = (event: CloseEvent) => {
this.isConnecting = false;
if (event.wasClean) {
log.info(`WebSocket closed cleanly, code=${event.code}, reason=${event.reason}`);
} else {
log.warn("WebSocket connection died. Attempting to reconnect...");
this.handleReconnect();
}
};
this.socket.onerror = (error: Event) => {
this.isConnecting = false;
throw createNetworkError("WebSocket connection error", { error, url: this.url });
};
}, 'WebSocketManager.connect');
handleReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
@@ -100,13 +99,17 @@ class WebSocketManager {
}
}
sendMessage(data: WebSocketMessage, requiresAck = false): Promise<WebSocketMessage | void> {
return new Promise((resolve, reject) => {
const nodeId = data.nodeId;
if (requiresAck && !nodeId) {
return reject(new Error("A nodeId is required for messages that need acknowledgment."));
}
sendMessage = withErrorHandling(async (data: WebSocketMessage, requiresAck = false): Promise<WebSocketMessage | void> => {
if (!data || typeof data !== 'object') {
throw createValidationError("Message data is required", { data });
}
const nodeId = data.nodeId;
if (requiresAck && !nodeId) {
throw createValidationError("A nodeId is required for messages that need acknowledgment", { data, requiresAck });
}
return new Promise((resolve, reject) => {
const message = JSON.stringify(data);
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
@@ -117,7 +120,7 @@ class WebSocketManager {
const timeout = setTimeout(() => {
this.ackCallbacks.delete(nodeId);
reject(new Error(`ACK timeout for nodeId ${nodeId}`));
reject(createNetworkError(`ACK timeout for nodeId ${nodeId}`, { nodeId, timeout: 10000 }));
log.warn(`ACK timeout for nodeId ${nodeId}.`);
}, 10000); // 10-second timeout
@@ -142,13 +145,16 @@ class WebSocketManager {
}
if (requiresAck) {
reject(new Error("Cannot send message with ACK required while disconnected."));
reject(createNetworkError("Cannot send message with ACK required while disconnected", {
socketState: this.socket?.readyState,
isConnecting: this.isConnecting
}));
} else {
resolve();
}
}
});
}
}, 'WebSocketManager.sendMessage');
flushMessageQueue() {
log.debug(`Flushing ${this.messageQueue.length} queued messages.`);