@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.
@@ -9,7 +9,8 @@ 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_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;
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_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;
13
+ import { ExponentialBackoff } from "@metamask/controller-utils";
13
14
  import { getErrorMessage } from "@metamask/utils";
14
15
  import { v4 as uuidV4 } from "uuid";
15
16
  import { projectLogger, createModuleLogger } from "./logger.mjs";
@@ -18,6 +19,7 @@ const log = createModuleLogger(projectLogger, SERVICE_NAME);
18
19
  const MESSENGER_EXPOSED_METHODS = [
19
20
  'connect',
20
21
  'disconnect',
22
+ 'forceReconnection',
21
23
  'sendMessage',
22
24
  'sendRequest',
23
25
  'subscribe',
@@ -148,6 +150,7 @@ export class BackendWebSocketService {
148
150
  _BackendWebSocketService_reconnectAttempts.set(this, 0);
149
151
  _BackendWebSocketService_reconnectTimer.set(this, null);
150
152
  _BackendWebSocketService_connectionTimeout.set(this, null);
153
+ _BackendWebSocketService_stableConnectionTimer.set(this, null);
151
154
  // Track the current connection promise to handle concurrent connection attempts
152
155
  _BackendWebSocketService_connectionPromise.set(this, null);
153
156
  _BackendWebSocketService_pendingRequests.set(this, new Map());
@@ -162,6 +165,8 @@ export class BackendWebSocketService {
162
165
  // Key: channel name (serves as unique identifier)
163
166
  // Value: ChannelCallback configuration
164
167
  _BackendWebSocketService_channelCallbacks.set(this, new Map());
168
+ // Backoff instance for reconnection delays (reset on stable connection)
169
+ _BackendWebSocketService_backoff.set(this, void 0);
165
170
  __classPrivateFieldSet(this, _BackendWebSocketService_messenger, options.messenger, "f");
166
171
  __classPrivateFieldSet(this, _BackendWebSocketService_isEnabled, options.isEnabled, "f");
167
172
  // Default to no-op trace function to keep core platform-agnostic
@@ -175,6 +180,8 @@ export class BackendWebSocketService {
175
180
  maxReconnectDelay: options.maxReconnectDelay ?? 5000,
176
181
  requestTimeout: options.requestTimeout ?? 30000,
177
182
  }, "f");
183
+ // Initialize backoff for reconnection delays
184
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
178
185
  // Subscribe to authentication and keyring controller events
179
186
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_subscribeEvents).call(this);
180
187
  // Register action handlers using the method actions pattern
@@ -212,38 +219,50 @@ export class BackendWebSocketService {
212
219
  return;
213
220
  }
214
221
  // If already connecting, wait for the existing connection attempt to complete
215
- if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING && __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f")) {
222
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f")) {
216
223
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
217
224
  return;
218
225
  }
219
- // Priority 2: Check authentication requirements (signed in)
220
- let bearerToken;
221
- try {
222
- const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
223
- if (!token) {
224
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
225
- return;
226
- }
227
- bearerToken = token;
228
- }
229
- catch (error) {
230
- log('Failed to check authentication requirements', { error });
231
- // Can't connect - schedule retry
232
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
226
+ // If a reconnect is already scheduled, defer to it to avoid bypassing exponential backoff
227
+ // This prevents rapid loops when server accepts then immediately closes connections
228
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
233
229
  return;
234
230
  }
235
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
236
- // Create and store the connection promise
237
- __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_establishConnection).call(this, bearerToken), "f");
231
+ // Create and store the connection promise IMMEDIATELY (before any async operations)
232
+ // This ensures subsequent connect() calls will wait for this promise instead of creating new connections
233
+ __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, (async () => {
234
+ // Priority 2: Check authentication requirements (signed in)
235
+ let bearerToken;
236
+ try {
237
+ const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
238
+ if (!token) {
239
+ throw new Error('Authentication required: user not signed in');
240
+ }
241
+ bearerToken = token;
242
+ }
243
+ catch (error) {
244
+ log('Failed to check authentication requirements', { error });
245
+ throw error;
246
+ }
247
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
248
+ // Establish the actual WebSocket connection
249
+ try {
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
+ }
258
+ })(), "f");
238
259
  try {
239
260
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
240
261
  }
