diff --git a/packages/core-backend/CHANGELOG.md b/packages/core-backend/CHANGELOG.md index 3253e7fe1ea..7696f469ad9 100644 --- a/packages/core-backend/CHANGELOG.md +++ b/packages/core-backend/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6823](https://github.com/MetaMask/core/pull/6823)) + - Previously, `AccountActivityService` and `BackendWebSocketService` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + +### Removed + +- **BREAKING:** Remove exported type aliases and constants that were specific to controller messenger integration ([#6823](https://github.com/MetaMask/core/pull/6823)) + - Removed type exports: `BackendWebSocketServiceAllowedActions`, `BackendWebSocketServiceAllowedEvents`, `AccountActivityServiceAllowedActions`, `AccountActivityServiceAllowedEvents` + - Removed constant exports: `ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS`, `ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS` + - These types and constants were internal implementation details that should not have been exposed. Consumers should use the service-specific messenger types directly. + ## [2.1.0] ### Added diff --git a/packages/core-backend/package.json b/packages/core-backend/package.json index fc9e36b71ec..a965aa457b7 100644 --- a/packages/core-backend/package.json +++ b/packages/core-backend/package.json @@ -47,8 +47,8 @@ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" }, "dependencies": { - "@metamask/base-controller": "^8.4.1", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/profile-sync-controller": "^25.1.1", "@metamask/utils": "^11.8.1", "uuid": "^8.3.2" diff --git a/packages/core-backend/src/AccountActivityService.test.ts b/packages/core-backend/src/AccountActivityService.test.ts index 6ba242469d1..db52e6d09d7 100644 --- a/packages/core-backend/src/AccountActivityService.test.ts +++ b/packages/core-backend/src/AccountActivityService.test.ts @@ -1,17 +1,17 @@ -import { Messenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; -import type { - AccountActivityServiceAllowedEvents, - AccountActivityServiceAllowedActions, -} from './AccountActivityService'; import { AccountActivityService, type AccountActivityServiceMessenger, type SubscriptionOptions, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS, } from './AccountActivityService'; import type { ServerNotificationMessage } from './BackendWebSocketService'; import { WebSocketState } from './BackendWebSocketService'; @@ -19,6 +19,18 @@ import type { Transaction, BalanceUpdate } from './types'; import type { AccountActivityMessage } from './types'; import { flushPromises } from '../../../tests/helpers'; +type AllAccountActivityServiceActions = + MessengerActions; + +type AllAccountActivityServiceEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllAccountActivityServiceActions, + AllAccountActivityServiceEvents +>; + // Helper function for completing async operations const completeAsyncOperations = async (timeoutMs = 0) => { await flushPromises(); @@ -48,25 +60,70 @@ const createMockInternalAccount = (options: { scopes: ['eip155:1'], // Required scopes property }); +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Creates a real messenger with registered mock actions for testing * Each call creates a completely independent messenger to ensure test isolation * * @returns Object containing the messenger and mock action functions */ -const getMessenger = () => { +const getMessenger = (): { + rootMessenger: RootMessenger; + messenger: AccountActivityServiceMessenger; + mocks: { + getSelectedAccount: jest.Mock; + connect: jest.Mock; + disconnect: jest.Mock; + subscribe: jest.Mock; + channelHasSubscription: jest.Mock; + getSubscriptionsByChannel: jest.Mock; + findSubscriptionsByChannelPrefix: jest.Mock; + addChannelCallback: jest.Mock; + removeChannelCallback: jest.Mock; + }; +} => { // Use any types for the root messenger to avoid complex type constraints in tests // Create a unique root messenger for each test - const rootMessenger = new Messenger< - AccountActivityServiceAllowedActions, - AccountActivityServiceAllowedEvents - >(); - const messenger: AccountActivityServiceMessenger = - rootMessenger.getRestricted({ - name: 'AccountActivityService', - allowedActions: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS], - allowedEvents: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS], - }); + const rootMessenger = getRootMessenger(); + const messenger: AccountActivityServiceMessenger = new Messenger< + 'AccountActivityService', + AllAccountActivityServiceActions, + AllAccountActivityServiceEvents, + RootMessenger + >({ + namespace: 'AccountActivityService', + parent: rootMessenger, + }); + + rootMessenger.delegate({ + actions: [ + 'AccountsController:getSelectedAccount', + 'BackendWebSocketService:connect', + 'BackendWebSocketService:disconnect', + 'BackendWebSocketService:subscribe', + 'BackendWebSocketService:getConnectionInfo', + 'BackendWebSocketService:channelHasSubscription', + 'BackendWebSocketService:getSubscriptionsByChannel', + 'BackendWebSocketService:findSubscriptionsByChannelPrefix', + 'BackendWebSocketService:addChannelCallback', + 'BackendWebSocketService:removeChannelCallback', + ], + events: [ + 'AccountsController:selectedAccountChange', + 'BackendWebSocketService:connectionStateChanged', + ], + messenger, + }); // Create mock action handlers const mockGetSelectedAccount = jest.fn(); @@ -215,10 +272,7 @@ type WithServiceOptions = { type WithServiceCallback = (payload: { service: AccountActivityService; messenger: AccountActivityServiceMessenger; - rootMessenger: Messenger< - AccountActivityServiceAllowedActions, - AccountActivityServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getSelectedAccount: jest.Mock; connect: jest.Mock; @@ -645,7 +699,7 @@ describe('AccountActivityService', () => { }); // Publish WebSocket ERROR state event - should flush tracked chains as down - await rootMessenger.publish( + rootMessenger.publish( 'BackendWebSocketService:connectionStateChanged', { state: WebSocketState.ERROR, @@ -679,7 +733,7 @@ describe('AccountActivityService', () => { mocks.getSelectedAccount.mockReturnValue(null); // Publish WebSocket ERROR state event without any tracked chains - await rootMessenger.publish( + rootMessenger.publish( 'BackendWebSocketService:connectionStateChanged', { state: WebSocketState.ERROR, @@ -730,7 +784,7 @@ describe('AccountActivityService', () => { }); // Publish account change event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', solanaAccount, ); @@ -760,7 +814,7 @@ describe('AccountActivityService', () => { }); // Publish account change event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', unknownAccount, ); @@ -782,7 +836,7 @@ describe('AccountActivityService', () => { mocks.getSelectedAccount.mockReturnValue(null); // Publish WebSocket connection event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'BackendWebSocketService:connectionStateChanged', { state: WebSocketState.CONNECTED, @@ -822,7 +876,7 @@ describe('AccountActivityService', () => { }); // Publish account change event on root messenger - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', newAccount, ); @@ -860,7 +914,7 @@ describe('AccountActivityService', () => { }); // Publish account change event on root messenger - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', newAccount, ); diff --git a/packages/core-backend/src/AccountActivityService.ts b/packages/core-backend/src/AccountActivityService.ts index c92ab1a2a2a..ac706e89e1c 100644 --- a/packages/core-backend/src/AccountActivityService.ts +++ b/packages/core-backend/src/AccountActivityService.ts @@ -9,9 +9,9 @@ import type { AccountsControllerGetSelectedAccountAction, AccountsControllerSelectedAccountChangeEvent, } from '@metamask/accounts-controller'; -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { TraceCallback } from '@metamask/controller-utils'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import type { AccountActivityServiceMethodActions } from './AccountActivityService-method-action-types'; import type { @@ -76,27 +76,7 @@ export type AccountActivityServiceOptions = { // Action types for the messaging system - using generated method actions export type AccountActivityServiceActions = AccountActivityServiceMethodActions; -// Allowed actions that AccountActivityService can call on other controllers -export const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = [ - 'AccountsController:getSelectedAccount', - 'BackendWebSocketService:connect', - 'BackendWebSocketService:disconnect', - 'BackendWebSocketService:subscribe', - 'BackendWebSocketService:getConnectionInfo', - 'BackendWebSocketService:channelHasSubscription', - 'BackendWebSocketService:getSubscriptionsByChannel', - 'BackendWebSocketService:findSubscriptionsByChannelPrefix', - 'BackendWebSocketService:addChannelCallback', - 'BackendWebSocketService:removeChannelCallback', -] as const; - -// Allowed events that AccountActivityService can listen to -export const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = [ - 'AccountsController:selectedAccountChange', - 'BackendWebSocketService:connectionStateChanged', -] as const; - -export type AccountActivityServiceAllowedActions = +type AllowedActions = | AccountsControllerGetSelectedAccountAction | BackendWebSocketServiceMethodActions; @@ -134,16 +114,14 @@ export type AccountActivityServiceEvents = | AccountActivityServiceSubscriptionErrorEvent | AccountActivityServiceStatusChangedEvent; -export type AccountActivityServiceAllowedEvents = +export type AllowedEvents = | AccountsControllerSelectedAccountChangeEvent | BackendWebSocketServiceConnectionStateChangedEvent; -export type AccountActivityServiceMessenger = RestrictedMessenger< +export type AccountActivityServiceMessenger = Messenger< typeof SERVICE_NAME, - AccountActivityServiceActions | AccountActivityServiceAllowedActions, - AccountActivityServiceEvents | AccountActivityServiceAllowedEvents, - AccountActivityServiceAllowedActions['type'], - AccountActivityServiceAllowedEvents['type'] + AccountActivityServiceActions | AllowedActions, + AccountActivityServiceEvents | AllowedEvents >; // ============================================================================= diff --git a/packages/core-backend/src/BackendWebSocketService.test.ts b/packages/core-backend/src/BackendWebSocketService.test.ts index d2281b72a51..b990d87c748 100644 --- a/packages/core-backend/src/BackendWebSocketService.test.ts +++ b/packages/core-backend/src/BackendWebSocketService.test.ts @@ -1,4 +1,10 @@ -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { BackendWebSocketService, @@ -6,10 +12,6 @@ import { WebSocketState, type BackendWebSocketServiceOptions, type BackendWebSocketServiceMessenger, - type BackendWebSocketServiceActions, - type BackendWebSocketServiceAllowedActions, - type BackendWebSocketServiceEvents, - type BackendWebSocketServiceAllowedEvents, } from './BackendWebSocketService'; import { flushPromises } from '../../../tests/helpers'; @@ -20,6 +22,18 @@ import { flushPromises } from '../../../tests/helpers'; // Type for global object with WebSocket mock type GlobalWithWebSocket = typeof global & { lastWebSocket: MockWebSocket }; +type AllBackendWebSocketServiceActions = + MessengerActions; + +type AllBackendWebSocketServiceEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllBackendWebSocketServiceActions, + AllBackendWebSocketServiceEvents +>; + // ===================================================== // MOCK WEBSOCKET CLASS // ===================================================== @@ -163,6 +177,17 @@ class MockWebSocket extends EventTarget { // TEST UTILITIES & MOCKS // ===================================================== +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Creates a real messenger with registered mock actions for testing * Each call creates a completely independent messenger to ensure test isolation @@ -171,19 +196,25 @@ class MockWebSocket extends EventTarget { */ const getMessenger = () => { // Create a unique root messenger for each test - const rootMessenger = new Messenger< - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents - >(); - const messenger = rootMessenger.getRestricted({ - name: 'BackendWebSocketService', - allowedActions: ['AuthenticationController:getBearerToken'], - allowedEvents: [ + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< + 'BackendWebSocketService', + AllBackendWebSocketServiceActions, + AllBackendWebSocketServiceEvents, + RootMessenger + >({ + namespace: 'BackendWebSocketService', + parent: rootMessenger, + }); + rootMessenger.delegate({ + actions: ['AuthenticationController:getBearerToken'], + events: [ 'AuthenticationController:stateChange', 'KeyringController:lock', 'KeyringController:unlock', ], - }) as unknown as BackendWebSocketServiceMessenger; + messenger, + }); // Create mock action handlers const mockGetBearerToken = jest.fn().mockResolvedValue('valid-default-token'); @@ -252,10 +283,7 @@ type TestSetupOptions = { type TestSetup = { service: BackendWebSocketService; messenger: BackendWebSocketServiceMessenger; - rootMessenger: Messenger< - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getBearerToken: jest.Mock; }; @@ -270,10 +298,7 @@ type TestSetup = { type WithServiceCallback = (payload: { service: BackendWebSocketService; messenger: BackendWebSocketServiceMessenger; - rootMessenger: Messenger< - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getBearerToken: jest.Mock; }; diff --git a/packages/core-backend/src/BackendWebSocketService.ts b/packages/core-backend/src/BackendWebSocketService.ts index 9a1a98297c8..5991ad4b85c 100644 --- a/packages/core-backend/src/BackendWebSocketService.ts +++ b/packages/core-backend/src/BackendWebSocketService.ts @@ -1,9 +1,9 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { TraceCallback } from '@metamask/controller-utils'; import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { AuthenticationController } from '@metamask/profile-sync-controller'; import { getErrorMessage } from '@metamask/utils'; import { v4 as uuidV4 } from 'uuid'; @@ -226,29 +226,27 @@ export type WebSocketConnectionInfo = { export type BackendWebSocketServiceActions = BackendWebSocketServiceMethodActions; -export type BackendWebSocketServiceAllowedActions = +type AllowedActions = AuthenticationController.AuthenticationControllerGetBearerToken; -export type BackendWebSocketServiceAllowedEvents = - | AuthenticationController.AuthenticationControllerStateChangeEvent - | KeyringControllerLockEvent - | KeyringControllerUnlockEvent; - // Event types for WebSocket connection state changes export type BackendWebSocketServiceConnectionStateChangedEvent = { type: 'BackendWebSocketService:connectionStateChanged'; payload: [WebSocketConnectionInfo]; }; +type AllowedEvents = + | AuthenticationController.AuthenticationControllerStateChangeEvent + | KeyringControllerLockEvent + | KeyringControllerUnlockEvent; + export type BackendWebSocketServiceEvents = BackendWebSocketServiceConnectionStateChangedEvent; -export type BackendWebSocketServiceMessenger = RestrictedMessenger< +export type BackendWebSocketServiceMessenger = Messenger< typeof SERVICE_NAME, - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents, - BackendWebSocketServiceAllowedActions['type'], - BackendWebSocketServiceAllowedEvents['type'] + BackendWebSocketServiceActions | AllowedActions, + BackendWebSocketServiceEvents | AllowedEvents >; /** diff --git a/packages/core-backend/src/index.ts b/packages/core-backend/src/index.ts index 4831e4569f2..e77bc517a75 100644 --- a/packages/core-backend/src/index.ts +++ b/packages/core-backend/src/index.ts @@ -19,8 +19,6 @@ export type { WebSocketConnectionInfo, WebSocketSubscription, BackendWebSocketServiceActions, - BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceAllowedEvents, BackendWebSocketServiceMessenger, BackendWebSocketServiceEvents, BackendWebSocketServiceConnectionStateChangedEvent, @@ -34,8 +32,6 @@ export type { SubscriptionOptions, AccountActivityServiceOptions, AccountActivityServiceActions, - AccountActivityServiceAllowedActions, - AccountActivityServiceAllowedEvents, AccountActivityServiceTransactionUpdatedEvent, AccountActivityServiceBalanceUpdatedEvent, AccountActivityServiceSubscriptionErrorEvent, @@ -43,8 +39,4 @@ export type { AccountActivityServiceEvents, AccountActivityServiceMessenger, } from './AccountActivityService'; -export { - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS, -} from './AccountActivityService'; export { AccountActivityService } from './AccountActivityService'; diff --git a/packages/core-backend/tsconfig.build.json b/packages/core-backend/tsconfig.build.json index 4f4a385017b..e386c7a5ecc 100644 --- a/packages/core-backend/tsconfig.build.json +++ b/packages/core-backend/tsconfig.build.json @@ -7,9 +7,9 @@ }, "references": [ { "path": "../accounts-controller/tsconfig.build.json" }, - { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" }, { "path": "../profile-sync-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/core-backend/tsconfig.json b/packages/core-backend/tsconfig.json index 6d40debda24..b44a91d630c 100644 --- a/packages/core-backend/tsconfig.json +++ b/packages/core-backend/tsconfig.json @@ -9,15 +9,15 @@ { "path": "../accounts-controller" }, - { - "path": "../base-controller" - }, { "path": "../controller-utils" }, { "path": "../keyring-controller" }, + { + "path": "../messenger" + }, { "path": "../profile-sync-controller" } diff --git a/yarn.lock b/yarn.lock index cc1e050c145..12cc42dddd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3090,9 +3090,9 @@ __metadata: dependencies: "@metamask/accounts-controller": "npm:^33.1.1" "@metamask/auto-changelog": "npm:^3.4.4" - "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/keyring-controller": "npm:^23.1.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/profile-sync-controller": "npm:^25.1.1" "@metamask/utils": "npm:^11.8.1" "@ts-bridge/cli": "npm:^0.6.1"