@metamask/core-backend 2.0.0 → 3.0.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.
@@ -10,9 +10,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  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");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _BackendWebSocketService_instances, _BackendWebSocketService_messenger, _BackendWebSocketService_options, _BackendWebSocketService_isEnabled, _BackendWebSocketService_trace, _BackendWebSocketService_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt, _BackendWebSocketService_manualDisconnect, _BackendWebSocketService_subscriptions, _BackendWebSocketService_channelCallbacks, _BackendWebSocketService_subscribeEvents, _BackendWebSocketService_buildAuthenticatedUrl, _BackendWebSocketService_establishConnection, _BackendWebSocketService_handleMessage, _BackendWebSocketService_isServerResponse, _BackendWebSocketService_isSubscriptionNotification, _BackendWebSocketService_isChannelMessage, _BackendWebSocketService_handleServerResponse, _BackendWebSocketService_handleChannelMessage, _BackendWebSocketService_handleSubscriptionNotification, _BackendWebSocketService_parseMessage, _BackendWebSocketService_handleClose, _BackendWebSocketService_handleError, _BackendWebSocketService_scheduleReconnect, _BackendWebSocketService_clearTimers, _BackendWebSocketService_clearPendingRequests, _BackendWebSocketService_clearSubscriptions, _BackendWebSocketService_setState;
13
+ 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_manualDisconnect, _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_handleClose, _BackendWebSocketService_handleError, _BackendWebSocketService_scheduleReconnect, _BackendWebSocketService_newBackoff, _BackendWebSocketService_clearTimers, _BackendWebSocketService_clearPendingRequests, _BackendWebSocketService_clearSubscriptions, _BackendWebSocketService_setState;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.BackendWebSocketService = exports.WebSocketEventType = exports.WebSocketState = exports.getCloseReason = void 0;
16
+ const controller_utils_1 = require("@metamask/controller-utils");
16
17
  const utils_1 = require("@metamask/utils");
17
18
  const uuid_1 = require("uuid");
18
19
  const logger_1 = require("./logger.cjs");
@@ -21,6 +22,7 @@ const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, SERVICE_NAM
21
22
  const MESSENGER_EXPOSED_METHODS = [
22
23
  'connect',
23
24
  'disconnect',
25
+ 'forceReconnection',
24
26
  'sendMessage',
25
27
  'sendRequest',
26
28
  'subscribe',
@@ -152,6 +154,7 @@ class BackendWebSocketService {
152
154
  _BackendWebSocketService_reconnectAttempts.set(this, 0);
153
155
  _BackendWebSocketService_reconnectTimer.set(this, null);
154
156
  _BackendWebSocketService_connectionTimeout.set(this, null);
157
+ _BackendWebSocketService_stableConnectionTimer.set(this, null);
155
158
  // Track the current connection promise to handle concurrent connection attempts
156
159
  _BackendWebSocketService_connectionPromise.set(this, null);
157
160
  _BackendWebSocketService_pendingRequests.set(this, new Map());
@@ -166,6 +169,8 @@ class BackendWebSocketService {
166
169
  // Key: channel name (serves as unique identifier)
167
170
  // Value: ChannelCallback configuration
168
171
  _BackendWebSocketService_channelCallbacks.set(this, new Map());
172
+ // Backoff instance for reconnection delays (reset on stable connection)
173
+ _BackendWebSocketService_backoff.set(this, void 0);
169
174
  __classPrivateFieldSet(this, _BackendWebSocketService_messenger, options.messenger, "f");
170
175
  __classPrivateFieldSet(this, _BackendWebSocketService_isEnabled, options.isEnabled, "f");
171
176
  // Default to no-op trace function to keep core platform-agnostic
@@ -179,6 +184,8 @@ class BackendWebSocketService {
179
184
  maxReconnectDelay: options.maxReconnectDelay ?? 5000,
180
185
  requestTimeout: options.requestTimeout ?? 30000,
181
186
  }, "f");
187
+ // Initialize backoff for reconnection delays
188
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
182
189
  // Subscribe to authentication and keyring controller events
183
190
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_subscribeEvents).call(this);
184
191
  // Register action handlers using the method actions pattern
@@ -216,38 +223,50 @@ class BackendWebSocketService {
216
223
  return;
217
224
  }
218
225
  // If already connecting, wait for the existing connection attempt to complete
219
- if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING && __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f")) {
226
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f")) {
220
227
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
221
228
  return;
222
229
  }
223
- // Priority 2: Check authentication requirements (signed in)
224
- let bearerToken;
225
- try {
226
- const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
227
- if (!token) {
228
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
229
- return;
230
- }
231
- bearerToken = token;
232
- }
233
- catch (error) {
234
- log('Failed to check authentication requirements', { error });
235
- // Can't connect - schedule retry
236
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
230
+ // If a reconnect is already scheduled, defer to it to avoid bypassing exponential backoff
231
+ // This prevents rapid loops when server accepts then immediately closes connections
232
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
237
233
  return;
238
234
  }
