@metamask-previews/core-backend 2.1.0-preview-f18d3aaf → 2.1.0-preview-8e6c5423

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +7 -26
  2. package/dist/AccountActivityService.cjs +10 -23
  3. package/dist/AccountActivityService.cjs.map +1 -1
  4. package/dist/AccountActivityService.d.cts +4 -6
  5. package/dist/AccountActivityService.d.cts.map +1 -1
  6. package/dist/AccountActivityService.d.mts +4 -6
  7. package/dist/AccountActivityService.d.mts.map +1 -1
  8. package/dist/AccountActivityService.mjs +9 -22
  9. package/dist/AccountActivityService.mjs.map +1 -1
  10. package/dist/BackendWebSocketService-method-action-types.cjs.map +1 -1
  11. package/dist/BackendWebSocketService-method-action-types.d.cts +1 -21
  12. package/dist/BackendWebSocketService-method-action-types.d.cts.map +1 -1
  13. package/dist/BackendWebSocketService-method-action-types.d.mts +1 -21
  14. package/dist/BackendWebSocketService-method-action-types.d.mts.map +1 -1
  15. package/dist/BackendWebSocketService-method-action-types.mjs.map +1 -1
  16. package/dist/BackendWebSocketService.cjs +22 -94
  17. package/dist/BackendWebSocketService.cjs.map +1 -1
  18. package/dist/BackendWebSocketService.d.cts +6 -21
  19. package/dist/BackendWebSocketService.d.cts.map +1 -1
  20. package/dist/BackendWebSocketService.d.mts +6 -21
  21. package/dist/BackendWebSocketService.d.mts.map +1 -1
  22. package/dist/BackendWebSocketService.mjs +22 -94
  23. package/dist/BackendWebSocketService.mjs.map +1 -1
  24. package/dist/index.cjs +2 -5
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +2 -3
  27. package/dist/index.d.cts.map +1 -1
  28. package/dist/index.d.mts +2 -3
  29. package/dist/index.d.mts.map +1 -1
  30. package/dist/index.mjs +0 -1
  31. package/dist/index.mjs.map +1 -1
  32. package/package.json +2 -2
@@ -9,8 +9,7 @@ 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, _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";
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;
14
13
  import { getErrorMessage } from "@metamask/utils";
15
14
  import { v4 as uuidV4 } from "uuid";
16
15
  import { projectLogger, createModuleLogger } from "./logger.mjs";
@@ -19,7 +18,6 @@ const log = createModuleLogger(projectLogger, SERVICE_NAME);
19
18
  const MESSENGER_EXPOSED_METHODS = [
20
19
  'connect',
21
20
  'disconnect',
22
- 'forceReconnection',
23
21
  'sendMessage',
24
22
  'sendRequest',
25
23
  'subscribe',
@@ -150,7 +148,6 @@ export class BackendWebSocketService {
150
148
  _BackendWebSocketService_reconnectAttempts.set(this, 0);
151
149
  _BackendWebSocketService_reconnectTimer.set(this, null);
152
150
  _BackendWebSocketService_connectionTimeout.set(this, null);
153
- _BackendWebSocketService_stableConnectionTimer.set(this, null);
154
151
  // Track the current connection promise to handle concurrent connection attempts
155
152
  _BackendWebSocketService_connectionPromise.set(this, null);
156
153
  _BackendWebSocketService_pendingRequests.set(this, new Map());
@@ -165,8 +162,6 @@ export class BackendWebSocketService {
165
162
  // Key: channel name (serves as unique identifier)
166
163
  // Value: ChannelCallback configuration
167
164
  _BackendWebSocketService_channelCallbacks.set(this, new Map());
168
- // Backoff instance for reconnection delays (reset on stable connection)
169
- _BackendWebSocketService_backoff.set(this, void 0);
170
165
  __classPrivateFieldSet(this, _BackendWebSocketService_messenger, options.messenger, "f");
171
166
  __classPrivateFieldSet(this, _BackendWebSocketService_isEnabled, options.isEnabled, "f");
172
167
  // Default to no-op trace function to keep core platform-agnostic
@@ -180,8 +175,6 @@ export class BackendWebSocketService {
180
175
  maxReconnectDelay: options.maxReconnectDelay ?? 5000,
181
176
  requestTimeout: options.requestTimeout ?? 30000,
182
177
  }, "f");
183
- // Initialize backoff for reconnection delays
184
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
185
178
  // Subscribe to authentication and keyring controller events
186
179
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_subscribeEvents).call(this);
187
180
  // Register action handlers using the method actions pattern
@@ -223,11 +216,6 @@ export class BackendWebSocketService {
223
216
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
224
217
  return;
225
218
  }
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")) {
229
- return;
230
- }
231
219
  // Create and store the connection promise IMMEDIATELY (before any async operations)
232
220
  // This ensures subsequent connect() calls will wait for this promise instead of creating new connections
233
221
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, (async () => {
@@ -236,12 +224,15 @@ export class BackendWebSocketService {
236
224
  try {
237
225
  const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
238
226
  if (!token) {
227
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
239
228
  throw new Error('Authentication required: user not signed in');
240
229
  }
241
230
  bearerToken = token;
242
231
  }
243
232
  catch (error) {
244
233
  log('Failed to check authentication requirements', { error });
234
+ // Can't connect - schedule retry
235
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
245
236
  throw error;
246
237
  }
247
238
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
@@ -253,17 +244,13 @@ export class BackendWebSocketService {
253
244
  const errorMessage = getErrorMessage(error);
254
245
  log('Connection attempt failed', { errorMessage, error });
255
246
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
247
+ // Rethrow to propagate error to caller
256
248
  throw error;
257
249
  }
258
250
  })(), "f");
