@tthr/vue 0.0.27 → 0.0.28
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/nuxt/runtime/composables.ts +26 -6
- package/package.json +1 -1
|
@@ -198,10 +198,12 @@ export function useTetherSubscription(
|
|
|
198
198
|
let heartbeatInterval: ReturnType<typeof setInterval> | null = null;
|
|
199
199
|
let heartbeatTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
200
200
|
let awaitingPong = false;
|
|
201
|
+
let isConnecting = false;
|
|
202
|
+
let isMounted = false;
|
|
201
203
|
|
|
202
204
|
// Heartbeat configuration
|
|
203
|
-
const HEARTBEAT_INTERVAL =
|
|
204
|
-
const HEARTBEAT_TIMEOUT =
|
|
205
|
+
const HEARTBEAT_INTERVAL = 25000; // 25 seconds (well under server's 90s timeout)
|
|
206
|
+
const HEARTBEAT_TIMEOUT = 15000; // 15 seconds to receive pong (generous for slow networks)
|
|
205
207
|
|
|
206
208
|
// Generate a unique subscription ID
|
|
207
209
|
const subscriptionId = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -214,7 +216,7 @@ export function useTetherSubscription(
|
|
|
214
216
|
ws.send(JSON.stringify({ type: 'ping' }));
|
|
215
217
|
|
|
216
218
|
heartbeatTimeout = setTimeout(() => {
|
|
217
|
-
if (awaitingPong) {
|
|
219
|
+
if (awaitingPong && isMounted) {
|
|
218
220
|
console.warn('[Tether] Heartbeat timeout - forcing reconnect');
|
|
219
221
|
ws?.close();
|
|
220
222
|
}
|
|
@@ -236,6 +238,13 @@ export function useTetherSubscription(
|
|
|
236
238
|
};
|
|
237
239
|
|
|
238
240
|
const connect = () => {
|
|
241
|
+
// Don't connect if unmounted or already connecting/connected
|
|
242
|
+
if (!isMounted) return;
|
|
243
|
+
if (isConnecting) return;
|
|
244
|
+
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
239
248
|
// Get config from window (set by plugin)
|
|
240
249
|
const config = (window as any).__TETHER_CONFIG__;
|
|
241
250
|
if (!config?.wsUrl || !config?.projectId) {
|
|
@@ -243,10 +252,12 @@ export function useTetherSubscription(
|
|
|
243
252
|
return;
|
|
244
253
|
}
|
|
245
254
|
|
|
255
|
+
isConnecting = true;
|
|
246
256
|
const wsUrl = `${config.wsUrl}/ws/${config.projectId}`;
|
|
247
257
|
ws = new WebSocket(wsUrl);
|
|
248
258
|
|
|
249
259
|
ws.onopen = () => {
|
|
260
|
+
isConnecting = false;
|
|
250
261
|
// Wait for connected message before subscribing
|
|
251
262
|
};
|
|
252
263
|
|
|
@@ -282,21 +293,26 @@ export function useTetherSubscription(
|
|
|
282
293
|
|
|
283
294
|
ws.onclose = () => {
|
|
284
295
|
isConnected.value = false;
|
|
296
|
+
isConnecting = false;
|
|
285
297
|
stopHeartbeat();
|
|
286
|
-
//
|
|
287
|
-
|
|
298
|
+
// Only reconnect if still mounted
|
|
299
|
+
if (isMounted) {
|
|
300
|
+
reconnectTimeout = setTimeout(connect, 3000);
|
|
301
|
+
}
|
|
288
302
|
};
|
|
289
303
|
|
|
290
304
|
ws.onerror = () => {
|
|
305
|
+
isConnecting = false;
|
|
291
306
|
ws?.close();
|
|
292
307
|
};
|
|
293
308
|
};
|
|
294
309
|
|
|
295
310
|
// Handle page visibility changes - reconnect when tab becomes visible
|
|
296
311
|
const handleVisibilityChange = () => {
|
|
312
|
+
if (!isMounted) return;
|
|
297
313
|
if (document.visibilityState === 'visible') {
|
|
298
314
|
// Tab became visible - check connection health
|
|
299
|
-
if (!ws || ws.readyState
|
|
315
|
+
if (!ws || ws.readyState === WebSocket.CLOSED) {
|
|
300
316
|
// Clear any pending reconnect and connect immediately
|
|
301
317
|
if (reconnectTimeout) {
|
|
302
318
|
clearTimeout(reconnectTimeout);
|
|
@@ -308,18 +324,22 @@ export function useTetherSubscription(
|
|
|
308
324
|
};
|
|
309
325
|
|
|
310
326
|
onMounted(() => {
|
|
327
|
+
isMounted = true;
|
|
311
328
|
connect();
|
|
312
329
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
313
330
|
});
|
|
314
331
|
|
|
315
332
|
onUnmounted(() => {
|
|
333
|
+
isMounted = false;
|
|
316
334
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
317
335
|
stopHeartbeat();
|
|
318
336
|
if (reconnectTimeout) {
|
|
319
337
|
clearTimeout(reconnectTimeout);
|
|
338
|
+
reconnectTimeout = null;
|
|
320
339
|
}
|
|
321
340
|
if (ws) {
|
|
322
341
|
ws.close();
|
|
342
|
+
ws = null;
|
|
323
343
|
}
|
|
324
344
|
});
|
|
325
345
|
}
|