@metamask-previews/core-backend 1.0.1-preview-0189b42 → 1.0.1-preview-5cf3ff79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _BackendWebSocketService_instances, _BackendWebSocketService_messenger, _BackendWebSocketService_options, _BackendWebSocketService_isEnabled, _BackendWebSocketService_trace, _BackendWebSocketService_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt, _BackendWebSocketService_manualDisconnect, _BackendWebSocketService_subscriptions, _BackendWebSocketService_channelCallbacks, _BackendWebSocketService_subscribeEvents, _BackendWebSocketService_buildAuthenticatedUrl, _BackendWebSocketService_establishConnection, _BackendWebSocketService_handleMessage, _BackendWebSocketService_isServerResponse, _BackendWebSocketService_isSubscriptionNotification, _BackendWebSocketService_isChannelMessage, _BackendWebSocketService_handleServerResponse, _BackendWebSocketService_handleChannelMessage, _BackendWebSocketService_handleSubscriptionNotification, _BackendWebSocketService_parseMessage, _BackendWebSocketService_handleClose, _BackendWebSocketService_handleError, _BackendWebSocketService_scheduleReconnect, _BackendWebSocketService_clearTimers, _BackendWebSocketService_clearPendingRequests, _BackendWebSocketService_clearSubscriptions, _BackendWebSocketService_setState;
13
+ var _BackendWebSocketService_instances, _BackendWebSocketService_messenger, _BackendWebSocketService_options, _BackendWebSocketService_isEnabled, _BackendWebSocketService_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt, _BackendWebSocketService_subscriptions, _BackendWebSocketService_channelCallbacks, _BackendWebSocketService_setupAuthentication, _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, _BackendWebSocketService_shouldReconnectOnClose;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.BackendWebSocketService = exports.WebSocketEventType = exports.WebSocketState = exports.getCloseReason = void 0;
16
16
  const utils_1 = require("@metamask/utils");
@@ -109,24 +109,18 @@ var WebSocketEventType;
109
109
  /**
110
110
  * WebSocket Service with automatic reconnection, session management and direct callback routing
111
111
  *
112
- * Connection Management:
113
- * - Automatically subscribes to AuthenticationController:stateChange (sign in/out)
114
- * - Automatically subscribes to KeyringController:lock/unlock events
115
- * - Idempotent connect() function safe for multiple rapid calls
116
- * - Auto-reconnects on unexpected disconnects (manualDisconnect = false)
117
- *
118
- * Platform Responsibilities:
119
- * - Call connect() when app opens/foregrounds
120
- * - Call disconnect() when app closes/backgrounds
121
- * - Provide isEnabled() callback (feature flag)
122
- * - Call destroy() on app termination
123
- *
124
112
  * Real-Time Performance Optimizations:
125
113
  * - Fast path message routing (zero allocations)
126
114
  * - Production mode removes try-catch overhead
127
115
  * - Optimized JSON parsing with fail-fast
128
116
  * - Direct callback routing bypasses event emitters
129
117
  * - Memory cleanup and resource management
118
+ *
119
+ * Mobile Integration:
120
+ * Mobile apps should handle lifecycle events (background/foreground) by:
121
+ * 1. Calling disconnect() when app goes to background
122
+ * 2. Calling connect() when app returns to foreground
123
+ * 3. Calling destroy() on app termination
130
124
  */