259
251
  try {
260
252
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
261
253
  }
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);
266
- }
267
254
  finally {
268
255
  // Clear the connection promise when done (success or failure)
269
256
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
@@ -271,8 +258,10 @@ export class BackendWebSocketService {
271
258
  }
272
259
  /**
273
260
  * Closes WebSocket connection
261
+ *
262
+ * @returns Promise that resolves when disconnection is complete
274
263
  */
275
- disconnect() {
264
+ async disconnect() {
276
265
  if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED ||
277
266
  __classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
278
267
  return;
@@ -284,41 +273,11 @@ export class BackendWebSocketService {
284
273
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
285
274
  // Clear any pending connection promise
286
275
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
287
- // Reset reconnect attempts on manual disconnect
288
- __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
289
276
  if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
290
277
  __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
291
278
  }
292
279
  log('WebSocket manually disconnected');
293
280
  }
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
- }
322
281
  /**
323
282
  * Sends a message through the WebSocket (fire-and-forget, no response expected)
324
283
  *
@@ -662,7 +621,7 @@ export class BackendWebSocketService {
662
621
  return subscription;
663
622
  }
664
623
  }
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() {
624
+ _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() {
666
625
  // Subscribe to authentication state changes (sign in/out)
667
626
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (state) => {
668
627
  if (state.isSignedIn) {
@@ -670,6 +629,7 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
670
629
  this.connect();
671
630
  }
672
631
  else {
632
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
673
633
  this.disconnect();
674
634
  }
675
635
  }, (state) => ({ isSignedIn: state.isSignedIn }));
@@ -680,6 +640,7 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
680
640
  });
681
641
  // Subscribe to wallet lock event
682
642
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:lock', () => {
643
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
683
644
  this.disconnect();
684
645
  });
685
646
  }, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
@@ -728,15 +689,8 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
728
689
  __classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
729
690
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
730
691
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "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");
692
+ // Reset reconnect attempts on successful connection
693
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
740
694
  resolve();
741
695
  });
742
696
  };
@@ -878,14 +832,7 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
878
832
  }, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
879
833
  // Calculate connection duration before we clear state
880
834
  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
- }
835
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
889
836
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
890
837
  // Clear any pending connection promise
891
838
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
@@ -915,43 +862,28 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
915
862
  }, () => {
916
863
  // Empty trace callback - just measuring the event
917
864
  });
865
+ // For any unexpected disconnects, attempt reconnection
866
+ // The manualDisconnect flag is the only gate - if it's false, we reconnect
918
867
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
919
868
  }, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
920
869
  // Placeholder for future error handling logic
921
870
  }, _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
927
871
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") + 1, "f");
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
- });
872
+ const rawDelay = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay * Math.pow(1.5, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") - 1);
873
+ const delay = Math.min(rawDelay, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay);
937
874
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, setTimeout(() => {
938
875
  // Clear timer reference first
939
876
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, null, "f");
940
877
  // Check if connection is still enabled before reconnecting
941
878
  if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
942
879
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
943
- // Create new backoff sequence when disabled
944
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
945
880
  return;
946
881
  }
947
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
948
- this.connect();
882
+ // Attempt to reconnect - if it fails, schedule another attempt
883
+ this.connect().catch(() => {
884
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
885
+ });
949
886
  }, 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");
955
887
  }, _BackendWebSocketService_clearTimers = function _BackendWebSocketService_clearTimers() {
956
888
  if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
957
889
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f"));
@@ -961,10 +893,6 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
961
893
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
962
894
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
963
895
  }
964
- if (__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f")) {
965
- clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f"));
966
- __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
967
- }
968
896
  }, _BackendWebSocketService_clearPendingRequests = function _BackendWebSocketService_clearPendingRequests(error) {
969
897
  for (const [, request] of __classPrivateFieldGet(this, _BackendWebSocketService_pendingRequests, "f")) {
970
898
  clearTimeout(request.timeout);
@@ -1 +1 @@
1
- {"version":3,"file":"BackendWebSocketService.mjs","sourceRoot":"","sources":["../src/BackendWebSocketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport type { TraceCallback } from '@metamask/controller-utils';\nimport { ExponentialBackoff } from '@metamask/controller-utils';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport { getErrorMessage } from '@metamask/utils';\nimport { v4 as uuidV4 } from 'uuid';\n\nimport type { BackendWebSocketServiceMethodActions } from './BackendWebSocketService-method-action-types';\nimport { projectLogger, createModuleLogger } from './logger';\n\nconst SERVICE_NAME = 'BackendWebSocketService' as const;\n\nconst log = createModuleLogger(projectLogger, SERVICE_NAME);\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'connect',\n 'disconnect',\n 'forceReconnection',\n 'sendMessage',\n 'sendRequest',\n 'subscribe',\n 'getConnectionInfo',\n 'getSubscriptionsByChannel',\n 'channelHasSubscription',\n 'findSubscriptionsByChannelPrefix',\n 'addChannelCallback',\n 'removeChannelCallback',\n 'getChannelCallbacks',\n] as const;\n\n/**\n * Gets human-readable close reason from RFC 6455 close code\n *\n * @param code - WebSocket close code\n * @returns Human-readable close reason\n */\nexport function getCloseReason(code: number): string {\n switch (code) {\n case 1000:\n return 'Normal Closure';\n case 1001:\n return 'Going Away';\n case 1002:\n return 'Protocol Error';\n case 1003:\n return 'Unsupported Data';\n case 1004:\n return 'Reserved';\n case 1005:\n return 'No Status Received';\n case 1006:\n return 'Abnormal Closure';\n case 1007:\n return 'Invalid frame payload data';\n case 1008:\n return 'Policy Violation';\n case 1009:\n return 'Message Too Big';\n case 1010:\n return 'Mandatory Extension';\n case 1011:\n return 'Internal Server Error';\n case 1012:\n return 'Service Restart';\n case 1013:\n return 'Try Again Later';\n case 1014:\n return 'Bad Gateway';\n case 1015:\n return 'TLS Handshake';\n default:\n if (code >= 3000 && code <= 3999) {\n return 'Library/Framework Error';\n }\n if (code >= 4000 && code <= 4999) {\n return 'Application Error';\n }\n return 'Unknown';\n }\n}\n\n/**\n * WebSocket connection states\n */\nexport enum WebSocketState {\n CONNECTING = 'connecting',\n CONNECTED = 'connected',\n DISCONNECTING = 'disconnecting',\n DISCONNECTED = 'disconnected',\n ERROR = 'error',\n}\n\n/**\n * WebSocket event types\n */\nexport enum WebSocketEventType {\n CONNECTED = 'connected',\n DISCONNECTED = 'disconnected',\n MESSAGE = 'message',\n ERROR = 'error',\n RECONNECTING = 'reconnecting',\n RECONNECTED = 'reconnected',\n}\n\n/**\n * Configuration options for the WebSocket service\n */\nexport type BackendWebSocketServiceOptions = {\n /** The WebSocket URL to connect to */\n url: string;\n\n /** The messenger for inter-service communication */\n messenger: BackendWebSocketServiceMessenger;\n\n /** Connection timeout in milliseconds (default: 10000) */\n timeout?: number;\n\n /** Initial reconnection delay in milliseconds (default: 500) */\n reconnectDelay?: number;\n\n /** Maximum reconnection delay in milliseconds (default: 5000) */\n maxReconnectDelay?: number;\n\n /** Request timeout in milliseconds (default: 30000) */\n requestTimeout?: number;\n\n /** Optional callback to determine if connection should be enabled (default: always enabled) */\n isEnabled?: () => boolean;\n\n /** Optional callback to trace performance of WebSocket operations (default: no-op) */\n traceFn?: TraceCallback;\n};\n\n/**\n * Client Request message\n * Used when client sends a request to the server\n */\nexport type ClientRequestMessage = {\n event: string;\n data: {\n requestId: string;\n channels?: string[];\n [key: string]: unknown;\n };\n};\n\n/**\n * Server Response message\n * Used when server responds to a client request\n */\nexport type ServerResponseMessage = {\n event: string;\n data: {\n requestId: string;\n subscriptionId?: string;\n succeeded?: string[];\n failed?: string[];\n [key: string]: unknown;\n };\n};\n\n/**\n * Server Notification message\n * Used when server sends unsolicited data to client\n * subscriptionId is optional for system-wide notifications\n */\nexport type ServerNotificationMessage = {\n event: string;\n subscriptionId?: string;\n channel: string;\n data: Record<string, unknown>;\n timestamp: number;\n};\n\n/**\n * Union type for all WebSocket messages\n */\nexport type WebSocketMessage =\n | ClientRequestMessage\n | ServerResponseMessage\n | ServerNotificationMessage;\n\n/**\n * Channel-based callback configuration\n */\nexport type ChannelCallback = {\n /** Channel name to match (also serves as the unique identifier) */\n channelName: string;\n /** Callback function */\n callback: (notification: ServerNotificationMessage) => void;\n};\n\n/**\n * Unified WebSocket subscription object used for both internal storage and external API\n */\nexport type WebSocketSubscription = {\n /** The subscription ID from the server */\n subscriptionId: string;\n /** Channel names for this subscription */\n channels: string[];\n /** Channel type with version (e.g., 'account-activity.v1') extracted from first channel */\n channelType: string;\n /** Callback function for handling notifications (optional for external use) */\n callback?: (notification: ServerNotificationMessage) => void;\n /** Function to unsubscribe and clean up */\n unsubscribe: (requestId?: string) => Promise<void>;\n};\n\n/**\n * WebSocket connection info\n */\nexport type WebSocketConnectionInfo = {\n state: WebSocketState;\n url: string;\n reconnectAttempts: number;\n timeout: number;\n reconnectDelay: number;\n maxReconnectDelay: number;\n requestTimeout: number;\n connectedAt?: number;\n};\n\n// Action types for the messaging system - using generated method actions\nexport type BackendWebSocketServiceActions =\n BackendWebSocketServiceMethodActions;\n\nexport type BackendWebSocketServiceAllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerToken;\n\nexport type BackendWebSocketServiceAllowedEvents =\n | AuthenticationController.AuthenticationControllerStateChangeEvent\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent;\n\n// Event types for WebSocket connection state changes\nexport type BackendWebSocketServiceConnectionStateChangedEvent = {\n type: 'BackendWebSocketService:connectionStateChanged';\n payload: [WebSocketConnectionInfo];\n};\n\nexport type BackendWebSocketServiceEvents =\n BackendWebSocketServiceConnectionStateChangedEvent;\n\nexport type BackendWebSocketServiceMessenger = RestrictedMessenger<\n typeof SERVICE_NAME,\n BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions,\n BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents,\n BackendWebSocketServiceAllowedActions['type'],\n BackendWebSocketServiceAllowedEvents['type']\n>;\n\n/**\n * WebSocket Service with automatic reconnection, session management and direct callback routing\n *\n * Connection Management:\n * - Automatically subscribes to AuthenticationController:stateChange (sign in/out)\n * - Automatically subscribes to KeyringController:lock/unlock events\n * - Idempotent connect() function safe for multiple rapid calls\n * - Auto-reconnects on unexpected disconnects (manualDisconnect = false)\n *\n * Platform Responsibilities:\n * - Call connect() when app opens/foregrounds\n * - Call disconnect() when app closes/backgrounds\n * - Provide isEnabled() callback (feature flag)\n * - Call destroy() on app termination\n *\n * Real-Time Performance Optimizations:\n * - Fast path message routing (zero allocations)\n * - Production mode removes try-catch overhead\n * - Optimized JSON parsing with fail-fast\n * - Direct callback routing bypasses event emitters\n * - Memory cleanup and resource management\n */\nexport class BackendWebSocketService {\n /**\n * The name of the service.\n */\n readonly name = SERVICE_NAME;\n\n readonly #messenger: BackendWebSocketServiceMessenger;\n\n readonly #options: Required<\n Omit<BackendWebSocketServiceOptions, 'messenger' | 'isEnabled' | 'traceFn'>\n >;\n\n readonly #isEnabled: (() => boolean) | undefined;\n\n readonly #trace: TraceCallback;\n\n #ws: WebSocket | undefined;\n\n #state: WebSocketState = WebSocketState.DISCONNECTED;\n\n #reconnectAttempts = 0;\n\n #reconnectTimer: NodeJS.Timeout | null = null;\n\n #connectionTimeout: NodeJS.Timeout | null = null;\n\n #stableConnectionTimer: NodeJS.Timeout | null = null;\n\n // Track the current connection promise to handle concurrent connection attempts\n #connectionPromise: Promise<void> | null = null;\n\n readonly #pendingRequests = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeout: NodeJS.Timeout;\n }\n >();\n\n #connectedAt: number = 0;\n\n // Track manual disconnects to prevent automatic reconnection\n #manualDisconnect = false;\n\n // Simplified subscription storage (single flat map)\n // Key: subscription ID string (e.g., 'sub_abc123def456')\n // Value: WebSocketSubscription object with channels, callback and metadata\n readonly #subscriptions = new Map<string, WebSocketSubscription>();\n\n // Channel-based callback storage\n // Key: channel name (serves as unique identifier)\n // Value: ChannelCallback configuration\n readonly #channelCallbacks = new Map<string, ChannelCallback>();\n\n // Backoff instance for reconnection delays (reset on stable connection)\n #backoff!: ReturnType<ExponentialBackoff<unknown>['next']>;\n\n // =============================================================================\n // 1. CONSTRUCTOR & INITIALIZATION\n // =============================================================================\n\n /**\n * Creates a new WebSocket service instance\n *\n * @param options - Configuration options for the WebSocket service\n */\n constructor(options: BackendWebSocketServiceOptions) {\n this.#messenger = options.messenger;\n this.#isEnabled = options.isEnabled;\n // Default to no-op trace function to keep core platform-agnostic\n this.#trace =\n options.traceFn ??\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (((_request: any, fn?: any) => fn?.()) as TraceCallback);\n\n this.#options = {\n url: options.url,\n timeout: options.timeout ?? 10000,\n reconnectDelay: options.reconnectDelay ?? 500,\n maxReconnectDelay: options.maxReconnectDelay ?? 5000,\n requestTimeout: options.requestTimeout ?? 30000,\n };\n\n // Initialize backoff for reconnection delays\n this.#newBackoff();\n\n // Subscribe to authentication and keyring controller events\n this.#subscribeEvents();\n\n // Register action handlers using the method actions pattern\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Setup event handling for authentication and wallet lock state\n *\n * Three event sources trigger connection/disconnection:\n * 1. AuthenticationController:stateChange (sign in/out)\n * 2. KeyringController:unlock (wallet unlocked)\n * 3. KeyringController:lock (wallet locked)\n *\n * All connect() calls are idempotent and validate all requirements.\n */\n #subscribeEvents(): void {\n // Subscribe to authentication state changes (sign in/out)\n this.#messenger.subscribe(\n 'AuthenticationController:stateChange',\n (state: AuthenticationController.AuthenticationControllerState) => {\n if (state.isSignedIn) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.connect();\n } else {\n this.disconnect();\n }\n },\n (state) => ({ isSignedIn: state.isSignedIn }),\n );\n\n // Subscribe to wallet unlock event\n this.#messenger.subscribe('KeyringController:unlock', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.connect();\n });\n\n // Subscribe to wallet lock event\n this.#messenger.subscribe('KeyringController:lock', () => {\n this.disconnect();\n });\n }\n\n // =============================================================================\n // 2. PUBLIC API METHODS\n // =============================================================================\n\n /**\n * Establishes WebSocket connection with smart reconnection behavior\n *\n * Connection Requirements (all must be true):\n * 1. Feature enabled (isEnabled() = true)\n * 2. Wallet unlocked (checked by getBearerToken)\n * 3. User signed in (checked by getBearerToken)\n *\n * Platform code should call this when app opens/foregrounds.\n * Automatically called on KeyringController:unlock event.\n *\n * @returns Promise that resolves when connection is established\n */\n async connect(): Promise<void> {\n // Reset manual disconnect flag when explicitly connecting\n this.#manualDisconnect = false;\n\n // Priority 1: Check if feature is enabled via callback (feature flag check)\n // If feature is disabled, stop all connection attempts\n if (this.#isEnabled && !this.#isEnabled()) {\n // Clear any pending reconnection attempts since feature is disabled\n this.#clearTimers();\n this.#reconnectAttempts = 0;\n return;\n }\n\n // If already connected, return immediately\n if (this.#state === WebSocketState.CONNECTED) {\n return;\n }\n\n // If already connecting, wait for the existing connection attempt to complete\n if (this.#connectionPromise) {\n await this.#connectionPromise;\n return;\n }\n\n // If a reconnect is already scheduled, defer to it to avoid bypassing exponential backoff\n // This prevents rapid loops when server accepts then immediately closes connections\n if (this.#reconnectTimer) {\n return;\n }\n\n // Create and store the connection promise IMMEDIATELY (before any async operations)\n // This ensures subsequent connect() calls will wait for this promise instead of creating new connections\n this.#connectionPromise = (async () => {\n // Priority 2: Check authentication requirements (signed in)\n let bearerToken: string;\n try {\n const token = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n );\n if (!token) {\n throw new Error('Authentication required: user not signed in');\n }\n bearerToken = token;\n } catch (error) {\n log('Failed to check authentication requirements', { error });\n throw error;\n }\n\n this.#setState(WebSocketState.CONNECTING);\n\n // Establish the actual WebSocket connection\n try {\n await this.#establishConnection(bearerToken);\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n log('Connection attempt failed', { errorMessage, error });\n this.#setState(WebSocketState.ERROR);\n throw error;\n }\n })();\n\n try {\n await this.#connectionPromise;\n } catch {\n // Always schedule reconnect on any failure\n // Exponential backoff will prevent aggressive retries\n this.#scheduleReconnect();\n } finally {\n // Clear the connection promise when done (success or failure)\n this.#connectionPromise = null;\n }\n }\n\n /**\n * Closes WebSocket connection\n */\n disconnect(): void {\n if (\n this.#state === WebSocketState.DISCONNECTED ||\n this.#state === WebSocketState.DISCONNECTING\n ) {\n return;\n }\n\n // Mark this as a manual disconnect to prevent automatic reconnection\n this.#manualDisconnect = true;\n\n this.#setState(WebSocketState.DISCONNECTING);\n this.#clearTimers();\n this.#clearPendingRequests(new Error('WebSocket disconnected'));\n\n // Clear any pending connection promise\n this.#connectionPromise = null;\n\n // Reset reconnect attempts on manual disconnect\n this.#reconnectAttempts = 0;\n\n if (this.#ws) {\n this.#ws.close(1000, 'Normal closure');\n }\n\n log('WebSocket manually disconnected');\n }\n\n /**\n * Forces a WebSocket reconnection to clean up subscription state\n *\n * This method is useful when subscription state may be out of sync and needs to be reset.\n * It performs a controlled disconnect-then-reconnect sequence:\n * - Disconnects cleanly to trigger subscription cleanup\n * - Schedules reconnection with exponential backoff to prevent rapid loops\n * - All subscriptions will be cleaned up automatically on disconnect\n *\n * Use cases:\n * - Recovering from subscription/unsubscription issues\n * - Cleaning up orphaned subscriptions\n * - Forcing a fresh subscription state\n *\n * @returns Promise that resolves when disconnection is complete (reconnection is scheduled)\n */\n async forceReconnection(): Promise<void> {\n // If a reconnect is already scheduled, don't force another one\n if (this.#reconnectTimer) {\n log('Reconnect already scheduled, skipping force reconnection');\n return;\n }\n\n log('Forcing WebSocket reconnection to clean up subscription state');\n\n // Perform controlled disconnect\n this.disconnect();\n\n // Schedule reconnection with exponential backoff\n this.#scheduleReconnect();\n }\n\n /**\n * Sends a message through the WebSocket (fire-and-forget, no response expected)\n *\n * This is a low-level method for sending messages without waiting for a response.\n * Most consumers should use `sendRequest()` instead, which handles request-response\n * correlation and provides proper error handling with timeouts.\n *\n * Use this method only when:\n * - You don't need a response from the server\n * - You're implementing custom message protocols\n * - You need fine-grained control over message timing\n *\n * @param message - The message to send\n * @throws Error if WebSocket is not connected or send fails\n *\n * @see sendRequest for request-response pattern with automatic correlation\n */\n sendMessage(message: ClientRequestMessage): void {\n if (this.#state !== WebSocketState.CONNECTED || !this.#ws) {\n throw new Error(`Cannot send message: WebSocket is ${this.#state}`);\n }\n\n try {\n this.#ws.send(JSON.stringify(message));\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n this.#handleError(new Error(errorMessage));\n throw new Error(errorMessage);\n }\n }\n\n /**\n * Sends a request and waits for a correlated response (recommended for most use cases)\n *\n * This is the recommended high-level method for request-response communication.\n * It automatically handles:\n * - Request ID generation and correlation\n * - Response matching with timeout protection\n * - Automatic reconnection on timeout\n * - Proper cleanup of pending requests\n *\n * @param message - The request message (can include optional requestId for testing)\n * @returns Promise that resolves with the response data\n * @throws Error if WebSocket is not connected, request times out, or response indicates failure\n *\n * @see sendMessage for fire-and-forget messaging without response handling\n */\n async sendRequest<T = ServerResponseMessage['data']>(\n message: Omit<ClientRequestMessage, 'data'> & {\n data?: Omit<ClientRequestMessage['data'], 'requestId'> & {\n requestId?: string;\n };\n },\n ): Promise<T> {\n if (this.#state !== WebSocketState.CONNECTED) {\n throw new Error(`Cannot send request: WebSocket is ${this.#state}`);\n }\n\n // Use provided requestId if available, otherwise generate a new one\n const requestId = message.data?.requestId ?? uuidV4();\n const requestMessage: ClientRequestMessage = {\n event: message.event,\n data: {\n ...message.data,\n requestId, // Set after spread to ensure it's not overwritten by undefined\n },\n };\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.#pendingRequests.delete(requestId);\n log('Request timeout - triggering reconnection', {\n timeout: this.#options.requestTimeout,\n });\n\n // Trigger reconnection on request timeout as it may indicate stale connection\n if (this.#state === WebSocketState.CONNECTED && this.#ws) {\n // Force close the current connection to trigger reconnection logic\n this.#ws.close(3000, 'Request timeout - forcing reconnect');\n }\n\n reject(\n new Error(`Request timeout after ${this.#options.requestTimeout}ms`),\n );\n }, this.#options.requestTimeout);\n\n // Store in pending requests for response correlation\n this.#pendingRequests.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeout,\n });\n\n // Send the request\n try {\n this.sendMessage(requestMessage);\n } catch (error) {\n this.#pendingRequests.delete(requestId);\n clearTimeout(timeout);\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n });\n }\n\n /**\n * Gets current connection information\n *\n * @returns Current connection status and details\n */\n getConnectionInfo(): WebSocketConnectionInfo {\n return {\n state: this.#state,\n url: this.#options.url,\n timeout: this.#options.timeout,\n reconnectDelay: this.#options.reconnectDelay,\n maxReconnectDelay: this.#options.maxReconnectDelay,\n requestTimeout: this.#options.requestTimeout,\n reconnectAttempts: this.#reconnectAttempts,\n connectedAt: this.#connectedAt,\n };\n }\n\n /**\n * Gets all subscription information for a specific channel\n *\n * @param channel - The channel name to look up\n * @returns Array of subscription details for all subscriptions containing the channel\n */\n getSubscriptionsByChannel(channel: string): WebSocketSubscription[] {\n const matchingSubscriptions: WebSocketSubscription[] = [];\n for (const [subscriptionId, subscription] of this.#subscriptions) {\n if (subscription.channels.includes(channel)) {\n matchingSubscriptions.push({\n subscriptionId,\n channels: subscription.channels,\n channelType: subscription.channelType,\n unsubscribe: subscription.unsubscribe,\n });\n }\n }\n return matchingSubscriptions;\n }\n\n /**\n * Checks if a channel has a subscription\n *\n * @param channel - The channel name to check\n * @returns True if the channel has a subscription, false otherwise\n */\n channelHasSubscription(channel: string): boolean {\n for (const subscription of this.#subscriptions.values()) {\n if (subscription.channels.includes(channel)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Finds all subscriptions that have channels starting with the specified prefix\n *\n * @param channelPrefix - The channel prefix to search for (e.g., \"account-activity.v1\")\n * @returns Array of subscription info for matching subscriptions\n */\n findSubscriptionsByChannelPrefix(\n channelPrefix: string,\n ): WebSocketSubscription[] {\n const matchingSubscriptions: WebSocketSubscription[] = [];\n\n for (const [subscriptionId, subscription] of this.#subscriptions) {\n // Check if any channel in this subscription starts with the prefix\n const hasMatchingChannel = subscription.channels.some((channel) =>\n channel.startsWith(channelPrefix),\n );\n\n if (hasMatchingChannel) {\n matchingSubscriptions.push({\n subscriptionId,\n channels: subscription.channels,\n channelType: subscription.channelType,\n unsubscribe: subscription.unsubscribe,\n });\n }\n }\n\n return matchingSubscriptions;\n }\n\n /**\n * Register a callback for specific channels (local callback only, no server subscription)\n *\n * **Key Difference from `subscribe()`:**\n * - `addChannelCallback()`: Registers a local callback without creating a server-side subscription.\n * The callback triggers on ANY message matching the channel name, regardless of subscriptionId.\n * Useful for system-wide notifications or when you don't control the subscription lifecycle.\n *\n * - `subscribe()`: Creates a proper server-side subscription with a subscriptionId.\n * The callback only triggers for messages with the matching subscriptionId.\n * Includes proper lifecycle management (unsubscribe, automatic cleanup on disconnect).\n *\n * **When to use `addChannelCallback()`:**\n * - Listening to system-wide notifications (e.g., 'system-notifications.v1')\n * - Monitoring channels where subscriptions are managed elsewhere\n * - Debug/logging scenarios where you want to observe all channel messages\n *\n * **When to use `subscribe()` instead:**\n * - Creating new subscriptions that need server-side registration\n * - When you need proper cleanup via unsubscribe\n * - Most application use cases (recommended approach)\n *\n * @param options - Channel callback configuration\n * @param options.channelName - Channel name to match exactly\n * @param options.callback - Function to call when channel matches\n *\n * @example\n * ```typescript\n * // Listen to system notifications (no server subscription needed)\n * webSocketService.addChannelCallback({\n * channelName: 'system-notifications.v1',\n * callback: (notification) => {\n * console.log('System notification:', notification.data);\n * }\n * });\n *\n * // For account-specific subscriptions, use subscribe() instead:\n * // const sub = await webSocketService.subscribe({\n * // channels: ['account-activity.v1.eip155:0:0x1234...'],\n * // callback: (notification) => { ... }\n * // });\n * ```\n *\n * @see subscribe for creating proper server-side subscriptions with lifecycle management\n */\n addChannelCallback(options: {\n channelName: string;\n callback: (notification: ServerNotificationMessage) => void;\n }): void {\n const channelCallback: ChannelCallback = {\n channelName: options.channelName,\n callback: options.callback,\n };\n\n // Check if callback already exists for this channel\n if (this.#channelCallbacks.has(options.channelName)) {\n return;\n }\n\n this.#channelCallbacks.set(options.channelName, channelCallback);\n }\n\n /**\n * Remove a channel callback\n *\n * @param channelName - The channel name returned from addChannelCallback\n * @returns True if callback was found and removed, false otherwise\n */\n removeChannelCallback(channelName: string): boolean {\n return this.#channelCallbacks.delete(channelName);\n }\n\n /**\n * Get all registered channel callbacks (for debugging)\n *\n * @returns Array of all registered channel callbacks\n */\n getChannelCallbacks(): ChannelCallback[] {\n return Array.from(this.#channelCallbacks.values());\n }\n\n /**\n * Destroy the service and clean up resources\n * Called when service is being destroyed or app is terminating\n */\n destroy(): void {\n this.#clearTimers();\n this.#clearSubscriptions();\n\n // Clear any pending connection promise\n this.#connectionPromise = null;\n\n // Clear all pending requests\n this.#clearPendingRequests(new Error('Service cleanup'));\n\n if (this.#ws && this.#ws.readyState === WebSocket.OPEN) {\n this.#ws.close(1000, 'Service cleanup');\n }\n\n // Set state to disconnected immediately\n this.#setState(WebSocketState.DISCONNECTED);\n }\n\n /**\n * Create and manage a subscription with server-side registration (recommended for most use cases)\n *\n * This is the recommended subscription API for high-level services. It creates a proper\n * server-side subscription and routes notifications based on subscriptionId.\n *\n * **Key Features:**\n * - Creates server-side subscription with unique subscriptionId\n * - Callback triggered only for messages with matching subscriptionId\n * - Automatic lifecycle management (cleanup on disconnect)\n * - Includes unsubscribe method for proper cleanup\n * - Request-response pattern with error handling\n *\n * **When to use `subscribe()`:**\n * - Creating new subscriptions (account activity, price updates, etc.)\n * - When you need proper cleanup/unsubscribe functionality\n * - Most application use cases\n *\n * **When to use `addChannelCallback()` instead:**\n * - System-wide notifications without server-side subscription\n * - Observing channels managed elsewhere\n * - Debug/logging scenarios\n *\n * @param options - Subscription configuration\n * @param options.channels - Array of channel names to subscribe to\n * @param options.callback - Callback function for handling notifications\n * @param options.requestId - Optional request ID for testing (will generate UUID if not provided)\n * @param options.channelType - Channel type identifier\n * @returns Subscription object with unsubscribe method\n *\n * @example\n * ```typescript\n * // AccountActivityService usage\n * const subscription = await webSocketService.subscribe({\n * channels: ['account-activity.v1.eip155:0:0x1234...'],\n * callback: (notification) => {\n * this.handleAccountActivity(notification.data);\n * }\n * });\n *\n * // Later, clean up\n * await subscription.unsubscribe();\n * ```\n *\n * @see addChannelCallback for local callbacks without server-side subscription\n */\n async subscribe(options: {\n /** Channel names to subscribe to */\n channels: string[];\n /** Channel type with version (e.g., 'account-activity.v1') for tracing and monitoring */\n channelType: string;\n /** Handler for incoming notifications */\n callback: (notification: ServerNotificationMessage) => void;\n /** Optional request ID for testing (will generate UUID if not provided) */\n requestId?: string;\n }): Promise<WebSocketSubscription> {\n const { channels, channelType, callback, requestId } = options;\n\n if (this.#state !== WebSocketState.CONNECTED) {\n throw new Error(\n `Cannot create subscription(s) ${channels.join(', ')}: WebSocket is ${this.#state}`,\n );\n }\n\n // Send subscription request and wait for response\n const subscriptionResponse = await this.sendRequest({\n event: 'subscribe',\n data: { channels, requestId },\n });\n\n if (!subscriptionResponse?.subscriptionId) {\n throw new Error('Invalid subscription response: missing subscription ID');\n }\n\n const { subscriptionId } = subscriptionResponse;\n\n // Create unsubscribe function\n const unsubscribe = async (unsubRequestId?: string): Promise<void> => {\n // Send unsubscribe request first\n await this.sendRequest({\n event: 'unsubscribe',\n data: {\n subscription: subscriptionId,\n channels,\n requestId: unsubRequestId,\n },\n });\n\n // Clean up subscription mapping\n this.#subscriptions.delete(subscriptionId);\n };\n\n const subscription = {\n subscriptionId,\n channels: [...channels],\n channelType,\n unsubscribe,\n };\n\n // Store subscription with subscription ID as key\n this.#subscriptions.set(subscriptionId, {\n subscriptionId,\n channels: [...channels], // Store copy of channels\n channelType,\n callback,\n unsubscribe,\n });\n\n return subscription;\n }\n\n // =============================================================================\n // 3. CONNECTION MANAGEMENT (PRIVATE)\n // =============================================================================\n\n /**\n * Builds an authenticated WebSocket URL with bearer token as query parameter.\n * Uses query parameter for WebSocket authentication since native WebSocket\n * doesn't support custom headers during handshake.\n *\n * @param bearerToken - The bearer token to use for authentication\n * @returns The authenticated WebSocket URL\n */\n #buildAuthenticatedUrl(bearerToken: string): string {\n const baseUrl = this.#options.url;\n\n // Add token as query parameter to the WebSocket URL\n const url = new URL(baseUrl);\n url.searchParams.set('token', bearerToken);\n\n return url.toString();\n }\n\n /**\n * Establishes the actual WebSocket connection\n *\n * @param bearerToken - The bearer token to use for authentication\n * @returns Promise that resolves when connection is established\n */\n async #establishConnection(bearerToken: string): Promise<void> {\n const wsUrl = this.#buildAuthenticatedUrl(bearerToken);\n const connectionStartTime = Date.now();\n\n return new Promise<void>((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n this.#connectionTimeout = setTimeout(() => {\n log('WebSocket connection timeout - forcing close', {\n timeout: this.#options.timeout,\n });\n ws.close();\n reject(\n new Error(\n `Failed to connect to WebSocket: Connection timeout after ${this.#options.timeout}ms`,\n ),\n );\n }, this.#options.timeout);\n\n ws.onopen = () => {\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n\n // Calculate connection latency\n const connectionLatency = Date.now() - connectionStartTime;\n\n // Trace successful connection with latency\n this.#trace(\n {\n name: `${SERVICE_NAME} Connection`,\n data: {\n reconnectAttempt: this.#reconnectAttempts,\n latency_ms: connectionLatency,\n },\n tags: {\n service: SERVICE_NAME,\n },\n },\n () => {\n this.#ws = ws;\n this.#setState(WebSocketState.CONNECTED);\n this.#connectedAt = Date.now();\n\n // Only reset after connection stays stable for a period (10 seconds)\n // This prevents rapid reconnect loops when server accepts then immediately closes\n this.#stableConnectionTimer = setTimeout(() => {\n this.#stableConnectionTimer = null;\n this.#reconnectAttempts = 0;\n // Create new backoff sequence for fresh start on next disconnect\n this.#newBackoff();\n log('Connection stable - reset reconnect attempts and backoff');\n }, 10000);\n\n resolve();\n },\n );\n };\n\n ws.onerror = (event: Event) => {\n log('WebSocket onerror event triggered', { event });\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n const error = new Error(`WebSocket connection error to ${wsUrl}`);\n reject(error);\n };\n\n ws.onclose = (event: CloseEvent) => {\n log('WebSocket onclose event triggered', {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean,\n });\n if (this.#state === WebSocketState.CONNECTING) {\n // Handle connection-phase close events\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n reject(\n new Error(\n `WebSocket connection closed during connection: ${event.code} ${event.reason}`,\n ),\n );\n } else {\n this.#handleClose(event);\n }\n };\n\n // Set up message handler immediately - no need to wait for connection\n ws.onmessage = (event: MessageEvent) => {\n try {\n const message = this.#parseMessage(event.data);\n this.#handleMessage(message);\n } catch {\n // Silently ignore invalid JSON messages\n }\n };\n });\n }\n\n // =============================================================================\n // 4. MESSAGE HANDLING (PRIVATE)\n // =============================================================================\n\n /**\n * Handles incoming WebSocket messages\n *\n * @param message - The WebSocket message to handle\n */\n #handleMessage(message: WebSocketMessage): void {\n // Handle server responses (correlated with requests) first\n if (this.#isServerResponse(message)) {\n this.#handleServerResponse(message);\n return;\n }\n\n // Handle subscription notifications with valid subscriptionId\n if (this.#isSubscriptionNotification(message)) {\n const notificationMsg = message as ServerNotificationMessage;\n const handled = this.#handleSubscriptionNotification(notificationMsg);\n // If subscription notification wasn't handled (falsy subscriptionId), fall through to channel handling\n if (handled) {\n return;\n }\n }\n\n // Trigger channel callbacks for any message with a channel property\n if (this.#isChannelMessage(message)) {\n const channelMsg = message as ServerNotificationMessage;\n this.#handleChannelMessage(channelMsg);\n }\n }\n\n /**\n * Checks if a message is a server response (correlated with client requests)\n *\n * @param message - The message to check\n * @returns True if the message is a server response\n */\n #isServerResponse(\n message: WebSocketMessage,\n ): message is ServerResponseMessage {\n return (\n 'data' in message &&\n message.data &&\n typeof message.data === 'object' &&\n 'requestId' in message.data\n );\n }\n\n /**\n * Checks if a message is a subscription notification (has subscriptionId)\n *\n * @param message - The message to check\n * @returns True if the message is a subscription notification with subscriptionId\n */\n #isSubscriptionNotification(message: WebSocketMessage): boolean {\n return 'subscriptionId' in message && !this.#isServerResponse(message);\n }\n\n /**\n * Checks if a message has a channel property (system or subscription notification)\n *\n * @param message - The message to check\n * @returns True if the message has a channel property\n */\n #isChannelMessage(\n message: WebSocketMessage,\n ): message is ServerNotificationMessage {\n return 'channel' in message;\n }\n\n /**\n * Handles server response messages (correlated with client requests)\n *\n * @param message - The server response message to handle\n */\n #handleServerResponse(message: ServerResponseMessage): void {\n const { requestId } = message.data;\n\n const request = this.#pendingRequests.get(requestId);\n if (!request) {\n return;\n }\n\n this.#pendingRequests.delete(requestId);\n clearTimeout(request.timeout);\n\n // Check if the response indicates failure\n if (message.data.failed && message.data.failed.length > 0) {\n request.reject(\n new Error(`Request failed: ${message.data.failed.join(', ')}`),\n );\n } else {\n request.resolve(message.data);\n }\n }\n\n /**\n * Handles messages with channel properties by triggering channel callbacks\n *\n * @param message - The message with channel property to handle\n */\n #handleChannelMessage(message: ServerNotificationMessage): void {\n if (this.#channelCallbacks.size === 0) {\n return;\n }\n\n // Calculate notification latency: time from server sent to client received\n const receivedAt = Date.now();\n const latency = receivedAt - message.timestamp;\n\n // Trace channel message processing with latency data\n this.#trace(\n {\n name: `${SERVICE_NAME} Channel Message`,\n data: {\n latency_ms: latency,\n event: message.event,\n },\n tags: {\n service: SERVICE_NAME,\n },\n },\n () => {\n // Direct lookup for exact channel match\n this.#channelCallbacks.get(message.channel)?.callback(message);\n },\n );\n }\n\n /**\n * Handles server notifications with subscription IDs\n *\n * @param message - The server notification message to handle\n * @returns True if the message was handled, false if it should fall through to channel handling\n */\n #handleSubscriptionNotification(message: ServerNotificationMessage): boolean {\n const { subscriptionId, timestamp, channel } = message;\n\n // Only handle if subscriptionId is defined and not null (allows \"0\" as valid ID)\n if (subscriptionId !== null && subscriptionId !== undefined) {\n const subscription = this.#subscriptions.get(subscriptionId);\n if (!subscription) {\n return false;\n }\n\n // Calculate notification latency: time from server sent to client received\n const receivedAt = Date.now();\n const latency = receivedAt - timestamp;\n\n // Trace notification processing wi th latency data\n // Use stored channelType instead of parsing each time\n this.#trace(\n {\n name: `${SERVICE_NAME} Notification`,\n data: {\n channel,\n latency_ms: latency,\n subscriptionId,\n },\n tags: {\n service: SERVICE_NAME,\n notification_type: subscription.channelType,\n },\n },\n () => {\n subscription.callback?.(message);\n },\n );\n return true;\n }\n\n return false;\n }\n\n /**\n * Parse WebSocket message data\n *\n * @param data - The raw message data to parse\n * @returns Parsed message\n */\n #parseMessage(data: string): WebSocketMessage {\n return JSON.parse(data);\n }\n\n // =============================================================================\n // 5. EVENT HANDLERS (PRIVATE)\n // =============================================================================\n\n /**\n * Handles WebSocket close events (mobile optimized)\n *\n * @param event - The WebSocket close event\n */\n #handleClose(event: CloseEvent): void {\n // Calculate connection duration before we clear state\n const connectionDuration = Date.now() - this.#connectedAt;\n\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n if (this.#stableConnectionTimer) {\n clearTimeout(this.#stableConnectionTimer);\n this.#stableConnectionTimer = null;\n }\n\n this.#connectedAt = 0;\n\n // Clear any pending connection promise\n this.#connectionPromise = null;\n\n // Clear subscriptions and pending requests on any disconnect\n // This ensures clean state for reconnection\n this.#clearPendingRequests(new Error('WebSocket connection closed'));\n this.#clearSubscriptions();\n\n // Update state to disconnected\n this.#setState(WebSocketState.DISCONNECTED);\n\n // Check if this was a manual disconnect\n if (this.#manualDisconnect) {\n // Manual disconnect - don't reconnect\n return;\n }\n\n // Trace unexpected disconnect with details\n this.#trace(\n {\n name: `${SERVICE_NAME} Disconnect`,\n data: {\n code: event.code,\n reason: event.reason || getCloseReason(event.code),\n connectionDuration_ms: connectionDuration,\n },\n tags: {\n service: SERVICE_NAME,\n disconnect_type: 'unexpected',\n },\n },\n () => {\n // Empty trace callback - just measuring the event\n },\n );\n\n this.#scheduleReconnect();\n }\n\n /**\n * Handles WebSocket errors\n *\n * @param _error - Error that occurred (unused)\n */\n #handleError(_error: Error): void {\n // Placeholder for future error handling logic\n }\n\n // =============================================================================\n // 6. STATE MANAGEMENT (PRIVATE)\n // =============================================================================\n\n /**\n * Schedules a connection attempt with exponential backoff and jitter\n *\n * This method is used for automatic reconnection with Cockatiel's exponential backoff:\n * - Prevents duplicate reconnection timers (idempotent)\n * - Applies exponential backoff with jitter based on previous failures\n * - Jitter uses decorrelated formula to prevent thundering herd problem\n * - Used ONLY for automatic retries, not user-initiated actions\n *\n * Call this from:\n * - connect() catch block (on connection failure)\n * - #handleClose() (on unexpected disconnect)\n *\n * For user-initiated actions (sign in, unlock), call connect() directly instead.\n *\n * If a reconnect is already scheduled, this is a no-op to prevent:\n * - Orphaned timers (memory leak)\n * - Inflated reconnect attempts counter\n * - Prematurely long delays\n */\n #scheduleReconnect(): void {\n // If a reconnect is already scheduled, don't schedule another one\n if (this.#reconnectTimer) {\n return;\n }\n\n // Increment attempts BEFORE calculating delay so backoff grows properly\n this.#reconnectAttempts += 1;\n\n // Use Cockatiel's exponential backoff to get delay with jitter\n const delay = this.#backoff.duration;\n\n // Progress to next backoff state for future reconnect attempts\n // Pass attempt number as context (though ExponentialBackoff doesn't use it)\n this.#backoff = this.#backoff.next({ attempt: this.#reconnectAttempts });\n\n log('Scheduling reconnect', {\n attempt: this.#reconnectAttempts,\n delay_ms: delay,\n });\n\n this.#reconnectTimer = setTimeout(() => {\n // Clear timer reference first\n this.#reconnectTimer = null;\n\n // Check if connection is still enabled before reconnecting\n if (this.#isEnabled && !this.#isEnabled()) {\n this.#reconnectAttempts = 0;\n // Create new backoff sequence when disabled\n this.#newBackoff();\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.connect();\n }, delay);\n }\n\n /**\n * Creates a new exponential backoff sequence\n */\n #newBackoff(): void {\n this.#backoff = new ExponentialBackoff({\n initialDelay: this.#options.reconnectDelay,\n maxDelay: this.#options.maxReconnectDelay,\n }).next();\n }\n\n /**\n * Clears all active timers\n */\n #clearTimers(): void {\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = null;\n }\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n if (this.#stableConnectionTimer) {\n clearTimeout(this.#stableConnectionTimer);\n this.#stableConnectionTimer = null;\n }\n }\n\n /**\n * Clears all pending requests and rejects them with the given error\n *\n * @param error - Error to reject with\n */\n #clearPendingRequests(error: Error): void {\n for (const [, request] of this.#pendingRequests) {\n clearTimeout(request.timeout);\n request.reject(error);\n }\n this.#pendingRequests.clear();\n }\n\n /**\n * Clears all active subscriptions\n */\n #clearSubscriptions(): void {\n this.#subscriptions.clear();\n }\n\n /**\n * Sets the connection state and emits state change events\n *\n * @param newState - The new WebSocket state\n */\n #setState(newState: WebSocketState): void {\n const oldState = this.#state;\n this.#state = newState;\n\n if (oldState !== newState) {\n // Publish connection state change event\n // Messenger handles listener errors internally, no need for try-catch\n this.#messenger.publish(\n 'BackendWebSocketService:connectionStateChanged',\n this.getConnectionInfo(),\n );\n }\n }\n}\n"]}
1
+ {"version":3,"file":"BackendWebSocketService.mjs","sourceRoot":"","sources":["../src/BackendWebSocketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TraceCallback } from '@metamask/controller-utils';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport { getErrorMessage } from '@metamask/utils';\nimport { v4 as uuidV4 } from 'uuid';\n\nimport type { BackendWebSocketServiceMethodActions } from './BackendWebSocketService-method-action-types';\nimport { projectLogger, createModuleLogger } from './logger';\n\nconst SERVICE_NAME = 'BackendWebSocketService' as const;\n\nconst log = createModuleLogger(projectLogger, SERVICE_NAME);\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'connect',\n 'disconnect',\n 'sendMessage',\n 'sendRequest',\n 'subscribe',\n 'getConnectionInfo',\n 'getSubscriptionsByChannel',\n 'channelHasSubscription',\n 'findSubscriptionsByChannelPrefix',\n 'addChannelCallback',\n 'removeChannelCallback',\n 'getChannelCallbacks',\n] as const;\n\n/**\n * Gets human-readable close reason from RFC 6455 close code\n *\n * @param code - WebSocket close code\n * @returns Human-readable close reason\n */\nexport function getCloseReason(code: number): string {\n switch (code) {\n case 1000:\n return 'Normal Closure';\n case 1001:\n return 'Going Away';\n case 1002:\n return 'Protocol Error';\n case 1003:\n return 'Unsupported Data';\n case 1004:\n return 'Reserved';\n case 1005:\n return 'No Status Received';\n case 1006:\n return 'Abnormal Closure';\n case 1007:\n return 'Invalid frame payload data';\n case 1008:\n return 'Policy Violation';\n case 1009:\n return 'Message Too Big';\n case 1010:\n return 'Mandatory Extension';\n case 1011:\n return 'Internal Server Error';\n case 1012:\n return 'Service Restart';\n case 1013:\n return 'Try Again Later';\n case 1014:\n return 'Bad Gateway';\n case 1015:\n return 'TLS Handshake';\n default:\n if (code >= 3000 && code <= 3999) {\n return 'Library/Framework Error';\n }\n if (code >= 4000 && code <= 4999) {\n return 'Application Error';\n }\n return 'Unknown';\n }\n}\n\n/**\n * WebSocket connection states\n */\nexport enum WebSocketState {\n CONNECTING = 'connecting',\n CONNECTED = 'connected',\n DISCONNECTING = 'disconnecting',\n DISCONNECTED = 'disconnected',\n ERROR = 'error',\n}\n\n/**\n * WebSocket event types\n */\nexport enum WebSocketEventType {\n CONNECTED = 'connected',\n DISCONNECTED = 'disconnected',\n MESSAGE = 'message',\n ERROR = 'error',\n RECONNECTING = 'reconnecting',\n RECONNECTED = 'reconnected',\n}\n\n/**\n * Configuration options for the WebSocket service\n */\nexport type BackendWebSocketServiceOptions = {\n /** The WebSocket URL to connect to */\n url: string;\n\n /** The messenger for inter-service communication */\n messenger: BackendWebSocketServiceMessenger;\n\n /** Connection timeout in milliseconds (default: 10000) */\n timeout?: number;\n\n /** Initial reconnection delay in milliseconds (default: 500) */\n reconnectDelay?: number;\n\n /** Maximum reconnection delay in milliseconds (default: 5000) */\n maxReconnectDelay?: number;\n\n /** Request timeout in milliseconds (default: 30000) */\n requestTimeout?: number;\n\n /** Optional callback to determine if connection should be enabled (default: always enabled) */\n isEnabled?: () => boolean;\n\n /** Optional callback to trace performance of WebSocket operations (default: no-op) */\n traceFn?: TraceCallback;\n};\n\n/**\n * Client Request message\n * Used when client sends a request to the server\n */\nexport type ClientRequestMessage = {\n event: string;\n data: {\n requestId: string;\n channels?: string[];\n [key: string]: unknown;\n };\n};\n\n/**\n * Server Response message\n * Used when server responds to a client request\n */\nexport type ServerResponseMessage = {\n event: string;\n data: {\n requestId: string;\n subscriptionId?: string;\n succeeded?: string[];\n failed?: string[];\n [key: string]: unknown;\n };\n};\n\n/**\n * Server Notification message\n * Used when server sends unsolicited data to client\n * subscriptionId is optional for system-wide notifications\n */\nexport type ServerNotificationMessage = {\n event: string;\n subscriptionId?: string;\n channel: string;\n data: Record<string, unknown>;\n timestamp: number;\n};\n\n/**\n * Union type for all WebSocket messages\n */\nexport type WebSocketMessage =\n | ClientRequestMessage\n | ServerResponseMessage\n | ServerNotificationMessage;\n\n/**\n * Channel-based callback configuration\n */\nexport type ChannelCallback = {\n /** Channel name to match (also serves as the unique identifier) */\n channelName: string;\n /** Callback function */\n callback: (notification: ServerNotificationMessage) => void;\n};\n\n/**\n * Unified WebSocket subscription object used for both internal storage and external API\n */\nexport type WebSocketSubscription = {\n /** The subscription ID from the server */\n subscriptionId: string;\n /** Channel names for this subscription */\n channels: string[];\n /** Channel type with version (e.g., 'account-activity.v1') extracted from first channel */\n channelType: string;\n /** Callback function for handling notifications (optional for external use) */\n callback?: (notification: ServerNotificationMessage) => void;\n /** Function to unsubscribe and clean up */\n unsubscribe: (requestId?: string) => Promise<void>;\n};\n\n/**\n * WebSocket connection info\n */\nexport type WebSocketConnectionInfo = {\n state: WebSocketState;\n url: string;\n reconnectAttempts: number;\n timeout: number;\n reconnectDelay: number;\n maxReconnectDelay: number;\n requestTimeout: number;\n connectedAt?: number;\n};\n\n// Action types for the messaging system - using generated method actions\nexport type BackendWebSocketServiceActions =\n BackendWebSocketServiceMethodActions;\n\ntype AllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerToken;\n\n// Event types for WebSocket connection state changes\nexport type BackendWebSocketServiceConnectionStateChangedEvent = {\n type: 'BackendWebSocketService:connectionStateChanged';\n payload: [WebSocketConnectionInfo];\n};\n\ntype AllowedEvents =\n | AuthenticationController.AuthenticationControllerStateChangeEvent\n | KeyringControllerLockEvent\n | KeyringControllerUnlockEvent;\n\nexport type BackendWebSocketServiceEvents =\n BackendWebSocketServiceConnectionStateChangedEvent;\n\nexport type BackendWebSocketServiceMessenger = Messenger<\n typeof SERVICE_NAME,\n BackendWebSocketServiceActions | AllowedActions,\n BackendWebSocketServiceEvents | AllowedEvents\n>;\n\n/**\n * WebSocket Service with automatic reconnection, session management and direct callback routing\n *\n * Connection Management:\n * - Automatically subscribes to AuthenticationController:stateChange (sign in/out)\n * - Automatically subscribes to KeyringController:lock/unlock events\n * - Idempotent connect() function safe for multiple rapid calls\n * - Auto-reconnects on unexpected disconnects (manualDisconnect = false)\n *\n * Platform Responsibilities:\n * - Call connect() when app opens/foregrounds\n * - Call disconnect() when app closes/backgrounds\n * - Provide isEnabled() callback (feature flag)\n * - Call destroy() on app termination\n *\n * Real-Time Performance Optimizations:\n * - Fast path message routing (zero allocations)\n * - Production mode removes try-catch overhead\n * - Optimized JSON parsing with fail-fast\n * - Direct callback routing bypasses event emitters\n * - Memory cleanup and resource management\n */\nexport class BackendWebSocketService {\n /**\n * The name of the service.\n */\n readonly name = SERVICE_NAME;\n\n readonly #messenger: BackendWebSocketServiceMessenger;\n\n readonly #options: Required<\n Omit<BackendWebSocketServiceOptions, 'messenger' | 'isEnabled' | 'traceFn'>\n >;\n\n readonly #isEnabled: (() => boolean) | undefined;\n\n readonly #trace: TraceCallback;\n\n #ws: WebSocket | undefined;\n\n #state: WebSocketState = WebSocketState.DISCONNECTED;\n\n #reconnectAttempts = 0;\n\n #reconnectTimer: NodeJS.Timeout | null = null;\n\n #connectionTimeout: NodeJS.Timeout | null = null;\n\n // Track the current connection promise to handle concurrent connection attempts\n #connectionPromise: Promise<void> | null = null;\n\n readonly #pendingRequests = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeout: NodeJS.Timeout;\n }\n >();\n\n #connectedAt: number = 0;\n\n // Track manual disconnects to prevent automatic reconnection\n #manualDisconnect = false;\n\n // Simplified subscription storage (single flat map)\n // Key: subscription ID string (e.g., 'sub_abc123def456')\n // Value: WebSocketSubscription object with channels, callback and metadata\n readonly #subscriptions = new Map<string, WebSocketSubscription>();\n\n // Channel-based callback storage\n // Key: channel name (serves as unique identifier)\n // Value: ChannelCallback configuration\n readonly #channelCallbacks = new Map<string, ChannelCallback>();\n\n // =============================================================================\n // 1. CONSTRUCTOR & INITIALIZATION\n // =============================================================================\n\n /**\n * Creates a new WebSocket service instance\n *\n * @param options - Configuration options for the WebSocket service\n */\n constructor(options: BackendWebSocketServiceOptions) {\n this.#messenger = options.messenger;\n this.#isEnabled = options.isEnabled;\n // Default to no-op trace function to keep core platform-agnostic\n this.#trace =\n options.traceFn ??\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (((_request: any, fn?: any) => fn?.()) as TraceCallback);\n\n this.#options = {\n url: options.url,\n timeout: options.timeout ?? 10000,\n reconnectDelay: options.reconnectDelay ?? 500,\n maxReconnectDelay: options.maxReconnectDelay ?? 5000,\n requestTimeout: options.requestTimeout ?? 30000,\n };\n\n // Subscribe to authentication and keyring controller events\n this.#subscribeEvents();\n\n // Register action handlers using the method actions pattern\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Setup event handling for authentication and wallet lock state\n *\n * Three event sources trigger connection/disconnection:\n * 1. AuthenticationController:stateChange (sign in/out)\n * 2. KeyringController:unlock (wallet unlocked)\n * 3. KeyringController:lock (wallet locked)\n *\n * All connect() calls are idempotent and validate all requirements.\n */\n #subscribeEvents(): void {\n // Subscribe to authentication state changes (sign in/out)\n this.#messenger.subscribe(\n 'AuthenticationController:stateChange',\n (state: AuthenticationController.AuthenticationControllerState) => {\n if (state.isSignedIn) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.connect();\n } else {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.disconnect();\n }\n },\n (state) => ({ isSignedIn: state.isSignedIn }),\n );\n\n // Subscribe to wallet unlock event\n this.#messenger.subscribe('KeyringController:unlock', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.connect();\n });\n\n // Subscribe to wallet lock event\n this.#messenger.subscribe('KeyringController:lock', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.disconnect();\n });\n }\n\n // =============================================================================\n // 2. PUBLIC API METHODS\n // =============================================================================\n\n /**\n * Establishes WebSocket connection with smart reconnection behavior\n *\n * Connection Requirements (all must be true):\n * 1. Feature enabled (isEnabled() = true)\n * 2. Wallet unlocked (checked by getBearerToken)\n * 3. User signed in (checked by getBearerToken)\n *\n * Platform code should call this when app opens/foregrounds.\n * Automatically called on KeyringController:unlock event.\n *\n * @returns Promise that resolves when connection is established\n */\n async connect(): Promise<void> {\n // Reset manual disconnect flag when explicitly connecting\n this.#manualDisconnect = false;\n\n // Priority 1: Check if feature is enabled via callback (feature flag check)\n // If feature is disabled, stop all connection attempts\n if (this.#isEnabled && !this.#isEnabled()) {\n // Clear any pending reconnection attempts since feature is disabled\n this.#clearTimers();\n this.#reconnectAttempts = 0;\n return;\n }\n\n // If already connected, return immediately\n if (this.#state === WebSocketState.CONNECTED) {\n return;\n }\n\n // If already connecting, wait for the existing connection attempt to complete\n if (this.#connectionPromise) {\n await this.#connectionPromise;\n return;\n }\n\n // Create and store the connection promise IMMEDIATELY (before any async operations)\n // This ensures subsequent connect() calls will wait for this promise instead of creating new connections\n this.#connectionPromise = (async () => {\n // Priority 2: Check authentication requirements (signed in)\n let bearerToken: string;\n try {\n const token = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n );\n if (!token) {\n this.#scheduleReconnect();\n throw new Error('Authentication required: user not signed in');\n }\n bearerToken = token;\n } catch (error) {\n log('Failed to check authentication requirements', { error });\n\n // Can't connect - schedule retry\n this.#scheduleReconnect();\n throw error;\n }\n\n this.#setState(WebSocketState.CONNECTING);\n\n // Establish the actual WebSocket connection\n try {\n await this.#establishConnection(bearerToken);\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n log('Connection attempt failed', { errorMessage, error });\n this.#setState(WebSocketState.ERROR);\n\n // Rethrow to propagate error to caller\n throw error;\n }\n })();\n\n try {\n await this.#connectionPromise;\n } finally {\n // Clear the connection promise when done (success or failure)\n this.#connectionPromise = null;\n }\n }\n\n /**\n * Closes WebSocket connection\n *\n * @returns Promise that resolves when disconnection is complete\n */\n async disconnect(): Promise<void> {\n if (\n this.#state === WebSocketState.DISCONNECTED ||\n this.#state === WebSocketState.DISCONNECTING\n ) {\n return;\n }\n\n // Mark this as a manual disconnect to prevent automatic reconnection\n this.#manualDisconnect = true;\n\n this.#setState(WebSocketState.DISCONNECTING);\n this.#clearTimers();\n this.#clearPendingRequests(new Error('WebSocket disconnected'));\n\n // Clear any pending connection promise\n this.#connectionPromise = null;\n\n if (this.#ws) {\n this.#ws.close(1000, 'Normal closure');\n }\n\n log('WebSocket manually disconnected');\n }\n\n /**\n * Sends a message through the WebSocket (fire-and-forget, no response expected)\n *\n * This is a low-level method for sending messages without waiting for a response.\n * Most consumers should use `sendRequest()` instead, which handles request-response\n * correlation and provides proper error handling with timeouts.\n *\n * Use this method only when:\n * - You don't need a response from the server\n * - You're implementing custom message protocols\n * - You need fine-grained control over message timing\n *\n * @param message - The message to send\n * @throws Error if WebSocket is not connected or send fails\n *\n * @see sendRequest for request-response pattern with automatic correlation\n */\n sendMessage(message: ClientRequestMessage): void {\n if (this.#state !== WebSocketState.CONNECTED || !this.#ws) {\n throw new Error(`Cannot send message: WebSocket is ${this.#state}`);\n }\n\n try {\n this.#ws.send(JSON.stringify(message));\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n this.#handleError(new Error(errorMessage));\n throw new Error(errorMessage);\n }\n }\n\n /**\n * Sends a request and waits for a correlated response (recommended for most use cases)\n *\n * This is the recommended high-level method for request-response communication.\n * It automatically handles:\n * - Request ID generation and correlation\n * - Response matching with timeout protection\n * - Automatic reconnection on timeout\n * - Proper cleanup of pending requests\n *\n * @param message - The request message (can include optional requestId for testing)\n * @returns Promise that resolves with the response data\n * @throws Error if WebSocket is not connected, request times out, or response indicates failure\n *\n * @see sendMessage for fire-and-forget messaging without response handling\n */\n async sendRequest<T = ServerResponseMessage['data']>(\n message: Omit<ClientRequestMessage, 'data'> & {\n data?: Omit<ClientRequestMessage['data'], 'requestId'> & {\n requestId?: string;\n };\n },\n ): Promise<T> {\n if (this.#state !== WebSocketState.CONNECTED) {\n throw new Error(`Cannot send request: WebSocket is ${this.#state}`);\n }\n\n // Use provided requestId if available, otherwise generate a new one\n const requestId = message.data?.requestId ?? uuidV4();\n const requestMessage: ClientRequestMessage = {\n event: message.event,\n data: {\n ...message.data,\n requestId, // Set after spread to ensure it's not overwritten by undefined\n },\n };\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.#pendingRequests.delete(requestId);\n log('Request timeout - triggering reconnection', {\n timeout: this.#options.requestTimeout,\n });\n\n // Trigger reconnection on request timeout as it may indicate stale connection\n if (this.#state === WebSocketState.CONNECTED && this.#ws) {\n // Force close the current connection to trigger reconnection logic\n this.#ws.close(3000, 'Request timeout - forcing reconnect');\n }\n\n reject(\n new Error(`Request timeout after ${this.#options.requestTimeout}ms`),\n );\n }, this.#options.requestTimeout);\n\n // Store in pending requests for response correlation\n this.#pendingRequests.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeout,\n });\n\n // Send the request\n try {\n this.sendMessage(requestMessage);\n } catch (error) {\n this.#pendingRequests.delete(requestId);\n clearTimeout(timeout);\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n });\n }\n\n /**\n * Gets current connection information\n *\n * @returns Current connection status and details\n */\n getConnectionInfo(): WebSocketConnectionInfo {\n return {\n state: this.#state,\n url: this.#options.url,\n timeout: this.#options.timeout,\n reconnectDelay: this.#options.reconnectDelay,\n maxReconnectDelay: this.#options.maxReconnectDelay,\n requestTimeout: this.#options.requestTimeout,\n reconnectAttempts: this.#reconnectAttempts,\n connectedAt: this.#connectedAt,\n };\n }\n\n /**\n * Gets all subscription information for a specific channel\n *\n * @param channel - The channel name to look up\n * @returns Array of subscription details for all subscriptions containing the channel\n */\n getSubscriptionsByChannel(channel: string): WebSocketSubscription[] {\n const matchingSubscriptions: WebSocketSubscription[] = [];\n for (const [subscriptionId, subscription] of this.#subscriptions) {\n if (subscription.channels.includes(channel)) {\n matchingSubscriptions.push({\n subscriptionId,\n channels: subscription.channels,\n channelType: subscription.channelType,\n unsubscribe: subscription.unsubscribe,\n });\n }\n }\n return matchingSubscriptions;\n }\n\n /**\n * Checks if a channel has a subscription\n *\n * @param channel - The channel name to check\n * @returns True if the channel has a subscription, false otherwise\n */\n channelHasSubscription(channel: string): boolean {\n for (const subscription of this.#subscriptions.values()) {\n if (subscription.channels.includes(channel)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Finds all subscriptions that have channels starting with the specified prefix\n *\n * @param channelPrefix - The channel prefix to search for (e.g., \"account-activity.v1\")\n * @returns Array of subscription info for matching subscriptions\n */\n findSubscriptionsByChannelPrefix(\n channelPrefix: string,\n ): WebSocketSubscription[] {\n const matchingSubscriptions: WebSocketSubscription[] = [];\n\n for (const [subscriptionId, subscription] of this.#subscriptions) {\n // Check if any channel in this subscription starts with the prefix\n const hasMatchingChannel = subscription.channels.some((channel) =>\n channel.startsWith(channelPrefix),\n );\n\n if (hasMatchingChannel) {\n matchingSubscriptions.push({\n subscriptionId,\n channels: subscription.channels,\n channelType: subscription.channelType,\n unsubscribe: subscription.unsubscribe,\n });\n }\n }\n\n return matchingSubscriptions;\n }\n\n /**\n * Register a callback for specific channels (local callback only, no server subscription)\n *\n * **Key Difference from `subscribe()`:**\n * - `addChannelCallback()`: Registers a local callback without creating a server-side subscription.\n * The callback triggers on ANY message matching the channel name, regardless of subscriptionId.\n * Useful for system-wide notifications or when you don't control the subscription lifecycle.\n *\n * - `subscribe()`: Creates a proper server-side subscription with a subscriptionId.\n * The callback only triggers for messages with the matching subscriptionId.\n * Includes proper lifecycle management (unsubscribe, automatic cleanup on disconnect).\n *\n * **When to use `addChannelCallback()`:**\n * - Listening to system-wide notifications (e.g., 'system-notifications.v1')\n * - Monitoring channels where subscriptions are managed elsewhere\n * - Debug/logging scenarios where you want to observe all channel messages\n *\n * **When to use `subscribe()` instead:**\n * - Creating new subscriptions that need server-side registration\n * - When you need proper cleanup via unsubscribe\n * - Most application use cases (recommended approach)\n *\n * @param options - Channel callback configuration\n * @param options.channelName - Channel name to match exactly\n * @param options.callback - Function to call when channel matches\n *\n * @example\n * ```typescript\n * // Listen to system notifications (no server subscription needed)\n * webSocketService.addChannelCallback({\n * channelName: 'system-notifications.v1',\n * callback: (notification) => {\n * console.log('System notification:', notification.data);\n * }\n * });\n *\n * // For account-specific subscriptions, use subscribe() instead:\n * // const sub = await webSocketService.subscribe({\n * // channels: ['account-activity.v1.eip155:0:0x1234...'],\n * // callback: (notification) => { ... }\n * // });\n * ```\n *\n * @see subscribe for creating proper server-side subscriptions with lifecycle management\n */\n addChannelCallback(options: {\n channelName: string;\n callback: (notification: ServerNotificationMessage) => void;\n }): void {\n const channelCallback: ChannelCallback = {\n channelName: options.channelName,\n callback: options.callback,\n };\n\n // Check if callback already exists for this channel\n if (this.#channelCallbacks.has(options.channelName)) {\n return;\n }\n\n this.#channelCallbacks.set(options.channelName, channelCallback);\n }\n\n /**\n * Remove a channel callback\n *\n * @param channelName - The channel name returned from addChannelCallback\n * @returns True if callback was found and removed, false otherwise\n */\n removeChannelCallback(channelName: string): boolean {\n return this.#channelCallbacks.delete(channelName);\n }\n\n /**\n * Get all registered channel callbacks (for debugging)\n *\n * @returns Array of all registered channel callbacks\n */\n getChannelCallbacks(): ChannelCallback[] {\n return Array.from(this.#channelCallbacks.values());\n }\n\n /**\n * Destroy the service and clean up resources\n * Called when service is being destroyed or app is terminating\n */\n destroy(): void {\n this.#clearTimers();\n this.#clearSubscriptions();\n\n // Clear any pending connection promise\n this.#connectionPromise = null;\n\n // Clear all pending requests\n this.#clearPendingRequests(new Error('Service cleanup'));\n\n if (this.#ws && this.#ws.readyState === WebSocket.OPEN) {\n this.#ws.close(1000, 'Service cleanup');\n }\n\n // Set state to disconnected immediately\n this.#setState(WebSocketState.DISCONNECTED);\n }\n\n /**\n * Create and manage a subscription with server-side registration (recommended for most use cases)\n *\n * This is the recommended subscription API for high-level services. It creates a proper\n * server-side subscription and routes notifications based on subscriptionId.\n *\n * **Key Features:**\n * - Creates server-side subscription with unique subscriptionId\n * - Callback triggered only for messages with matching subscriptionId\n * - Automatic lifecycle management (cleanup on disconnect)\n * - Includes unsubscribe method for proper cleanup\n * - Request-response pattern with error handling\n *\n * **When to use `subscribe()`:**\n * - Creating new subscriptions (account activity, price updates, etc.)\n * - When you need proper cleanup/unsubscribe functionality\n * - Most application use cases\n *\n * **When to use `addChannelCallback()` instead:**\n * - System-wide notifications without server-side subscription\n * - Observing channels managed elsewhere\n * - Debug/logging scenarios\n *\n * @param options - Subscription configuration\n * @param options.channels - Array of channel names to subscribe to\n * @param options.callback - Callback function for handling notifications\n * @param options.requestId - Optional request ID for testing (will generate UUID if not provided)\n * @param options.channelType - Channel type identifier\n * @returns Subscription object with unsubscribe method\n *\n * @example\n * ```typescript\n * // AccountActivityService usage\n * const subscription = await webSocketService.subscribe({\n * channels: ['account-activity.v1.eip155:0:0x1234...'],\n * callback: (notification) => {\n * this.handleAccountActivity(notification.data);\n * }\n * });\n *\n * // Later, clean up\n * await subscription.unsubscribe();\n * ```\n *\n * @see addChannelCallback for local callbacks without server-side subscription\n */\n async subscribe(options: {\n /** Channel names to subscribe to */\n channels: string[];\n /** Channel type with version (e.g., 'account-activity.v1') for tracing and monitoring */\n channelType: string;\n /** Handler for incoming notifications */\n callback: (notification: ServerNotificationMessage) => void;\n /** Optional request ID for testing (will generate UUID if not provided) */\n requestId?: string;\n }): Promise<WebSocketSubscription> {\n const { channels, channelType, callback, requestId } = options;\n\n if (this.#state !== WebSocketState.CONNECTED) {\n throw new Error(\n `Cannot create subscription(s) ${channels.join(', ')}: WebSocket is ${this.#state}`,\n );\n }\n\n // Send subscription request and wait for response\n const subscriptionResponse = await this.sendRequest({\n event: 'subscribe',\n data: { channels, requestId },\n });\n\n if (!subscriptionResponse?.subscriptionId) {\n throw new Error('Invalid subscription response: missing subscription ID');\n }\n\n const { subscriptionId } = subscriptionResponse;\n\n // Create unsubscribe function\n const unsubscribe = async (unsubRequestId?: string): Promise<void> => {\n // Send unsubscribe request first\n await this.sendRequest({\n event: 'unsubscribe',\n data: {\n subscription: subscriptionId,\n channels,\n requestId: unsubRequestId,\n },\n });\n\n // Clean up subscription mapping\n this.#subscriptions.delete(subscriptionId);\n };\n\n const subscription = {\n subscriptionId,\n channels: [...channels],\n channelType,\n unsubscribe,\n };\n\n // Store subscription with subscription ID as key\n this.#subscriptions.set(subscriptionId, {\n subscriptionId,\n channels: [...channels], // Store copy of channels\n channelType,\n callback,\n unsubscribe,\n });\n\n return subscription;\n }\n\n // =============================================================================\n // 3. CONNECTION MANAGEMENT (PRIVATE)\n // =============================================================================\n\n /**\n * Builds an authenticated WebSocket URL with bearer token as query parameter.\n * Uses query parameter for WebSocket authentication since native WebSocket\n * doesn't support custom headers during handshake.\n *\n * @param bearerToken - The bearer token to use for authentication\n * @returns The authenticated WebSocket URL\n */\n #buildAuthenticatedUrl(bearerToken: string): string {\n const baseUrl = this.#options.url;\n\n // Add token as query parameter to the WebSocket URL\n const url = new URL(baseUrl);\n url.searchParams.set('token', bearerToken);\n\n return url.toString();\n }\n\n /**\n * Establishes the actual WebSocket connection\n *\n * @param bearerToken - The bearer token to use for authentication\n * @returns Promise that resolves when connection is established\n */\n async #establishConnection(bearerToken: string): Promise<void> {\n const wsUrl = this.#buildAuthenticatedUrl(bearerToken);\n const connectionStartTime = Date.now();\n\n return new Promise<void>((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n this.#connectionTimeout = setTimeout(() => {\n log('WebSocket connection timeout - forcing close', {\n timeout: this.#options.timeout,\n });\n ws.close();\n reject(\n new Error(\n `Failed to connect to WebSocket: Connection timeout after ${this.#options.timeout}ms`,\n ),\n );\n }, this.#options.timeout);\n\n ws.onopen = () => {\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n\n // Calculate connection latency\n const connectionLatency = Date.now() - connectionStartTime;\n\n // Trace successful connection with latency\n this.#trace(\n {\n name: `${SERVICE_NAME} Connection`,\n data: {\n reconnectAttempt: this.#reconnectAttempts,\n latency_ms: connectionLatency,\n },\n tags: {\n service: SERVICE_NAME,\n },\n },\n () => {\n this.#ws = ws;\n this.#setState(WebSocketState.CONNECTED);\n this.#connectedAt = Date.now();\n\n // Reset reconnect attempts on successful connection\n this.#reconnectAttempts = 0;\n\n resolve();\n },\n );\n };\n\n ws.onerror = (event: Event) => {\n log('WebSocket onerror event triggered', { event });\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n const error = new Error(`WebSocket connection error to ${wsUrl}`);\n reject(error);\n };\n\n ws.onclose = (event: CloseEvent) => {\n log('WebSocket onclose event triggered', {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean,\n });\n if (this.#state === WebSocketState.CONNECTING) {\n // Handle connection-phase close events\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n reject(\n new Error(\n `WebSocket connection closed during connection: ${event.code} ${event.reason}`,\n ),\n );\n } else {\n this.#handleClose(event);\n }\n };\n\n // Set up message handler immediately - no need to wait for connection\n ws.onmessage = (event: MessageEvent) => {\n try {\n const message = this.#parseMessage(event.data);\n this.#handleMessage(message);\n } catch {\n // Silently ignore invalid JSON messages\n }\n };\n });\n }\n\n // =============================================================================\n // 4. MESSAGE HANDLING (PRIVATE)\n // =============================================================================\n\n /**\n * Handles incoming WebSocket messages\n *\n * @param message - The WebSocket message to handle\n */\n #handleMessage(message: WebSocketMessage): void {\n // Handle server responses (correlated with requests) first\n if (this.#isServerResponse(message)) {\n this.#handleServerResponse(message);\n return;\n }\n\n // Handle subscription notifications with valid subscriptionId\n if (this.#isSubscriptionNotification(message)) {\n const notificationMsg = message as ServerNotificationMessage;\n const handled = this.#handleSubscriptionNotification(notificationMsg);\n // If subscription notification wasn't handled (falsy subscriptionId), fall through to channel handling\n if (handled) {\n return;\n }\n }\n\n // Trigger channel callbacks for any message with a channel property\n if (this.#isChannelMessage(message)) {\n const channelMsg = message as ServerNotificationMessage;\n this.#handleChannelMessage(channelMsg);\n }\n }\n\n /**\n * Checks if a message is a server response (correlated with client requests)\n *\n * @param message - The message to check\n * @returns True if the message is a server response\n */\n #isServerResponse(\n message: WebSocketMessage,\n ): message is ServerResponseMessage {\n return (\n 'data' in message &&\n message.data &&\n typeof message.data === 'object' &&\n 'requestId' in message.data\n );\n }\n\n /**\n * Checks if a message is a subscription notification (has subscriptionId)\n *\n * @param message - The message to check\n * @returns True if the message is a subscription notification with subscriptionId\n */\n #isSubscriptionNotification(message: WebSocketMessage): boolean {\n return 'subscriptionId' in message && !this.#isServerResponse(message);\n }\n\n /**\n * Checks if a message has a channel property (system or subscription notification)\n *\n * @param message - The message to check\n * @returns True if the message has a channel property\n */\n #isChannelMessage(\n message: WebSocketMessage,\n ): message is ServerNotificationMessage {\n return 'channel' in message;\n }\n\n /**\n * Handles server response messages (correlated with client requests)\n *\n * @param message - The server response message to handle\n */\n #handleServerResponse(message: ServerResponseMessage): void {\n const { requestId } = message.data;\n\n const request = this.#pendingRequests.get(requestId);\n if (!request) {\n return;\n }\n\n this.#pendingRequests.delete(requestId);\n clearTimeout(request.timeout);\n\n // Check if the response indicates failure\n if (message.data.failed && message.data.failed.length > 0) {\n request.reject(\n new Error(`Request failed: ${message.data.failed.join(', ')}`),\n );\n } else {\n request.resolve(message.data);\n }\n }\n\n /**\n * Handles messages with channel properties by triggering channel callbacks\n *\n * @param message - The message with channel property to handle\n */\n #handleChannelMessage(message: ServerNotificationMessage): void {\n if (this.#channelCallbacks.size === 0) {\n return;\n }\n\n // Calculate notification latency: time from server sent to client received\n const receivedAt = Date.now();\n const latency = receivedAt - message.timestamp;\n\n // Trace channel message processing with latency data\n this.#trace(\n {\n name: `${SERVICE_NAME} Channel Message`,\n data: {\n latency_ms: latency,\n event: message.event,\n },\n tags: {\n service: SERVICE_NAME,\n },\n },\n () => {\n // Direct lookup for exact channel match\n this.#channelCallbacks.get(message.channel)?.callback(message);\n },\n );\n }\n\n /**\n * Handles server notifications with subscription IDs\n *\n * @param message - The server notification message to handle\n * @returns True if the message was handled, false if it should fall through to channel handling\n */\n #handleSubscriptionNotification(message: ServerNotificationMessage): boolean {\n const { subscriptionId, timestamp, channel } = message;\n\n // Only handle if subscriptionId is defined and not null (allows \"0\" as valid ID)\n if (subscriptionId !== null && subscriptionId !== undefined) {\n const subscription = this.#subscriptions.get(subscriptionId);\n if (!subscription) {\n return false;\n }\n\n // Calculate notification latency: time from server sent to client received\n const receivedAt = Date.now();\n const latency = receivedAt - timestamp;\n\n // Trace notification processing wi th latency data\n // Use stored channelType instead of parsing each time\n this.#trace(\n {\n name: `${SERVICE_NAME} Notification`,\n data: {\n channel,\n latency_ms: latency,\n subscriptionId,\n },\n tags: {\n service: SERVICE_NAME,\n notification_type: subscription.channelType,\n },\n },\n () => {\n subscription.callback?.(message);\n },\n );\n return true;\n }\n\n return false;\n }\n\n /**\n * Parse WebSocket message data\n *\n * @param data - The raw message data to parse\n * @returns Parsed message\n */\n #parseMessage(data: string): WebSocketMessage {\n return JSON.parse(data);\n }\n\n // =============================================================================\n // 5. EVENT HANDLERS (PRIVATE)\n // =============================================================================\n\n /**\n * Handles WebSocket close events (mobile optimized)\n *\n * @param event - The WebSocket close event\n */\n #handleClose(event: CloseEvent): void {\n // Calculate connection duration before we clear state\n const connectionDuration = Date.now() - this.#connectedAt;\n\n this.#clearTimers();\n this.#connectedAt = 0;\n\n // Clear any pending connection promise\n this.#connectionPromise = null;\n\n // Clear subscriptions and pending requests on any disconnect\n // This ensures clean state for reconnection\n this.#clearPendingRequests(new Error('WebSocket connection closed'));\n this.#clearSubscriptions();\n\n // Update state to disconnected\n this.#setState(WebSocketState.DISCONNECTED);\n\n // Check if this was a manual disconnect\n if (this.#manualDisconnect) {\n // Manual disconnect - don't reconnect\n return;\n }\n\n // Trace unexpected disconnect with details\n this.#trace(\n {\n name: `${SERVICE_NAME} Disconnect`,\n data: {\n code: event.code,\n reason: event.reason || getCloseReason(event.code),\n connectionDuration_ms: connectionDuration,\n },\n tags: {\n service: SERVICE_NAME,\n disconnect_type: 'unexpected',\n },\n },\n () => {\n // Empty trace callback - just measuring the event\n },\n );\n\n // For any unexpected disconnects, attempt reconnection\n // The manualDisconnect flag is the only gate - if it's false, we reconnect\n this.#scheduleReconnect();\n }\n\n /**\n * Handles WebSocket errors\n *\n * @param _error - Error that occurred (unused)\n */\n #handleError(_error: Error): void {\n // Placeholder for future error handling logic\n }\n\n // =============================================================================\n // 6. STATE MANAGEMENT (PRIVATE)\n // =============================================================================\n\n /**\n * Schedules a reconnection attempt with exponential backoff\n */\n #scheduleReconnect(): void {\n this.#reconnectAttempts += 1;\n\n const rawDelay =\n this.#options.reconnectDelay * Math.pow(1.5, this.#reconnectAttempts - 1);\n const delay = Math.min(rawDelay, this.#options.maxReconnectDelay);\n\n this.#reconnectTimer = setTimeout(() => {\n // Clear timer reference first\n this.#reconnectTimer = null;\n\n // Check if connection is still enabled before reconnecting\n if (this.#isEnabled && !this.#isEnabled()) {\n this.#reconnectAttempts = 0;\n return;\n }\n\n // Attempt to reconnect - if it fails, schedule another attempt\n this.connect().catch(() => {\n this.#scheduleReconnect();\n });\n }, delay);\n }\n\n /**\n * Clears all active timers\n */\n #clearTimers(): void {\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = null;\n }\n if (this.#connectionTimeout) {\n clearTimeout(this.#connectionTimeout);\n this.#connectionTimeout = null;\n }\n }\n\n /**\n * Clears all pending requests and rejects them with the given error\n *\n * @param error - Error to reject with\n */\n #clearPendingRequests(error: Error): void {\n for (const [, request] of this.#pendingRequests) {\n clearTimeout(request.timeout);\n request.reject(error);\n }\n this.#pendingRequests.clear();\n }\n\n /**\n * Clears all active subscriptions\n */\n #clearSubscriptions(): void {\n this.#subscriptions.clear();\n }\n\n /**\n * Sets the connection state and emits state change events\n *\n * @param newState - The new WebSocket state\n */\n #setState(newState: WebSocketState): void {\n const oldState = this.#state;\n this.#state = newState;\n\n if (oldState !== newState) {\n // Publish connection state change event\n // Messenger handles listener errors internally, no need for try-catch\n this.#messenger.publish(\n 'BackendWebSocketService:connectionStateChanged',\n this.getConnectionInfo(),\n );\n }\n }\n}\n"]}
package/dist/index.cjs CHANGED
@@ -3,12 +3,9 @@
3
3
  * @file Backend platform services for MetaMask.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AccountActivityService = exports.ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = exports.ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = exports.BackendWebSocketService = void 0;
6
+ exports.AccountActivityService = exports.BackendWebSocketService = void 0;
7
7
  var BackendWebSocketService_1 = require("./BackendWebSocketService.cjs");
8
8
  Object.defineProperty(exports, "BackendWebSocketService", { enumerable: true, get: function () { return BackendWebSocketService_1.BackendWebSocketService; } });
9
9
  var AccountActivityService_1 = require("./AccountActivityService.cjs");
10
- Object.defineProperty(exports, "ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS", { enumerable: true, get: function () { return AccountActivityService_1.ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS; } });
11
- Object.defineProperty(exports, "ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS", { enumerable: true, get: function () { return AccountActivityService_1.ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS; } });
12
- var AccountActivityService_2 = require("./AccountActivityService.cjs");
13
- Object.defineProperty(exports, "AccountActivityService", { enumerable: true, get: function () { return AccountActivityService_2.AccountActivityService; } });
10
+ Object.defineProperty(exports, "AccountActivityService", { enumerable: true, get: function () { return AccountActivityService_1.AccountActivityService; } });
14
11
  //# sourceMappingURL=index.cjs.map