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