131
125
  class BackendWebSocketService {
132
126
  // =============================================================================
@@ -146,7 +140,6 @@ class BackendWebSocketService {
146
140
  _BackendWebSocketService_messenger.set(this, void 0);
147
141
  _BackendWebSocketService_options.set(this, void 0);
148
142
  _BackendWebSocketService_isEnabled.set(this, void 0);
149
- _BackendWebSocketService_trace.set(this, void 0);
150
143
  _BackendWebSocketService_ws.set(this, void 0);
151
144
  _BackendWebSocketService_state.set(this, WebSocketState.DISCONNECTED);
152
145
  _BackendWebSocketService_reconnectAttempts.set(this, 0);
@@ -155,9 +148,7 @@ class BackendWebSocketService {
155
148
  // Track the current connection promise to handle concurrent connection attempts
156
149
  _BackendWebSocketService_connectionPromise.set(this, null);
157
150
  _BackendWebSocketService_pendingRequests.set(this, new Map());
158
- _BackendWebSocketService_connectedAt.set(this, 0);
159
- // Track manual disconnects to prevent automatic reconnection
160
- _BackendWebSocketService_manualDisconnect.set(this, false);
151
+ _BackendWebSocketService_connectedAt.set(this, null);
161
152
  // Simplified subscription storage (single flat map)
162
153
  // Key: subscription ID string (e.g., 'sub_abc123def456')
163
154
  // Value: WebSocketSubscription object with channels, callback and metadata
@@ -168,8 +159,6 @@ class BackendWebSocketService {
168
159
  _BackendWebSocketService_channelCallbacks.set(this, new Map());
169
160
  __classPrivateFieldSet(this, _BackendWebSocketService_messenger, options.messenger, "f");
170
161
  __classPrivateFieldSet(this, _BackendWebSocketService_isEnabled, options.isEnabled, "f");
171
- // Default to no-op trace function to keep core platform-agnostic
172
- __classPrivateFieldSet(this, _BackendWebSocketService_trace, options.traceFn ?? ((_request, fn) => fn?.()), "f");
173
162
  __classPrivateFieldSet(this, _BackendWebSocketService_options, {
174
163
  url: options.url,
175
164
  timeout: options.timeout ?? 10000,
@@ -177,8 +166,8 @@ class BackendWebSocketService {
177
166
  maxReconnectDelay: options.maxReconnectDelay ?? 5000,
178
167
  requestTimeout: options.requestTimeout ?? 30000,
179
168
  }, "f");
180
- // Subscribe to authentication and keyring controller events
181
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_subscribeEvents).call(this);
169
+ // Setup authentication (always enabled)
170
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setupAuthentication).call(this);
182
171
  // Register action handlers using the method actions pattern
183
172
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
184
173
  }
@@ -188,23 +177,18 @@ class BackendWebSocketService {
188
177
  /**
189
178
  * Establishes WebSocket connection with smart reconnection behavior
190
179
  *
191
- * Connection Requirements (all must be true):
192
- * 1. Feature enabled (isEnabled() = true)
193
- * 2. Wallet unlocked (checked by getBearerToken)
194
- * 3. User signed in (checked by getBearerToken)
195
- *
196
- * Platform code should call this when app opens/foregrounds.
197
- * Automatically called on KeyringController:unlock event.
180
+ * Simplified Priority System (using AuthenticationController):
181
+ * 1. App closed/backgrounded → Stop all attempts (save resources)
182
+ * 2. User not signed in (wallet locked OR not authenticated) → Keep retrying
183
+ * 3. User signed in (wallet unlocked + authenticated) → Connect successfully
198
184
  *
199
185
  * @returns Promise that resolves when connection is established
200
186
  */
201
187
  async connect() {
202
- // Reset manual disconnect flag when explicitly connecting
203
- __classPrivateFieldSet(this, _BackendWebSocketService_manualDisconnect, false, "f");
204
- // Priority 1: Check if feature is enabled via callback (feature flag check)
205
- // If feature is disabled, stop all connection attempts
188
+ // Priority 1: Check if connection is enabled via callback (app lifecycle check)
189
+ // If app is closed/backgrounded, stop all connection attempts to save resources
206
190
  if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
207
- // Clear any pending reconnection attempts since feature is disabled
191
+ // Clear any pending reconnection attempts since app is disabled
208
192
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
209
193
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
210
194
  return;
@@ -218,9 +202,10 @@ class BackendWebSocketService {
218
202
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
219
203
  return;
220
204
  }
221
- // Priority 2: Check authentication requirements (signed in)
205
+ // Priority 2: Check authentication requirements (simplified - just check if signed in)
222
206
  let bearerToken;
223
207
  try {
208
+ // AuthenticationController.getBearerToken() handles wallet unlock checks internally
224
209
  const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
225
210
  if (!token) {
226
211
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
@@ -229,10 +214,8 @@ class BackendWebSocketService {
229
214
  bearerToken = token;
230
215
  }
231
216
  catch (error) {
232
- log('Failed to get bearer token (wallet locked or not signed in)', {
233
- error,
234
- });
235
- // Can't connect - schedule retry
217
+ log('Failed to check authentication requirements', { error });
218
+ // If we can't connect for ANY reason, schedule a retry
236
219
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
237
220
  return;
238
221
  }
@@ -246,8 +229,7 @@ class BackendWebSocketService {
246
229
  const errorMessage = (0, utils_1.getErrorMessage)(error);
247
230
  log('Connection attempt failed', { errorMessage, error });
248
231
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
249
- // Rethrow to propagate error to caller
250
- throw error;
232
+ throw new Error(`Failed to connect to WebSocket: ${errorMessage}`);
251
233
  }
252
234
  finally {
253
235
  // Clear the connection promise when done (success or failure)
@@ -264,8 +246,6 @@ class BackendWebSocketService {
264
246
  __classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
265
247
  return;
266
248
  }
267
- // Mark this as a manual disconnect to prevent automatic reconnection
268
- __classPrivateFieldSet(this, _BackendWebSocketService_manualDisconnect, true, "f");
269
249
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTING);
270
250
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
271
251
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
@@ -274,6 +254,7 @@ class BackendWebSocketService {
274
254
  if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
275
255
  __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
276
256
  }
257
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
277
258
  log('WebSocket manually disconnected');
278
259
  }
279
260
  /**
@@ -375,7 +356,7 @@ class BackendWebSocketService {
375
356
  state: __classPrivateFieldGet(this, _BackendWebSocketService_state, "f"),
376
357
  url: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url,
377
358
  reconnectAttempts: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
378
- connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f"),
359
+ connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f") ?? undefined,
379
360
  };
380
361
  }
381
362
  /**
@@ -391,7 +372,6 @@ class BackendWebSocketService {
391
372
  matchingSubscriptions.push({
392
373
  subscriptionId,
393
374
  channels: subscription.channels,
394
- channelType: subscription.channelType,
395
375
  unsubscribe: subscription.unsubscribe,
396
376
  });
397
377
  }
@@ -427,7 +407,6 @@ class BackendWebSocketService {
427
407
  matchingSubscriptions.push({
428
408
  subscriptionId,
429
409
  channels: subscription.channels,
430
- channelType: subscription.channelType,
431
410
  unsubscribe: subscription.unsubscribe,
432
411
  });
433
412
  }
@@ -521,8 +500,6 @@ class BackendWebSocketService {
521
500
  if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f") && __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").readyState === WebSocket.OPEN) {
522
501
  __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Service cleanup');
523
502
  }
524
- // Set state to disconnected immediately
525
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
526
503
  }
527
504
  /**
528
505
  * Create and manage a subscription with server-side registration (recommended for most use cases)
@@ -551,7 +528,6 @@ class BackendWebSocketService {
551
528
  * @param options.channels - Array of channel names to subscribe to
552
529
  * @param options.callback - Callback function for handling notifications
553
530
  * @param options.requestId - Optional request ID for testing (will generate UUID if not provided)
554
- * @param options.channelType - Channel type identifier
555
531
  * @returns Subscription object with unsubscribe method
556
532
  *
557
533
  * @example
@@ -571,7 +547,7 @@ class BackendWebSocketService {
571
547
  * @see addChannelCallback for local callbacks without server-side subscription
572
548
  */
573
549
  async subscribe(options) {
574
- const { channels, channelType, callback, requestId } = options;
550
+ const { channels, callback, requestId } = options;
575
551
  if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") !== WebSocketState.CONNECTED) {
576
552
  throw new Error(`Cannot create subscription(s) ${channels.join(', ')}: WebSocket is ${__classPrivateFieldGet(this, _BackendWebSocketService_state, "f")}`);
577
553
  }
@@ -584,6 +560,10 @@ class BackendWebSocketService {
584
560
  throw new Error('Invalid subscription response: missing subscription ID');
585
561
  }
586
562
  const { subscriptionId } = subscriptionResponse;
563
+ // Check for failures
564
+ if (subscriptionResponse.failed && subscriptionResponse.failed.length > 0) {
565
+ throw new Error(`Subscription failed for channels: ${subscriptionResponse.failed.join(', ')}`);
566
+ }
587
567
  // Create unsubscribe function
588
568
  const unsubscribe = async (unsubRequestId) => {
589
569
  // Send unsubscribe request first
@@ -601,14 +581,12 @@ class BackendWebSocketService {
601
581
  const subscription = {
602
582
  subscriptionId,
603
583
  channels: [...channels],
604
- channelType,
605
584
  unsubscribe,
606
585
  };
607
586
  // Store subscription with subscription ID as key
608
587
  __classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").set(subscriptionId, {
609
588
  subscriptionId,
610
589
  channels: [...channels],
611
- channelType,
612
590
  callback,
613
591
  unsubscribe,
614
592
  });
@@ -616,28 +594,33 @@ class BackendWebSocketService {
616
594
  }
617
595
  }
618
596
  exports.BackendWebSocketService = BackendWebSocketService;
619
- _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() {
620
- // Subscribe to authentication state changes (sign in/out)
621
- __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (state) => {
622
- if (state.isSignedIn) {
623
- // eslint-disable-next-line no-void
624
- void this.connect();
625
- }
626
- else {
627
- // eslint-disable-next-line no-void
628
- void this.disconnect();
629
- }
630
- });
631
- // Subscribe to wallet unlock event
632
- __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:unlock', () => {
633
- // eslint-disable-next-line no-void
634
- void this.connect();
635
- });
636
- // Subscribe to wallet lock event
637
- __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:lock', () => {
638
- // eslint-disable-next-line no-void
639
- void this.disconnect();
640
- });
597
+ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_options = new WeakMap(), _BackendWebSocketService_isEnabled = 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_subscriptions = new WeakMap(), _BackendWebSocketService_channelCallbacks = new WeakMap(), _BackendWebSocketService_instances = new WeakSet(), _BackendWebSocketService_setupAuthentication = function _BackendWebSocketService_setupAuthentication() {
598
+ try {
599
+ // Subscribe to authentication state changes - this includes wallet unlock state
600
+ // AuthenticationController can only be signed in if wallet is unlocked
601
+ // Using selector to only listen for isSignedIn property changes for better performance
602
+ __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (isSignedIn) => {
603
+ if (isSignedIn) {
604
+ // User signed in (wallet unlocked + authenticated) - try to connect
605
+ // Clear any pending reconnection timer since we're attempting connection
606
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
607
+ this.connect().catch((error) => {
608
+ log('Failed to connect after sign-in', { error });
609
+ });
610
+ }
611
+ else {
612
+ // User signed out (wallet locked OR signed out) - disconnect and stop reconnection attempts
613
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
614
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
615
+ this.disconnect().catch((error) => {
616
+ log('Failed to disconnect after sign-out', { error });
617
+ });
618
+ }
619
+ }, (state) => state.isSignedIn);
620
+ }
621
+ catch (error) {
622
+ throw new Error(`Authentication setup failed: ${(0, utils_1.getErrorMessage)(error)}`);
623
+ }
641
624
  }, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
