@metamask/core-backend 3.0.0 → 4.1.0
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/CHANGELOG.md +48 -1
- package/README.md +1 -1
- package/dist/AccountActivityService.cjs +19 -7
- package/dist/AccountActivityService.cjs.map +1 -1
- package/dist/AccountActivityService.d.cts +4 -4
- package/dist/AccountActivityService.d.cts.map +1 -1
- package/dist/AccountActivityService.d.mts +4 -4
- package/dist/AccountActivityService.d.mts.map +1 -1
- package/dist/AccountActivityService.mjs +19 -7
- package/dist/AccountActivityService.mjs.map +1 -1
- package/dist/BackendWebSocketService.cjs +128 -174
- package/dist/BackendWebSocketService.cjs.map +1 -1
- package/dist/BackendWebSocketService.d.cts +8 -6
- package/dist/BackendWebSocketService.d.cts.map +1 -1
- package/dist/BackendWebSocketService.d.mts +8 -6
- package/dist/BackendWebSocketService.d.mts.map +1 -1
- package/dist/BackendWebSocketService.mjs +128 -174
- package/dist/BackendWebSocketService.mjs.map +1 -1
- package/dist/index.cjs +2 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -11
|
@@ -9,13 +9,18 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _BackendWebSocketService_instances, _BackendWebSocketService_messenger, _BackendWebSocketService_options, _BackendWebSocketService_isEnabled, _BackendWebSocketService_trace, _BackendWebSocketService_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_stableConnectionTimer, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt,
|
|
12
|
+
var _BackendWebSocketService_instances, _BackendWebSocketService_messenger, _BackendWebSocketService_options, _BackendWebSocketService_isEnabled, _BackendWebSocketService_trace, _BackendWebSocketService_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_stableConnectionTimer, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt, _BackendWebSocketService_subscriptions, _BackendWebSocketService_channelCallbacks, _BackendWebSocketService_backoff, _BackendWebSocketService_subscribeEvents, _BackendWebSocketService_buildAuthenticatedUrl, _BackendWebSocketService_establishConnection, _BackendWebSocketService_handleMessage, _BackendWebSocketService_isServerResponse, _BackendWebSocketService_isSubscriptionNotification, _BackendWebSocketService_isChannelMessage, _BackendWebSocketService_handleServerResponse, _BackendWebSocketService_handleChannelMessage, _BackendWebSocketService_handleSubscriptionNotification, _BackendWebSocketService_parseMessage, _BackendWebSocketService_handleError, _BackendWebSocketService_scheduleReconnect, _BackendWebSocketService_newBackoff, _BackendWebSocketService_clearTimers, _BackendWebSocketService_clearPendingRequests, _BackendWebSocketService_clearSubscriptions, _BackendWebSocketService_setState;
|
|
13
13
|
import { ExponentialBackoff } from "@metamask/controller-utils";
|
|
14
14
|
import { getErrorMessage } from "@metamask/utils";
|
|
15
15
|
import { v4 as uuidV4 } from "uuid";
|
|
16
16
|
import { projectLogger, createModuleLogger } from "./logger.mjs";
|
|
17
17
|
const SERVICE_NAME = 'BackendWebSocketService';
|
|
18
18
|
const log = createModuleLogger(projectLogger, SERVICE_NAME);
|
|
19
|
+
// WebSocket close codes and reasons for internal operations
|
|
20
|
+
const MANUAL_DISCONNECT_CODE = 4999;
|
|
21
|
+
const MANUAL_DISCONNECT_REASON = 'Internal: Manual disconnect';
|
|
22
|
+
const FORCE_RECONNECT_CODE = 4998;
|
|
23
|
+
const FORCE_RECONNECT_REASON = 'Internal: Force reconnect';
|
|
19
24
|
const MESSENGER_EXPOSED_METHODS = [
|
|
20
25
|
'connect',
|
|
21
26
|
'disconnect',
|
|
@@ -88,8 +93,10 @@ export var WebSocketState;
|
|
|
88
93
|
(function (WebSocketState) {
|
|
89
94
|
WebSocketState["CONNECTING"] = "connecting";
|
|
90
95
|
WebSocketState["CONNECTED"] = "connected";
|
|
96
|
+
/** @deprecated This value is no longer used internally and will be removed in a future major release */
|
|
91
97
|
WebSocketState["DISCONNECTING"] = "disconnecting";
|
|
92
98
|
WebSocketState["DISCONNECTED"] = "disconnected";
|
|
99
|
+
/** @deprecated TThis value is no longer used internally and will be removed in a future major release */
|
|
93
100
|
WebSocketState["ERROR"] = "error";
|
|
94
101
|
})(WebSocketState || (WebSocketState = {}));
|
|
95
102
|
/**
|
|
@@ -155,8 +162,6 @@ export class BackendWebSocketService {
|
|
|
155
162
|
_BackendWebSocketService_connectionPromise.set(this, null);
|
|
156
163
|
_BackendWebSocketService_pendingRequests.set(this, new Map());
|
|
157
164
|
_BackendWebSocketService_connectedAt.set(this, 0);
|
|
158
|
-
// Track manual disconnects to prevent automatic reconnection
|
|
159
|
-
_BackendWebSocketService_manualDisconnect.set(this, false);
|
|
160
165
|
// Simplified subscription storage (single flat map)
|
|
161
166
|
// Key: subscription ID string (e.g., 'sub_abc123def456')
|
|
162
167
|
// Value: WebSocketSubscription object with channels, callback and metadata
|
|
@@ -176,8 +181,8 @@ export class BackendWebSocketService {
|
|
|
176
181
|
__classPrivateFieldSet(this, _BackendWebSocketService_options, {
|
|
177
182
|
url: options.url,
|
|
178
183
|
timeout: options.timeout ?? 10000,
|
|
179
|
-
reconnectDelay: options.reconnectDelay ??
|
|
180
|
-
maxReconnectDelay: options.maxReconnectDelay ??
|
|
184
|
+
reconnectDelay: options.reconnectDelay ?? 10000,
|
|
185
|
+
maxReconnectDelay: options.maxReconnectDelay ?? 60000,
|
|
181
186
|
requestTimeout: options.requestTimeout ?? 30000,
|
|
182
187
|
}, "f");
|
|
183
188
|
// Initialize backoff for reconnection delays
|
|
@@ -204,8 +209,6 @@ export class BackendWebSocketService {
|
|
|
204
209
|
* @returns Promise that resolves when connection is established
|
|
205
210
|
*/
|
|
206
211
|
async connect() {
|
|
207
|
-
// Reset manual disconnect flag when explicitly connecting
|
|
208
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_manualDisconnect, false, "f");
|
|
209
212
|
// Priority 1: Check if feature is enabled via callback (feature flag check)
|
|
210
213
|
// If feature is disabled, stop all connection attempts
|
|
211
214
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
|
|
@@ -244,17 +247,8 @@ export class BackendWebSocketService {
|
|
|
244
247
|
log('Failed to check authentication requirements', { error });
|
|
245
248
|
throw error;
|
|
246
249
|
}
|
|
247
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
|
|
248
250
|
// Establish the actual WebSocket connection
|
|
249
|
-
|
|
250
|
-
await __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_establishConnection).call(this, bearerToken);
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
const errorMessage = getErrorMessage(error);
|
|
254
|
-
log('Connection attempt failed', { errorMessage, error });
|
|
255
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
|
|
256
|
-
throw error;
|
|
257
|
-
}
|
|
251
|
+
await __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_establishConnection).call(this, bearerToken);
|
|
258
252
|
})(), "f");
|
|
259
253
|
try {
|
|
260
254
|
await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
|
|
@@ -273,22 +267,11 @@ export class BackendWebSocketService {
|
|
|
273
267
|
* Closes WebSocket connection
|
|
274
268
|
*/
|
|
275
269
|
disconnect() {
|
|
276
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED ||
|
|
277
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
|
|
270
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED || !__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
|
|
278
271
|
return;
|
|
279
272
|
}
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTING);
|
|
283
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
284
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
|
|
285
|
-
// Clear any pending connection promise
|
|
286
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
|
|
287
|
-
// Reset reconnect attempts on manual disconnect
|
|
288
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
289
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
|
|
290
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
|
|
291
|
-
}
|
|
273
|
+
// Close WebSocket with manual disconnect code and reason
|
|
274
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(MANUAL_DISCONNECT_CODE, MANUAL_DISCONNECT_REASON);
|
|
292
275
|
log('WebSocket manually disconnected');
|
|
293
276
|
}
|
|
294
277
|
/**
|
|
@@ -308,16 +291,15 @@ export class BackendWebSocketService {
|
|
|
308
291
|
* @returns Promise that resolves when disconnection is complete (reconnection is scheduled)
|
|
309
292
|
*/
|
|
310
293
|
async forceReconnection() {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
294
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED || !__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
|
|
295
|
+
log('WebSocket already disconnected, scheduling reconnect');
|
|
296
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
314
297
|
return;
|
|
315
298
|
}
|
|
316
299
|
log('Forcing WebSocket reconnection to clean up subscription state');
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
300
|
+
// This ensures ws.onclose will schedule a reconnect (not treat it as manual disconnect)
|
|
301
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
302
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(FORCE_RECONNECT_CODE, FORCE_RECONNECT_REASON);
|
|
321
303
|
}
|
|
322
304
|
/**
|
|
323
305
|
* Sends a message through the WebSocket (fire-and-forget, no response expected)
|
|
@@ -559,17 +541,13 @@ export class BackendWebSocketService {
|
|
|
559
541
|
* Called when service is being destroyed or app is terminating
|
|
560
542
|
*/
|
|
561
543
|
destroy() {
|
|
544
|
+
// Always clear timers first to prevent reconnection attempts after destruction
|
|
545
|
+
// This handles the case where destroy() is called while DISCONNECTED with a pending reconnect timer
|
|
562
546
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('Service cleanup'));
|
|
568
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f") && __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").readyState === WebSocket.OPEN) {
|
|
569
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Service cleanup');
|
|
570
|
-
}
|
|
571
|
-
// Set state to disconnected immediately
|
|
572
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
547
|
+
// Reset reconnect attempts to prevent any future reconnection logic
|
|
548
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
549
|
+
// Disconnect the WebSocket if connected (will be no-op if already disconnected)
|
|
550
|
+
this.disconnect();
|
|
573
551
|
}
|
|
574
552
|
/**
|
|
575
553
|
* Create and manage a subscription with server-side registration (recommended for most use cases)
|
|
@@ -654,7 +632,7 @@ export class BackendWebSocketService {
|
|
|
654
632
|
// Store subscription with subscription ID as key
|
|
655
633
|
__classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").set(subscriptionId, {
|
|
656
634
|
subscriptionId,
|
|
657
|
-
channels: [...channels],
|
|
635
|
+
channels: [...channels], // Store copy of channels
|
|
658
636
|
channelType,
|
|
659
637
|
callback,
|
|
660
638
|
unsubscribe,
|
|
@@ -662,7 +640,7 @@ export class BackendWebSocketService {
|
|
|
662
640
|
return subscription;
|
|
663
641
|
}
|
|
664
642
|
}
|
|
665
|
-
_BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_options = new WeakMap(), _BackendWebSocketService_isEnabled = new WeakMap(), _BackendWebSocketService_trace = new WeakMap(), _BackendWebSocketService_ws = new WeakMap(), _BackendWebSocketService_state = new WeakMap(), _BackendWebSocketService_reconnectAttempts = new WeakMap(), _BackendWebSocketService_reconnectTimer = new WeakMap(), _BackendWebSocketService_connectionTimeout = new WeakMap(), _BackendWebSocketService_stableConnectionTimer = new WeakMap(), _BackendWebSocketService_connectionPromise = new WeakMap(), _BackendWebSocketService_pendingRequests = new WeakMap(), _BackendWebSocketService_connectedAt = new WeakMap(),
|
|
643
|
+
_BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_options = new WeakMap(), _BackendWebSocketService_isEnabled = new WeakMap(), _BackendWebSocketService_trace = new WeakMap(), _BackendWebSocketService_ws = new WeakMap(), _BackendWebSocketService_state = new WeakMap(), _BackendWebSocketService_reconnectAttempts = new WeakMap(), _BackendWebSocketService_reconnectTimer = new WeakMap(), _BackendWebSocketService_connectionTimeout = new WeakMap(), _BackendWebSocketService_stableConnectionTimer = new WeakMap(), _BackendWebSocketService_connectionPromise = new WeakMap(), _BackendWebSocketService_pendingRequests = new WeakMap(), _BackendWebSocketService_connectedAt = new WeakMap(), _BackendWebSocketService_subscriptions = new WeakMap(), _BackendWebSocketService_channelCallbacks = new WeakMap(), _BackendWebSocketService_backoff = new WeakMap(), _BackendWebSocketService_instances = new WeakSet(), _BackendWebSocketService_subscribeEvents = function _BackendWebSocketService_subscribeEvents() {
|
|
666
644
|
// Subscribe to authentication state changes (sign in/out)
|
|
667
645
|
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (state) => {
|
|
668
646
|
if (state.isSignedIn) {
|
|
@@ -697,34 +675,31 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
|
|
|
697
675
|
*/
|
|
698
676
|
async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
699
677
|
const wsUrl = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_buildAuthenticatedUrl).call(this, bearerToken);
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
},
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
service: SERVICE_NAME,
|
|
726
|
-
},
|
|
727
|
-
}, () => {
|
|
678
|
+
// Transition to CONNECTING state before creating WebSocket
|
|
679
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
|
|
680
|
+
return __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
681
|
+
name: `${SERVICE_NAME} Connection`,
|
|
682
|
+
data: {
|
|
683
|
+
reconnectAttempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
684
|
+
},
|
|
685
|
+
tags: {
|
|
686
|
+
service: SERVICE_NAME,
|
|
687
|
+
},
|
|
688
|
+
}, () => {
|
|
689
|
+
return new Promise((resolve, reject) => {
|
|
690
|
+
const ws = new WebSocket(wsUrl);
|
|
691
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, setTimeout(() => {
|
|
692
|
+
log('WebSocket connection timeout - forcing close', {
|
|
693
|
+
timeout: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout,
|
|
694
|
+
});
|
|
695
|
+
// Close the WebSocket - onclose will handle rejection and state change
|
|
696
|
+
ws.close();
|
|
697
|
+
}, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout), "f");
|
|
698
|
+
ws.onopen = () => {
|
|
699
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
700
|
+
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
701
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
702
|
+
}
|
|
728
703
|
__classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
|
|
729
704
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
|
|
730
705
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
|
|
@@ -738,45 +713,79 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
738
713
|
log('Connection stable - reset reconnect attempts and backoff');
|
|
739
714
|
}, 10000), "f");
|
|
740
715
|
resolve();
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
};
|
|
752
|
-
ws.onclose = (event) => {
|
|
753
|
-
log('WebSocket onclose event triggered', {
|
|
754
|
-
code: event.code,
|
|
755
|
-
reason: event.reason,
|
|
756
|
-
wasClean: event.wasClean,
|
|
757
|
-
});
|
|
758
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
|
|
759
|
-
// Handle connection-phase close events
|
|
760
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
761
|
-
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
762
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
716
|
+
};
|
|
717
|
+
ws.onclose = (event) => {
|
|
718
|
+
log('WebSocket onclose event triggered', {
|
|
719
|
+
code: event.code,
|
|
720
|
+
reason: event.reason || getCloseReason(event.code),
|
|
721
|
+
wasClean: event.wasClean,
|
|
722
|
+
});
|
|
723
|
+
// Guard against duplicate close events
|
|
724
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED) {
|
|
725
|
+
return;
|
|
763
726
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
727
|
+
// Detect if this is a manual disconnect or service cleanup based on close code
|
|
728
|
+
const isManualDisconnect = event.code === MANUAL_DISCONNECT_CODE &&
|
|
729
|
+
event.reason === MANUAL_DISCONNECT_REASON;
|
|
730
|
+
// If connection hasn't been established yet, handle the connection promise
|
|
731
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
|
|
732
|
+
if (isManualDisconnect) {
|
|
733
|
+
// Manual disconnect during connection - resolve to prevent reconnection
|
|
734
|
+
resolve();
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
// Failed connection attempt - reject to trigger reconnection
|
|
738
|
+
reject(new Error(`WebSocket connection closed during connection: ${event.code} ${event.reason}`));
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
// Calculate connection duration before we clear state (only if we were connected)
|
|
742
|
+
const connectionDuration_ms = __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f") > 0 ? Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f") : 0;
|
|
743
|
+
// Clear all timers
|
|
744
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
745
|
+
// Clear WebSocket reference to allow garbage collection
|
|
746
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_ws, undefined, "f");
|
|
747
|
+
// Clear connection tracking
|
|
748
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
|
|
749
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
|
|
750
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error(`WebSocket connection closed: ${event.code} ${event.reason || getCloseReason(event.code)}`));
|
|
751
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearSubscriptions).call(this);
|
|
752
|
+
// Update state to disconnected
|
|
753
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
754
|
+
// Check if this was a manual disconnect
|
|
755
|
+
if (isManualDisconnect) {
|
|
756
|
+
// Manual disconnect - reset attempts and don't reconnect
|
|
757
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
// Unexpected disconnect - schedule reconnection
|
|
761
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
762
|
+
}
|
|
763
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
764
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
765
|
+
name: `${SERVICE_NAME} Disconnection`,
|
|
766
|
+
data: {
|
|
767
|
+
code: event.code,
|
|
768
|
+
reason: event.reason || getCloseReason(event.code),
|
|
769
|
+
wasClean: event.wasClean,
|
|
770
|
+
reconnectAttempts: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
771
|
+
...(connectionDuration_ms > 0 && { connectionDuration_ms }),
|
|
772
|
+
},
|
|
773
|
+
tags: {
|
|
774
|
+
service: SERVICE_NAME,
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
};
|
|
778
|
+
// Set up message handler immediately - no need to wait for connection
|
|
779
|
+
ws.onmessage = (event) => {
|
|
780
|
+
try {
|
|
781
|
+
const message = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_parseMessage).call(this, event.data);
|
|
782
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_handleMessage).call(this, message);
|
|
783
|
+
}
|
|
784
|
+
catch {
|
|
785
|
+
// Silently ignore invalid JSON messages
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
});
|
|
780
789
|
});
|
|
781
790
|
}, _BackendWebSocketService_handleMessage = function _BackendWebSocketService_handleMessage(message) {
|
|
782
791
|
// Handle server responses (correlated with requests) first
|
|
@@ -826,23 +835,7 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
826
835
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").size === 0) {
|
|
827
836
|
return;
|
|
828
837
|
}
|
|
829
|
-
|
|
830
|
-
const receivedAt = Date.now();
|
|
831
|
-
const latency = receivedAt - message.timestamp;
|
|
832
|
-
// Trace channel message processing with latency data
|
|
833
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
834
|
-
name: `${SERVICE_NAME} Channel Message`,
|
|
835
|
-
data: {
|
|
836
|
-
latency_ms: latency,
|
|
837
|
-
event: message.event,
|
|
838
|
-
},
|
|
839
|
-
tags: {
|
|
840
|
-
service: SERVICE_NAME,
|
|
841
|
-
},
|
|
842
|
-
}, () => {
|
|
843
|
-
// Direct lookup for exact channel match
|
|
844
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").get(message.channel)?.callback(message);
|
|
845
|
-
});
|
|
838
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").get(message.channel)?.callback(message);
|
|
846
839
|
}, _BackendWebSocketService_handleSubscriptionNotification = function _BackendWebSocketService_handleSubscriptionNotification(message) {
|
|
847
840
|
const { subscriptionId, timestamp, channel } = message;
|
|
848
841
|
// Only handle if subscriptionId is defined and not null (allows "0" as valid ID)
|
|
@@ -856,6 +849,8 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
856
849
|
const latency = receivedAt - timestamp;
|
|
857
850
|
// Trace notification processing wi th latency data
|
|
858
851
|
// Use stored channelType instead of parsing each time
|
|
852
|
+
// Promise result intentionally not awaited
|
|
853
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
859
854
|
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
860
855
|
name: `${SERVICE_NAME} Notification`,
|
|
861
856
|
data: {
|
|
@@ -875,47 +870,6 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
875
870
|
return false;
|
|
876
871
|
}, _BackendWebSocketService_parseMessage = function _BackendWebSocketService_parseMessage(data) {
|
|
877
872
|
return JSON.parse(data);
|
|
878
|
-
}, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
|
|
879
|
-
// Calculate connection duration before we clear state
|
|
880
|
-
const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
|
|
881
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
882
|
-
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
883
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
884
|
-
}
|
|
885
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f")) {
|
|
886
|
-
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f"));
|
|
887
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
|
|
888
|
-
}
|
|
889
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
|
|
890
|
-
// Clear any pending connection promise
|
|
891
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
|
|
892
|
-
// Clear subscriptions and pending requests on any disconnect
|
|
893
|
-
// This ensures clean state for reconnection
|
|
894
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket connection closed'));
|
|
895
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearSubscriptions).call(this);
|
|
896
|
-
// Update state to disconnected
|
|
897
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
898
|
-
// Check if this was a manual disconnect
|
|
899
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_manualDisconnect, "f")) {
|
|
900
|
-
// Manual disconnect - don't reconnect
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
// Trace unexpected disconnect with details
|
|
904
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
905
|
-
name: `${SERVICE_NAME} Disconnect`,
|
|
906
|
-
data: {
|
|
907
|
-
code: event.code,
|
|
908
|
-
reason: event.reason || getCloseReason(event.code),
|
|
909
|
-
connectionDuration_ms: connectionDuration,
|
|
910
|
-
},
|
|
911
|
-
tags: {
|
|
912
|
-
service: SERVICE_NAME,
|
|
913
|
-
disconnect_type: 'unexpected',
|
|
914
|
-
},
|
|
915
|
-
}, () => {
|
|
916
|
-
// Empty trace callback - just measuring the event
|
|
917
|
-
});
|
|
918
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
919
873
|
}, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
|
|
920
874
|
// Placeholder for future error handling logic
|
|
921
875
|
}, _BackendWebSocketService_scheduleReconnect = function _BackendWebSocketService_scheduleReconnect() {
|