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