@metamask-previews/core-backend 2.1.0-preview-8e6c5423 → 3.0.0-preview-5cda4487

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.
@@ -21,6 +21,26 @@ export type BackendWebSocketServiceDisconnectAction = {
21
21
  type: `BackendWebSocketService:disconnect`;
22
22
  handler: BackendWebSocketService['disconnect'];
23
23
  };
24
+ /**
25
+ * Forces a WebSocket reconnection to clean up subscription state
26
+ *
27
+ * This method is useful when subscription state may be out of sync and needs to be reset.
28
+ * It performs a controlled disconnect-then-reconnect sequence:
29
+ * - Disconnects cleanly to trigger subscription cleanup
30
+ * - Schedules reconnection with exponential backoff to prevent rapid loops
31
+ * - All subscriptions will be cleaned up automatically on disconnect
32
+ *
33
+ * Use cases:
34
+ * - Recovering from subscription/unsubscription issues
35
+ * - Cleaning up orphaned subscriptions
36
+ * - Forcing a fresh subscription state
37
+ *
38
+ * @returns Promise that resolves when disconnection is complete (reconnection is scheduled)
39
+ */
40
+ export type BackendWebSocketServiceForceReconnectionAction = {
41
+ type: `BackendWebSocketService:forceReconnection`;
42
+ handler: BackendWebSocketService['forceReconnection'];
43
+ };
24
44
  /**
25
45
  * Sends a message through the WebSocket
26
46
  *
@@ -142,5 +162,5 @@ export type BackendWebSocketServiceSubscribeAction = {
142
162
  /**
143
163
  * Union of all BackendWebSocketService action types.
144
164
  */
145
- export type BackendWebSocketServiceMethodActions = BackendWebSocketServiceConnectAction | BackendWebSocketServiceDisconnectAction | BackendWebSocketServiceSendMessageAction | BackendWebSocketServiceSendRequestAction | BackendWebSocketServiceGetConnectionInfoAction | BackendWebSocketServiceGetSubscriptionsByChannelAction | BackendWebSocketServiceChannelHasSubscriptionAction | BackendWebSocketServiceFindSubscriptionsByChannelPrefixAction | BackendWebSocketServiceAddChannelCallbackAction | BackendWebSocketServiceRemoveChannelCallbackAction | BackendWebSocketServiceGetChannelCallbacksAction | BackendWebSocketServiceSubscribeAction;
165
+ export type BackendWebSocketServiceMethodActions = BackendWebSocketServiceConnectAction | BackendWebSocketServiceDisconnectAction | BackendWebSocketServiceForceReconnectionAction | BackendWebSocketServiceSendMessageAction | BackendWebSocketServiceSendRequestAction | BackendWebSocketServiceGetConnectionInfoAction | BackendWebSocketServiceGetSubscriptionsByChannelAction | BackendWebSocketServiceChannelHasSubscriptionAction | BackendWebSocketServiceFindSubscriptionsByChannelPrefixAction | BackendWebSocketServiceAddChannelCallbackAction | BackendWebSocketServiceRemoveChannelCallbackAction | BackendWebSocketServiceGetChannelCallbacksAction | BackendWebSocketServiceSubscribeAction;
146
166
  //# sourceMappingURL=BackendWebSocketService-method-action-types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BackendWebSocketService-method-action-types.d.mts","sourceRoot":"","sources":["../src/BackendWebSocketService-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,sCAAkC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,oCAAoC,GAAG;IACjD,IAAI,EAAE,iCAAiC,CAAC;IACxC,OAAO,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,uCAAuC,GAAG;IACpD,IAAI,EAAE,oCAAoC,CAAC;IAC3C,OAAO,EAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;CAChD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;CACjD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;CACjD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,uBAAuB,CAAC,mBAAmB,CAAC,CAAC;CACvD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,sDAAsD,GAAG;IACnE,IAAI,EAAE,mDAAmD,CAAC;IAC1D,OAAO,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAC;CAC/D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mDAAmD,GAAG;IAChE,IAAI,EAAE,gDAAgD,CAAC;IACvD,OAAO,EAAE,uBAAuB,CAAC,wBAAwB,CAAC,CAAC;CAC5D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,6DAA6D,GAAG;IAC1E,IAAI,EAAE,0DAA0D,CAAC;IACjE,OAAO,EAAE,uBAAuB,CAAC,kCAAkC,CAAC,CAAC;CACtE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,+CAA+C,GAAG;IAC5D,IAAI,EAAE,4CAA4C,CAAC;IACnD,OAAO,EAAE,uBAAuB,CAAC,oBAAoB,CAAC,CAAC;CACxD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC/D,IAAI,EAAE,+CAA+C,CAAC;IACtD,OAAO,EAAE,uBAAuB,CAAC,uBAAuB,CAAC,CAAC;CAC3D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gDAAgD,GAAG;IAC7D,IAAI,EAAE,6CAA6C,CAAC;IACpD,OAAO,EAAE,uBAAuB,CAAC,qBAAqB,CAAC,CAAC;CACzD,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,sCAAsC,GAAG;IACnD,IAAI,EAAE,mCAAmC,CAAC;IAC1C,OAAO,EAAE,uBAAuB,CAAC,WAAW,CAAC,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAC5C,oCAAoC,GACpC,uCAAuC,GACvC,wCAAwC,GACxC,wCAAwC,GACxC,8CAA8C,GAC9C,sDAAsD,GACtD,mDAAmD,GACnD,6DAA6D,GAC7D,+CAA+C,GAC/C,kDAAkD,GAClD,gDAAgD,GAChD,sCAAsC,CAAC"}