642
625
  const baseUrl = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url;
643
626
  // Add token as query parameter to the WebSocket URL
@@ -653,7 +636,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
653
636
  */
654
637
  async function _BackendWebSocketService_establishConnection(bearerToken) {
655
638
  const wsUrl = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_buildAuthenticatedUrl).call(this, bearerToken);
656
- const connectionStartTime = Date.now();
657
639
  return new Promise((resolve, reject) => {
658
640
  const ws = new WebSocket(wsUrl);
659
641
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, setTimeout(() => {
@@ -661,48 +643,40 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
661
643
  timeout: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout,
662
644
  });
663
645
  ws.close();
664
- reject(new Error(`Failed to connect to WebSocket: Connection timeout after ${__classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout}ms`));
646
+ reject(new Error(`Connection timeout after ${__classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout}ms`));
665
647
  }, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout), "f");
666
648
  ws.onopen = () => {
667
649
  if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
668
650
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
669
651
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
670
652
  }
671
- // Calculate connection latency
672
- const connectionLatency = Date.now() - connectionStartTime;
673
- // Trace successful connection with latency
674
- __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
675
- name: `${SERVICE_NAME} Connection`,
676
- data: {
677
- reconnectAttempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
678
- latency_ms: connectionLatency,
679
- },
680
- tags: {
681
- service: SERVICE_NAME,
682
- },
683
- }, () => {
684
- __classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
685
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
686
- __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
687
- // Reset reconnect attempts on successful connection
688
- __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
689
- resolve();
690
- });
653
+ __classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
654
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
655
+ __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
656
+ // Reset reconnect attempts on successful connection
657
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
658
+ resolve();
691
659
  };
