@metamask-previews/core-backend 1.0.1-preview-5f3688c1 → 1.0.1-preview-0189b42
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 +30 -0
- package/README.md +62 -22
- package/dist/AccountActivityService.cjs +37 -88
- package/dist/AccountActivityService.cjs.map +1 -1
- package/dist/AccountActivityService.d.cts +3 -7
- package/dist/AccountActivityService.d.cts.map +1 -1
- package/dist/AccountActivityService.d.mts +3 -7
- package/dist/AccountActivityService.d.mts.map +1 -1
- package/dist/AccountActivityService.mjs +37 -88
- package/dist/AccountActivityService.mjs.map +1 -1
- package/dist/BackendWebSocketService.cjs +175 -96
- package/dist/BackendWebSocketService.cjs.map +1 -1
- package/dist/BackendWebSocketService.d.cts +31 -12
- package/dist/BackendWebSocketService.d.cts.map +1 -1
- package/dist/BackendWebSocketService.d.mts +31 -12
- package/dist/BackendWebSocketService.d.mts.map +1 -1
- package/dist/BackendWebSocketService.mjs +175 -96
- package/dist/BackendWebSocketService.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +4 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +4 -2
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +4 -3
|
@@ -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_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt, _BackendWebSocketService_subscriptions, _BackendWebSocketService_channelCallbacks,
|
|
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;
|
|
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,18 +109,24 @@ 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
|
+
*
|
|
112
124
|
* Real-Time Performance Optimizations:
|
|
113
125
|
* - Fast path message routing (zero allocations)
|
|
114
126
|
* - Production mode removes try-catch overhead
|
|
115
127
|
* - Optimized JSON parsing with fail-fast
|
|
116
128
|
* - Direct callback routing bypasses event emitters
|
|
117
129
|
* - 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
|
|
124
130
|
*/
|
|
125
131
|
class BackendWebSocketService {
|
|
126
132
|
// =============================================================================
|
|
@@ -140,6 +146,7 @@ class BackendWebSocketService {
|
|
|
140
146
|
_BackendWebSocketService_messenger.set(this, void 0);
|
|
141
147
|
_BackendWebSocketService_options.set(this, void 0);
|
|
142
148
|
_BackendWebSocketService_isEnabled.set(this, void 0);
|
|
149
|
+
_BackendWebSocketService_trace.set(this, void 0);
|
|
143
150
|
_BackendWebSocketService_ws.set(this, void 0);
|
|
144
151
|
_BackendWebSocketService_state.set(this, WebSocketState.DISCONNECTED);
|
|
145
152
|
_BackendWebSocketService_reconnectAttempts.set(this, 0);
|
|
@@ -148,7 +155,9 @@ class BackendWebSocketService {
|
|
|
148
155
|
// Track the current connection promise to handle concurrent connection attempts
|
|
149
156
|
_BackendWebSocketService_connectionPromise.set(this, null);
|
|
150
157
|
_BackendWebSocketService_pendingRequests.set(this, new Map());
|
|
151
|
-
_BackendWebSocketService_connectedAt.set(this,
|
|
158
|
+
_BackendWebSocketService_connectedAt.set(this, 0);
|
|
159
|
+
// Track manual disconnects to prevent automatic reconnection
|
|
160
|
+
_BackendWebSocketService_manualDisconnect.set(this, false);
|
|
152
161
|
// Simplified subscription storage (single flat map)
|
|
153
162
|
// Key: subscription ID string (e.g., 'sub_abc123def456')
|
|
154
163
|
// Value: WebSocketSubscription object with channels, callback and metadata
|
|
@@ -159,6 +168,8 @@ class BackendWebSocketService {
|
|
|
159
168
|
_BackendWebSocketService_channelCallbacks.set(this, new Map());
|
|
160
169
|
__classPrivateFieldSet(this, _BackendWebSocketService_messenger, options.messenger, "f");
|
|
161
170
|
__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");
|
|
162
173
|
__classPrivateFieldSet(this, _BackendWebSocketService_options, {
|
|
163
174
|
url: options.url,
|
|
164
175
|
timeout: options.timeout ?? 10000,
|
|
@@ -166,8 +177,8 @@ class BackendWebSocketService {
|
|
|
166
177
|
maxReconnectDelay: options.maxReconnectDelay ?? 5000,
|
|
167
178
|
requestTimeout: options.requestTimeout ?? 30000,
|
|
168
179
|
}, "f");
|
|
169
|
-
//
|
|
170
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m",
|
|
180
|
+
// Subscribe to authentication and keyring controller events
|
|
181
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_subscribeEvents).call(this);
|
|
171
182
|
// Register action handlers using the method actions pattern
|
|
172
183
|
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
173
184
|
}
|
|
@@ -177,18 +188,23 @@ class BackendWebSocketService {
|
|
|
177
188
|
/**
|
|
178
189
|
* Establishes WebSocket connection with smart reconnection behavior
|
|
179
190
|
*
|
|
180
|
-
*
|
|
181
|
-
* 1.
|
|
182
|
-
* 2.
|
|
183
|
-
* 3. User signed in (
|
|
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.
|
|
184
198
|
*
|
|
185
199
|
* @returns Promise that resolves when connection is established
|
|
186
200
|
*/
|
|
187
201
|
async connect() {
|
|
188
|
-
//
|
|
189
|
-
|
|
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
|
|
190
206
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
|
|
191
|
-
// Clear any pending reconnection attempts since
|
|
207
|
+
// Clear any pending reconnection attempts since feature is disabled
|
|
192
208
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
193
209
|
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
194
210
|
return;
|
|
@@ -202,10 +218,9 @@ class BackendWebSocketService {
|
|
|
202
218
|
await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
|
|
203
219
|
return;
|
|
204
220
|
}
|
|
205
|
-
// Priority 2: Check authentication requirements (
|
|
221
|
+
// Priority 2: Check authentication requirements (signed in)
|
|
206
222
|
let bearerToken;
|
|
207
223
|
try {
|
|
208
|
-
// AuthenticationController.getBearerToken() handles wallet unlock checks internally
|
|
209
224
|
const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
|
|
210
225
|
if (!token) {
|
|
211
226
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
@@ -214,8 +229,10 @@ class BackendWebSocketService {
|
|
|
214
229
|
bearerToken = token;
|
|
215
230
|
}
|
|
216
231
|
catch (error) {
|
|
217
|
-
log('Failed to
|
|
218
|
-
|
|
232
|
+
log('Failed to get bearer token (wallet locked or not signed in)', {
|
|
233
|
+
error,
|
|
234
|
+
});
|
|
235
|
+
// Can't connect - schedule retry
|
|
219
236
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
220
237
|
return;
|
|
221
238
|
}
|
|
@@ -229,7 +246,8 @@ class BackendWebSocketService {
|
|
|
229
246
|
const errorMessage = (0, utils_1.getErrorMessage)(error);
|
|
230
247
|
log('Connection attempt failed', { errorMessage, error });
|
|
231
248
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
|
|
232
|
-
|
|
249
|
+
// Rethrow to propagate error to caller
|
|
250
|
+
throw error;
|
|
233
251
|
}
|
|
234
252
|
finally {
|
|
235
253
|
// Clear the connection promise when done (success or failure)
|
|
@@ -246,6 +264,8 @@ class BackendWebSocketService {
|
|
|
246
264
|
__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
|
|
247
265
|
return;
|
|
248
266
|
}
|
|
267
|
+
// Mark this as a manual disconnect to prevent automatic reconnection
|
|
268
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_manualDisconnect, true, "f");
|
|
249
269
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTING);
|
|
250
270
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
251
271
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
|
|
@@ -254,7 +274,6 @@ class BackendWebSocketService {
|
|
|
254
274
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
|
|
255
275
|
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
|
|
256
276
|
}
|
|
257
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
258
277
|
log('WebSocket manually disconnected');
|
|
259
278
|
}
|
|
260
279
|
/**
|
|
@@ -356,7 +375,7 @@ class BackendWebSocketService {
|
|
|
356
375
|
state: __classPrivateFieldGet(this, _BackendWebSocketService_state, "f"),
|
|
357
376
|
url: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url,
|
|
358
377
|
reconnectAttempts: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
359
|
-
connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f")
|
|
378
|
+
connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f"),
|
|
360
379
|
};
|
|
361
380
|
}
|
|
362
381
|
/**
|
|
@@ -372,6 +391,7 @@ class BackendWebSocketService {
|
|
|
372
391
|
matchingSubscriptions.push({
|
|
373
392
|
subscriptionId,
|
|
374
393
|
channels: subscription.channels,
|
|
394
|
+
channelType: subscription.channelType,
|
|
375
395
|
unsubscribe: subscription.unsubscribe,
|
|
376
396
|
});
|
|
377
397
|
}
|
|
@@ -407,6 +427,7 @@ class BackendWebSocketService {
|
|
|
407
427
|
matchingSubscriptions.push({
|
|
408
428
|
subscriptionId,
|
|
409
429
|
channels: subscription.channels,
|
|
430
|
+
channelType: subscription.channelType,
|
|
410
431
|
unsubscribe: subscription.unsubscribe,
|
|
411
432
|
});
|
|
412
433
|
}
|
|
@@ -500,6 +521,8 @@ class BackendWebSocketService {
|
|
|
500
521
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f") && __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").readyState === WebSocket.OPEN) {
|
|
501
522
|
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Service cleanup');
|
|
502
523
|
}
|
|
524
|
+
// Set state to disconnected immediately
|
|
525
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
503
526
|
}
|
|
504
527
|
/**
|
|
505
528
|
* Create and manage a subscription with server-side registration (recommended for most use cases)
|
|
@@ -528,6 +551,7 @@ class BackendWebSocketService {
|
|
|
528
551
|
* @param options.channels - Array of channel names to subscribe to
|
|
529
552
|
* @param options.callback - Callback function for handling notifications
|
|
530
553
|
* @param options.requestId - Optional request ID for testing (will generate UUID if not provided)
|
|
554
|
+
* @param options.channelType - Channel type identifier
|
|
531
555
|
* @returns Subscription object with unsubscribe method
|
|
532
556
|
*
|
|
533
557
|
* @example
|
|
@@ -547,7 +571,7 @@ class BackendWebSocketService {
|
|
|
547
571
|
* @see addChannelCallback for local callbacks without server-side subscription
|
|
548
572
|
*/
|
|
549
573
|
async subscribe(options) {
|
|
550
|
-
const { channels, callback, requestId } = options;
|
|
574
|
+
const { channels, channelType, callback, requestId } = options;
|
|
551
575
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") !== WebSocketState.CONNECTED) {
|
|
552
576
|
throw new Error(`Cannot create subscription(s) ${channels.join(', ')}: WebSocket is ${__classPrivateFieldGet(this, _BackendWebSocketService_state, "f")}`);
|
|
553
577
|
}
|
|
@@ -560,10 +584,6 @@ class BackendWebSocketService {
|
|
|
560
584
|
throw new Error('Invalid subscription response: missing subscription ID');
|
|
561
585
|
}
|
|
562
586
|
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
|
-
}
|
|
567
587
|
// Create unsubscribe function
|
|
568
588
|
const unsubscribe = async (unsubRequestId) => {
|
|
569
589
|
// Send unsubscribe request first
|
|
@@ -581,12 +601,14 @@ class BackendWebSocketService {
|
|
|
581
601
|
const subscription = {
|
|
582
602
|
subscriptionId,
|
|
583
603
|
channels: [...channels],
|
|
604
|
+
channelType,
|
|
584
605
|
unsubscribe,
|
|
585
606
|
};
|
|
586
607
|
// Store subscription with subscription ID as key
|
|
587
608
|
__classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").set(subscriptionId, {
|
|
588
609
|
subscriptionId,
|
|
589
610
|
channels: [...channels],
|
|
611
|
+
channelType,
|
|
590
612
|
callback,
|
|
591
613
|
unsubscribe,
|
|
592
614
|
});
|
|
@@ -594,33 +616,28 @@ class BackendWebSocketService {
|
|
|
594
616
|
}
|
|
595
617
|
}
|
|
596
618
|
exports.BackendWebSocketService = BackendWebSocketService;
|
|
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(),
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}, (state) => state.isSignedIn);
|
|
620
|
-
}
|
|
621
|
-
catch (error) {
|
|
622
|
-
throw new Error(`Authentication setup failed: ${(0, utils_1.getErrorMessage)(error)}`);
|
|
623
|
-
}
|
|
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
|
+
});
|
|
624
641
|
}, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
|
|
625
642
|
const baseUrl = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url;
|
|
626
643
|
// Add token as query parameter to the WebSocket URL
|
|
@@ -636,6 +653,7 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
|
|
|
636
653
|
*/
|
|
637
654
|
async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
638
655
|
const wsUrl = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_buildAuthenticatedUrl).call(this, bearerToken);
|
|
656
|
+
const connectionStartTime = Date.now();
|
|
639
657
|
return new Promise((resolve, reject) => {
|
|
640
658
|
const ws = new WebSocket(wsUrl);
|
|
641
659
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, setTimeout(() => {
|
|
@@ -643,40 +661,48 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
643
661
|
timeout: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout,
|
|
644
662
|
});
|
|
645
663
|
ws.close();
|
|
646
|
-
reject(new Error(`Connection timeout after ${__classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout}ms`));
|
|
664
|
+
reject(new Error(`Failed to connect to WebSocket: Connection timeout after ${__classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout}ms`));
|
|
647
665
|
}, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout), "f");
|
|
648
666
|
ws.onopen = () => {
|
|
649
667
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
650
668
|
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
651
669
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
652
670
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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
|
+
});
|
|
659
691
|
};
|
|
660
692
|
ws.onerror = (event) => {
|
|
661
693
|
log('WebSocket onerror event triggered', { event });
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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}`));
|
|
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");
|
|
674
698
|
}
|
|
699
|
+
const error = new Error(`WebSocket connection error to ${wsUrl}`);
|
|
700
|
+
reject(error);
|
|
675
701
|
};
|
|
676
702
|
ws.onclose = (event) => {
|
|
677
703
|
log('WebSocket onclose event triggered', {
|
|
678
704
|
code: event.code,
|
|
679
|
-
reason: event.reason
|
|
705
|
+
reason: event.reason,
|
|
680
706
|
wasClean: event.wasClean,
|
|
681
707
|
});
|
|
682
708
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
|
|
@@ -748,42 +774,98 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
748
774
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").size === 0) {
|
|
749
775
|
return;
|
|
750
776
|
}
|
|
751
|
-
//
|
|
752
|
-
|
|
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
|
+
});
|
|
753
796
|
}, _BackendWebSocketService_handleSubscriptionNotification = function _BackendWebSocketService_handleSubscriptionNotification(message) {
|
|
754
|
-
const { subscriptionId } = message;
|
|
797
|
+
const { subscriptionId, timestamp, channel } = message;
|
|
755
798
|
// Only handle if subscriptionId is defined and not null (allows "0" as valid ID)
|
|
756
799
|
if (subscriptionId !== null && subscriptionId !== undefined) {
|
|
757
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").get(subscriptionId)
|
|
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
|
+
});
|
|
758
823
|
return true;
|
|
759
824
|
}
|
|
760
825
|
return false;
|
|
761
826
|
}, _BackendWebSocketService_parseMessage = function _BackendWebSocketService_parseMessage(data) {
|
|
762
827
|
return JSON.parse(data);
|
|
763
828
|
}, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
|
|
829
|
+
// Calculate connection duration before we clear state
|
|
830
|
+
const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
|
|
764
831
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
765
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt,
|
|
832
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
|
|
766
833
|
// Clear any pending connection promise
|
|
767
834
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
|
|
768
835
|
// Clear subscriptions and pending requests on any disconnect
|
|
769
836
|
// This ensures clean state for reconnection
|
|
770
837
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket connection closed'));
|
|
771
838
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearSubscriptions).call(this);
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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');
|
|
775
845
|
return;
|
|
776
846
|
}
|
|
777
|
-
//
|
|
778
|
-
__classPrivateFieldGet(this,
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
if (shouldReconnect) {
|
|
782
|
-
log('Connection lost unexpectedly, will attempt reconnection', {
|
|
847
|
+
// Trace unexpected disconnect with details
|
|
848
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
849
|
+
name: `${SERVICE_NAME} Disconnect`,
|
|
850
|
+
data: {
|
|
783
851
|
code: event.code,
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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);
|
|
787
869
|
}, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
|
|
788
870
|
// Placeholder for future error handling logic
|
|
789
871
|
}, _BackendWebSocketService_scheduleReconnect = function _BackendWebSocketService_scheduleReconnect() {
|
|
@@ -834,8 +916,5 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
834
916
|
// Messenger handles listener errors internally, no need for try-catch
|
|
835
917
|
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").publish('BackendWebSocketService:connectionStateChanged', this.getConnectionInfo());
|
|
836
918
|
}
|
|
837
|
-
}, _BackendWebSocketService_shouldReconnectOnClose = function _BackendWebSocketService_shouldReconnectOnClose(code) {
|
|
838
|
-
// Don't reconnect only on normal closure (manual disconnect)
|
|
839
|
-
return code !== 1000;
|
|
840
919
|
};
|
|
841
920
|
//# sourceMappingURL=BackendWebSocketService.cjs.map
|