1
+ {"version":3,"file":"BackendWebSocketService-method-action-types.d.mts","sourceRoot":"","sources":["../src/BackendWebSocketService-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,sCAAkC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,oCAAoC,GAAG;IACjD,IAAI,EAAE,iCAAiC,CAAC;IACxC,OAAO,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,uCAAuC,GAAG;IACpD,IAAI,EAAE,oCAAoC,CAAC;IAC3C,OAAO,EAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;CAChD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,uBAAuB,CAAC,mBAAmB,CAAC,CAAC;CACvD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;CACjD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;CACjD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,uBAAuB,CAAC,mBAAmB,CAAC,CAAC;CACvD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,sDAAsD,GAAG;IACnE,IAAI,EAAE,mDAAmD,CAAC;IAC1D,OAAO,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAC;CAC/D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mDAAmD,GAAG;IAChE,IAAI,EAAE,gDAAgD,CAAC;IACvD,OAAO,EAAE,uBAAuB,CAAC,wBAAwB,CAAC,CAAC;CAC5D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,6DAA6D,GAAG;IAC1E,IAAI,EAAE,0DAA0D,CAAC;IACjE,OAAO,EAAE,uBAAuB,CAAC,kCAAkC,CAAC,CAAC;CACtE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,+CAA+C,GAAG;IAC5D,IAAI,EAAE,4CAA4C,CAAC;IACnD,OAAO,EAAE,uBAAuB,CAAC,oBAAoB,CAAC,CAAC;CACxD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC/D,IAAI,EAAE,+CAA+C,CAAC;IACtD,OAAO,EAAE,uBAAuB,CAAC,uBAAuB,CAAC,CAAC;CAC3D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gDAAgD,GAAG;IAC7D,IAAI,EAAE,6CAA6C,CAAC;IACpD,OAAO,EAAE,uBAAuB,CAAC,qBAAqB,CAAC,CAAC;CACzD,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,sCAAsC,GAAG;IACnD,IAAI,EAAE,mCAAmC,CAAC;IAC1C,OAAO,EAAE,uBAAuB,CAAC,WAAW,CAAC,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAC5C,oCAAoC,GACpC,uCAAuC,GACvC,8CAA8C,GAC9C,wCAAwC,GACxC,wCAAwC,GACxC,8CAA8C,GAC9C,sDAAsD,GACtD,mDAAmD,GACnD,6DAA6D,GAC7D,+CAA+C,GAC/C,kDAAkD,GAClD,gDAAgD,GAChD,sCAAsC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"BackendWebSocketService-method-action-types.mjs","sourceRoot":"","sources":["../src/BackendWebSocketService-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated by `scripts/generate-method-action-types.ts`.\n * Do not edit manually.\n */\n\nimport type { BackendWebSocketService } from './BackendWebSocketService';\n\n/**\n * Establishes WebSocket connection\n *\n * @returns Promise that resolves when connection is established\n */\nexport type BackendWebSocketServiceConnectAction = {\n type: `BackendWebSocketService:connect`;\n handler: BackendWebSocketService['connect'];\n};\n\n/**\n * Closes WebSocket connection\n *\n * @returns Promise that resolves when disconnection is complete\n */\nexport type BackendWebSocketServiceDisconnectAction = {\n type: `BackendWebSocketService:disconnect`;\n handler: BackendWebSocketService['disconnect'];\n};\n\n/**\n * Sends a message through the WebSocket\n *\n * @param message - The message to send\n * @returns Promise that resolves when message is sent\n */\nexport type BackendWebSocketServiceSendMessageAction = {\n type: `BackendWebSocketService:sendMessage`;\n handler: BackendWebSocketService['sendMessage'];\n};\n\n/**\n * Sends a request and waits for a correlated response\n *\n * @param message - The request message\n * @returns Promise that resolves with the response data\n */\nexport type BackendWebSocketServiceSendRequestAction = {\n type: `BackendWebSocketService:sendRequest`;\n handler: BackendWebSocketService['sendRequest'];\n};\n\n/**\n * Gets current connection information\n *\n * @returns Current connection status and details\n */\nexport type BackendWebSocketServiceGetConnectionInfoAction = {\n type: `BackendWebSocketService:getConnectionInfo`;\n handler: BackendWebSocketService['getConnectionInfo'];\n};\n\n/**\n * Gets all subscription information for a specific channel\n *\n * @param channel - The channel name to look up\n * @returns Array of subscription details for all subscriptions containing the channel\n */\nexport type BackendWebSocketServiceGetSubscriptionsByChannelAction = {\n type: `BackendWebSocketService:getSubscriptionsByChannel`;\n handler: BackendWebSocketService['getSubscriptionsByChannel'];\n};\n\n/**\n * Checks if a channel has a subscription\n *\n * @param channel - The channel name to check\n * @returns True if the channel has a subscription, false otherwise\n */\nexport type BackendWebSocketServiceChannelHasSubscriptionAction = {\n type: `BackendWebSocketService:channelHasSubscription`;\n handler: BackendWebSocketService['channelHasSubscription'];\n};\n\n/**\n * Finds all subscriptions that have channels starting with the specified prefix\n *\n * @param channelPrefix - The channel prefix to search for (e.g., \"account-activity.v1\")\n * @returns Array of subscription info for matching subscriptions\n */\nexport type BackendWebSocketServiceFindSubscriptionsByChannelPrefixAction = {\n type: `BackendWebSocketService:findSubscriptionsByChannelPrefix`;\n handler: BackendWebSocketService['findSubscriptionsByChannelPrefix'];\n};\n\n/**\n * Register a callback for specific channels\n *\n * @param options - Channel callback configuration\n * @param options.channelName - Channel name to match exactly\n * @param options.callback - Function to call when channel matches\n *\n * @example\n * ```typescript\n * // Listen to specific account activity channel\n * webSocketService.addChannelCallback({\n * channelName: 'account-activity.v1.eip155:0:0x1234...',\n * callback: (notification) => {\n * console.log('Account activity:', notification.data);\n * }\n * });\n *\n * // Listen to system notifications channel\n * webSocketService.addChannelCallback({\n * channelName: 'system-notifications.v1',\n * callback: (notification) => {\n * console.log('System notification:', notification.data);\n * }\n * });\n * ```\n */\nexport type BackendWebSocketServiceAddChannelCallbackAction = {\n type: `BackendWebSocketService:addChannelCallback`;\n handler: BackendWebSocketService['addChannelCallback'];\n};\n\n/**\n * Remove a channel callback\n *\n * @param channelName - The channel name to remove callback for\n * @returns True if callback was found and removed, false otherwise\n */\nexport type BackendWebSocketServiceRemoveChannelCallbackAction = {\n type: `BackendWebSocketService:removeChannelCallback`;\n handler: BackendWebSocketService['removeChannelCallback'];\n};\n\n/**\n * Get all registered channel callbacks (for debugging)\n */\nexport type BackendWebSocketServiceGetChannelCallbacksAction = {\n type: `BackendWebSocketService:getChannelCallbacks`;\n handler: BackendWebSocketService['getChannelCallbacks'];\n};\n\n/**\n * Create and manage a subscription with direct callback routing\n *\n * @param options - Subscription configuration\n * @param options.channels - Array of channel names to subscribe to\n * @param options.callback - Callback function for handling notifications\n * @returns Promise that resolves with subscription object containing unsubscribe method\n */\nexport type BackendWebSocketServiceSubscribeAction = {\n type: `BackendWebSocketService:subscribe`;\n handler: BackendWebSocketService['subscribe'];\n};\n\n/**\n * Union of all BackendWebSocketService action types.\n */\nexport type BackendWebSocketServiceMethodActions =\n | BackendWebSocketServiceConnectAction\n | BackendWebSocketServiceDisconnectAction\n | BackendWebSocketServiceSendMessageAction\n | BackendWebSocketServiceSendRequestAction\n | BackendWebSocketServiceGetConnectionInfoAction\n | BackendWebSocketServiceGetSubscriptionsByChannelAction\n | BackendWebSocketServiceChannelHasSubscriptionAction\n | BackendWebSocketServiceFindSubscriptionsByChannelPrefixAction\n | BackendWebSocketServiceAddChannelCallbackAction\n | BackendWebSocketServiceRemoveChannelCallbackAction\n | BackendWebSocketServiceGetChannelCallbacksAction\n | BackendWebSocketServiceSubscribeAction;\n"]}
1
+ {"version":3,"file":"BackendWebSocketService-method-action-types.mjs","sourceRoot":"","sources":["../src/BackendWebSocketService-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated by `scripts/generate-method-action-types.ts`.\n * Do not edit manually.\n */\n\nimport type { BackendWebSocketService } from './BackendWebSocketService';\n\n/**\n * Establishes WebSocket connection\n *\n * @returns Promise that resolves when connection is established\n */\nexport type BackendWebSocketServiceConnectAction = {\n type: `BackendWebSocketService:connect`;\n handler: BackendWebSocketService['connect'];\n};\n\n/**\n * Closes WebSocket connection\n *\n * @returns Promise that resolves when disconnection is complete\n */\nexport type BackendWebSocketServiceDisconnectAction = {\n type: `BackendWebSocketService:disconnect`;\n handler: BackendWebSocketService['disconnect'];\n};\n\n/**\n * Forces a WebSocket reconnection to clean up subscription state\n *\n * This method is useful when subscription state may be out of sync and needs to be reset.\n * It performs a controlled disconnect-then-reconnect sequence:\n * - Disconnects cleanly to trigger subscription cleanup\n * - Schedules reconnection with exponential backoff to prevent rapid loops\n * - All subscriptions will be cleaned up automatically on disconnect\n *\n * Use cases:\n * - Recovering from subscription/unsubscription issues\n * - Cleaning up orphaned subscriptions\n * - Forcing a fresh subscription state\n *\n * @returns Promise that resolves when disconnection is complete (reconnection is scheduled)\n */\nexport type BackendWebSocketServiceForceReconnectionAction = {\n type: `BackendWebSocketService:forceReconnection`;\n handler: BackendWebSocketService['forceReconnection'];\n};\n\n/**\n * Sends a message through the WebSocket\n *\n * @param message - The message to send\n * @returns Promise that resolves when message is sent\n */\nexport type BackendWebSocketServiceSendMessageAction = {\n type: `BackendWebSocketService:sendMessage`;\n handler: BackendWebSocketService['sendMessage'];\n};\n\n/**\n * Sends a request and waits for a correlated response\n *\n * @param message - The request message\n * @returns Promise that resolves with the response data\n */\nexport type BackendWebSocketServiceSendRequestAction = {\n type: `BackendWebSocketService:sendRequest`;\n handler: BackendWebSocketService['sendRequest'];\n};\n\n/**\n * Gets current connection information\n *\n * @returns Current connection status and details\n */\nexport type BackendWebSocketServiceGetConnectionInfoAction = {\n type: `BackendWebSocketService:getConnectionInfo`;\n handler: BackendWebSocketService['getConnectionInfo'];\n};\n\n/**\n * Gets all subscription information for a specific channel\n *\n * @param channel - The channel name to look up\n * @returns Array of subscription details for all subscriptions containing the channel\n */\nexport type BackendWebSocketServiceGetSubscriptionsByChannelAction = {\n type: `BackendWebSocketService:getSubscriptionsByChannel`;\n handler: BackendWebSocketService['getSubscriptionsByChannel'];\n};\n\n/**\n * Checks if a channel has a subscription\n *\n * @param channel - The channel name to check\n * @returns True if the channel has a subscription, false otherwise\n */\nexport type BackendWebSocketServiceChannelHasSubscriptionAction = {\n type: `BackendWebSocketService:channelHasSubscription`;\n handler: BackendWebSocketService['channelHasSubscription'];\n};\n\n/**\n * Finds all subscriptions that have channels starting with the specified prefix\n *\n * @param channelPrefix - The channel prefix to search for (e.g., \"account-activity.v1\")\n * @returns Array of subscription info for matching subscriptions\n */\nexport type BackendWebSocketServiceFindSubscriptionsByChannelPrefixAction = {\n type: `BackendWebSocketService:findSubscriptionsByChannelPrefix`;\n handler: BackendWebSocketService['findSubscriptionsByChannelPrefix'];\n};\n\n/**\n * Register a callback for specific channels\n *\n * @param options - Channel callback configuration\n * @param options.channelName - Channel name to match exactly\n * @param options.callback - Function to call when channel matches\n *\n * @example\n * ```typescript\n * // Listen to specific account activity channel\n * webSocketService.addChannelCallback({\n * channelName: 'account-activity.v1.eip155:0:0x1234...',\n * callback: (notification) => {\n * console.log('Account activity:', notification.data);\n * }\n * });\n *\n * // Listen to system notifications channel\n * webSocketService.addChannelCallback({\n * channelName: 'system-notifications.v1',\n * callback: (notification) => {\n * console.log('System notification:', notification.data);\n * }\n * });\n * ```\n */\nexport type BackendWebSocketServiceAddChannelCallbackAction = {\n type: `BackendWebSocketService:addChannelCallback`;\n handler: BackendWebSocketService['addChannelCallback'];\n};\n\n/**\n * Remove a channel callback\n *\n * @param channelName - The channel name to remove callback for\n * @returns True if callback was found and removed, false otherwise\n */\nexport type BackendWebSocketServiceRemoveChannelCallbackAction = {\n type: `BackendWebSocketService:removeChannelCallback`;\n handler: BackendWebSocketService['removeChannelCallback'];\n};\n\n/**\n * Get all registered channel callbacks (for debugging)\n */\nexport type BackendWebSocketServiceGetChannelCallbacksAction = {\n type: `BackendWebSocketService:getChannelCallbacks`;\n handler: BackendWebSocketService['getChannelCallbacks'];\n};\n\n/**\n * Create and manage a subscription with direct callback routing\n *\n * @param options - Subscription configuration\n * @param options.channels - Array of channel names to subscribe to\n * @param options.callback - Callback function for handling notifications\n * @returns Promise that resolves with subscription object containing unsubscribe method\n */\nexport type BackendWebSocketServiceSubscribeAction = {\n type: `BackendWebSocketService:subscribe`;\n handler: BackendWebSocketService['subscribe'];\n};\n\n/**\n * Union of all BackendWebSocketService action types.\n */\nexport type BackendWebSocketServiceMethodActions =\n | BackendWebSocketServiceConnectAction\n | BackendWebSocketServiceDisconnectAction\n | BackendWebSocketServiceForceReconnectionAction\n | BackendWebSocketServiceSendMessageAction\n | BackendWebSocketServiceSendRequestAction\n | BackendWebSocketServiceGetConnectionInfoAction\n | BackendWebSocketServiceGetSubscriptionsByChannelAction\n | BackendWebSocketServiceChannelHasSubscriptionAction\n | BackendWebSocketServiceFindSubscriptionsByChannelPrefixAction\n | BackendWebSocketServiceAddChannelCallbackAction\n | BackendWebSocketServiceRemoveChannelCallbackAction\n | BackendWebSocketServiceGetChannelCallbacksAction\n | BackendWebSocketServiceSubscribeAction;\n"]}
@@ -10,9 +10,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _BackendWebSocketService_instances, _BackendWebSocketService_messenger, _BackendWebSocketService_options, _BackendWebSocketService_isEnabled, _BackendWebSocketService_trace, _BackendWebSocketService_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt, _BackendWebSocketService_manualDisconnect, _BackendWebSocketService_subscriptions, _BackendWebSocketService_channelCallbacks, _BackendWebSocketService_subscribeEvents, _BackendWebSocketService_buildAuthenticatedUrl, _BackendWebSocketService_establishConnection, _BackendWebSocketService_handleMessage, _BackendWebSocketService_isServerResponse, _BackendWebSocketService_isSubscriptionNotification, _BackendWebSocketService_isChannelMessage, _BackendWebSocketService_handleServerResponse, _BackendWebSocketService_handleChannelMessage, _BackendWebSocketService_handleSubscriptionNotification, _BackendWebSocketService_parseMessage, _BackendWebSocketService_handleClose, _BackendWebSocketService_handleError, _BackendWebSocketService_scheduleReconnect, _BackendWebSocketService_clearTimers, _BackendWebSocketService_clearPendingRequests, _BackendWebSocketService_clearSubscriptions, _BackendWebSocketService_setState;
13
+ var _BackendWebSocketService_instances, _BackendWebSocketService_messenger, _BackendWebSocketService_options, _BackendWebSocketService_isEnabled, _BackendWebSocketService_trace, _BackendWebSocketService_ws, _BackendWebSocketService_state, _BackendWebSocketService_reconnectAttempts, _BackendWebSocketService_reconnectTimer, _BackendWebSocketService_connectionTimeout, _BackendWebSocketService_stableConnectionTimer, _BackendWebSocketService_connectionPromise, _BackendWebSocketService_pendingRequests, _BackendWebSocketService_connectedAt, _BackendWebSocketService_manualDisconnect, _BackendWebSocketService_subscriptions, _BackendWebSocketService_channelCallbacks, _BackendWebSocketService_backoff, _BackendWebSocketService_subscribeEvents, _BackendWebSocketService_buildAuthenticatedUrl, _BackendWebSocketService_establishConnection, _BackendWebSocketService_handleMessage, _BackendWebSocketService_isServerResponse, _BackendWebSocketService_isSubscriptionNotification, _BackendWebSocketService_isChannelMessage, _BackendWebSocketService_handleServerResponse, _BackendWebSocketService_handleChannelMessage, _BackendWebSocketService_handleSubscriptionNotification, _BackendWebSocketService_parseMessage, _BackendWebSocketService_handleClose, _BackendWebSocketService_handleError, _BackendWebSocketService_scheduleReconnect, _BackendWebSocketService_newBackoff, _BackendWebSocketService_clearTimers, _BackendWebSocketService_clearPendingRequests, _BackendWebSocketService_clearSubscriptions, _BackendWebSocketService_setState;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.BackendWebSocketService = exports.WebSocketEventType = exports.WebSocketState = exports.getCloseReason = void 0;
16
+ const controller_utils_1 = require("@metamask/controller-utils");
16
17
  const utils_1 = require("@metamask/utils");
17
18
  const uuid_1 = require("uuid");
18
19
  const logger_1 = require("./logger.cjs");
@@ -21,6 +22,7 @@ const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, SERVICE_NAM
21
22
  const MESSENGER_EXPOSED_METHODS = [
22
23
  'connect',
23
24
  'disconnect',
25
+ 'forceReconnection',
24
26
  'sendMessage',
25
27
  'sendRequest',
26
28
  'subscribe',
@@ -152,6 +154,7 @@ class BackendWebSocketService {
152
154
  _BackendWebSocketService_reconnectAttempts.set(this, 0);
153
155
  _BackendWebSocketService_reconnectTimer.set(this, null);
154
156
  _BackendWebSocketService_connectionTimeout.set(this, null);
157
+ _BackendWebSocketService_stableConnectionTimer.set(this, null);
155
158
  // Track the current connection promise to handle concurrent connection attempts
156
159
  _BackendWebSocketService_connectionPromise.set(this, null);
157
160
  _BackendWebSocketService_pendingRequests.set(this, new Map());
@@ -166,6 +169,8 @@ class BackendWebSocketService {
166
169
  // Key: channel name (serves as unique identifier)
167
170
  // Value: ChannelCallback configuration
168
171
  _BackendWebSocketService_channelCallbacks.set(this, new Map());
172
+ // Backoff instance for reconnection delays (reset on stable connection)
173
+ _BackendWebSocketService_backoff.set(this, void 0);
169
174
  __classPrivateFieldSet(this, _BackendWebSocketService_messenger, options.messenger, "f");
170
175
  __classPrivateFieldSet(this, _BackendWebSocketService_isEnabled, options.isEnabled, "f");
171
176
  // Default to no-op trace function to keep core platform-agnostic
@@ -179,6 +184,8 @@ class BackendWebSocketService {
179
184
  maxReconnectDelay: options.maxReconnectDelay ?? 5000,
180
185
  requestTimeout: options.requestTimeout ?? 30000,
181
186
  }, "f");
187
+ // Initialize backoff for reconnection delays
188
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
182
189
  // Subscribe to authentication and keyring controller events
183
190
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_subscribeEvents).call(this);
184
191
  // Register action handlers using the method actions pattern
@@ -220,6 +227,11 @@ class BackendWebSocketService {
220
227
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
221
228
  return;
222
229
  }
230
+ // If a reconnect is already scheduled, defer to it to avoid bypassing exponential backoff
231
+ // This prevents rapid loops when server accepts then immediately closes connections
232
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
233
+ return;
234
+ }
223
235
  // Create and store the connection promise IMMEDIATELY (before any async operations)
224
236
  // This ensures subsequent connect() calls will wait for this promise instead of creating new connections
225
237
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, (async () => {
@@ -228,15 +240,12 @@ class BackendWebSocketService {
228
240
  try {
229
241
  const token = await __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").call('AuthenticationController:getBearerToken');
230
242
  if (!token) {
231
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
232
243
  throw new Error('Authentication required: user not signed in');
233
244
  }
234
245
  bearerToken = token;
235
246
  }
236
247
  catch (error) {
237
248
  log('Failed to check authentication requirements', { error });
238
- // Can't connect - schedule retry
239
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
240
249
  throw error;
241
250
  }
242
251
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTING);
@@ -248,13 +257,17 @@ class BackendWebSocketService {
248
257
  const errorMessage = (0, utils_1.getErrorMessage)(error);
249
258
  log('Connection attempt failed', { errorMessage, error });
250
259
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.ERROR);
251
- // Rethrow to propagate error to caller
252
260
  throw error;
253
261
  }
254
262
  })(), "f");
