@metamask-previews/core-backend 1.0.1-preview-8da5960 → 2.0.0-preview-a2a9f78c
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 +37 -1
- package/README.md +62 -22
- package/dist/AccountActivityService.cjs +38 -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 +38 -88
- package/dist/AccountActivityService.mjs.map +1 -1
- package/dist/BackendWebSocketService.cjs +176 -103
- package/dist/BackendWebSocketService.cjs.map +1 -1
- package/dist/BackendWebSocketService.d.cts +35 -12
- package/dist/BackendWebSocketService.d.cts.map +1 -1
- package/dist/BackendWebSocketService.d.mts +35 -12
- package/dist/BackendWebSocketService.d.mts.map +1 -1
- package/dist/BackendWebSocketService.mjs +176 -103
- 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,10 @@ 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 ??
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
174
|
+
((_request, fn) => fn?.()), "f");
|
|
162
175
|
__classPrivateFieldSet(this, _BackendWebSocketService_options, {
|
|
163
176
|
url: options.url,
|
|
164
177
|
timeout: options.timeout ?? 10000,
|
|
@@ -166,8 +179,8 @@ class BackendWebSocketService {
|
|
|
166
179
|
maxReconnectDelay: options.maxReconnectDelay ?? 5000,
|
|
167
180
|
requestTimeout: options.requestTimeout ?? 30000,
|
|
168
181
|
}, "f");
|
|
169
|
-
//
|
|
170
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m",
|
|
182
|
+
// Subscribe to authentication and keyring controller events
|
|
183
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_subscribeEvents).call(this);
|
|
171
184
|
// Register action handlers using the method actions pattern
|
|
172
185
|
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
173
186
|
}
|
|
@@ -177,18 +190,23 @@ class BackendWebSocketService {
|
|
|
177
190
|
/**
|
|
178
191
|
* Establishes WebSocket connection with smart reconnection behavior
|
|
179
192
|
*
|
|
180
|
-
*
|
|
181
|
-
* 1.
|
|
182
|
-
* 2.
|
|
183
|
-
* 3. User signed in (
|
|
193
|
+
* Connection Requirements (all must be true):
|
|
194
|
+
* 1. Feature enabled (isEnabled() = true)
|
|
195
|
+
* 2. Wallet unlocked (checked by getBearerToken)
|
|
196
|
+
* 3. User signed in (checked by getBearerToken)
|
|
197
|
+
*
|
|
198
|
+
* Platform code should call this when app opens/foregrounds.
|
|
199
|
+
* Automatically called on KeyringController:unlock event.
|
|
184
200
|
*
|
|
185
201
|
* @returns Promise that resolves when connection is established
|
|
186
202
|
*/
|
|
187
203
|
async connect() {
|
|
188
|
-
//
|
|
189
|
-
|
|
204
|
+
// Reset manual disconnect flag when explicitly connecting
|
|
205
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_manualDisconnect, false, "f");
|
|
206
|
+
// Priority 1: Check if feature is enabled via callback (feature flag check)
|
|
207
|
+
// If feature is disabled, stop all connection attempts
|
|
190
208
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
|
|
191
|
-
// Clear any pending reconnection attempts since
|
|
209
|
+
// Clear any pending reconnection attempts since feature is disabled
|
|
192
210
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
193
211
|
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
194
212
|
return;
|
|
@@ -202,10 +220,9 @@ class BackendWebSocketService {
|
|
|
202
220
|
await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
|
|
203
221
|
return;
|
|
204
222
|
}
|
|
205
|
-
// Priority 2: Check authentication requirements (
|
|
223
|
+
// Priority 2: Check authentication requirements (signed in)
|
|
206
224
|
let bearerToken;
|
|
207
225
|
try {
|
|
208
|
-
// AuthenticationController.getBearerToken() handles wallet unlock checks internally
|
|
209
226
|
const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
|
|
210
227
|
if (!token) {
|
|
211
228
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
|
|
@@ -215,7 +232,7 @@ class BackendWebSocketService {
|
|
|
215
232
|
}
|
|
216
233
|
catch (error) {
|
|
217
234
|
log('Failed to check authentication requirements', { error });
|
|
218
|
-
//
|
|
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
|
/**
|
|
@@ -355,8 +374,12 @@ class BackendWebSocketService {
|
|
|
355
374
|
return {
|
|
356
375
|
state: __classPrivateFieldGet(this, _BackendWebSocketService_state, "f"),
|
|
357
376
|
url: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url,
|
|
377
|
+
timeout: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout,
|
|
378
|
+
reconnectDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay,
|
|
379
|
+
maxReconnectDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay,
|
|
380
|
+
requestTimeout: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").requestTimeout,
|
|
358
381
|
reconnectAttempts: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
359
|
-
connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f")
|
|
382
|
+
connectedAt: __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f"),
|
|
360
383
|
};
|
|
361
384
|
}
|
|
362
385
|
/**
|
|
@@ -372,6 +395,7 @@ class BackendWebSocketService {
|
|
|
372
395
|
matchingSubscriptions.push({
|
|
373
396
|
subscriptionId,
|
|
374
397
|
channels: subscription.channels,
|
|
398
|
+
channelType: subscription.channelType,
|
|
375
399
|
unsubscribe: subscription.unsubscribe,
|
|
376
400
|
});
|
|
377
401
|
}
|
|
@@ -407,6 +431,7 @@ class BackendWebSocketService {
|
|
|
407
431
|
matchingSubscriptions.push({
|
|
408
432
|
subscriptionId,
|
|
409
433
|
channels: subscription.channels,
|
|
434
|
+
channelType: subscription.channelType,
|
|
410
435
|
unsubscribe: subscription.unsubscribe,
|
|
411
436
|
});
|
|
412
437
|
}
|
|
@@ -500,6 +525,8 @@ class BackendWebSocketService {
|
|
|
500
525
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f") && __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").readyState === WebSocket.OPEN) {
|
|
501
526
|
__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Service cleanup');
|
|
502
527
|
}
|
|
528
|
+
// Set state to disconnected immediately
|
|
529
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
503
530
|
}
|
|
504
531
|
/**
|
|
505
532
|
* Create and manage a subscription with server-side registration (recommended for most use cases)
|
|
@@ -528,6 +555,7 @@ class BackendWebSocketService {
|
|
|
528
555
|
* @param options.channels - Array of channel names to subscribe to
|
|
529
556
|
* @param options.callback - Callback function for handling notifications
|
|
530
557
|
* @param options.requestId - Optional request ID for testing (will generate UUID if not provided)
|
|
558
|
+
* @param options.channelType - Channel type identifier
|
|
531
559
|
* @returns Subscription object with unsubscribe method
|
|
532
560
|
*
|
|
533
561
|
* @example
|
|
@@ -547,7 +575,7 @@ class BackendWebSocketService {
|
|
|
547
575
|
* @see addChannelCallback for local callbacks without server-side subscription
|
|
548
576
|
*/
|
|
549
577
|
async subscribe(options) {
|
|
550
|
-
const { channels, callback, requestId } = options;
|
|
578
|
+
const { channels, channelType, callback, requestId } = options;
|
|
551
579
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") !== WebSocketState.CONNECTED) {
|
|
552
580
|
throw new Error(`Cannot create subscription(s) ${channels.join(', ')}: WebSocket is ${__classPrivateFieldGet(this, _BackendWebSocketService_state, "f")}`);
|
|
553
581
|
}
|
|
@@ -560,10 +588,6 @@ class BackendWebSocketService {
|
|
|
560
588
|
throw new Error('Invalid subscription response: missing subscription ID');
|
|
561
589
|
}
|
|
562
590
|
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
591
|
// Create unsubscribe function
|
|
568
592
|
const unsubscribe = async (unsubRequestId) => {
|
|
569
593
|
// Send unsubscribe request first
|
|
@@ -581,12 +605,14 @@ class BackendWebSocketService {
|
|
|
581
605
|
const subscription = {
|
|
582
606
|
subscriptionId,
|
|
583
607
|
channels: [...channels],
|
|
608
|
+
channelType,
|
|
584
609
|
unsubscribe,
|
|
585
610
|
};
|
|
586
611
|
// Store subscription with subscription ID as key
|
|
587
612
|
__classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").set(subscriptionId, {
|
|
588
613
|
subscriptionId,
|
|
589
614
|
channels: [...channels],
|
|
615
|
+
channelType,
|
|
590
616
|
callback,
|
|
591
617
|
unsubscribe,
|
|
592
618
|
});
|
|
@@ -594,33 +620,28 @@ class BackendWebSocketService {
|
|
|
594
620
|
}
|
|
595
621
|
}
|
|
596
622
|
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
|
-
}
|
|
623
|
+
_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() {
|
|
624
|
+
// Subscribe to authentication state changes (sign in/out)
|
|
625
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (state) => {
|
|
626
|
+
if (state.isSignedIn) {
|
|
627
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
628
|
+
this.connect();
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
632
|
+
this.disconnect();
|
|
633
|
+
}
|
|
634
|
+
}, (state) => ({ isSignedIn: state.isSignedIn }));
|
|
635
|
+
// Subscribe to wallet unlock event
|
|
636
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:unlock', () => {
|
|
637
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
638
|
+
this.connect();
|
|
639
|
+
});
|
|
640
|
+
// Subscribe to wallet lock event
|
|
641
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:lock', () => {
|
|
642
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
643
|
+
this.disconnect();
|
|
644
|
+
});
|
|
624
645
|
}, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
|
|
625
646
|
const baseUrl = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").url;
|
|
626
647
|
// Add token as query parameter to the WebSocket URL
|
|
@@ -636,6 +657,7 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
|
|
|
636
657
|
*/
|
|
637
658
|
async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
638
659
|
const wsUrl = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_buildAuthenticatedUrl).call(this, bearerToken);
|
|
660
|
+
const connectionStartTime = Date.now();
|
|
639
661
|
return new Promise((resolve, reject) => {
|
|
640
662
|
const ws = new WebSocket(wsUrl);
|
|
641
663
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, setTimeout(() => {
|
|
@@ -643,40 +665,47 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
643
665
|
timeout: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout,
|
|
644
666
|
});
|
|
645
667
|
ws.close();
|
|
646
|
-
reject(new Error(`Connection timeout after ${__classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout}ms`));
|
|
668
|
+
reject(new Error(`Failed to connect to WebSocket: Connection timeout after ${__classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout}ms`));
|
|
647
669
|
}, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").timeout), "f");
|
|
648
670
|
ws.onopen = () => {
|
|
649
671
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
650
672
|
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
651
673
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
652
674
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
675
|
+
// Calculate connection latency
|
|
676
|
+
const connectionLatency = Date.now() - connectionStartTime;
|
|
677
|
+
// Trace successful connection with latency
|
|
678
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
679
|
+
name: `${SERVICE_NAME} Connection`,
|
|
680
|
+
data: {
|
|
681
|
+
reconnectAttempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
682
|
+
latency_ms: connectionLatency,
|
|
683
|
+
},
|
|
684
|
+
tags: {
|
|
685
|
+
service: SERVICE_NAME,
|
|
686
|
+
},
|
|
687
|
+
}, () => {
|
|
688
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
|
|
689
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
|
|
690
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
|
|
691
|
+
// Reset reconnect attempts on successful connection
|
|
692
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
693
|
+
resolve();
|
|
694
|
+
});
|
|
659
695
|
};
|
|
660
696
|
ws.onerror = (event) => {
|
|
661
697
|
log('WebSocket onerror event triggered', { event });
|
|
662
|
-
if (__classPrivateFieldGet(this,
|
|
663
|
-
|
|
664
|
-
|
|
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
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
|
|
699
|
+
clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
|
|
700
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
|
|
674
701
|
}
|
|
702
|
+
const error = new Error(`WebSocket connection error to ${wsUrl}`);
|
|
703
|
+
reject(error);
|
|
675
704
|
};
|
|
676
705
|
ws.onclose = (event) => {
|
|
677
706
|
log('WebSocket onclose event triggered', {
|
|
678
707
|
code: event.code,
|
|
679
|
-
reason: event.reason
|
|
708
|
+
reason: event.reason,
|
|
680
709
|
wasClean: event.wasClean,
|
|
681
710
|
});
|
|
682
711
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.CONNECTING) {
|
|
@@ -710,7 +739,8 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
710
739
|
}
|
|
711
740
|
// Handle subscription notifications with valid subscriptionId
|
|
712
741
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_isSubscriptionNotification).call(this, message)) {
|
|
713
|
-
const
|
|
742
|
+
const notificationMsg = message;
|
|
743
|
+
const handled = __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_handleSubscriptionNotification).call(this, notificationMsg);
|
|
714
744
|
// If subscription notification wasn't handled (falsy subscriptionId), fall through to channel handling
|
|
715
745
|
if (handled) {
|
|
716
746
|
return;
|
|
@@ -718,7 +748,8 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
718
748
|
}
|
|
719
749
|
// Trigger channel callbacks for any message with a channel property
|
|
720
750
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_isChannelMessage).call(this, message)) {
|
|
721
|
-
|
|
751
|
+
const channelMsg = message;
|
|
752
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_handleChannelMessage).call(this, channelMsg);
|
|
722
753
|
}
|
|
723
754
|
}, _BackendWebSocketService_isServerResponse = function _BackendWebSocketService_isServerResponse(message) {
|
|
724
755
|
return ('data' in message &&
|
|
@@ -748,58 +779,104 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
748
779
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").size === 0) {
|
|
749
780
|
return;
|
|
750
781
|
}
|
|
751
|
-
//
|
|
752
|
-
|
|
782
|
+
// Calculate notification latency: time from server sent to client received
|
|
783
|
+
const receivedAt = Date.now();
|
|
784
|
+
const latency = receivedAt - message.timestamp;
|
|
785
|
+
// Trace channel message processing with latency data
|
|
786
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
787
|
+
name: `${SERVICE_NAME} Channel Message`,
|
|
788
|
+
data: {
|
|
789
|
+
channel: message.channel,
|
|
790
|
+
latency_ms: latency,
|
|
791
|
+
event: message.event,
|
|
792
|
+
},
|
|
793
|
+
tags: {
|
|
794
|
+
service: SERVICE_NAME,
|
|
795
|
+
channel_type: message.channel,
|
|
796
|
+
},
|
|
797
|
+
}, () => {
|
|
798
|
+
// Direct lookup for exact channel match
|
|
799
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_channelCallbacks, "f").get(message.channel)?.callback(message);
|
|
800
|
+
});
|
|
753
801
|
}, _BackendWebSocketService_handleSubscriptionNotification = function _BackendWebSocketService_handleSubscriptionNotification(message) {
|
|
754
|
-
const { subscriptionId } = message;
|
|
802
|
+
const { subscriptionId, timestamp, channel } = message;
|
|
755
803
|
// Only handle if subscriptionId is defined and not null (allows "0" as valid ID)
|
|
756
804
|
if (subscriptionId !== null && subscriptionId !== undefined) {
|
|
757
|
-
__classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").get(subscriptionId)
|
|
805
|
+
const subscription = __classPrivateFieldGet(this, _BackendWebSocketService_subscriptions, "f").get(subscriptionId);
|
|
806
|
+
if (!subscription) {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
// Calculate notification latency: time from server sent to client received
|
|
810
|
+
const receivedAt = Date.now();
|
|
811
|
+
const latency = receivedAt - timestamp;
|
|
812
|
+
// Trace notification processing with latency data
|
|
813
|
+
// Use stored channelType instead of parsing each time
|
|
814
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
815
|
+
name: `${SERVICE_NAME} Notification`,
|
|
816
|
+
data: {
|
|
817
|
+
channel,
|
|
818
|
+
latency_ms: latency,
|
|
819
|
+
subscriptionId,
|
|
820
|
+
},
|
|
821
|
+
tags: {
|
|
822
|
+
service: SERVICE_NAME,
|
|
823
|
+
notification_type: subscription.channelType,
|
|
824
|
+
},
|
|
825
|
+
}, () => {
|
|
826
|
+
subscription.callback?.(message);
|
|
827
|
+
});
|
|
758
828
|
return true;
|
|
759
829
|
}
|
|
760
830
|
return false;
|
|
761
831
|
}, _BackendWebSocketService_parseMessage = function _BackendWebSocketService_parseMessage(data) {
|
|
762
832
|
return JSON.parse(data);
|
|
763
833
|
}, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
|
|
834
|
+
// Calculate connection duration before we clear state
|
|
835
|
+
const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
|
|
764
836
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
|
|
765
|
-
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt,
|
|
837
|
+
__classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
|
|
766
838
|
// Clear any pending connection promise
|
|
767
839
|
__classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
|
|
768
840
|
// Clear subscriptions and pending requests on any disconnect
|
|
769
841
|
// This ensures clean state for reconnection
|
|
770
842
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket connection closed'));
|
|
771
843
|
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearSubscriptions).call(this);
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
844
|
+
// Update state to disconnected
|
|
845
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.DISCONNECTED);
|
|
846
|
+
// Check if this was a manual disconnect
|
|
847
|
+
if (__classPrivateFieldGet(this, _BackendWebSocketService_manualDisconnect, "f")) {
|
|
848
|
+
// Manual disconnect - don't reconnect
|
|
775
849
|
return;
|
|
776
850
|
}
|
|
777
|
-
//
|
|
778
|
-
__classPrivateFieldGet(this,
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
if (shouldReconnect) {
|
|
782
|
-
log('Connection lost unexpectedly, will attempt reconnection', {
|
|
851
|
+
// Trace unexpected disconnect with details
|
|
852
|
+
__classPrivateFieldGet(this, _BackendWebSocketService_trace, "f").call(this, {
|
|
853
|
+
name: `${SERVICE_NAME} Disconnect`,
|
|
854
|
+
data: {
|
|
783
855
|
code: event.code,
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
856
|
+
reason: event.reason || getCloseReason(event.code),
|
|
857
|
+
connectionDuration_ms: connectionDuration,
|
|
858
|
+
},
|
|
859
|
+
tags: {
|
|
860
|
+
service: SERVICE_NAME,
|
|
861
|
+
disconnect_type: 'unexpected',
|
|
862
|
+
},
|
|
863
|
+
}, () => {
|
|
864
|
+
// Empty trace callback - just measuring the event
|
|
865
|
+
});
|
|
866
|
+
// For any unexpected disconnects, attempt reconnection
|
|
867
|
+
// The manualDisconnect flag is the only gate - if it's false, we reconnect
|
|
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() {
|
|
790
872
|
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") + 1, "f");
|
|
791
873
|
const rawDelay = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay * Math.pow(1.5, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") - 1);
|
|
792
874
|
const delay = Math.min(rawDelay, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay);
|
|
793
|
-
log('Scheduling reconnection attempt', {
|
|
794
|
-
attempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
|
|
795
|
-
delayMs: delay,
|
|
796
|
-
});
|
|
797
875
|
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, setTimeout(() => {
|
|
798
876
|
// Clear timer reference first
|
|
799
877
|
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, null, "f");
|
|
800
878
|
// Check if connection is still enabled before reconnecting
|
|
801
879
|
if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
|
|
802
|
-
log('Reconnection disabled by isEnabled - stopping all attempts');
|
|
803
880
|
__classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
|
|
804
881
|
return;
|
|
805
882
|
}
|
|
@@ -829,13 +906,9 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
|
|
|
829
906
|
const oldState = __classPrivateFieldGet(this, _BackendWebSocketService_state, "f");
|
|
830
907
|
__classPrivateFieldSet(this, _BackendWebSocketService_state, newState, "f");
|
|
831
908
|
if (oldState !== newState) {
|
|
832
|
-
log('WebSocket state changed', { oldState, newState });
|
|
833
909
|
// Publish connection state change event
|
|
834
910
|
// Messenger handles listener errors internally, no need for try-catch
|
|
835
911
|
__classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").publish('BackendWebSocketService:connectionStateChanged', this.getConnectionInfo());
|
|
836
912
|
}
|
|
837
|
-
}, _BackendWebSocketService_shouldReconnectOnClose = function _BackendWebSocketService_shouldReconnectOnClose(code) {
|
|
838
|
-
// Don't reconnect only on normal closure (manual disconnect)
|
|
839
|
-
return code !== 1000;
|
|
840
913
|
};
|
|
841
914
|
//# sourceMappingURL=BackendWebSocketService.cjs.map
|