image-asset-manager
Version:
A comprehensive image asset management tool for frontend projects
223 lines • 7.48 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebSocketClient = void 0;
exports.createWebSocketClient = createWebSocketClient;
exports.useWebSocketClient = useWebSocketClient;
class WebSocketClient {
constructor(options) {
this.ws = null;
this.handlers = {};
this.reconnectAttempts = 0;
this.reconnectTimer = null;
this.pingTimer = null;
this.isConnected = false;
this.isReconnecting = false;
this.options = {
reconnectInterval: 3000,
maxReconnectAttempts: 10,
pingInterval: 30000,
...options,
};
}
connect() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
console.log("📡 WebSocket already connected");
return;
}
try {
console.log(`📡 Connecting to WebSocket: ${this.options.url}`);
this.ws = new WebSocket(this.options.url);
this.setupEventHandlers();
}
catch (error) {
console.error("📡 Failed to create WebSocket connection:", error);
this.scheduleReconnect();
}
}
disconnect() {
this.isReconnecting = false;
this.clearTimers();
if (this.ws) {
this.ws.close(1000, "Client disconnect");
this.ws = null;
}
this.isConnected = false;
console.log("📡 WebSocket disconnected");
}
setupEventHandlers() {
if (!this.ws)
return;
this.ws.onopen = () => {
console.log("📡 WebSocket connected");
this.isConnected = true;
this.isReconnecting = false;
this.reconnectAttempts = 0;
this.handlers.onConnect?.();
this.startPing();
};
this.ws.onclose = (event) => {
console.log(`📡 WebSocket closed: ${event.code} - ${event.reason}`);
this.isConnected = false;
this.clearTimers();
this.handlers.onDisconnect?.();
// Attempt to reconnect unless it was a clean close
if (event.code !== 1000 && !this.isReconnecting) {
this.scheduleReconnect();
}
};
this.ws.onerror = (error) => {
console.error("📡 WebSocket error:", error);
this.handlers.onError?.(error);
};
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
this.handleMessage(message);
}
catch (error) {
console.error("📡 Failed to parse WebSocket message:", error);
}
};
}
handleMessage(message) {
// Call general message handler
this.handlers.onMessage?.(message);
// Call specific handlers based on message type
switch (message.type) {
case "initial-data":
console.log("📡 Received initial data:", message.data);
break;
case "file-change":
console.log("📡 File change detected:", message.data);
this.handlers.onFileChange?.(message.data);
break;
case "stats-update":
console.log("📡 Stats updated:", message.data);
this.handlers.onStatsUpdate?.(message.data);
break;
case "data-updated":
console.log("📡 Data updated:", message.data);
this.handlers.onDataUpdated?.(message.data);
break;
case "pong":
// Server responded to ping
break;
default:
console.log(`📡 Received message: ${message.type}`, message.data);
}
}
scheduleReconnect() {
if (this.isReconnecting ||
this.reconnectAttempts >= this.options.maxReconnectAttempts) {
if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
console.error("📡 Max reconnection attempts reached");
}
return;
}
this.isReconnecting = true;
this.reconnectAttempts++;
console.log(`📡 Scheduling reconnect attempt ${this.reconnectAttempts}/${this.options.maxReconnectAttempts} in ${this.options.reconnectInterval}ms`);
this.reconnectTimer = setTimeout(() => {
this.connect();
}, this.options.reconnectInterval);
}
startPing() {
this.pingTimer = setInterval(() => {
if (this.isConnected && this.ws?.readyState === WebSocket.OPEN) {
this.send("ping", { timestamp: Date.now() });
}
}, this.options.pingInterval);
}
clearTimers() {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.pingTimer) {
clearInterval(this.pingTimer);
this.pingTimer = null;
}
}
send(type, data) {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
console.warn("📡 Cannot send message: WebSocket not connected");
return;
}
const message = {
type,
data,
timestamp: Date.now(),
};
try {
this.ws.send(JSON.stringify(message));
}
catch (error) {
console.error("📡 Failed to send WebSocket message:", error);
}
}
// Convenience methods for common operations
subscribe(events) {
this.send("subscribe", { events });
}
requestData(type, params) {
this.send("request-data", { type, ...params });
}
requestImages(page = 1, limit = 20, filters = {}) {
this.requestData("images", { page, limit, filters });
}
requestStats() {
this.requestData("stats");
}
requestDuplicates() {
this.requestData("duplicates");
}
// Event handler registration
on(event, handler) {
this.handlers[event] = handler;
}
off(event) {
delete this.handlers[event];
}
// Status getters
isConnectedStatus() {
return this.isConnected;
}
getReconnectAttempts() {
return this.reconnectAttempts;
}
getReadyState() {
return this.ws?.readyState ?? WebSocket.CLOSED;
}
}
exports.WebSocketClient = WebSocketClient;
// Factory function for easy instantiation
function createWebSocketClient(port = 3000, handlers = {}) {
const client = new WebSocketClient({
url: `ws://localhost:${port}/ws`,
});
// Register handlers
Object.entries(handlers).forEach(([event, handler]) => {
client.on(event, handler);
});
return client;
}
// Example usage for React components
function useWebSocketClient(port = 3000) {
const client = new WebSocketClient({
url: `ws://localhost:${port}/ws`,
});
const connect = () => client.connect();
const disconnect = () => client.disconnect();
const send = (type, data) => client.send(type, data);
const subscribe = (events) => client.subscribe(events);
return {
client,
connect,
disconnect,
send,
subscribe,
isConnected: () => client.isConnectedStatus(),
on: (event, handler) => client.on(event, handler),
};
}
//# sourceMappingURL=WebSocketClient.js.map