File size: 5,257 Bytes
b190b45 452f691 dd7ffbd 452f691 b190b45 452f691 dd7ffbd b190b45 dd7ffbd 452f691 dd7ffbd 452f691 dd7ffbd 452f691 96af7c9 452f691 dd7ffbd 452f691 b190b45 452f691 dd7ffbd 452f691 b190b45 452f691 dd7ffbd b190b45 dd7ffbd 96af7c9 dd7ffbd 452f691 dd7ffbd b190b45 452f691 b190b45 452f691 b190b45 452f691 dd7ffbd b190b45 dd7ffbd 452f691 96af7c9 dd7ffbd 96af7c9 452f691 dd7ffbd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
/**
* WebSocket Client (OPTIONAL)
*
* IMPORTANT: WebSocket is completely optional. All data can be retrieved via HTTP REST API.
* This WebSocket client is provided as an alternative method for users who prefer real-time streaming.
* If WebSocket is unavailable or you prefer HTTP, use the HTTP endpoints instead.
*
* The application automatically falls back to HTTP polling if WebSocket fails.
*/
class WSClient {
constructor() {
this.socket = null;
this.status = 'disconnected';
this.statusSubscribers = new Set();
this.globalSubscribers = new Set();
this.typeSubscribers = new Map();
this.eventLog = [];
this.backoff = 1000;
this.maxBackoff = 16000;
this.shouldReconnect = true;
this.isOptional = true; // Mark as optional feature
}
get url() {
const { protocol, host } = window.location;
const wsProtocol = protocol === 'https:' ? 'wss:' : 'ws:';
// For HuggingFace Space: wss://Really-amin-Datasourceforcryptocurrency-2.hf.space/ws
return `${wsProtocol}//${host}/ws`;
}
logEvent(event) {
const entry = { ...event, time: new Date().toISOString() };
this.eventLog.push(entry);
this.eventLog = this.eventLog.slice(-100);
}
onStatusChange(callback) {
this.statusSubscribers.add(callback);
callback(this.status);
return () => this.statusSubscribers.delete(callback);
}
onMessage(callback) {
this.globalSubscribers.add(callback);
return () => this.globalSubscribers.delete(callback);
}
subscribe(type, callback) {
if (!this.typeSubscribers.has(type)) {
this.typeSubscribers.set(type, new Set());
}
const set = this.typeSubscribers.get(type);
set.add(callback);
return () => set.delete(callback);
}
updateStatus(newStatus) {
this.status = newStatus;
this.statusSubscribers.forEach((cb) => cb(newStatus));
}
/**
* Connect to WebSocket (OPTIONAL - HTTP endpoints work fine)
* This is just an alternative method for real-time updates.
* If connection fails, use HTTP polling instead.
*/
connect() {
if (this.socket && (this.status === 'connecting' || this.status === 'connected')) {
return;
}
console.log('[WebSocket] Attempting optional WebSocket connection (HTTP endpoints are recommended)');
this.updateStatus('connecting');
this.socket = new WebSocket(this.url);
this.logEvent({ type: 'status', status: 'connecting', note: 'optional' });
this.socket.addEventListener('open', () => {
this.backoff = 1000;
this.updateStatus('connected');
this.logEvent({ type: 'status', status: 'connected' });
});
this.socket.addEventListener('message', (event) => {
try {
const data = JSON.parse(event.data);
this.logEvent({ type: 'message', messageType: data.type || 'unknown' });
this.globalSubscribers.forEach((cb) => cb(data));
if (data.type && this.typeSubscribers.has(data.type)) {
this.typeSubscribers.get(data.type).forEach((cb) => cb(data));
}
} catch (error) {
console.error('WS message parse error', error);
}
});
this.socket.addEventListener('close', () => {
this.updateStatus('disconnected');
this.logEvent({ type: 'status', status: 'disconnected', note: 'optional - use HTTP if needed' });
// Don't auto-reconnect aggressively - WebSocket is optional
// Users can use HTTP endpoints instead
if (this.shouldReconnect && this.backoff < this.maxBackoff) {
const delay = this.backoff;
this.backoff = Math.min(this.backoff * 2, this.maxBackoff);
console.log(`[WebSocket] Optional reconnection in ${delay}ms (or use HTTP endpoints)`);
setTimeout(() => this.connect(), delay);
} else if (this.shouldReconnect) {
console.log('[WebSocket] Max reconnection attempts reached. Use HTTP endpoints instead.');
}
});
this.socket.addEventListener('error', (error) => {
console.warn('[WebSocket] Optional WebSocket error (non-critical):', error);
console.info('[WebSocket] Tip: Use HTTP REST API endpoints instead - they work perfectly');
this.logEvent({
type: 'error',
details: error.message || 'unknown',
timestamp: new Date().toISOString(),
note: 'optional - HTTP endpoints available'
});
this.updateStatus('error');
// Don't close immediately - let close event handle cleanup
// This allows for proper reconnection logic
});
}
disconnect() {
this.shouldReconnect = false;
if (this.socket) {
this.socket.close();
}
}
getEvents() {
return [...this.eventLog];
}
}
const wsClient = new WSClient();
export default wsClient;
|