@xtr-dev/zod-rpc
Version:
Simple, type-safe RPC library with Zod validation and automatic TypeScript inference
145 lines • 5.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWebSocketTransport = exports.WebSocketTransport = void 0;
const errors_1 = require("../errors");
/**
* WebSocket transport implementation for real-time RPC communication.
* Supports automatic reconnection and handles connection lifecycle management.
*
* @example
* ```typescript
* // For client connections
* const transport = createWebSocketTransport('ws://localhost:8080');
*
* // For server connections with existing WebSocket
* const transport = createWebSocketTransport(ws, false); // No auto-reconnect
* ```
*
* @group Transport Layer
*/
class WebSocketTransport {
constructor(websocket, autoReconnect = true) {
this.websocket = websocket;
this.autoReconnect = autoReconnect;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
if (!websocket) {
throw new Error('WebSocket instance is required');
}
this.setupEventHandlers();
}
async send(message) {
if (!this.isConnected()) {
throw new errors_1.TransportError('WebSocket is not connected');
}
try {
this.websocket.send(JSON.stringify(message));
}
catch (error) {
throw new errors_1.TransportError(`Failed to send message: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
onMessage(handler) {
this.messageHandler = handler;
}
async connect() {
return new Promise((resolve, reject) => {
if (this.isConnected()) {
resolve();
return;
}
const onOpen = () => {
this.reconnectAttempts = 0;
this.websocket.removeEventListener('open', onOpen);
this.websocket.removeEventListener('error', onError);
resolve();
};
const onError = (_event) => {
this.websocket.removeEventListener('open', onOpen);
this.websocket.removeEventListener('error', onError);
reject(new errors_1.TransportError('Failed to connect to WebSocket'));
};
this.websocket.addEventListener('open', onOpen);
this.websocket.addEventListener('error', onError);
});
}
async disconnect() {
return new Promise((resolve) => {
if (!this.isConnected()) {
resolve();
return;
}
const onClose = () => {
this.websocket.removeEventListener('close', onClose);
resolve();
};
this.websocket.addEventListener('close', onClose);
this.websocket.close();
});
}
isConnected() {
return this.websocket.readyState === WebSocket.OPEN;
}
setupEventHandlers() {
this.websocket.addEventListener('message', (event) => {
try {
const message = JSON.parse(event.data);
this.messageHandler?.(message);
}
catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
});
this.websocket.addEventListener('close', () => {
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
setTimeout(() => {
this.attemptReconnect();
}, this.reconnectDelay * Math.pow(2, this.reconnectAttempts));
}
});
this.websocket.addEventListener('error', (event) => {
console.error('WebSocket error:', event);
});
}
async attemptReconnect() {
this.reconnectAttempts++;
try {
await this.connect();
}
catch (error) {
console.error(`Reconnection attempt ${this.reconnectAttempts} failed:`, error);
}
}
}
exports.WebSocketTransport = WebSocketTransport;
// Implementation
function createWebSocketTransport(urlOrWebSocket, protocolsOrAutoReconnect, autoReconnect = true) {
if (typeof urlOrWebSocket === 'string') {
// URL string case
const url = urlOrWebSocket;
const protocols = typeof protocolsOrAutoReconnect === 'boolean' ? undefined : protocolsOrAutoReconnect;
const shouldAutoReconnect = typeof protocolsOrAutoReconnect === 'boolean' ? protocolsOrAutoReconnect : autoReconnect;
if (!url) {
throw new Error('WebSocket URL is required');
}
try {
const websocket = new WebSocket(url, protocols);
return new WebSocketTransport(websocket, shouldAutoReconnect);
}
catch (error) {
throw new Error(`Failed to create WebSocket: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
else {
// WebSocket instance case
const websocket = urlOrWebSocket;
const shouldAutoReconnect = typeof protocolsOrAutoReconnect === 'boolean' ? protocolsOrAutoReconnect : true;
if (!websocket) {
throw new Error('WebSocket instance is required');
}
return new WebSocketTransport(websocket, shouldAutoReconnect);
}
}
exports.createWebSocketTransport = createWebSocketTransport;
//# sourceMappingURL=websocket.js.map