@abstraxn/relayer
Version:
Abstraxn Relayer package for handling gas-less transactions, facilitating smart contract interactions, and relaying user transactions efficiently.
203 lines • 7.17 kB
JavaScript
import { io } from 'socket.io-client';
export class WebSocketManager {
constructor(relayerUrl, config = {}) {
this.socket = null;
this.subscribedTransactions = new Map();
this.isConnected = false;
this.relayerUrl = relayerUrl;
this.config = {
enabled: true,
autoConnect: true,
reconnection: true,
timeout: 60000,
...config,
};
}
/**
* Connect to WebSocket server
*/
connect() {
var _a;
if (!this.config.enabled || ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected)) {
return;
}
try {
// Parse the URL to extract host and query parameters
const url = new URL(this.relayerUrl);
// Use only the origin (protocol + host) and preserve query parameters
const wsUrl = `${url.protocol.replace('http', 'ws')}//${url.host}/${url.search}`;
this.socket = io(`${wsUrl}`, {
transports: ['websocket', 'polling'],
autoConnect: this.config.autoConnect,
reconnection: this.config.reconnection,
timeout: this.config.timeout,
});
this.setupEventHandlers();
if (this.config.autoConnect) {
this.socket.connect();
}
}
catch (error) {
console.error('Failed to initialize WebSocket connection:', error);
}
}
/**
* Disconnect from WebSocket server
*/
disconnect() {
if (this.socket) {
this.socket.disconnect();
this.socket = null;
this.isConnected = false;
this.subscribedTransactions.clear();
}
}
/**
* Check if WebSocket is connected
*/
connected() {
var _a;
return this.isConnected && ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected) === true;
}
/**
* Subscribe to transaction updates
*/
async subscribeToTransaction(txId, events = {}) {
if (!this.socket || !this.connected()) {
throw new Error('WebSocket not connected. Call connect() first.');
}
// Store event handlers for this transaction
this.subscribedTransactions.set(txId, events);
// Subscribe to transaction updates via WebSocket
this.socket.emit('subscribe_transaction', { txId });
return new Promise((resolve, reject) => {
var _a, _b;
const timeout = setTimeout(() => {
reject(new Error(`Subscription timeout for transaction ${txId}`));
}, 5000);
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.once('subscribed', (data) => {
var _a;
if (((_a = data.data) === null || _a === void 0 ? void 0 : _a.txId) === txId) {
clearTimeout(timeout);
resolve();
}
});
(_b = this.socket) === null || _b === void 0 ? void 0 : _b.once('error', (error) => {
clearTimeout(timeout);
reject(error);
});
});
}
/**
* Unsubscribe from transaction updates
*/
unsubscribeFromTransaction(txId) {
if (this.socket && this.connected()) {
this.socket.emit('unsubscribe_transaction', { txId });
}
this.subscribedTransactions.delete(txId);
}
/**
* Get current transaction status via WebSocket
*/
getTransactionStatus(txId) {
if (this.socket && this.connected()) {
this.socket.emit('get_transaction_status', { txId });
}
}
/**
* Setup WebSocket event handlers
*/
setupEventHandlers() {
if (!this.socket)
return;
// Connection events
this.socket.on('connect', () => {
this.isConnected = true;
this.notifyGlobalEvent('onConnect');
});
this.socket.on('disconnect', () => {
this.isConnected = false;
this.notifyGlobalEvent('onDisconnect');
});
this.socket.on('error', (error) => {
const errorObj = error instanceof Error ? error : new Error((error === null || error === void 0 ? void 0 : error.message) || 'WebSocket error');
this.notifyGlobalEvent('onError', errorObj);
});
// Transaction events
this.socket.on('transaction_created', (data) => {
this.handleTransactionCreated(data);
});
this.socket.on('transaction_update', (update) => {
this.handleTransactionUpdate(update);
});
// Subscription confirmation
this.socket.on('subscribed', (data) => {
console.log('Subscribed to transaction:', data === null || data === void 0 ? void 0 : data.txId);
});
this.socket.on('unsubscribed', (data) => {
console.log('Unsubscribed from transaction:', data === null || data === void 0 ? void 0 : data.txId);
});
}
/**
* Handle transaction creation events
*/
handleTransactionCreated(event) {
const events = this.subscribedTransactions.get(event.txId);
if (events === null || events === void 0 ? void 0 : events.onTransactionCreated) {
try {
events.onTransactionCreated(event);
}
catch (error) {
console.error('Error in transaction created handler:', error);
}
}
}
/**
* Handle transaction status updates
*/
handleTransactionUpdate(update) {
const events = this.subscribedTransactions.get(update.txId);
if (events === null || events === void 0 ? void 0 : events.onTransactionUpdate) {
try {
events.onTransactionUpdate(update);
}
catch (error) {
console.error('Error in transaction update handler:', error);
}
}
// Auto-unsubscribe when transaction is final
if (['confirmed', 'failed', 'rejected'].includes(update.status)) {
setTimeout(() => {
this.unsubscribeFromTransaction(update.txId);
}, 1000); // Give a moment for final event handling
}
}
/**
* Notify global event handlers
*/
notifyGlobalEvent(eventType, data) {
this.subscribedTransactions.forEach((events) => {
const handler = events[eventType];
if (handler && typeof handler === 'function') {
try {
handler(data);
}
catch (error) {
console.error(`Error in ${String(eventType)} handler:`, error);
}
}
});
}
/**
* Get connection status information
*/
getConnectionInfo() {
return {
connected: this.connected(),
subscribedTransactions: this.subscribedTransactions.size,
url: this.relayerUrl,
};
}
}
//# sourceMappingURL=WebSocketManager.js.map