241
- catch (error) {
242
- const errorMessage = getErrorMessage(error);
243
- log('Connection attempt failed', { errorMessage, error });
244
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
245
- // Rethrow to propagate error to caller
246
- throw error;
262
+ catch {
263
+ // Always schedule reconnect on any failure
264
+ // Exponential backoff will prevent aggressive retries
265
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
247
266
  }
248
267
  finally {
249
268
  // Clear the connection promise when done (success or failure)
@@ -252,10 +271,8 @@ export class BackendWebSocketService {
252
271
  }
253
272
  /**
254
273
  * Closes WebSocket connection
255
- *
256
- * @returns Promise that resolves when disconnection is complete
257
274
  */
258
- async disconnect() {
275
+ disconnect() {
259
276
  if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED ||
260
277
  __classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
261
278
  return;
@@ -267,11 +284,41 @@ export class BackendWebSocketService {
267
284
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
268
285
  // Clear any pending connection promise
269
286
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
287
+ // Reset reconnect attempts on manual disconnect
288
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
270
289
  if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
271
290
  __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
272
291
  }
273
292
  log('WebSocket manually disconnected');
274
293
  }
294
+ /**
295
+ * Forces a WebSocket reconnection to clean up subscription state
296
+ *
297
+ * This method is useful when subscription state may be out of sync and needs to be reset.
298
+ * It performs a controlled disconnect-then-reconnect sequence:
299
+ * - Disconnects cleanly to trigger subscription cleanup
300
+ * - Schedules reconnection with exponential backoff to prevent rapid loops
301
+ * - All subscriptions will be cleaned up automatically on disconnect
302
+ *
303
+ * Use cases:
304
+ * - Recovering from subscription/unsubscription issues
305
+ * - Cleaning up orphaned subscriptions
306
+ * - Forcing a fresh subscription state
307
+ *
308
+ * @returns Promise that resolves when disconnection is complete (reconnection is scheduled)
309
+ */
310
+ async forceReconnection() {
311
+ // If a reconnect is already scheduled, don't force another one
312
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
313
+ log('Reconnect already scheduled, skipping force reconnection');
314
+ return;
315
+ }
316
+ log('Forcing WebSocket reconnection to clean up subscription state');
317
+ // Perform controlled disconnect
318
+ this.disconnect();
319
+ // Schedule reconnection with exponential backoff
320
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
321
+ }
275
322
  /**
276
323
  * Sends a message through the WebSocket (fire-and-forget, no response expected)
277
324
  *
@@ -615,7 +662,7 @@ export class BackendWebSocketService {
615
662
  return subscription;
616
663
  }
617
664
  }
618
- _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() {
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(), _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() {
619
666
  // Subscribe to authentication state changes (sign in/out)
620
667
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (state) => {
621
668
  if (state.isSignedIn) {
@@ -623,7 +670,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
623
670
  this.connect();
624
671
  }
625
672
  else {
626
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
627
673
  this.disconnect();
628
674
  }
629
675
  }, (state) => ({ isSignedIn: state.isSignedIn }));
@@ -634,7 +680,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
634
680
  });
635
681
  // Subscribe to wallet lock event
636
682
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:lock', () => {
637
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
638
683
  this.disconnect();
639
684
  });
640
685
  }, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
@@ -683,8 +728,15 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
683
728
  __classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
684
729
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
685
730
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
686
- // Reset reconnect attempts on successful connection
687
- __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
731
+ // Only reset after connection stays stable for a period (10 seconds)
732
+ // This prevents rapid reconnect loops when server accepts then immediately closes
733
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, setTimeout(() => {
734
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
735
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
736
+ // Create new backoff sequence for fresh start on next disconnect
737
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
738
+ log('Connection stable - reset reconnect attempts and backoff');
739
+ }, 10000), "f");
688
740
  resolve();
689
741
  });
690
742
  };
@@ -781,13 +833,11 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
781
833
  __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
782
834
  name: `${SERVICE_NAME} Channel Message`,
783
835
  data: {
784
- channel: message.channel,
785
836
  latency_ms: latency,
786
837
  event: message.event,
787
838
  },
788
839
  tags: {
789
840
  service: SERVICE_NAME,
790
- channel_type: message.channel,
791
841
  },
792
842
  }, () => {
793
843
  // Direct lookup for exact channel match
@@ -804,7 +854,7 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
804
854
  // Calculate notification latency: time from server sent to client received
805
855
  const receivedAt = Date.now();
806
856
  const latency = receivedAt - timestamp;
807
- // Trace notification processing with latency data
857
+ // Trace notification processing wi th latency data
808
858
  // Use stored channelType instead of parsing each time
809
859
  __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
810
860
  name: `${SERVICE_NAME} Notification`,
@@ -828,7 +878,14 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
828
878
  }, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
829
879
  // Calculate connection duration before we clear state
830
880
  const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
831
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
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
+ }
832
889
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
833
890
  // Clear any pending connection promise
834
891
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
@@ -858,28 +915,43 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
858
915
  }, () => {
859
916
  // Empty trace callback - just measuring the event
860
917
  });
861
- // For any unexpected disconnects, attempt reconnection
862
- // The manualDisconnect flag is the only gate - if it's false, we reconnect
863
918
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
864
919
  }, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
865
920
  // Placeholder for future error handling logic
866
921
  }, _BackendWebSocketService_scheduleReconnect = function _BackendWebSocketService_scheduleReconnect() {
922
+ // If a reconnect is already scheduled, don't schedule another one
923
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
924
+ return;
925
+ }
926
+ // Increment attempts BEFORE calculating delay so backoff grows properly
867
927
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") + 1, "f");
868
- const rawDelay = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay * Math.pow(1.5, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") - 1);
869
- const delay = Math.min(rawDelay, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay);
928
+ // Use Cockatiel's exponential backoff to get delay with jitter
929
+ const delay = __classPrivateFieldGet(this, _BackendWebSocketService_backoff, "f").duration;
930
+ // Progress to next backoff state for future reconnect attempts
931
+ // Pass attempt number as context (though ExponentialBackoff doesn't use it)
932
+ __classPrivateFieldSet(this, _BackendWebSocketService_backoff, __classPrivateFieldGet(this, _BackendWebSocketService_backoff, "f").next({ attempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") }), "f");
933
+ log('Scheduling reconnect', {
934
+ attempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
935
+ delay_ms: delay,
936
+ });
870
937
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, setTimeout(() => {
871
938
  // Clear timer reference first
872
939
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, null, "f");
873
940
  // Check if connection is still enabled before reconnecting
874
941
  if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
875
942
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
943
+ // Create new backoff sequence when disabled
944
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
876
945
  return;
877
946
  }
878
- // Attempt to reconnect - if it fails, schedule another attempt
879
- this.connect().catch(() => {
880
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
881
- });
947
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
948
+ this.connect();
882
949
  }, delay), "f");
950
+ }, _BackendWebSocketService_newBackoff = function _BackendWebSocketService_newBackoff() {
951
+ __classPrivateFieldSet(this, _BackendWebSocketService_backoff, new ExponentialBackoff({
952
+ initialDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay,
953
+ maxDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay,
954
+ }).next(), "f");
883
955
  }, _BackendWebSocketService_clearTimers = function _BackendWebSocketService_clearTimers() {
884
956
  if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
885
957
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f"));
@@ -889,6 +961,10 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
889
961
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
890
962
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
891
963
  }
964
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f")) {
965
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f"));
966
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
967
+ }
892
968
  }, _BackendWebSocketService_clearPendingRequests = function _BackendWebSocketService_clearPendingRequests(error) {
893
969
  for (const [, request] of __classPrivateFieldGet(this, _BackendWebSocketService_pendingRequests, "f")) {
894
970
  clearTimeout(request.timeout);