@metamask-previews/core-backend 1.0.1-preview-e7dac947 → 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.
- package/CHANGELOG.md +0 -31
- package/README.md +22 -62
- package/dist/AccountActivityService.cjs +88 -37
- package/dist/AccountActivityService.cjs.map +1 -1
- package/dist/AccountActivityService.d.cts +7 -3
- package/dist/AccountActivityService.d.cts.map +1 -1
- package/dist/AccountActivityService.d.mts +7 -3
- package/dist/AccountActivityService.d.mts.map +1 -1
- package/dist/AccountActivityService.mjs +88 -37
- package/dist/AccountActivityService.mjs.map +1 -1
- package/dist/BackendWebSocketService.cjs +96 -175
- package/dist/BackendWebSocketService.cjs.map +1 -1
- package/dist/BackendWebSocketService.d.cts +11 -30
- package/dist/BackendWebSocketService.d.cts.map +1 -1
- package/dist/BackendWebSocketService.d.mts +11 -30
- package/dist/BackendWebSocketService.d.mts.map +1 -1
- package/dist/BackendWebSocketService.mjs +96 -175
- package/dist/BackendWebSocketService.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -4
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +2 -4
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -9,7 +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,
|
|
12
|
+
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;
|
|
13
13
|
import { getErrorMessage } from "@metamask/utils";
|
|
14
14
|
import { v4 as uuidV4 } from "uuid";
|
|
15
15
|
import { projectLogger, createModuleLogger } from "./logger.mjs";
|
|
@@ -105,24 +105,18 @@ export var WebSocketEventType;
|
|
|
105
105
|
/**
|
|
106
106
|
* WebSocket Service with automatic reconnection, session management and direct callback routing
|
|
107
107
|
*
|
|
108
|
-
* Connection Management:
|
|
109
|
-
* - Automatically subscribes to AuthenticationController:stateChange (sign in/out)
|
|
110
|
-
* - Automatically subscribes to KeyringController:lock/unlock events
|
|
111
|
-
* - Idempotent connect() function safe for multiple rapid calls
|
|
112
|
-
* - Auto-reconnects on unexpected disconnects (manualDisconnect = false)
|
|
113
|
-
*
|
|
114
|
-
* Platform Responsibilities:
|
|
115
|
-
* - Call connect() when app opens/foregrounds
|
|
116
|
-
* - Call disconnect() when app closes/backgrounds
|
|
117
|
-
* - Provide isEnabled() callback (feature flag)
|
|
118
|
-
* - Call destroy() on app termination
|
|
119
|
-
*
|
|
120
108
|
* Real-Time Performance Optimizations:
|
|
121
109
|
* - Fast path message routing (zero allocations)
|
|
122
110
|
* - Production mode removes try-catch overhead
|
|
123
111
|
* - Optimized JSON parsing with fail-fast
|
|
124
112
|
* - Direct callback routing bypasses event emitters
|
|
125
113
|
* - Memory cleanup and resource management
|
|
114
|
+
*
|
|
115
|
+
* Mobile Integration:
|
|
116
|
+
* Mobile apps should handle lifecycle events (background/foreground) by:
|
|
117
|
+
* 1. Calling disconnect() when app goes to background
|
|
118
|
+
* 2. Calling connect() when app returns to foreground
|
|
119
|
+
* 3. Calling destroy() on app termination
|
|
126
120
|
*/
|
|
127
121
|
export class BackendWebSocketService {
|
|
128
122
|
// =============================================================================
|
|
@@ -142,7 +136,6 @@ export class BackendWebSocketService {
|
|
|
142
136
|
_BackendWebSocketService_messenger.set(this, void 0);
|
|
143
137
|
_BackendWebSocketService_options.set(this, void 0);
|
|
144
138
|
_BackendWebSocketService_isEnabled.set(this, void 0);
|
|
145
|
-
_BackendWebSocketService_trace.set(this, void 0);
|
|
146
139
|
_BackendWebSocketService_ws.set(this, void 0);
|
|
147
140
|
_BackendWebSocketService_state.set(this, WebSocketState.DISCONNECTED);
|
|
148
141
|
_BackendWebSocketService_reconnectAttempts.set(this, 0);
|
|
@@ -151,9 +144,7 @@ export class BackendWebSocketService {
|
|
|
151
144
|
// Track the current connection promise to handle concurrent connection attempts
|
|
152
145
|
_BackendWebSocketService_connectionPromise.set(this, null);
|
|
153
146
|
_BackendWebSocketService_pendingRequests.set(this, new Map());
|
|
154
|
-
_BackendWebSocketService_connectedAt.set(this,
|
|
155
|
-
// Track manual disconnects to prevent automatic reconnection
|
|
156
|
-
_BackendWebSocketService_manualDisconnect.set(this, false);
|
|
147
|
+
_BackendWebSocketService_connectedAt.set(this, null);
|
|
157
148
|
// Simplified subscription storage (single flat map)
|
|
158
149
|
// Key: subscription ID string (e.g., 'sub_abc123def456')
|
|
159
150
|
// Value: WebSocketSubscription object with channels, callback and metadata
|
|
@@ -164,8 +155,6 @@ export class BackendWebSocketService {
|
|
|
164
155
|
_BackendWebSocketService_channelCallbacks.set(this, new Map());
|
|
165
156
|
__classPrivateFieldSet(this, _BackendWebSocketService_messenger, options.messenger, "f");
|
|
166
157
|
__classPrivateFieldSet(this, _BackendWebSocketService_isEnabled, options.isEnabled, "f");
|
|
167
|
-
// Default to no-op trace function to keep core platform-agnostic
|
|
168
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_trace, options.traceFn ?? ((_request, fn) => fn?.()), "f");
|
|
169
158
|
__classPrivateFieldSet(this, _BackendWebSocketService_options, {
|
|
170
159
|
url: options.url,
|
|
171
160
|
timeout: options.timeout ?? 10000,
|
|
@@ -173,8 +162,8 @@ export class BackendWebSocketService {
|
|
|
173
162
|
maxReconnectDelay: options.maxReconnectDelay ?? 5000,
|
|
174
163
|
requestTimeout: options.requestTimeout ?? 30000,
|
|
175
164
|
}, "f");
|
|
176
|
-
//
|
|
177
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m",
|
|
165
|
+
// Setup authentication (always enabled)
|
|
166
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setupAuthentication).call(this);
|
|
178
167
|
// Register action handlers using the method actions pattern
|
|
179
168
|
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
180
169
|
}
|
|
@@ -184,23 +173,18 @@ export class BackendWebSocketService {
|
|
|
184
173
|
/**
|
|
185
174
|
* Establishes WebSocket connection with smart reconnection behavior
|
|
186
175
|
*
|
|
187
|
-
*
|
|
188
|
-
* 1.
|
|
189
|
-
* 2.
|
|
190
|
-
* 3. User signed in (
|
|
191
|
-
*
|
|
192
|
-
* Platform code should call this when app opens/foregrounds.
|
|
193
|
-
* Automatically called on KeyringController:unlock event.
|
|
176
|
+
* Simplified Priority System (using AuthenticationController):
|
|
177
|
+
* 1. App closed/backgrounded → Stop all attempts (save resources)
|
|
178
|
+
* 2. User not signed in (wallet locked OR not authenticated) → Keep retrying
|
|
179
|
+
* 3. User signed in (wallet unlocked + authenticated) → Connect successfully
|
|
194
180
|
*
|
|
195
181
|
* @returns Promise that resolves when connection is established
|
|
196
182
|
*/
|
|
197
183
|
async connect() {
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
// Priority 1: Check if feature is enabled via callback (feature flag check)
|
|
201
|
-
// If feature is disabled, stop all connection attempts
|
|
184
|
+
// Priority 1: Check if connection is enabled via callback (app lifecycle check)
|
|
185
|
+
// If app is closed/backgrounded, stop all connection attempts to save resources
|
|
202
186
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
|
|
203
|
-
// Clear any pending reconnection attempts since
|
|
187
|
+
// Clear any pending reconnection attempts since app is disabled
|
|
204
188
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
205
189
|
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
206
190
|
return;
|
|
@@ -214,9 +198,10 @@ export class BackendWebSocketService {
|
|
|
214
198
|
await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
|
|
215
199
|
return;
|
|
216
200
|
}
|
|
217
|
-
// Priority 2: Check authentication requirements (signed in)
|
|
201
|
+
// Priority 2: Check authentication requirements (simplified - just check if signed in)
|
|
218
202
|
let bearerToken;
|
|
219
203
|
try {
|
|
204
|
+
// AuthenticationController.getBearerToken() handles wallet unlock checks internally
|
|
220
205
|
const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
|
|
221
206
|
if (!token) {
|
|
222
207
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
@@ -225,10 +210,8 @@ export class BackendWebSocketService {
|
|
|
225
210
|
bearerToken = token;
|
|
226
211
|
}
|
|
227
212
|
catch (error) {
|
|
228
|
-
log('Failed to
|
|
229
|
-
|
|
230
|
-
});
|
|
231
|
-
// Can't connect - schedule retry
|
|
213
|
+
log('Failed to check authentication requirements', { error });
|
|
214
|
+
// If we can't connect for ANY reason, schedule a retry
|
|
232
215
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
233
216
|
return;
|
|
234
217
|
}
|
|
@@ -242,8 +225,7 @@ export class BackendWebSocketService {
|
|
|
242
225
|
const errorMessage = getErrorMessage(error);
|
|
243
226
|
log('Connection attempt failed', { errorMessage, error });
|
|
244
227
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
|
|
245
|
-
|
|
246
|
-
throw error;
|
|
228
|
+
throw new Error(`Failed to connect to WebSocket: ${errorMessage}`);
|
|
247
229
|
}
|
|
248
230
|
finally {
|
|
249
231
|
// Clear the connection promise when done (success or failure)
|
|
@@ -260,8 +242,6 @@ export class BackendWebSocketService {
|
|
|
260
242
|
__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
|
|
261
243
|
return;
|
|
262
244
|
}
|
|
263
|
-
// Mark this as a manual disconnect to prevent automatic reconnection
|
|
264
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_manualDisconnect, true, "f");
|
|
265
245
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTING);
|
|
266
246
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
267
247
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
|
|
@@ -270,6 +250,7 @@ export class BackendWebSocketService {
|
|
|
270
250
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
|
|
271
251
|
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
|
|
272
252
|
}
|
|
253
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
273
254
|
log('WebSocket manually disconnected');
|
|
274
255
|
}
|
|
275
256
|
/**
|
|
@@ -371,7 +352,7 @@ export class BackendWebSocketService {
|
|
|
371
352
|
state: __classPrivateFieldGet(this, _BackendWebSocketService_state, "f"),
|
|
372
353
|
url: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url,
|
|
373
354
|
reconnectAttempts: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
374
|
-
connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f"),
|
|
355
|
+
connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f") ?? undefined,
|
|
375
356
|
};
|
|
376
357
|
}
|
|
377
358
|
/**
|
|
@@ -387,7 +368,6 @@ export class BackendWebSocketService {
|
|
|
387
368
|
matchingSubscriptions.push({
|
|
388
369
|
subscriptionId,
|
|
389
370
|
channels: subscription.channels,
|
|
390
|
-
channelType: subscription.channelType,
|
|
391
371
|
unsubscribe: subscription.unsubscribe,
|
|
392
372
|
});
|
|
393
373
|
}
|
|
@@ -423,7 +403,6 @@ export class BackendWebSocketService {
|
|
|
423
403
|
matchingSubscriptions.push({
|
|
424
404
|
subscriptionId,
|
|
425
405
|
channels: subscription.channels,
|
|
426
|
-
channelType: subscription.channelType,
|
|
427
406
|
unsubscribe: subscription.unsubscribe,
|
|
428
407
|
});
|
|
429
408
|
}
|
|
@@ -517,8 +496,6 @@ export class BackendWebSocketService {
|
|
|
517
496
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f") && __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").readyState === WebSocket.OPEN) {
|
|
518
497
|
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Service cleanup');
|
|
519
498
|
}
|
|
520
|
-
// Set state to disconnected immediately
|
|
521
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
522
499
|
}
|
|
523
500
|
/**
|
|
524
501
|
* Create and manage a subscription with server-side registration (recommended for most use cases)
|
|
@@ -547,7 +524,6 @@ export class BackendWebSocketService {
|
|
|
547
524
|
* @param options.channels - Array of channel names to subscribe to
|
|
548
525
|
* @param options.callback - Callback function for handling notifications
|
|
549
526
|
* @param options.requestId - Optional request ID for testing (will generate UUID if not provided)
|
|
550
|
-
* @param options.channelType - Channel type identifier
|
|
551
527
|
* @returns Subscription object with unsubscribe method
|
|
552
528
|
*
|
|
553
529
|
* @example
|
|
@@ -567,7 +543,7 @@ export class BackendWebSocketService {
|
|
|
567
543
|
* @see addChannelCallback for local callbacks without server-side subscription
|
|
568
544
|
*/
|
|
569
545
|
async subscribe(options) {
|
|
570
|
-
const { channels,
|
|
546
|
+
const { channels, callback, requestId } = options;
|
|
571
547
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") !== WebSocketState.CONNECTED) {
|
|
572
548
|
throw new Error(`Cannot create subscription(s) ${channels.join(', ')}: WebSocket is ${__classPrivateFieldGet(this, _BackendWebSocketService_state, "f")}`);
|
|
573
549
|
}
|
|
@@ -580,6 +556,10 @@ export class BackendWebSocketService {
|
|
|
580
556
|
throw new Error('Invalid subscription response: missing subscription ID');
|
|
581
557
|
}
|
|
582
558
|
const { subscriptionId } = subscriptionResponse;
|
|
559
|
+
// Check for failures
|
|
560
|
+
if (subscriptionResponse.failed && subscriptionResponse.failed.length > 0) {
|
|
561
|
+
throw new Error(`Subscription failed for channels: ${subscriptionResponse.failed.join(', ')}`);
|
|
562
|
+
}
|
|
583
563
|
// Create unsubscribe function
|
|
584
564
|
const unsubscribe = async (unsubRequestId) => {
|
|
585
565
|
// Send unsubscribe request first
|
|
@@ -597,42 +577,45 @@ export class BackendWebSocketService {
|
|
|
597
577
|
const subscription = {
|
|
598
578
|
subscriptionId,
|
|
599
579
|
channels: [...channels],
|
|
600
|
-
channelType,
|
|
601
580
|
unsubscribe,
|
|
602
581
|
};
|
|
603
582
|
// Store subscription with subscription ID as key
|
|
604
583
|
__classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").set(subscriptionId, {
|
|
605
584
|
subscriptionId,
|
|
606
585
|
channels: [...channels],
|
|
607
|
-
channelType,
|
|
608
586
|
callback,
|
|
609
587
|
unsubscribe,
|
|
610
588
|
});
|
|
611
589
|
return subscription;
|
|
612
590
|
}
|
|
613
591
|
}
|
|
614
|
-
_BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_options = new WeakMap(), _BackendWebSocketService_isEnabled = new WeakMap(),
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
592
|
+
_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() {
|
|
593
|
+
try {
|
|
594
|
+
// Subscribe to authentication state changes - this includes wallet unlock state
|
|
595
|
+
// AuthenticationController can only be signed in if wallet is unlocked
|
|
596
|
+
// Using selector to only listen for isSignedIn property changes for better performance
|
|
597
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (isSignedIn) => {
|
|
598
|
+
if (isSignedIn) {
|
|
599
|
+
// User signed in (wallet unlocked + authenticated) - try to connect
|
|
600
|
+
// Clear any pending reconnection timer since we're attempting connection
|
|
601
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
602
|
+
this.connect().catch((error) => {
|
|
603
|
+
log('Failed to connect after sign-in', { error });
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
// User signed out (wallet locked OR signed out) - disconnect and stop reconnection attempts
|
|
608
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
609
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
610
|
+
this.disconnect().catch((error) => {
|
|
611
|
+
log('Failed to disconnect after sign-out', { error });
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
}, (state) => state.isSignedIn);
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
throw new Error(`Authentication setup failed: ${getErrorMessage(error)}`);
|
|
618
|
+
}
|
|
636
619
|
}, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
|
|
637
620
|
const baseUrl = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url;
|
|
638
621
|
// Add token as query parameter to the WebSocket URL
|
|
@@ -648,7 +631,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
|
|
|
648
631
|
*/
|
|
649
632
|
async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
650
633
|
const wsUrl = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_buildAuthenticatedUrl).call(this, bearerToken);
|
|
651
|
-
const connectionStartTime = Date.now();
|
|
652
634
|
return new Promise((resolve, reject) => {
|
|
653
635
|
const ws = new WebSocket(wsUrl);
|
|
654
636
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, setTimeout(() => {
|
|
@@ -656,48 +638,40 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
656
638
|
timeout: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout,
|
|
657
639
|
});
|
|
658
640
|
ws.close();
|
|
659
|
-
reject(new Error(`
|
|
641
|
+
reject(new Error(`Connection timeout after ${__classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout}ms`));
|
|
660
642
|
}, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout), "f");
|
|
661
643
|
ws.onopen = () => {
|
|
662
644
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
663
645
|
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
664
646
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
665
647
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
reconnectAttempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
673
|
-
latency_ms: connectionLatency,
|
|
674
|
-
},
|
|
675
|
-
tags: {
|
|
676
|
-
service: SERVICE_NAME,
|
|
677
|
-
},
|
|
678
|
-
}, () => {
|
|
679
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
|
|
680
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
|
|
681
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
|
|
682
|
-
// Reset reconnect attempts on successful connection
|
|
683
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
684
|
-
resolve();
|
|
685
|
-
});
|
|
648
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
|
|
649
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
|
|
650
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
|
|
651
|
+
// Reset reconnect attempts on successful connection
|
|
652
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
653
|
+
resolve();
|
|
686
654
|
};
|
|
687
655
|
ws.onerror = (event) => {
|
|
688
656
|
log('WebSocket onerror event triggered', { event });
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
657
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
|
|
658
|
+
// Handle connection-phase errors
|
|
659
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
660
|
+
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
661
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
662
|
+
}
|
|
663
|
+
const error = new Error(`WebSocket connection error to ${wsUrl}`);
|
|
664
|
+
reject(error);
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
// Handle runtime errors
|
|
668
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_handleError).call(this, new Error(`WebSocket error: ${event.type}`));
|
|
693
669
|
}
|
|
694
|
-
const error = new Error(`WebSocket connection error to ${wsUrl}`);
|
|
695
|
-
reject(error);
|
|
696
670
|
};
|
|
697
671
|
ws.onclose = (event) => {
|
|
698
672
|
log('WebSocket onclose event triggered', {
|
|
699
673
|
code: event.code,
|
|
700
|
-
reason: event.reason,
|
|
674
|
+
reason: event.reason || 'none',
|
|
701
675
|
wasClean: event.wasClean,
|
|
702
676
|
});
|
|
703
677
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
|
|
@@ -769,98 +743,42 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
769
743
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").size === 0) {
|
|
770
744
|
return;
|
|
771
745
|
}
|
|
772
|
-
//
|
|
773
|
-
|
|
774
|
-
const latency = receivedAt - message.timestamp;
|
|
775
|
-
// Trace channel message processing with latency data
|
|
776
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
777
|
-
name: `${SERVICE_NAME} Channel Message`,
|
|
778
|
-
data: {
|
|
779
|
-
channel: message.channel,
|
|
780
|
-
latency_ms: latency,
|
|
781
|
-
event: message.event,
|
|
782
|
-
},
|
|
783
|
-
tags: {
|
|
784
|
-
service: SERVICE_NAME,
|
|
785
|
-
channel_type: message.channel,
|
|
786
|
-
},
|
|
787
|
-
}, () => {
|
|
788
|
-
// Direct lookup for exact channel match
|
|
789
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").get(message.channel)?.callback(message);
|
|
790
|
-
});
|
|
746
|
+
// Direct lookup for exact channel match
|
|
747
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").get(message.channel)?.callback(message);
|
|
791
748
|
}, _BackendWebSocketService_handleSubscriptionNotification = function _BackendWebSocketService_handleSubscriptionNotification(message) {
|
|
792
|
-
const { subscriptionId
|
|
749
|
+
const { subscriptionId } = message;
|
|
793
750
|
// Only handle if subscriptionId is defined and not null (allows "0" as valid ID)
|
|
794
751
|
if (subscriptionId !== null && subscriptionId !== undefined) {
|
|
795
|
-
|
|
796
|
-
if (!subscription) {
|
|
797
|
-
return false;
|
|
798
|
-
}
|
|
799
|
-
// Calculate notification latency: time from server sent to client received
|
|
800
|
-
const receivedAt = Date.now();
|
|
801
|
-
const latency = receivedAt - timestamp;
|
|
802
|
-
// Trace notification processing with latency data
|
|
803
|
-
// Use stored channelType instead of parsing each time
|
|
804
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
805
|
-
name: `${SERVICE_NAME} Notification`,
|
|
806
|
-
data: {
|
|
807
|
-
channel,
|
|
808
|
-
latency_ms: latency,
|
|
809
|
-
subscriptionId,
|
|
810
|
-
},
|
|
811
|
-
tags: {
|
|
812
|
-
service: SERVICE_NAME,
|
|
813
|
-
notification_type: subscription.channelType,
|
|
814
|
-
},
|
|
815
|
-
}, () => {
|
|
816
|
-
subscription.callback?.(message);
|
|
817
|
-
});
|
|
752
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").get(subscriptionId)?.callback?.(message);
|
|
818
753
|
return true;
|
|
819
754
|
}
|
|
820
755
|
return false;
|
|
821
756
|
}, _BackendWebSocketService_parseMessage = function _BackendWebSocketService_parseMessage(data) {
|
|
822
757
|
return JSON.parse(data);
|
|
823
758
|
}, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
|
|
824
|
-
// Calculate connection duration before we clear state
|
|
825
|
-
const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
|
|
826
759
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
827
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt,
|
|
760
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, null, "f");
|
|
828
761
|
// Clear any pending connection promise
|
|
829
762
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
|
|
830
763
|
// Clear subscriptions and pending requests on any disconnect
|
|
831
764
|
// This ensures clean state for reconnection
|
|
832
765
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket connection closed'));
|
|
833
766
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearSubscriptions).call(this);
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
if (__classPrivateFieldGet(this, _BackendWebSocketService_manualDisconnect, "f")) {
|
|
838
|
-
// Manual disconnect - don't reconnect
|
|
839
|
-
log('WebSocket closed due to manual disconnect, not reconnecting');
|
|
767
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
|
|
768
|
+
// Manual disconnect
|
|
769
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
840
770
|
return;
|
|
841
771
|
}
|
|
842
|
-
//
|
|
843
|
-
__classPrivateFieldGet(this,
|
|
844
|
-
|
|
845
|
-
|
|
772
|
+
// For unexpected disconnects, update the state to reflect that we're disconnected
|
|
773
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
774
|
+
// Check if we should attempt reconnection based on close code
|
|
775
|
+
const shouldReconnect = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_shouldReconnectOnClose).call(this, event.code);
|
|
776
|
+
if (shouldReconnect) {
|
|
777
|
+
log('Connection lost unexpectedly, will attempt reconnection', {
|
|
846
778
|
code: event.code,
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
tags: {
|
|
851
|
-
service: SERVICE_NAME,
|
|
852
|
-
disconnect_type: 'unexpected',
|
|
853
|
-
},
|
|
854
|
-
}, () => {
|
|
855
|
-
// Empty trace callback - just measuring the event
|
|
856
|
-
});
|
|
857
|
-
// For any unexpected disconnects, attempt reconnection
|
|
858
|
-
// The manualDisconnect flag is the only gate - if it's false, we reconnect
|
|
859
|
-
log('Connection lost unexpectedly, will attempt reconnection', {
|
|
860
|
-
code: event.code,
|
|
861
|
-
reason: event.reason,
|
|
862
|
-
});
|
|
863
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
779
|
+
});
|
|
780
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
781
|
+
}
|
|
864
782
|
}, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
|
|
865
783
|
// Placeholder for future error handling logic
|
|
866
784
|
}, _BackendWebSocketService_scheduleReconnect = function _BackendWebSocketService_scheduleReconnect() {
|
|
@@ -911,5 +829,8 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
911
829
|
// Messenger handles listener errors internally, no need for try-catch
|
|
912
830
|
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").publish('BackendWebSocketService:connectionStateChanged', this.getConnectionInfo());
|
|
913
831
|
}
|
|
832
|
+
}, _BackendWebSocketService_shouldReconnectOnClose = function _BackendWebSocketService_shouldReconnectOnClose(code) {
|
|
833
|
+
// Don't reconnect only on normal closure (manual disconnect)
|
|
834
|
+
return code !== 1000;
|
|
914
835
|
};
|
|
915
836
|
//# sourceMappingURL=BackendWebSocketService.mjs.map
|