239
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
240
- // Create and store the connection promise
241
- __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_establishConnection).call(this, bearerToken), "f");
235
+ // Create and store the connection promise IMMEDIATELY (before any async operations)
236
+ // This ensures subsequent connect() calls will wait for this promise instead of creating new connections
237
+ __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, (async () => {
238
+ // Priority 2: Check authentication requirements (signed in)
239
+ let bearerToken;
240
+ try {
241
+ const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
242
+ if (!token) {
243
+ throw new Error('Authentication required: user not signed in');
244
+ }
245
+ bearerToken = token;
246
+ }
247
+ catch (error) {
248
+ log('Failed to check authentication requirements', { error });
249
+ throw error;
250
+ }
251
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
252
+ // Establish the actual WebSocket connection
253
+ try {
254
+ await __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_establishConnection).call(this, bearerToken);
255
+ }
256
+ catch (error) {
257
+ const errorMessage = (0, utils_1.getErrorMessage)(error);
258
+ log('Connection attempt failed', { errorMessage, error });
259
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
260
+ throw error;
261
+ }
262
+ })(), "f");
242
263
  try {
243
264
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
244
265
  }
245
- catch (error) {
246
- const errorMessage = (0, utils_1.getErrorMessage)(error);
247
- log('Connection attempt failed', { errorMessage, error });
248
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
249
- // Rethrow to propagate error to caller
250
- throw error;
266
+ catch {
267
+ // Always schedule reconnect on any failure
268
+ // Exponential backoff will prevent aggressive retries
269
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
251
270
  }
252
271
  finally {
253
272
  // Clear the connection promise when done (success or failure)
@@ -256,10 +275,8 @@ class BackendWebSocketService {
256
275
  }
257
276
  /**
258
277
  * Closes WebSocket connection
259
- *
260
- * @returns Promise that resolves when disconnection is complete
261
278
  */
262
- async disconnect() {
279
+ disconnect() {
263
280
  if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED ||
264
281
  __classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
265
282
  return;
@@ -271,11 +288,41 @@ class BackendWebSocketService {
271
288
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
272
289
  // Clear any pending connection promise
273
290
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
291
+ // Reset reconnect attempts on manual disconnect
292
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
274
293
  if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
275
294
  __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
276
295
  }
277
296
  log('WebSocket manually disconnected');
278
297
  }
298
+ /**
299
+ * Forces a WebSocket reconnection to clean up subscription state
300
+ *
301
+ * This method is useful when subscription state may be out of sync and needs to be reset.
302
+ * It performs a controlled disconnect-then-reconnect sequence:
303
+ * - Disconnects cleanly to trigger subscription cleanup
304
+ * - Schedules reconnection with exponential backoff to prevent rapid loops
305
+ * - All subscriptions will be cleaned up automatically on disconnect
306
+ *
307
+ * Use cases:
308
+ * - Recovering from subscription/unsubscription issues
309
+ * - Cleaning up orphaned subscriptions
310
+ * - Forcing a fresh subscription state
311
+ *
312
+ * @returns Promise that resolves when disconnection is complete (reconnection is scheduled)
313
+ */
314
+ async forceReconnection() {
315
+ // If a reconnect is already scheduled, don't force another one
316
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
317
+ log('Reconnect already scheduled, skipping force reconnection');
318
+ return;
319
+ }
320
+ log('Forcing WebSocket reconnection to clean up subscription state');
321
+ // Perform controlled disconnect
322
+ this.disconnect();
323
+ // Schedule reconnection with exponential backoff
324
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
325
+ }
279
326
  /**
280
327
  * Sends a message through the WebSocket (fire-and-forget, no response expected)
281
328
  *
@@ -620,7 +667,7 @@ class BackendWebSocketService {
620
667
  }
621
668
  }
622
669
  exports.BackendWebSocketService = BackendWebSocketService;
623
- _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_connectionPromise = new WeakMap(), _BackendWebSocketService_pendingRequests = new WeakMap(), _BackendWebSocketService_connectedAt = new WeakMap(), _BackendWebSocketService_manualDisconnect = new WeakMap(), _BackendWebSocketService_subscriptions = new WeakMap(), _BackendWebSocketService_channelCallbacks = new WeakMap(), _BackendWebSocketService_instances = new WeakSet(), _BackendWebSocketService_subscribeEvents = function _BackendWebSocketService_subscribeEvents() {
670
+ _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_manualDisconnect = new WeakMap(), _BackendWebSocketService_subscriptions = new WeakMap(), _BackendWebSocketService_channelCallbacks = new WeakMap(), _BackendWebSocketService_backoff = new WeakMap(), _BackendWebSocketService_instances = new WeakSet(), _BackendWebSocketService_subscribeEvents = function _BackendWebSocketService_subscribeEvents() {
624
671
  // Subscribe to authentication state changes (sign in/out)
625
672
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (state) => {
626
673
  if (state.isSignedIn) {
@@ -628,7 +675,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
628
675
  this.connect();
629
676
  }
630
677
  else {
631
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
632
678
  this.disconnect();
633
679
  }
634
680
  }, (state) => ({ isSignedIn: state.isSignedIn }));
@@ -639,7 +685,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
639
685
  });
640
686
  // Subscribe to wallet lock event
641
687
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:lock', () => {
642
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
643
688
  this.disconnect();
644
689
  });
645
690
  }, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
@@ -688,8 +733,15 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
688
733
  __classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
689
734
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
690
735
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
691
- // Reset reconnect attempts on successful connection
692
- __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
736
+ // Only reset after connection stays stable for a period (10 seconds)
737
+ // This prevents rapid reconnect loops when server accepts then immediately closes
738
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, setTimeout(() => {
739
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
740
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
741
+ // Create new backoff sequence for fresh start on next disconnect
742
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
743
+ log('Connection stable - reset reconnect attempts and backoff');
744
+ }, 10000), "f");
693
745
  resolve();
694
746
  });
695
747
  };
@@ -786,13 +838,11 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
786
838
  __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
787
839
  name: `${SERVICE_NAME} Channel Message`,
788
840
  data: {
789
- channel: message.channel,
790
841
  latency_ms: latency,
791
842
  event: message.event,
792
843
  },
793
844
  tags: {
794
845
  service: SERVICE_NAME,
795
- channel_type: message.channel,
796
846
  },
797
847
  }, () => {
798
848
  // Direct lookup for exact channel match
@@ -809,7 +859,7 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
809
859
  // Calculate notification latency: time from server sent to client received
810
860
  const receivedAt = Date.now();
811
861
  const latency = receivedAt - timestamp;
812
- // Trace notification processing with latency data
862
+ // Trace notification processing wi th latency data
813
863
  // Use stored channelType instead of parsing each time
814
864
  __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
815
865
  name: `${SERVICE_NAME} Notification`,
@@ -833,7 +883,14 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
833
883
  }, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
834
884
  // Calculate connection duration before we clear state
835
885
  const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
836
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
886
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
887
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
888
+ __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
889
+ }
890
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f")) {
891
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f"));
892
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
893
+ }
837
894
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
838
895
  // Clear any pending connection promise
839
896
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
@@ -863,28 +920,43 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
863
920
  }, () => {
864
921
  // Empty trace callback - just measuring the event
865
922
  });
866
- // For any unexpected disconnects, attempt reconnection
867
- // The manualDisconnect flag is the only gate - if it's false, we reconnect
868
923
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
869
924
  }, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
870
925
  // Placeholder for future error handling logic
871
926
  }, _BackendWebSocketService_scheduleReconnect = function _BackendWebSocketService_scheduleReconnect() {
927
+ // If a reconnect is already scheduled, don't schedule another one
928
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
929
+ return;
930
+ }
931
+ // Increment attempts BEFORE calculating delay so backoff grows properly
872
932
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") + 1, "f");
873
- const rawDelay = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay * Math.pow(1.5, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") - 1);
874
- const delay = Math.min(rawDelay, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay);
933
+ // Use Cockatiel's exponential backoff to get delay with jitter
934
+ const delay = __classPrivateFieldGet(this, _BackendWebSocketService_backoff, "f").duration;
935
+ // Progress to next backoff state for future reconnect attempts
936
+ // Pass attempt number as context (though ExponentialBackoff doesn't use it)
937
+ __classPrivateFieldSet(this, _BackendWebSocketService_backoff, __classPrivateFieldGet(this, _BackendWebSocketService_backoff, "f").next({ attempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") }), "f");
938
+ log('Scheduling reconnect', {
939
+ attempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
940
+ delay_ms: delay,
941
+ });
875
942
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, setTimeout(() => {
876
943
  // Clear timer reference first
877
944
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, null, "f");
878
945
  // Check if connection is still enabled before reconnecting
879
946
  if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
880
947
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
948
+ // Create new backoff sequence when disabled
949
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
881
950
  return;
882
951
  }
883
- // Attempt to reconnect - if it fails, schedule another attempt
884
- this.connect().catch(() => {
885
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
886
- });
952
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
953
+ this.connect();
887
954
  }, delay), "f");
955
+ }, _BackendWebSocketService_newBackoff = function _BackendWebSocketService_newBackoff() {
956
+ __classPrivateFieldSet(this, _BackendWebSocketService_backoff, new controller_utils_1.ExponentialBackoff({
957
+ initialDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay,
958
+ maxDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay,
959
+ }).next(), "f");
888
960
  }, _BackendWebSocketService_clearTimers = function _BackendWebSocketService_clearTimers() {
889
961
  if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
890
962
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f"));
@@ -894,6 +966,10 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
894
966
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
895
967
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
896
968
  }
969
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f")) {
970
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f"));
971
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
972
+ }
897
973
  }, _BackendWebSocketService_clearPendingRequests = function _BackendWebSocketService_clearPendingRequests(error) {
898
974
  for (const [, request] of __classPrivateFieldGet(this, _BackendWebSocketService_pendingRequests, "f")) {
899
975
  clearTimeout(request.timeout);