255
263
  try {
256
264
  await __classPrivateFieldGet(this, _BackendWebSocketService_connectionPromise, "f");
257
265
  }
266
+ catch {
267
+ // Always schedule reconnect on any failure
268
+ // Exponential backoff will prevent aggressive retries
269
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
270
+ }
258
271
  finally {
259
272
  // Clear the connection promise when done (success or failure)
260
273
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
@@ -262,10 +275,8 @@ class BackendWebSocketService {
262
275
  }
263
276
  /**
264
277
  * Closes WebSocket connection
265
- *
266
- * @returns Promise that resolves when disconnection is complete
267
278
  */
268
- async disconnect() {
279
+ disconnect() {
269
280
  if (__classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTED ||
270
281
  __classPrivateFieldGet(this, _BackendWebSocketService_state, "f") === WebSocketState.DISCONNECTING) {
271
282
  return;
@@ -277,11 +288,41 @@ class BackendWebSocketService {
277
288
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearPendingRequests).call(this, new Error('WebSocket disconnected'));
278
289
  // Clear any pending connection promise
279
290
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
291
+ // Reset reconnect attempts on manual disconnect
292
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
280
293
  if (__classPrivateFieldGet(this, _BackendWebSocketService_ws, "f")) {
281
294
  __classPrivateFieldGet(this, _BackendWebSocketService_ws, "f").close(1000, 'Normal closure');
282
295
  }
283
296
  log('WebSocket manually disconnected');
284
297
  }
298
+ /**
299
+ * Forces a WebSocket reconnection to clean up subscription state
300
+ *
301
+ * This method is useful when subscription state may be out of sync and needs to be reset.
302
+ * It performs a controlled disconnect-then-reconnect sequence:
303
+ * - Disconnects cleanly to trigger subscription cleanup
304
+ * - Schedules reconnection with exponential backoff to prevent rapid loops
305
+ * - All subscriptions will be cleaned up automatically on disconnect
306
+ *
307
+ * Use cases:
308
+ * - Recovering from subscription/unsubscription issues
309
+ * - Cleaning up orphaned subscriptions
310
+ * - Forcing a fresh subscription state
311
+ *
312
+ * @returns Promise that resolves when disconnection is complete (reconnection is scheduled)
313
+ */
314
+ async forceReconnection() {
315
+ // If a reconnect is already scheduled, don't force another one
316
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
317
+ log('Reconnect already scheduled, skipping force reconnection');
318
+ return;
319
+ }
320
+ log('Forcing WebSocket reconnection to clean up subscription state');
321
+ // Perform controlled disconnect
322
+ this.disconnect();
323
+ // Schedule reconnection with exponential backoff
324
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
325
+ }
285
326
  /**
286
327
  * Sends a message through the WebSocket (fire-and-forget, no response expected)
287
328
  *
@@ -626,7 +667,7 @@ class BackendWebSocketService {
626
667
  }
627
668
  }
628
669
  exports.BackendWebSocketService = BackendWebSocketService;
629
- _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_options = new WeakMap(), _BackendWebSocketService_isEnabled = new WeakMap(), _BackendWebSocketService_trace = new WeakMap(), _BackendWebSocketService_ws = new WeakMap(), _BackendWebSocketService_state = new WeakMap(), _BackendWebSocketService_reconnectAttempts = new WeakMap(), _BackendWebSocketService_reconnectTimer = new WeakMap(), _BackendWebSocketService_connectionTimeout = new WeakMap(), _BackendWebSocketService_connectionPromise = new WeakMap(), _BackendWebSocketService_pendingRequests = new WeakMap(), _BackendWebSocketService_connectedAt = new WeakMap(), _BackendWebSocketService_manualDisconnect = new WeakMap(), _BackendWebSocketService_subscriptions = new WeakMap(), _BackendWebSocketService_channelCallbacks = new WeakMap(), _BackendWebSocketService_instances = new WeakSet(), _BackendWebSocketService_subscribeEvents = function _BackendWebSocketService_subscribeEvents() {
670
+ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_options = new WeakMap(), _BackendWebSocketService_isEnabled = new WeakMap(), _BackendWebSocketService_trace = new WeakMap(), _BackendWebSocketService_ws = new WeakMap(), _BackendWebSocketService_state = new WeakMap(), _BackendWebSocketService_reconnectAttempts = new WeakMap(), _BackendWebSocketService_reconnectTimer = new WeakMap(), _BackendWebSocketService_connectionTimeout = new WeakMap(), _BackendWebSocketService_stableConnectionTimer = new WeakMap(), _BackendWebSocketService_connectionPromise = new WeakMap(), _BackendWebSocketService_pendingRequests = new WeakMap(), _BackendWebSocketService_connectedAt = new WeakMap(), _BackendWebSocketService_manualDisconnect = new WeakMap(), _BackendWebSocketService_subscriptions = new WeakMap(), _BackendWebSocketService_channelCallbacks = new WeakMap(), _BackendWebSocketService_backoff = new WeakMap(), _BackendWebSocketService_instances = new WeakSet(), _BackendWebSocketService_subscribeEvents = function _BackendWebSocketService_subscribeEvents() {
630
671
  // Subscribe to authentication state changes (sign in/out)
631
672
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('AuthenticationController:stateChange', (state) => {
632
673
  if (state.isSignedIn) {
@@ -634,7 +675,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
634
675
  this.connect();
635
676
  }
636
677
  else {
637
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
638
678
  this.disconnect();
639
679
  }
640
680
  }, (state) => ({ isSignedIn: state.isSignedIn }));
@@ -645,7 +685,6 @@ _BackendWebSocketService_messenger = new WeakMap(), _BackendWebSocketService_opt
645
685
  });
646
686
  // Subscribe to wallet lock event
647
687
  __classPrivateFieldGet(this, _BackendWebSocketService_messenger, "f").subscribe('KeyringController:lock', () => {
648
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
649
688
  this.disconnect();
650
689
  });
651
690
  }, _BackendWebSocketService_buildAuthenticatedUrl = function _BackendWebSocketService_buildAuthenticatedUrl(bearerToken) {
@@ -694,8 +733,15 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
694
733
  __classPrivateFieldSet(this, _BackendWebSocketService_ws, ws, "f");
695
734
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_setState).call(this, WebSocketState.CONNECTED);
696
735
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, Date.now(), "f");
697
- // Reset reconnect attempts on successful connection
698
- __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
736
+ // Only reset after connection stays stable for a period (10 seconds)
737
+ // This prevents rapid reconnect loops when server accepts then immediately closes
738
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, setTimeout(() => {
739
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
740
+ __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
741
+ // Create new backoff sequence for fresh start on next disconnect
742
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
743
+ log('Connection stable - reset reconnect attempts and backoff');
744
+ }, 10000), "f");
699
745
  resolve();
700
746
  });
701
747
  };
@@ -837,7 +883,14 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
837
883
  }, _BackendWebSocketService_handleClose = function _BackendWebSocketService_handleClose(event) {
838
884
  // Calculate connection duration before we clear state
839
885
  const connectionDuration = Date.now() - __classPrivateFieldGet(this, _BackendWebSocketService_connectedAt, "f");
840
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_clearTimers).call(this);
886
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f")) {
887
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
888
+ __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
889
+ }
890
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f")) {
891
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f"));
892
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
893
+ }
841
894
  __classPrivateFieldSet(this, _BackendWebSocketService_connectedAt, 0, "f");
842
895
  // Clear any pending connection promise
843
896
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionPromise, null, "f");
@@ -867,28 +920,43 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
867
920
  }, () => {
868
921
  // Empty trace callback - just measuring the event
869
922
  });
870
- // For any unexpected disconnects, attempt reconnection
871
- // The manualDisconnect flag is the only gate - if it's false, we reconnect
872
923
  __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
873
924
  }, _BackendWebSocketService_handleError = function _BackendWebSocketService_handleError(_error) {
874
925
  // Placeholder for future error handling logic
875
926
  }, _BackendWebSocketService_scheduleReconnect = function _BackendWebSocketService_scheduleReconnect() {
927
+ // If a reconnect is already scheduled, don't schedule another one
928
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
929
+ return;
930
+ }
931
+ // Increment attempts BEFORE calculating delay so backoff grows properly
876
932
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") + 1, "f");
877
- const rawDelay = __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay * Math.pow(1.5, __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") - 1);
878
- const delay = Math.min(rawDelay, __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay);
933
+ // Use Cockatiel's exponential backoff to get delay with jitter
934
+ const delay = __classPrivateFieldGet(this, _BackendWebSocketService_backoff, "f").duration;
935
+ // Progress to next backoff state for future reconnect attempts
936
+ // Pass attempt number as context (though ExponentialBackoff doesn't use it)
937
+ __classPrivateFieldSet(this, _BackendWebSocketService_backoff, __classPrivateFieldGet(this, _BackendWebSocketService_backoff, "f").next({ attempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f") }), "f");
938
+ log('Scheduling reconnect', {
939
+ attempt: __classPrivateFieldGet(this, _BackendWebSocketService_reconnectAttempts, "f"),
940
+ delay_ms: delay,
941
+ });
879
942
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, setTimeout(() => {
880
943
  // Clear timer reference first
881
944
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectTimer, null, "f");
882
945
  // Check if connection is still enabled before reconnecting
883
946
  if (__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f") && !__classPrivateFieldGet(this, _BackendWebSocketService_isEnabled, "f").call(this)) {
884
947
  __classPrivateFieldSet(this, _BackendWebSocketService_reconnectAttempts, 0, "f");
948
+ // Create new backoff sequence when disabled
949
+ __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_newBackoff).call(this);
885
950
  return;
886
951
  }
887
- // Attempt to reconnect - if it fails, schedule another attempt
888
- this.connect().catch(() => {
889
- __classPrivateFieldGet(this, _BackendWebSocketService_instances, "m", _BackendWebSocketService_scheduleReconnect).call(this);
890
- });
952
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
953
+ this.connect();
891
954
  }, delay), "f");
955
+ }, _BackendWebSocketService_newBackoff = function _BackendWebSocketService_newBackoff() {
956
+ __classPrivateFieldSet(this, _BackendWebSocketService_backoff, new controller_utils_1.ExponentialBackoff({
957
+ initialDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").reconnectDelay,
958
+ maxDelay: __classPrivateFieldGet(this, _BackendWebSocketService_options, "f").maxReconnectDelay,
959
+ }).next(), "f");
892
960
  }, _BackendWebSocketService_clearTimers = function _BackendWebSocketService_clearTimers() {
893
961
  if (__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f")) {
894
962
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_reconnectTimer, "f"));
@@ -898,6 +966,10 @@ async function _BackendWebSocketService_establishConnection(bearerToken) {
898
966
  clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_connectionTimeout, "f"));
899
967
  __classPrivateFieldSet(this, _BackendWebSocketService_connectionTimeout, null, "f");
900
968
  }
969
+ if (__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f")) {
970
+ clearTimeout(__classPrivateFieldGet(this, _BackendWebSocketService_stableConnectionTimer, "f"));
971
+ __classPrivateFieldSet(this, _BackendWebSocketService_stableConnectionTimer, null, "f");
972
+ }
901
973
  }, _BackendWebSocketService_clearPendingRequests = function _BackendWebSocketService_clearPendingRequests(error) {
902
974
  for (const [, request] of __classPrivateFieldGet(this, _BackendWebSocketService_pendingRequests, "f")) {
903
975
  clearTimeout(request.timeout);