692
660
  ws.onerror = (event) => {
693
661
  log('WebSocket onerror event triggered', { event });
694
- // Handle connection-phase errors
695
- if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
696
- clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
697
- __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
662
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
663
+ // Handle connection-phase errors
664
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
665
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
666
+ __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
667
+ }
668
+ const error = new Error(`WebSocket connection error to ${wsUrl}`);
669
+ reject(error);
670
+ }
671
+ else {
672
+ // Handle runtime errors
673
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_handleError).call(this, new Error(`WebSocket error: ${event.type}`));
698
674
  }
699
- const error = new Error(`WebSocket connection error to ${wsUrl}`);
700
- reject(error);
701
675
  };
702
676
  ws.onclose = (event) => {
703
677
  log('WebSocket onclose event triggered', {
704
678
  code: event.code,
705
- reason: event.reason,
679
+ reason: event.reason || 'none',
706
680
  wasClean: event.wasClean,
707
681
  });
708
682
  if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
@@ -774,98 +748,42 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
774
748
  if (__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").size === 0) {
775
749
  return;
776
750
  }
777
- // Calculate notification latency: time from server sent to client received
778
- const receivedAt = Date.now();
779
- const latency = receivedAt - message.timestamp;
780
- // Trace channel message processing with latency data
781
- __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
782
- name: `${SERVICE_NAME} Channel Message`,
783
- data: {
784
- channel: message.channel,
785
- latency_ms: latency,
786
- event: message.event,
787
- },
788
- tags: {
789
- service: SERVICE_NAME,
790
- channel_type: message.channel,
791
- },
792
- }, () => {
793
- // Direct lookup for exact channel match
794
- __classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").get(message.channel)?.callback(message);
795
- });
751
+ // Direct lookup for exact channel match
752
+ __classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").get(message.channel)?.callback(message);
796
753
  }, _BackendWebSocketService_handleSubscriptionNotification = function _BackendWebSocketService_handleSubscriptionNotification(message) {
797
- const { subscriptionId, timestamp, channel } = message;
754
+ const { subscriptionId } = message;
798
755
  // Only handle if subscriptionId is defined and not null (allows "0" as valid ID)
799
756
  if (subscriptionId !== null && subscriptionId !== undefined) {
800
- const subscription = __classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").get(subscriptionId);
801
- if (!subscription) {
802
- return false;
803
- }
804
- // Calculate notification latency: time from server sent to client received
805
- const receivedAt = Date.now();
806
- const latency = receivedAt - timestamp;
807
- // Trace notification processing with latency data
808
- // Use stored channelType instead of parsing each time
809
- __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
810
- name: `${SERVICE_NAME} Notification`,
811
- data: {
812
- channel,
813
- latency_ms: latency,
814
- subscriptionId,
815
- },
816
- tags: {
817
- service: SERVICE_NAME,
818
- notification_type: subscription.channelType,
819
- },
820
- }, () => {
821
- subscription.callback?.(message);
822
- });
757
+ __classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").get(subscriptionId)?.callback?.(message);
823
758
  return true;
