web-mojo 2.1.970 → 2.1.971
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +4 -3
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.es.js +2 -2
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +2 -2
- package/dist/chunks/ChatView-1XDivrqI.js +2 -0
- package/dist/chunks/ChatView-1XDivrqI.js.map +1 -0
- package/dist/chunks/{ChatView-Bc8uGufN.js → ChatView-CT7mZ1Eg.js} +3 -5
- package/dist/chunks/ChatView-CT7mZ1Eg.js.map +1 -0
- package/dist/chunks/{MetricsMiniChartWidget-CFoUehOz.js → MetricsMiniChartWidget-DjoS-JvM.js} +2 -2
- package/dist/chunks/{MetricsMiniChartWidget-CFoUehOz.js.map → MetricsMiniChartWidget-DjoS-JvM.js.map} +1 -1
- package/dist/chunks/{MetricsMiniChartWidget-D3wJf0_r.js → MetricsMiniChartWidget-a1noF3LT.js} +2 -2
- package/dist/chunks/{MetricsMiniChartWidget-D3wJf0_r.js.map → MetricsMiniChartWidget-a1noF3LT.js.map} +1 -1
- package/dist/chunks/{WebApp-I1ndjEPg.js → WebApp-BDrC19LA.js} +2 -2
- package/dist/chunks/{WebApp-I1ndjEPg.js.map → WebApp-BDrC19LA.js.map} +1 -1
- package/dist/chunks/{WebApp-Dq9blJuq.js → WebApp-D6VuPAaK.js} +4 -4
- package/dist/chunks/{WebApp-Dq9blJuq.js.map → WebApp-D6VuPAaK.js.map} +1 -1
- package/dist/chunks/WebSocketClient-GhSkABpt.js +209 -0
- package/dist/chunks/WebSocketClient-GhSkABpt.js.map +1 -0
- package/dist/chunks/WebSocketClient-ff34AsBG.js +2 -0
- package/dist/chunks/WebSocketClient-ff34AsBG.js.map +1 -0
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +2 -2
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +5 -5
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/ChatView-B7SuYLwL.js +0 -2
- package/dist/chunks/ChatView-B7SuYLwL.js.map +0 -1
- package/dist/chunks/ChatView-Bc8uGufN.js.map +0 -1
- package/dist/chunks/WebSocketClient-B6ribe3B.js +0 -436
- package/dist/chunks/WebSocketClient-B6ribe3B.js.map +0 -1
- package/dist/chunks/WebSocketClient-BITilqco.js +0 -2
- package/dist/chunks/WebSocketClient-BITilqco.js.map +0 -1
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
class WebSocketClient {
|
|
2
|
-
constructor(options = {}) {
|
|
3
|
-
this.url = options.url || null;
|
|
4
|
-
this.protocols = options.protocols || [];
|
|
5
|
-
this.socket = null;
|
|
6
|
-
this.isConnected = false;
|
|
7
|
-
this.isConnecting = false;
|
|
8
|
-
this.shouldReconnect = options.autoReconnect !== false;
|
|
9
|
-
this._authed = false;
|
|
10
|
-
this._authFailed = false;
|
|
11
|
-
this.maxReconnectAttempts = Number.isFinite(options.maxReconnectAttempts) ? options.maxReconnectAttempts : Infinity;
|
|
12
|
-
this.reconnectInterval = options.reconnectInterval || 3e3;
|
|
13
|
-
this.reconnectBackoff = options.reconnectBackoff || 1.5;
|
|
14
|
-
this.reconnectAttempts = 0;
|
|
15
|
-
this.reconnectTimer = null;
|
|
16
|
-
this.pingInterval = options.pingInterval || 3e4;
|
|
17
|
-
this.pongTimeout = options.pongTimeout || 5e3;
|
|
18
|
-
this.pingTimer = null;
|
|
19
|
-
this.pongTimer = null;
|
|
20
|
-
this.pausePingWhenHidden = options.pausePingWhenHidden !== false;
|
|
21
|
-
this._onVisibilityChange = this._onVisibilityChange?.bind ? this._onVisibilityChange.bind(this) : () => {
|
|
22
|
-
};
|
|
23
|
-
this.getToken = options.getToken || null;
|
|
24
|
-
this.tokenPrefix = options.tokenPrefix || "bearer";
|
|
25
|
-
this.refreshToken = options.refreshToken || null;
|
|
26
|
-
this.autoSubscribeOwnTopic = options.autoSubscribeOwnTopic !== false;
|
|
27
|
-
this.subscriptions = /* @__PURE__ */ new Set();
|
|
28
|
-
this.eventBus = options.eventBus || null;
|
|
29
|
-
this.listeners = {};
|
|
30
|
-
this.dataTransform = options.dataTransform || null;
|
|
31
|
-
this.debug = options.debug || false;
|
|
32
|
-
this._onOpen = this._onOpen.bind(this);
|
|
33
|
-
this._onMessage = this._onMessage.bind(this);
|
|
34
|
-
this._onError = this._onError.bind(this);
|
|
35
|
-
this._onClose = this._onClose.bind(this);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Connect to WebSocket server
|
|
39
|
-
* @param {string} url - WebSocket URL (optional if set in constructor)
|
|
40
|
-
* @returns {Promise} Promise that resolves when connected
|
|
41
|
-
*/
|
|
42
|
-
connect(url = null) {
|
|
43
|
-
if (url) {
|
|
44
|
-
this.url = url;
|
|
45
|
-
}
|
|
46
|
-
if (!this.url) {
|
|
47
|
-
throw new Error("WebSocket URL is required");
|
|
48
|
-
}
|
|
49
|
-
if (this.isConnected || this.isConnecting) {
|
|
50
|
-
return Promise.resolve();
|
|
51
|
-
}
|
|
52
|
-
this.isConnecting = true;
|
|
53
|
-
return new Promise((resolve, reject) => {
|
|
54
|
-
try {
|
|
55
|
-
this.debug && console.log("[WebSocket] Connecting to:", this.url);
|
|
56
|
-
this.socket = new WebSocket(this.url, this.protocols);
|
|
57
|
-
this.socket.addEventListener("open", this._onOpen);
|
|
58
|
-
this.socket.addEventListener("message", this._onMessage);
|
|
59
|
-
this.socket.addEventListener("error", this._onError);
|
|
60
|
-
this.socket.addEventListener("close", this._onClose);
|
|
61
|
-
this._connectResolve = resolve;
|
|
62
|
-
this._connectReject = reject;
|
|
63
|
-
} catch (error) {
|
|
64
|
-
this.isConnecting = false;
|
|
65
|
-
reject(error);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Disconnect from WebSocket server
|
|
71
|
-
*/
|
|
72
|
-
disconnect() {
|
|
73
|
-
this.shouldReconnect = false;
|
|
74
|
-
this._clearTimers();
|
|
75
|
-
if (this.socket) {
|
|
76
|
-
this.debug && console.log("[WebSocket] Disconnecting");
|
|
77
|
-
this.socket.close(1e3, "Client disconnecting");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Send data to WebSocket server
|
|
82
|
-
* @param {*} data - Data to send
|
|
83
|
-
*/
|
|
84
|
-
send(data) {
|
|
85
|
-
if (!this.isConnected || !this.socket) {
|
|
86
|
-
throw new Error("WebSocket is not connected");
|
|
87
|
-
}
|
|
88
|
-
const message = typeof data === "string" ? data : JSON.stringify(data);
|
|
89
|
-
this.socket.send(message);
|
|
90
|
-
this.debug && console.log("[WebSocket] Sent:", message);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Subscribe to a topic (queued until authenticated)
|
|
94
|
-
* @param {string} topic
|
|
95
|
-
*/
|
|
96
|
-
subscribe(topic) {
|
|
97
|
-
if (!topic) return;
|
|
98
|
-
this.subscriptions.add(topic);
|
|
99
|
-
if (this._authed && this.isConnected) {
|
|
100
|
-
this.send({ action: "subscribe", topic });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Unsubscribe from a topic
|
|
105
|
-
* @param {string} topic
|
|
106
|
-
*/
|
|
107
|
-
unsubscribe(topic) {
|
|
108
|
-
if (!topic) return;
|
|
109
|
-
this.subscriptions.delete(topic);
|
|
110
|
-
if (this.isConnected) {
|
|
111
|
-
this.send({ action: "unsubscribe", topic });
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Send ping (application-level heartbeat)
|
|
116
|
-
*/
|
|
117
|
-
ping() {
|
|
118
|
-
if (this.isConnected) {
|
|
119
|
-
this.send({ action: "ping" });
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Add event listener
|
|
124
|
-
* @param {string} event - Event name
|
|
125
|
-
* @param {function} callback - Event callback
|
|
126
|
-
*/
|
|
127
|
-
on(event, callback) {
|
|
128
|
-
if (!this.listeners[event]) {
|
|
129
|
-
this.listeners[event] = [];
|
|
130
|
-
}
|
|
131
|
-
this.listeners[event].push(callback);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Remove event listener
|
|
135
|
-
* @param {string} event - Event name
|
|
136
|
-
* @param {function} callback - Event callback to remove
|
|
137
|
-
*/
|
|
138
|
-
off(event, callback) {
|
|
139
|
-
if (!this.listeners[event]) return;
|
|
140
|
-
if (!callback) {
|
|
141
|
-
delete this.listeners[event];
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const index = this.listeners[event].indexOf(callback);
|
|
145
|
-
if (index !== -1) {
|
|
146
|
-
this.listeners[event].splice(index, 1);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Emit event to listeners
|
|
151
|
-
* @param {string} event - Event name
|
|
152
|
-
* @param {*} data - Event data
|
|
153
|
-
*/
|
|
154
|
-
emit(event, data) {
|
|
155
|
-
if (this.listeners[event]) {
|
|
156
|
-
this.listeners[event].forEach((callback) => {
|
|
157
|
-
try {
|
|
158
|
-
callback(data);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
console.error(`[WebSocket] Error in ${event} listener:`, error);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
if (this.eventBus && typeof this.eventBus.emit === "function") {
|
|
165
|
-
this.eventBus.emit(`websocket:${event}`, {
|
|
166
|
-
websocket: this,
|
|
167
|
-
data
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Get current connection status
|
|
173
|
-
* @returns {string} Connection status
|
|
174
|
-
*/
|
|
175
|
-
getStatus() {
|
|
176
|
-
if (this.isConnected) return "connected";
|
|
177
|
-
if (this.isConnecting) return "connecting";
|
|
178
|
-
return "disconnected";
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Get connection statistics
|
|
182
|
-
* @returns {object} Connection stats
|
|
183
|
-
*/
|
|
184
|
-
getStats() {
|
|
185
|
-
return {
|
|
186
|
-
url: this.url,
|
|
187
|
-
isConnected: this.isConnected,
|
|
188
|
-
isConnecting: this.isConnecting,
|
|
189
|
-
reconnectAttempts: this.reconnectAttempts,
|
|
190
|
-
maxReconnectAttempts: this.maxReconnectAttempts,
|
|
191
|
-
shouldReconnect: this.shouldReconnect
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
// Private methods
|
|
195
|
-
_onOpen(event) {
|
|
196
|
-
this.debug && console.log("[WebSocket] Connected");
|
|
197
|
-
this.isConnected = true;
|
|
198
|
-
this.isConnecting = false;
|
|
199
|
-
this.reconnectAttempts = 0;
|
|
200
|
-
this._authed = false;
|
|
201
|
-
this._startHeartbeat();
|
|
202
|
-
if (typeof document !== "undefined" && document.addEventListener) {
|
|
203
|
-
document.removeEventListener("visibilitychange", this._onVisibilityChange);
|
|
204
|
-
document.addEventListener("visibilitychange", this._onVisibilityChange);
|
|
205
|
-
}
|
|
206
|
-
const token = this.getToken ? this.getToken() : null;
|
|
207
|
-
if (!token) {
|
|
208
|
-
console.warn("[WebSocket] No token provided at open; waiting for auth_required or external authenticate");
|
|
209
|
-
} else {
|
|
210
|
-
this.send({ type: "authenticate", token, prefix: this.tokenPrefix || void 0 });
|
|
211
|
-
}
|
|
212
|
-
if (this._connectResolve) {
|
|
213
|
-
this._connectResolve();
|
|
214
|
-
this._connectResolve = null;
|
|
215
|
-
this._connectReject = null;
|
|
216
|
-
}
|
|
217
|
-
this.emit("connected", { url: this.url });
|
|
218
|
-
}
|
|
219
|
-
_onMessage(event) {
|
|
220
|
-
this.debug && console.log("[WebSocket] Received:", event.data);
|
|
221
|
-
let data;
|
|
222
|
-
try {
|
|
223
|
-
data = JSON.parse(event.data);
|
|
224
|
-
} catch (error) {
|
|
225
|
-
data = event.data;
|
|
226
|
-
}
|
|
227
|
-
if (data && data.type === "pong") {
|
|
228
|
-
this._clearPongTimeout();
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
if (this.dataTransform && typeof this.dataTransform === "function") {
|
|
232
|
-
try {
|
|
233
|
-
data = this.dataTransform(data);
|
|
234
|
-
} catch (error) {
|
|
235
|
-
console.error("[WebSocket] Error transforming data:", error);
|
|
236
|
-
this.emit("error", { error, originalData: data });
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (data && typeof data === "object") {
|
|
241
|
-
switch (data.type) {
|
|
242
|
-
case "auth_required":
|
|
243
|
-
this.emit("auth-required", data);
|
|
244
|
-
break;
|
|
245
|
-
case "auth_timeout": {
|
|
246
|
-
this._authFailed = true;
|
|
247
|
-
this.emit("auth-timeout", data);
|
|
248
|
-
this.disconnect();
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
case "auth_success": {
|
|
252
|
-
this._authed = true;
|
|
253
|
-
this.emit("auth-success", data);
|
|
254
|
-
if (this.autoSubscribeOwnTopic) {
|
|
255
|
-
const { instance_kind, instance_id } = data;
|
|
256
|
-
if (instance_kind && instance_id !== void 0) {
|
|
257
|
-
this.subscribe(`${instance_kind}:${instance_id}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
for (const topic of this.subscriptions) {
|
|
261
|
-
this.send({ action: "subscribe", topic });
|
|
262
|
-
}
|
|
263
|
-
break;
|
|
264
|
-
}
|
|
265
|
-
case "subscribed":
|
|
266
|
-
this.emit("subscribed", data);
|
|
267
|
-
break;
|
|
268
|
-
case "unsubscribed":
|
|
269
|
-
this.emit("unsubscribed", data);
|
|
270
|
-
break;
|
|
271
|
-
case "notification":
|
|
272
|
-
this.emit("notification", data);
|
|
273
|
-
break;
|
|
274
|
-
case "error": {
|
|
275
|
-
this.emit("server-error", data);
|
|
276
|
-
const msg = (data.message || "").toString();
|
|
277
|
-
if (/token|auth/i.test(msg)) {
|
|
278
|
-
if (this.refreshToken) {
|
|
279
|
-
(async () => {
|
|
280
|
-
try {
|
|
281
|
-
await this.refreshToken();
|
|
282
|
-
this.disconnect();
|
|
283
|
-
setTimeout(() => this.connect().catch(() => {
|
|
284
|
-
}), 250);
|
|
285
|
-
} catch (e) {
|
|
286
|
-
console.warn("[WebSocket] Token refresh failed:", e);
|
|
287
|
-
this._authFailed = true;
|
|
288
|
-
this.disconnect();
|
|
289
|
-
}
|
|
290
|
-
})();
|
|
291
|
-
} else {
|
|
292
|
-
this._authFailed = true;
|
|
293
|
-
this.disconnect();
|
|
294
|
-
}
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
this.emit("message", data);
|
|
302
|
-
this.emit("data", data);
|
|
303
|
-
}
|
|
304
|
-
_onError(event) {
|
|
305
|
-
console.error("[WebSocket] Error:", event);
|
|
306
|
-
this.emit("error", {
|
|
307
|
-
error: event.error || new Error("WebSocket error"),
|
|
308
|
-
event
|
|
309
|
-
});
|
|
310
|
-
if (this._connectReject) {
|
|
311
|
-
this._connectReject(event.error || new Error("WebSocket connection failed"));
|
|
312
|
-
this._connectResolve = null;
|
|
313
|
-
this._connectReject = null;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
_onClose(event) {
|
|
317
|
-
this.debug && console.log("[WebSocket] Closed:", event.code, event.reason);
|
|
318
|
-
this.isConnected = false;
|
|
319
|
-
this.isConnecting = false;
|
|
320
|
-
this._authed = false;
|
|
321
|
-
this._clearTimers();
|
|
322
|
-
if (typeof document !== "undefined" && document.removeEventListener) {
|
|
323
|
-
document.removeEventListener("visibilitychange", this._onVisibilityChange);
|
|
324
|
-
}
|
|
325
|
-
if (this.socket) {
|
|
326
|
-
this.socket.removeEventListener("open", this._onOpen);
|
|
327
|
-
this.socket.removeEventListener("message", this._onMessage);
|
|
328
|
-
this.socket.removeEventListener("error", this._onError);
|
|
329
|
-
this.socket.removeEventListener("close", this._onClose);
|
|
330
|
-
this.socket = null;
|
|
331
|
-
}
|
|
332
|
-
this.emit("disconnected", {
|
|
333
|
-
code: event.code,
|
|
334
|
-
reason: event.reason,
|
|
335
|
-
wasClean: event.wasClean
|
|
336
|
-
});
|
|
337
|
-
if (this.shouldReconnect && !this._authFailed && event.code !== 1e3) {
|
|
338
|
-
this._attemptReconnect();
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
_attemptReconnect() {
|
|
342
|
-
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
343
|
-
this.debug && console.log("[WebSocket] Max reconnect attempts reached");
|
|
344
|
-
this.emit("reconnect-failed", {
|
|
345
|
-
attempts: this.reconnectAttempts,
|
|
346
|
-
maxAttempts: this.maxReconnectAttempts
|
|
347
|
-
});
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
this.reconnectAttempts++;
|
|
351
|
-
const delay = this.reconnectInterval * Math.pow(this.reconnectBackoff, this.reconnectAttempts - 1);
|
|
352
|
-
this.debug && console.log(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
353
|
-
this.emit("reconnecting", {
|
|
354
|
-
attempt: this.reconnectAttempts,
|
|
355
|
-
delay,
|
|
356
|
-
maxAttempts: this.maxReconnectAttempts
|
|
357
|
-
});
|
|
358
|
-
this.reconnectTimer = setTimeout(() => {
|
|
359
|
-
if (this.shouldReconnect) {
|
|
360
|
-
this.connect().catch((error) => {
|
|
361
|
-
console.error("[WebSocket] Reconnection failed:", error);
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
}, delay);
|
|
365
|
-
}
|
|
366
|
-
_startHeartbeat() {
|
|
367
|
-
if (!this.pingInterval) return;
|
|
368
|
-
if (this.pingTimer) return;
|
|
369
|
-
this.pingTimer = setInterval(() => {
|
|
370
|
-
if (this.isConnected && this.socket) {
|
|
371
|
-
if (this.pausePingWhenHidden && typeof document !== "undefined" && document.hidden) {
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
this.ping();
|
|
375
|
-
this._startPongTimeout();
|
|
376
|
-
}
|
|
377
|
-
}, this.pingInterval);
|
|
378
|
-
}
|
|
379
|
-
_startPongTimeout() {
|
|
380
|
-
this._clearPongTimeout();
|
|
381
|
-
this.pongTimer = setTimeout(() => {
|
|
382
|
-
console.warn("[WebSocket] Pong timeout - connection may be stale");
|
|
383
|
-
if (this.socket && typeof this.socket.close === "function") {
|
|
384
|
-
this.socket.close(1006, "Pong timeout");
|
|
385
|
-
}
|
|
386
|
-
}, this.pongTimeout);
|
|
387
|
-
}
|
|
388
|
-
_clearPongTimeout() {
|
|
389
|
-
if (this.pongTimer) {
|
|
390
|
-
clearTimeout(this.pongTimer);
|
|
391
|
-
this.pongTimer = null;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
_clearTimers() {
|
|
395
|
-
if (this.reconnectTimer) {
|
|
396
|
-
clearTimeout(this.reconnectTimer);
|
|
397
|
-
this.reconnectTimer = null;
|
|
398
|
-
}
|
|
399
|
-
if (this.pingTimer) {
|
|
400
|
-
clearInterval(this.pingTimer);
|
|
401
|
-
this.pingTimer = null;
|
|
402
|
-
}
|
|
403
|
-
this._clearPongTimeout();
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Handle visibility change for heartbeat
|
|
407
|
-
*/
|
|
408
|
-
_onVisibilityChange() {
|
|
409
|
-
if (!this.pausePingWhenHidden) return;
|
|
410
|
-
if (typeof document === "undefined") return;
|
|
411
|
-
if (document.hidden) {
|
|
412
|
-
if (this.pingTimer) {
|
|
413
|
-
clearInterval(this.pingTimer);
|
|
414
|
-
this.pingTimer = null;
|
|
415
|
-
}
|
|
416
|
-
} else {
|
|
417
|
-
this._startHeartbeat();
|
|
418
|
-
this.ping();
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Static method to create and connect WebSocket
|
|
423
|
-
* @param {string} url - WebSocket URL
|
|
424
|
-
* @param {object} options - Connection options
|
|
425
|
-
* @returns {Promise<WebSocketClient>} Connected WebSocket client
|
|
426
|
-
*/
|
|
427
|
-
static async connect(url, options = {}) {
|
|
428
|
-
const client = new WebSocketClient({ ...options, url });
|
|
429
|
-
await client.connect();
|
|
430
|
-
return client;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
export {
|
|
434
|
-
WebSocketClient as W
|
|
435
|
-
};
|
|
436
|
-
//# sourceMappingURL=WebSocketClient-B6ribe3B.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketClient-B6ribe3B.js","sources":["../../src/core/services/WebSocketClient.js"],"sourcesContent":["/**\n * WebSocket - Real-time WebSocket client for MOJO framework\n * Provides connection management, auto-reconnect, and event integration\n */\n\nexport default class WebSocketClient {\n constructor(options = {}) {\n this.url = options.url || null;\n this.protocols = options.protocols || [];\n \n // Connection state\n this.socket = null;\n this.isConnected = false;\n this.isConnecting = false;\n this.shouldReconnect = options.autoReconnect !== false;\n this._authed = false;\n this._authFailed = false;\n \n // Reconnection settings\n this.maxReconnectAttempts = Number.isFinite(options.maxReconnectAttempts) ? options.maxReconnectAttempts : Infinity;\n this.reconnectInterval = options.reconnectInterval || 3000;\n this.reconnectBackoff = options.reconnectBackoff || 1.5;\n this.reconnectAttempts = 0;\n this.reconnectTimer = null;\n \n // Heartbeat/ping settings (JSON ping/pong)\n this.pingInterval = options.pingInterval || 30000;\n this.pongTimeout = options.pongTimeout || 5000;\n this.pingTimer = null;\n this.pongTimer = null;\n this.pausePingWhenHidden = options.pausePingWhenHidden !== false; // default true\n this._onVisibilityChange = this._onVisibilityChange?.bind ? this._onVisibilityChange.bind(this) : () => {};\n \n // Auth / token\n this.getToken = options.getToken || null; // () => string\n this.tokenPrefix = options.tokenPrefix || 'bearer';\n this.refreshToken = options.refreshToken || null; // async () => string\n this.autoSubscribeOwnTopic = options.autoSubscribeOwnTopic !== false; // default true\n \n // Subscriptions\n this.subscriptions = new Set(); // always re-subscribe post-auth_success\n \n // Event handling\n this.eventBus = options.eventBus || null;\n this.listeners = {};\n \n // Data transformation\n this.dataTransform = options.dataTransform || null;\n \n // Options\n this.debug = options.debug || false;\n \n // Bind methods\n this._onOpen = this._onOpen.bind(this);\n this._onMessage = this._onMessage.bind(this);\n this._onError = this._onError.bind(this);\n this._onClose = this._onClose.bind(this);\n }\n\n /**\n * Connect to WebSocket server\n * @param {string} url - WebSocket URL (optional if set in constructor)\n * @returns {Promise} Promise that resolves when connected\n */\n connect(url = null) {\n if (url) {\n this.url = url;\n }\n \n if (!this.url) {\n throw new Error('WebSocket URL is required');\n }\n \n if (this.isConnected || this.isConnecting) {\n return Promise.resolve();\n }\n \n this.isConnecting = true;\n \n return new Promise((resolve, reject) => {\n try {\n this.debug && console.log('[WebSocket] Connecting to:', this.url);\n \n this.socket = new WebSocket(this.url, this.protocols);\n \n this.socket.addEventListener('open', this._onOpen);\n this.socket.addEventListener('message', this._onMessage);\n this.socket.addEventListener('error', this._onError);\n this.socket.addEventListener('close', this._onClose);\n \n // Store promise resolvers for connection result\n this._connectResolve = resolve;\n this._connectReject = reject;\n \n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from WebSocket server\n */\n disconnect() {\n this.shouldReconnect = false;\n this._clearTimers();\n \n if (this.socket) {\n this.debug && console.log('[WebSocket] Disconnecting');\n this.socket.close(1000, 'Client disconnecting');\n }\n }\n\n /**\n * Send data to WebSocket server\n * @param {*} data - Data to send\n */\n send(data) {\n if (!this.isConnected || !this.socket) {\n throw new Error('WebSocket is not connected');\n }\n \n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.socket.send(message);\n \n this.debug && console.log('[WebSocket] Sent:', message);\n }\n\n /**\n * Subscribe to a topic (queued until authenticated)\n * @param {string} topic\n */\n subscribe(topic) {\n if (!topic) return;\n this.subscriptions.add(topic);\n if (this._authed && this.isConnected) {\n this.send({ action: 'subscribe', topic });\n }\n }\n\n /**\n * Unsubscribe from a topic\n * @param {string} topic\n */\n unsubscribe(topic) {\n if (!topic) return;\n this.subscriptions.delete(topic);\n if (this.isConnected) {\n this.send({ action: 'unsubscribe', topic });\n }\n }\n\n /**\n * Send ping (application-level heartbeat)\n */\n ping() {\n if (this.isConnected) {\n this.send({ action: 'ping' });\n }\n }\n\n /**\n * Add event listener\n * @param {string} event - Event name\n * @param {function} callback - Event callback\n */\n on(event, callback) {\n if (!this.listeners[event]) {\n this.listeners[event] = [];\n }\n this.listeners[event].push(callback);\n }\n\n /**\n * Remove event listener\n * @param {string} event - Event name\n * @param {function} callback - Event callback to remove\n */\n off(event, callback) {\n if (!this.listeners[event]) return;\n \n if (!callback) {\n delete this.listeners[event];\n return;\n }\n \n const index = this.listeners[event].indexOf(callback);\n if (index !== -1) {\n this.listeners[event].splice(index, 1);\n }\n }\n\n /**\n * Emit event to listeners\n * @param {string} event - Event name\n * @param {*} data - Event data\n */\n emit(event, data) {\n // Emit to local listeners\n if (this.listeners[event]) {\n this.listeners[event].forEach(callback => {\n try {\n callback(data);\n } catch (error) {\n console.error(`[WebSocket] Error in ${event} listener:`, error);\n }\n });\n }\n \n // Emit to external EventBus if available\n if (this.eventBus && typeof this.eventBus.emit === 'function') {\n this.eventBus.emit(`websocket:${event}`, {\n websocket: this,\n data\n });\n }\n }\n\n /**\n * Get current connection status\n * @returns {string} Connection status\n */\n getStatus() {\n if (this.isConnected) return 'connected';\n if (this.isConnecting) return 'connecting';\n return 'disconnected';\n }\n\n /**\n * Get connection statistics\n * @returns {object} Connection stats\n */\n getStats() {\n return {\n url: this.url,\n isConnected: this.isConnected,\n isConnecting: this.isConnecting,\n reconnectAttempts: this.reconnectAttempts,\n maxReconnectAttempts: this.maxReconnectAttempts,\n shouldReconnect: this.shouldReconnect\n };\n }\n\n // Private methods\n\n _onOpen(event) {\n this.debug && console.log('[WebSocket] Connected');\n \n this.isConnected = true;\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n this._authed = false;\n \n // Start heartbeat\n this._startHeartbeat();\n // Visibility handling for heartbeat\n if (typeof document !== 'undefined' && document.addEventListener) {\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n }\n \n // Authenticate immediately\n const token = this.getToken ? this.getToken() : null;\n if (!token) {\n console.warn('[WebSocket] No token provided at open; waiting for auth_required or external authenticate');\n } else {\n this.send({ type: 'authenticate', token, prefix: this.tokenPrefix || undefined });\n }\n \n // Resolve connection promise\n if (this._connectResolve) {\n this._connectResolve();\n this._connectResolve = null;\n this._connectReject = null;\n }\n \n this.emit('connected', { url: this.url });\n }\n\n _onMessage(event) {\n this.debug && console.log('[WebSocket] Received:', event.data);\n \n let data;\n try {\n data = JSON.parse(event.data);\n } catch (error) {\n data = event.data;\n }\n \n // Handle heartbeat pong (JSON only)\n if (data && data.type === 'pong') {\n this._clearPongTimeout();\n return;\n }\n \n // Transform data if transformer provided\n if (this.dataTransform && typeof this.dataTransform === 'function') {\n try {\n data = this.dataTransform(data);\n } catch (error) {\n console.error('[WebSocket] Error transforming data:', error);\n this.emit('error', { error, originalData: data });\n return;\n }\n }\n\n // Protocol handling\n if (data && typeof data === 'object') {\n switch (data.type) {\n case 'auth_required':\n this.emit('auth-required', data);\n break;\n case 'auth_timeout': {\n this._authFailed = true;\n this.emit('auth-timeout', data);\n this.disconnect();\n return;\n }\n case 'auth_success': {\n this._authed = true;\n this.emit('auth-success', data);\n // Auto-subscribe to own topic if enabled\n if (this.autoSubscribeOwnTopic) {\n const { instance_kind, instance_id } = data;\n if (instance_kind && instance_id !== undefined) {\n this.subscribe(`${instance_kind}:${instance_id}`);\n }\n }\n // Flush desired subscriptions\n for (const topic of this.subscriptions) {\n this.send({ action: 'subscribe', topic });\n }\n break;\n }\n case 'subscribed':\n this.emit('subscribed', data);\n break;\n case 'unsubscribed':\n this.emit('unsubscribed', data);\n break;\n case 'notification':\n this.emit('notification', data);\n break;\n case 'error': {\n this.emit('server-error', data);\n const msg = (data.message || '').toString();\n if (/token|auth/i.test(msg)) {\n if (this.refreshToken) {\n (async () => {\n try {\n await this.refreshToken();\n this.disconnect();\n // allow a short delay before reconnect to use refreshed token\n setTimeout(() => this.connect().catch(() => {}), 250);\n } catch (e) {\n console.warn('[WebSocket] Token refresh failed:', e);\n this._authFailed = true;\n this.disconnect();\n }\n })();\n } else {\n this._authFailed = true;\n this.disconnect();\n }\n break;\n }\n break;\n }\n default:\n break;\n }\n }\n \n this.emit('message', data);\n this.emit('data', data); // Alias for convenience\n }\n\n _onError(event) {\n console.error('[WebSocket] Error:', event);\n \n this.emit('error', { \n error: event.error || new Error('WebSocket error'),\n event \n });\n \n // Reject connection promise if still connecting\n if (this._connectReject) {\n this._connectReject(event.error || new Error('WebSocket connection failed'));\n this._connectResolve = null;\n this._connectReject = null;\n }\n }\n\n _onClose(event) {\n this.debug && console.log('[WebSocket] Closed:', event.code, event.reason);\n \n this.isConnected = false;\n this.isConnecting = false;\n this._authed = false;\n \n this._clearTimers();\n if (typeof document !== 'undefined' && document.removeEventListener) {\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n }\n \n // Clean up socket\n if (this.socket) {\n this.socket.removeEventListener('open', this._onOpen);\n this.socket.removeEventListener('message', this._onMessage);\n this.socket.removeEventListener('error', this._onError);\n this.socket.removeEventListener('close', this._onClose);\n this.socket = null;\n }\n \n this.emit('disconnected', { \n code: event.code, \n reason: event.reason,\n wasClean: event.wasClean\n });\n \n // Attempt reconnection if enabled and not a clean close and not due to auth failure\n if (this.shouldReconnect && !this._authFailed && event.code !== 1000) {\n this._attemptReconnect();\n }\n }\n\n _attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.debug && console.log('[WebSocket] Max reconnect attempts reached');\n this.emit('reconnect-failed', { \n attempts: this.reconnectAttempts,\n maxAttempts: this.maxReconnectAttempts\n });\n return;\n }\n \n this.reconnectAttempts++;\n const delay = this.reconnectInterval * Math.pow(this.reconnectBackoff, this.reconnectAttempts - 1);\n \n this.debug && console.log(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n \n this.emit('reconnecting', { \n attempt: this.reconnectAttempts,\n delay,\n maxAttempts: this.maxReconnectAttempts\n });\n \n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this.connect().catch(error => {\n console.error('[WebSocket] Reconnection failed:', error);\n });\n }\n }, delay);\n }\n\n _startHeartbeat() {\n if (!this.pingInterval) return;\n if (this.pingTimer) return; // avoid duplicates\n \n this.pingTimer = setInterval(() => {\n if (this.isConnected && this.socket) {\n // pause pings when hidden (optional)\n if (this.pausePingWhenHidden && typeof document !== 'undefined' && document.hidden) {\n return;\n }\n this.ping();\n this._startPongTimeout();\n }\n }, this.pingInterval);\n }\n\n _startPongTimeout() {\n this._clearPongTimeout();\n this.pongTimer = setTimeout(() => {\n console.warn('[WebSocket] Pong timeout - connection may be stale');\n if (this.socket && typeof this.socket.close === 'function') {\n this.socket.close(1006, 'Pong timeout');\n }\n }, this.pongTimeout);\n }\n\n _clearPongTimeout() {\n if (this.pongTimer) {\n clearTimeout(this.pongTimer);\n this.pongTimer = null;\n }\n }\n\n _clearTimers() {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n \n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n \n this._clearPongTimeout();\n }\n\n /**\n * Handle visibility change for heartbeat\n */\n _onVisibilityChange() {\n if (!this.pausePingWhenHidden) return;\n if (typeof document === 'undefined') return;\n if (document.hidden) {\n // Pause heartbeat to save power\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n } else {\n // Resume heartbeat and send a ping\n this._startHeartbeat();\n this.ping();\n }\n }\n\n /**\n * Static method to create and connect WebSocket\n * @param {string} url - WebSocket URL\n * @param {object} options - Connection options\n * @returns {Promise<WebSocketClient>} Connected WebSocket client\n */\n static async connect(url, options = {}) {\n const client = new WebSocketClient({ ...options, url });\n await client.connect();\n return client;\n }\n}"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";class WebSocketClient{constructor(t={}){this.url=t.url||null,this.protocols=t.protocols||[],this.socket=null,this.isConnected=!1,this.isConnecting=!1,this.shouldReconnect=!1!==t.autoReconnect,this._authed=!1,this._authFailed=!1,this.maxReconnectAttempts=Number.isFinite(t.maxReconnectAttempts)?t.maxReconnectAttempts:1/0,this.reconnectInterval=t.reconnectInterval||3e3,this.reconnectBackoff=t.reconnectBackoff||1.5,this.reconnectAttempts=0,this.reconnectTimer=null,this.pingInterval=t.pingInterval||3e4,this.pongTimeout=t.pongTimeout||5e3,this.pingTimer=null,this.pongTimer=null,this.pausePingWhenHidden=!1!==t.pausePingWhenHidden,this._onVisibilityChange=this._onVisibilityChange?.bind?this._onVisibilityChange.bind(this):()=>{},this.getToken=t.getToken||null,this.tokenPrefix=t.tokenPrefix||"bearer",this.refreshToken=t.refreshToken||null,this.autoSubscribeOwnTopic=!1!==t.autoSubscribeOwnTopic,this.subscriptions=/* @__PURE__ */new Set,this.eventBus=t.eventBus||null,this.listeners={},this.dataTransform=t.dataTransform||null,this.debug=t.debug||!1,this._onOpen=this._onOpen.bind(this),this._onMessage=this._onMessage.bind(this),this._onError=this._onError.bind(this),this._onClose=this._onClose.bind(this)}connect(t=null){if(t&&(this.url=t),!this.url)throw new Error("WebSocket URL is required");return this.isConnected||this.isConnecting?Promise.resolve():(this.isConnecting=!0,new Promise((t,e)=>{try{this.debug&&this.url,this.socket=new WebSocket(this.url,this.protocols),this.socket.addEventListener("open",this._onOpen),this.socket.addEventListener("message",this._onMessage),this.socket.addEventListener("error",this._onError),this.socket.addEventListener("close",this._onClose),this._connectResolve=t,this._connectReject=e}catch(i){this.isConnecting=!1,e(i)}}))}disconnect(){this.shouldReconnect=!1,this._clearTimers(),this.socket&&(this.debug,this.socket.close(1e3,"Client disconnecting"))}send(t){if(!this.isConnected||!this.socket)throw new Error("WebSocket is not connected");const e="string"==typeof t?t:JSON.stringify(t);this.socket.send(e),this.debug}subscribe(t){t&&(this.subscriptions.add(t),this._authed&&this.isConnected&&this.send({action:"subscribe",topic:t}))}unsubscribe(t){t&&(this.subscriptions.delete(t),this.isConnected&&this.send({action:"unsubscribe",topic:t}))}ping(){this.isConnected&&this.send({action:"ping"})}on(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)}off(t,e){if(!this.listeners[t])return;if(!e)return void delete this.listeners[t];const i=this.listeners[t].indexOf(e);-1!==i&&this.listeners[t].splice(i,1)}emit(t,e){this.listeners[t]&&this.listeners[t].forEach(i=>{try{i(e)}catch(s){console.error(`[WebSocket] Error in ${t} listener:`,s)}}),this.eventBus&&"function"==typeof this.eventBus.emit&&this.eventBus.emit(`websocket:${t}`,{websocket:this,data:e})}getStatus(){return this.isConnected?"connected":this.isConnecting?"connecting":"disconnected"}getStats(){return{url:this.url,isConnected:this.isConnected,isConnecting:this.isConnecting,reconnectAttempts:this.reconnectAttempts,maxReconnectAttempts:this.maxReconnectAttempts,shouldReconnect:this.shouldReconnect}}_onOpen(t){this.debug,this.isConnected=!0,this.isConnecting=!1,this.reconnectAttempts=0,this._authed=!1,this._startHeartbeat(),"undefined"!=typeof document&&document.addEventListener&&(document.removeEventListener("visibilitychange",this._onVisibilityChange),document.addEventListener("visibilitychange",this._onVisibilityChange));const e=this.getToken?this.getToken():null;e?this.send({type:"authenticate",token:e,prefix:this.tokenPrefix||void 0}):console.warn("[WebSocket] No token provided at open; waiting for auth_required or external authenticate"),this._connectResolve&&(this._connectResolve(),this._connectResolve=null,this._connectReject=null),this.emit("connected",{url:this.url})}_onMessage(t){let e;this.debug&&t.data;try{e=JSON.parse(t.data)}catch(i){e=t.data}if(e&&"pong"===e.type)this._clearPongTimeout();else{if(this.dataTransform&&"function"==typeof this.dataTransform)try{e=this.dataTransform(e)}catch(i){return console.error("[WebSocket] Error transforming data:",i),void this.emit("error",{error:i,originalData:e})}if(e&&"object"==typeof e)switch(e.type){case"auth_required":this.emit("auth-required",e);break;case"auth_timeout":return this._authFailed=!0,this.emit("auth-timeout",e),void this.disconnect();case"auth_success":if(this._authed=!0,this.emit("auth-success",e),this.autoSubscribeOwnTopic){const{instance_kind:t,instance_id:i}=e;t&&void 0!==i&&this.subscribe(`${t}:${i}`)}for(const t of this.subscriptions)this.send({action:"subscribe",topic:t});break;case"subscribed":this.emit("subscribed",e);break;case"unsubscribed":this.emit("unsubscribed",e);break;case"notification":this.emit("notification",e);break;case"error":{this.emit("server-error",e);const t=(e.message||"").toString();if(/token|auth/i.test(t)){this.refreshToken?(async()=>{try{await this.refreshToken(),this.disconnect(),setTimeout(()=>this.connect().catch(()=>{}),250)}catch(t){console.warn("[WebSocket] Token refresh failed:",t),this._authFailed=!0,this.disconnect()}})():(this._authFailed=!0,this.disconnect());break}break}}this.emit("message",e),this.emit("data",e)}}_onError(t){console.error("[WebSocket] Error:",t),this.emit("error",{error:t.error||new Error("WebSocket error"),event:t}),this._connectReject&&(this._connectReject(t.error||new Error("WebSocket connection failed")),this._connectResolve=null,this._connectReject=null)}_onClose(t){this.debug&&(t.code,t.reason),this.isConnected=!1,this.isConnecting=!1,this._authed=!1,this._clearTimers(),"undefined"!=typeof document&&document.removeEventListener&&document.removeEventListener("visibilitychange",this._onVisibilityChange),this.socket&&(this.socket.removeEventListener("open",this._onOpen),this.socket.removeEventListener("message",this._onMessage),this.socket.removeEventListener("error",this._onError),this.socket.removeEventListener("close",this._onClose),this.socket=null),this.emit("disconnected",{code:t.code,reason:t.reason,wasClean:t.wasClean}),this.shouldReconnect&&!this._authFailed&&1e3!==t.code&&this._attemptReconnect()}_attemptReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts)return this.debug,void this.emit("reconnect-failed",{attempts:this.reconnectAttempts,maxAttempts:this.maxReconnectAttempts});this.reconnectAttempts++;const t=this.reconnectInterval*Math.pow(this.reconnectBackoff,this.reconnectAttempts-1);this.debug&&this.reconnectAttempts,this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:t,maxAttempts:this.maxReconnectAttempts}),this.reconnectTimer=setTimeout(()=>{this.shouldReconnect&&this.connect().catch(t=>{console.error("[WebSocket] Reconnection failed:",t)})},t)}_startHeartbeat(){this.pingInterval&&(this.pingTimer||(this.pingTimer=setInterval(()=>{if(this.isConnected&&this.socket){if(this.pausePingWhenHidden&&"undefined"!=typeof document&&document.hidden)return;this.ping(),this._startPongTimeout()}},this.pingInterval)))}_startPongTimeout(){this._clearPongTimeout(),this.pongTimer=setTimeout(()=>{console.warn("[WebSocket] Pong timeout - connection may be stale"),this.socket&&"function"==typeof this.socket.close&&this.socket.close(1006,"Pong timeout")},this.pongTimeout)}_clearPongTimeout(){this.pongTimer&&(clearTimeout(this.pongTimer),this.pongTimer=null)}_clearTimers(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this._clearPongTimeout()}_onVisibilityChange(){this.pausePingWhenHidden&&"undefined"!=typeof document&&(document.hidden?this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null):(this._startHeartbeat(),this.ping()))}static async connect(t,e={}){const i=new WebSocketClient({...e,url:t});return await i.connect(),i}}exports.WebSocketClient=WebSocketClient;
|
|
2
|
-
//# sourceMappingURL=WebSocketClient-BITilqco.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketClient-BITilqco.js","sources":["../../src/core/services/WebSocketClient.js"],"sourcesContent":["/**\n * WebSocket - Real-time WebSocket client for MOJO framework\n * Provides connection management, auto-reconnect, and event integration\n */\n\nexport default class WebSocketClient {\n constructor(options = {}) {\n this.url = options.url || null;\n this.protocols = options.protocols || [];\n \n // Connection state\n this.socket = null;\n this.isConnected = false;\n this.isConnecting = false;\n this.shouldReconnect = options.autoReconnect !== false;\n this._authed = false;\n this._authFailed = false;\n \n // Reconnection settings\n this.maxReconnectAttempts = Number.isFinite(options.maxReconnectAttempts) ? options.maxReconnectAttempts : Infinity;\n this.reconnectInterval = options.reconnectInterval || 3000;\n this.reconnectBackoff = options.reconnectBackoff || 1.5;\n this.reconnectAttempts = 0;\n this.reconnectTimer = null;\n \n // Heartbeat/ping settings (JSON ping/pong)\n this.pingInterval = options.pingInterval || 30000;\n this.pongTimeout = options.pongTimeout || 5000;\n this.pingTimer = null;\n this.pongTimer = null;\n this.pausePingWhenHidden = options.pausePingWhenHidden !== false; // default true\n this._onVisibilityChange = this._onVisibilityChange?.bind ? this._onVisibilityChange.bind(this) : () => {};\n \n // Auth / token\n this.getToken = options.getToken || null; // () => string\n this.tokenPrefix = options.tokenPrefix || 'bearer';\n this.refreshToken = options.refreshToken || null; // async () => string\n this.autoSubscribeOwnTopic = options.autoSubscribeOwnTopic !== false; // default true\n \n // Subscriptions\n this.subscriptions = new Set(); // always re-subscribe post-auth_success\n \n // Event handling\n this.eventBus = options.eventBus || null;\n this.listeners = {};\n \n // Data transformation\n this.dataTransform = options.dataTransform || null;\n \n // Options\n this.debug = options.debug || false;\n \n // Bind methods\n this._onOpen = this._onOpen.bind(this);\n this._onMessage = this._onMessage.bind(this);\n this._onError = this._onError.bind(this);\n this._onClose = this._onClose.bind(this);\n }\n\n /**\n * Connect to WebSocket server\n * @param {string} url - WebSocket URL (optional if set in constructor)\n * @returns {Promise} Promise that resolves when connected\n */\n connect(url = null) {\n if (url) {\n this.url = url;\n }\n \n if (!this.url) {\n throw new Error('WebSocket URL is required');\n }\n \n if (this.isConnected || this.isConnecting) {\n return Promise.resolve();\n }\n \n this.isConnecting = true;\n \n return new Promise((resolve, reject) => {\n try {\n this.debug && console.log('[WebSocket] Connecting to:', this.url);\n \n this.socket = new WebSocket(this.url, this.protocols);\n \n this.socket.addEventListener('open', this._onOpen);\n this.socket.addEventListener('message', this._onMessage);\n this.socket.addEventListener('error', this._onError);\n this.socket.addEventListener('close', this._onClose);\n \n // Store promise resolvers for connection result\n this._connectResolve = resolve;\n this._connectReject = reject;\n \n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from WebSocket server\n */\n disconnect() {\n this.shouldReconnect = false;\n this._clearTimers();\n \n if (this.socket) {\n this.debug && console.log('[WebSocket] Disconnecting');\n this.socket.close(1000, 'Client disconnecting');\n }\n }\n\n /**\n * Send data to WebSocket server\n * @param {*} data - Data to send\n */\n send(data) {\n if (!this.isConnected || !this.socket) {\n throw new Error('WebSocket is not connected');\n }\n \n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.socket.send(message);\n \n this.debug && console.log('[WebSocket] Sent:', message);\n }\n\n /**\n * Subscribe to a topic (queued until authenticated)\n * @param {string} topic\n */\n subscribe(topic) {\n if (!topic) return;\n this.subscriptions.add(topic);\n if (this._authed && this.isConnected) {\n this.send({ action: 'subscribe', topic });\n }\n }\n\n /**\n * Unsubscribe from a topic\n * @param {string} topic\n */\n unsubscribe(topic) {\n if (!topic) return;\n this.subscriptions.delete(topic);\n if (this.isConnected) {\n this.send({ action: 'unsubscribe', topic });\n }\n }\n\n /**\n * Send ping (application-level heartbeat)\n */\n ping() {\n if (this.isConnected) {\n this.send({ action: 'ping' });\n }\n }\n\n /**\n * Add event listener\n * @param {string} event - Event name\n * @param {function} callback - Event callback\n */\n on(event, callback) {\n if (!this.listeners[event]) {\n this.listeners[event] = [];\n }\n this.listeners[event].push(callback);\n }\n\n /**\n * Remove event listener\n * @param {string} event - Event name\n * @param {function} callback - Event callback to remove\n */\n off(event, callback) {\n if (!this.listeners[event]) return;\n \n if (!callback) {\n delete this.listeners[event];\n return;\n }\n \n const index = this.listeners[event].indexOf(callback);\n if (index !== -1) {\n this.listeners[event].splice(index, 1);\n }\n }\n\n /**\n * Emit event to listeners\n * @param {string} event - Event name\n * @param {*} data - Event data\n */\n emit(event, data) {\n // Emit to local listeners\n if (this.listeners[event]) {\n this.listeners[event].forEach(callback => {\n try {\n callback(data);\n } catch (error) {\n console.error(`[WebSocket] Error in ${event} listener:`, error);\n }\n });\n }\n \n // Emit to external EventBus if available\n if (this.eventBus && typeof this.eventBus.emit === 'function') {\n this.eventBus.emit(`websocket:${event}`, {\n websocket: this,\n data\n });\n }\n }\n\n /**\n * Get current connection status\n * @returns {string} Connection status\n */\n getStatus() {\n if (this.isConnected) return 'connected';\n if (this.isConnecting) return 'connecting';\n return 'disconnected';\n }\n\n /**\n * Get connection statistics\n * @returns {object} Connection stats\n */\n getStats() {\n return {\n url: this.url,\n isConnected: this.isConnected,\n isConnecting: this.isConnecting,\n reconnectAttempts: this.reconnectAttempts,\n maxReconnectAttempts: this.maxReconnectAttempts,\n shouldReconnect: this.shouldReconnect\n };\n }\n\n // Private methods\n\n _onOpen(event) {\n this.debug && console.log('[WebSocket] Connected');\n \n this.isConnected = true;\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n this._authed = false;\n \n // Start heartbeat\n this._startHeartbeat();\n // Visibility handling for heartbeat\n if (typeof document !== 'undefined' && document.addEventListener) {\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n }\n \n // Authenticate immediately\n const token = this.getToken ? this.getToken() : null;\n if (!token) {\n console.warn('[WebSocket] No token provided at open; waiting for auth_required or external authenticate');\n } else {\n this.send({ type: 'authenticate', token, prefix: this.tokenPrefix || undefined });\n }\n \n // Resolve connection promise\n if (this._connectResolve) {\n this._connectResolve();\n this._connectResolve = null;\n this._connectReject = null;\n }\n \n this.emit('connected', { url: this.url });\n }\n\n _onMessage(event) {\n this.debug && console.log('[WebSocket] Received:', event.data);\n \n let data;\n try {\n data = JSON.parse(event.data);\n } catch (error) {\n data = event.data;\n }\n \n // Handle heartbeat pong (JSON only)\n if (data && data.type === 'pong') {\n this._clearPongTimeout();\n return;\n }\n \n // Transform data if transformer provided\n if (this.dataTransform && typeof this.dataTransform === 'function') {\n try {\n data = this.dataTransform(data);\n } catch (error) {\n console.error('[WebSocket] Error transforming data:', error);\n this.emit('error', { error, originalData: data });\n return;\n }\n }\n\n // Protocol handling\n if (data && typeof data === 'object') {\n switch (data.type) {\n case 'auth_required':\n this.emit('auth-required', data);\n break;\n case 'auth_timeout': {\n this._authFailed = true;\n this.emit('auth-timeout', data);\n this.disconnect();\n return;\n }\n case 'auth_success': {\n this._authed = true;\n this.emit('auth-success', data);\n // Auto-subscribe to own topic if enabled\n if (this.autoSubscribeOwnTopic) {\n const { instance_kind, instance_id } = data;\n if (instance_kind && instance_id !== undefined) {\n this.subscribe(`${instance_kind}:${instance_id}`);\n }\n }\n // Flush desired subscriptions\n for (const topic of this.subscriptions) {\n this.send({ action: 'subscribe', topic });\n }\n break;\n }\n case 'subscribed':\n this.emit('subscribed', data);\n break;\n case 'unsubscribed':\n this.emit('unsubscribed', data);\n break;\n case 'notification':\n this.emit('notification', data);\n break;\n case 'error': {\n this.emit('server-error', data);\n const msg = (data.message || '').toString();\n if (/token|auth/i.test(msg)) {\n if (this.refreshToken) {\n (async () => {\n try {\n await this.refreshToken();\n this.disconnect();\n // allow a short delay before reconnect to use refreshed token\n setTimeout(() => this.connect().catch(() => {}), 250);\n } catch (e) {\n console.warn('[WebSocket] Token refresh failed:', e);\n this._authFailed = true;\n this.disconnect();\n }\n })();\n } else {\n this._authFailed = true;\n this.disconnect();\n }\n break;\n }\n break;\n }\n default:\n break;\n }\n }\n \n this.emit('message', data);\n this.emit('data', data); // Alias for convenience\n }\n\n _onError(event) {\n console.error('[WebSocket] Error:', event);\n \n this.emit('error', { \n error: event.error || new Error('WebSocket error'),\n event \n });\n \n // Reject connection promise if still connecting\n if (this._connectReject) {\n this._connectReject(event.error || new Error('WebSocket connection failed'));\n this._connectResolve = null;\n this._connectReject = null;\n }\n }\n\n _onClose(event) {\n this.debug && console.log('[WebSocket] Closed:', event.code, event.reason);\n \n this.isConnected = false;\n this.isConnecting = false;\n this._authed = false;\n \n this._clearTimers();\n if (typeof document !== 'undefined' && document.removeEventListener) {\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n }\n \n // Clean up socket\n if (this.socket) {\n this.socket.removeEventListener('open', this._onOpen);\n this.socket.removeEventListener('message', this._onMessage);\n this.socket.removeEventListener('error', this._onError);\n this.socket.removeEventListener('close', this._onClose);\n this.socket = null;\n }\n \n this.emit('disconnected', { \n code: event.code, \n reason: event.reason,\n wasClean: event.wasClean\n });\n \n // Attempt reconnection if enabled and not a clean close and not due to auth failure\n if (this.shouldReconnect && !this._authFailed && event.code !== 1000) {\n this._attemptReconnect();\n }\n }\n\n _attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.debug && console.log('[WebSocket] Max reconnect attempts reached');\n this.emit('reconnect-failed', { \n attempts: this.reconnectAttempts,\n maxAttempts: this.maxReconnectAttempts\n });\n return;\n }\n \n this.reconnectAttempts++;\n const delay = this.reconnectInterval * Math.pow(this.reconnectBackoff, this.reconnectAttempts - 1);\n \n this.debug && console.log(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n \n this.emit('reconnecting', { \n attempt: this.reconnectAttempts,\n delay,\n maxAttempts: this.maxReconnectAttempts\n });\n \n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this.connect().catch(error => {\n console.error('[WebSocket] Reconnection failed:', error);\n });\n }\n }, delay);\n }\n\n _startHeartbeat() {\n if (!this.pingInterval) return;\n if (this.pingTimer) return; // avoid duplicates\n \n this.pingTimer = setInterval(() => {\n if (this.isConnected && this.socket) {\n // pause pings when hidden (optional)\n if (this.pausePingWhenHidden && typeof document !== 'undefined' && document.hidden) {\n return;\n }\n this.ping();\n this._startPongTimeout();\n }\n }, this.pingInterval);\n }\n\n _startPongTimeout() {\n this._clearPongTimeout();\n this.pongTimer = setTimeout(() => {\n console.warn('[WebSocket] Pong timeout - connection may be stale');\n if (this.socket && typeof this.socket.close === 'function') {\n this.socket.close(1006, 'Pong timeout');\n }\n }, this.pongTimeout);\n }\n\n _clearPongTimeout() {\n if (this.pongTimer) {\n clearTimeout(this.pongTimer);\n this.pongTimer = null;\n }\n }\n\n _clearTimers() {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n \n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n \n this._clearPongTimeout();\n }\n\n /**\n * Handle visibility change for heartbeat\n */\n _onVisibilityChange() {\n if (!this.pausePingWhenHidden) return;\n if (typeof document === 'undefined') return;\n if (document.hidden) {\n // Pause heartbeat to save power\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n } else {\n // Resume heartbeat and send a ping\n this._startHeartbeat();\n this.ping();\n }\n }\n\n /**\n * Static method to create and connect WebSocket\n * @param {string} url - WebSocket URL\n * @param {object} options - Connection options\n * @returns {Promise<WebSocketClient>} Connected WebSocket client\n */\n static async connect(url, options = {}) {\n const client = new WebSocketClient({ ...options, url });\n await client.connect();\n return client;\n }\n}"],"names":["WebSocketClient","constructor","options","this","url","protocols","socket","isConnected","isConnecting","shouldReconnect","autoReconnect","_authed","_authFailed","maxReconnectAttempts","Number","isFinite","Infinity","reconnectInterval","reconnectBackoff","reconnectAttempts","reconnectTimer","pingInterval","pongTimeout","pingTimer","pongTimer","pausePingWhenHidden","_onVisibilityChange","bind","getToken","tokenPrefix","refreshToken","autoSubscribeOwnTopic","subscriptions","Set","eventBus","listeners","dataTransform","debug","_onOpen","_onMessage","_onError","_onClose","connect","Error","Promise","resolve","reject","WebSocket","addEventListener","_connectResolve","_connectReject","error","disconnect","_clearTimers","close","send","data","message","JSON","stringify","subscribe","topic","add","action","unsubscribe","delete","ping","on","event","callback","push","off","index","indexOf","splice","emit","forEach","console","websocket","getStatus","getStats","_startHeartbeat","document","removeEventListener","token","type","prefix","warn","parse","_clearPongTimeout","originalData","instance_kind","instance_id","msg","toString","test","setTimeout","catch","e","code","reason","wasClean","_attemptReconnect","attempts","maxAttempts","delay","Math","pow","attempt","setInterval","hidden","_startPongTimeout","clearTimeout","clearInterval","client"],"mappings":"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"}
|