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