824
759
  }
825
760
  return false;
826
761
  }, _BackendWebSocketService_parseMessage = function _BackendWebSocketService_parseMessage(data) {
827
762
  return JSON.parse(data);
828
763
  }, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
829
- // Calculate connection duration before we clear state
830
- const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
831
764
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
832
- __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
765
+ __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, null, "f");
833
766
  // Clear any pending connection promise
834
767
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
835
768
  // Clear subscriptions and pending requests on any disconnect
836
769
  // This ensures clean state for reconnection
837
770
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket connection closed'));
838
771
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearSubscriptions).call(this);
839
- // Update state to disconnected
840
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
841
- // Check if this was a manual disconnect
842
- if (__classPrivateFieldGet(this, _BackendWebSocketService_manualDisconnect, "f")) {
843
- // Manual disconnect - don't reconnect
844
- log('WebSocket closed due to manual disconnect, not reconnecting');
772
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
773
+ // Manual disconnect
774
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
845
775
  return;
846
776
  }
847
- // Trace unexpected disconnect with details
848
- __classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
849
- name: `${SERVICE_NAME} Disconnect`,
850
- data: {
777
+ // For unexpected disconnects, update the state to reflect that we're disconnected
778
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
779
+ // Check if we should attempt reconnection based on close code
780
+ const shouldReconnect = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_shouldReconnectOnClose).call(this, event.code);
781
+ if (shouldReconnect) {
782
+ log('Connection lost unexpectedly, will attempt reconnection', {
851
783
  code: event.code,
852
- reason: event.reason || getCloseReason(event.code),
853
- connectionDuration_ms: connectionDuration,
854
- },
855
- tags: {
856
- service: SERVICE_NAME,
857
- disconnect_type: 'unexpected',
858
- },
859
- }, () => {
860
- // Empty trace callback - just measuring the event
861
- });
862
- // For any unexpected disconnects, attempt reconnection
863
- // The manualDisconnect flag is the only gate - if it's false, we reconnect
864
- log('Connection lost unexpectedly, will attempt reconnection', {
865
- code: event.code,
866
- reason: event.reason,
867
- });
868
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
784
+ });
785
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
786
+ }
869
787
  }, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
870
788
  // Placeholder for future error handling logic
871
789
  }, _BackendWebSocketService_scheduleReconnect = function _BackendWebSocketService_scheduleReconnect() {
@@ -916,5 +834,8 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
916
834
  // Messenger handles listener errors internally, no need for try-catch
917
835
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").publish('BackendWebSocketService:connectionStateChanged', this.getConnectionInfo());
918
836
  }
837
+ }, _BackendWebSocketService_shouldReconnectOnClose = function _BackendWebSocketService_shouldReconnectOnClose(code) {
838
+ // Don't reconnect only on normal closure (manual disconnect)
839
+ return code !== 1000;
919
840
  };
920
841
  //# sourceMappingURL=BackendWebSocketService.cjs.map